引き続き絶賛脱線中の、iOS 系のチップス集的なブログ 同じような処理だけどこっちの方がいいよってやつ の続きを眺めていきますね。今回はそのうちの前回に見ていた アイコン
のところの続きから。何気ないようなところにも Swift 的な観点から得られるものって意外とあるのを感じたりしつつ、そんな調子でいつもよりアプリ作りに近いあたりから Swift を観察してみましょう。よろしくお願いしますね。
————————————————————————————————————————————— 熊谷さんのやさしい Swift 勉強会 #297
00:00 開始 00:57 今日はアイコンの話から 02:00 アイコンに共通認識を持てる 03:33 SF Symbols の SF は、San Francisco フォントのこと 04:47 performAccessibilityAudit に見るエラー表現 07:40 さまざまなエラーの表現方法 15:35 エラー表現の方法を組み合わせる機会は少ない印象 17:11 昔によくあったエラー表現 18:18 ソフトエラーとハードエラー 21:43 エラーの重みを表現する 24:25 Swift の表現力を活かしていく 26:03 クロージングと次回の展望 —————————————————————————————————————————————
Transcription & Summarize : 熊谷さんのやさしい Swift 勉強会 #297
はい、始めていきますね。今日は前回に引き続き、「なっぺーさんのブログ」を参考に進めていきます。ブログの内容についてですが、Swiftに関するさまざまな処理方法について、具体例を交えて解説しています。同じような処理でも、こっちのほうが良いというポイントを扱っていて、とても興味深いです。
このブログは主にiOS関連の話題が中心ですが、Swiftの言語仕様にも触れていますので、私たちのSwift勉強会でも取り上げる価値がありますね。しばらく見てきて、残りわずかとなってきました。
前回は、アイコンの扱い方について詳しく見ていきました。どこまで進んだか振り返ると、次に紹介されたコードの書き方をブログの著者がお薦めしている、という内容でした。具体的には、UIイメージ
で独自の画像を用意して表示するよりも、SwiftのSF Symbols
を使うほうがアクセシビリティの面で優れている、という話でした。
システムがあらかじめ提供するアイコン集を使うことで、ユーザーがそのアイコンの意味を知らなくても分かるようになります。これが、SF Symbolsを使う理由の一つです。たとえば、リロードボタンのように、お馴染みのアイコンを先に紹介しましたが、やはり統一されたアイコンを使うほうが利便性が高いということです。
ところで、SF Symbolsの「SF」という略称についてですが、これは「San Francisco」の略です。Appleが提供するフォント「San Francisco」とシームレスに連携するために名付けられています。ただ将来、フォントが別のものに変わった場合にはどうなるのか、という疑問もありますが、現時点では考えなくても良いでしょう。
さて、前回の内容の続きとして、エラーハンドリングについても触れておきたいと思います。Swiftではエラーハンドリング方法がいくつかあります。「throws」を使ってエラーを返す方法、ブール値で真偽を返す方法(成功ならtrue
、失敗ならfalse
)、オプショナル型を使って成功時に値を返し、失敗時にはnil
を返す方法などがあります。
また、エラーハンドリングの中で、「エラーを無視しても良いか」という判定を行うフラグ的な表現もあることを学びました。前回紹介したperformAccessibilityAudit
という機能のインターフェースを使ったエラー処理が、ちょうどその例に当たりますね。
エラーハンドリングについては、すでに勉強会で触れたこともありますが、この機会にもう一度おさらいしておきたいと思います。これで今日の内容は一通りカバーできたと思います。では、次に進めていきましょう。 エラーの話に戻っていきたいと思います。特に強調したいのは、エラーの想定についての話です。昔ながらのよくあるエラー処理方法では、ブール値を返すというものがあります。これは最もオーソドックスというか、レガシーな機能と言えるでしょう。具体的には、アクションが成功したかどうかをブール値で返すというものです。成功したら true
、失敗したら false
といった具合ですね。これはかつてのプログラミング言語でよく使われていた方法です。
アクションを実行して、成功したか失敗したかを判定します。この方法では、警告を表示することはなく、その程度の軽量な処理に適しています。しかし、この方法ではどんなエラーが起こったかについての詳細な情報は分かりません。
詳細なエラーメッセージが欲しくなったときには、エラーハンドリングとしてブール値を返すのではなく、throws
を使ってエラーハンドリングを行います。たとえば、アクションエラーとかを定義し、Error
プロトコルに準拠させます。この中で、さまざまなエラーを定義します。
例えば、case unexpectedError
や case notEnoughMemory
といった感じでエラーを列挙します。これにより、アクションがエラーを返すときに、エラーのタイプを明示的に指定できます。この方法により、エラーが発生する可能性がある箇所に try
をつけて、do
ブロックでエラーハンドリングを行います。もしエラーが発生した場合には、適切な処理を実行するようにします。
例として、以下のようにコードを書きます。
do {
try performAction()
} catch let error as ActionError {
switch error {
case .unexpectedError:
// エラー処理
case .notEnoughMemory:
// エラー処理
}
}
このようにしてエラーを想定したコードを書きます。エラーによってリカバリーを試みるのがエラーハンドリングの特徴です。
さらに、どんなエラーが起ころうとも、その後の処理を続けられない状況であれば、エラーハンドリングを複雑にする必要はありません。この場合は、プロセスを終了させても良いです。例えば、fatalError("Error message here")
で終了させることが可能です。
Swiftでは、オプショナルも使用します。例えば、アクションが正常に処理された場合に整数値が返されるとします。このとき、成功したら値が入り、失敗したら nil
を返すようにします。しかし、失敗時にありえない数字を返すのではなく、明示的に nil
を返すことで安全性を確保します。こうして、どんなエラーが起こるかを想定しつつ、適切に対処することができます。
エラーハンドリングの基本的な考え方は以上です。この方法を使って、実際のコードを書く際に応用してください。 そういった背景から、かつては-1を返すという方法がよく使われていましたが、現在ではオプショナルを使うことが一般的です。値がない場合でもエラーとして扱わず、結果としてnil
が取れるようにするのです。これにより、コードは次のように書けます。「もし値があるなら」という条件で処理を進め、値がなければリカバリー処理を行うことが可能になります。
このようなエラーハンドリングには主に4つのパターンがあり、オプショナルを返す方法、throw
を使ったエラーハンドリング、Throws
を使用するパターン、そして致命的なエラー(Fatal Error)が考えられます。それぞれの方法は独立しており、併用することは一般的ではありません。
エラーの詳細を返しつつ、成功したかどうかをBool
で返す設計は通常見かけません。先ほどの例ですが、エラーハンドリングと組み合わせて使うBool
は、エラーの状況を無視できるかどうかを示しています。これはこれで面白いアプローチですね。
以前の方式では、コマンド実行後にInt
型でエラーコードを返し、成功なら0、失敗なら-1という形でした。これがBool
型へと変わりました。過去には、アクションが失敗した場合にグローバルなエラーメッセージ変数にエラー内容を格納し、処理を続けるというレガシーな手法も存在しました。現在ではこのような方法は基本的には使われません。
エラー判定が無視できるかどうかについても重要です。簡単に言えば、警告(Warning)はエラーといえども無視することが可能ですが、エラーは無視できず処理を進められません。エラーが発生しても処理を継続できる場合は、ソフトエラーと呼びます。一方で、ハードエラーは処理を継続できない、根本的なエラーです。ハードエラーの例としては、物理的損傷が原因となるエラーが挙げられます。
例えば、昔のフロッピーディスクが物理的に壊れると、データが読めなくなります。ハードディスクでもクリオセクターが発生すると書き込みが不可能になります。このような場合は処理を継続することができません。ハードエラーは致命的であり、根本的に無理な状況を意味します。対照的に、ソフトエラーは一時的な障害やノイズ混入、誤操作などで発生し、一定の条件下で処理を継続できるものです。 読み込んだときに何かエラーが発生し、ハードウェアが動作しなかった場合、原因は様々です。例えば、ファイルをオープンしようとしたけれどロックがかかっていて開けなかったということもあります。エラーの種類によっては、特定の時間をおいてから再度試行(リトライ)することが有効です。ネットワーク経由でサーバーと通信していてアクセスが禁止された場合は、これは「ハードエラー」と言えますが、リクエストを送信したけれど応答がない場合は何度かリクエストを再試行するという方法も考えられます。このように、同じエラーでも状況によって「重み」や原因が異なるので、エラー判定が重要になります。
そのような場面でSwiftのAPIが役立ちます。昔のプログラミングでは主にエラーコードを返す方式を取っていましたが、現在ではthrows
を使ってエラーハンドリングを行います。例えば、エラー型としてActionError
を定義し、Error
プロトコルに準拠させます。この型には、ケースごとにエラーコードを持たせることができます。
enum ActionError: Error {
case invalidArgument
case noMemory
// その他のエラーケース
}
具体的なケースとして、invalidArgument
はエラーコード1、noMemory
はエラーコード1000などとします。そして、エラーハンドリングの実装では以下のようなdo-try-catch
構文を使います。
do {
// エラーを発生させる可能性のある処理
} catch ActionError.invalidArgument {
// エラーがinvalidArgumentの場合の処理
} catch ActionError.noMemory {
// エラーがnoMemoryの場合の処理
} catch {
// その他のエラー処理
}
今風のSwiftのエラーハンドリングでは、より詳細なエラー情報を扱うことができ、アクセス制限がかかった場合などの特定の事情を明示できます。このように、細かいエラー情報を伝えることは、エラーハンドリングにおいて重要な要素です。
最後に、SF Symbolsを使う例として、アクセシビリティに対応していることが示されました。詳細なエラー情報の伝達やエラーハンドリングの重要性を再認識しました。このような話題を通じて、新たな学びを得られて良かったと思います。
さて、次回はXcodeを開くコマンドについて引き続き探求していきます。時間になりましたので、今日はこれで終わりにします。お疲れ様でした。ありがとうございました。