- 投稿日:2020-05-18T23:21:32+09:00
【Swift】Optional型を安全に扱う5つの方法
Optional型とは
nilを代入できる型のこと。
let a: Int? = Int("42") // Optionalで宣言 let b: Optional<Int> = Int("42") // 上と同義ただし他の型の変数に代入したりする場合、unwrapという処理が必要になる。
その際、nilが代入されている可能性があるので慎重に扱わなければなりません。
ここではOptional型を安全にunwrapして扱うための5つの方法をまとめました。1. Force Unwrapping
変数の中身がなんであれとにかくunwrapする。
もしnilだった場合はruntime errorとなってしまうため、nilでないことが確かな場合に使うと良い。
"Unconditional Unwrapping"とも言うらしい。let myOptioal: String? // Optional<String>で宣言 myOptional = "Angela" // 文字列を代入 print(myOptional!) // "Angela"が出力されるlet myOptioal: String? // Optional<String>で宣言 myOptional = nil // nilを代入 print(myOptional!) // runtime errorとなる2. Check for nil value
if statementでunwrapしようとしている変数の中身がnilかどうか判定し、trueの場合の処理でunwrapをする。
let myOptioal: String? // Optional<String>で宣言 myOptional = nil // nilを代入 if myOptional != nil { print(myOptional!) // nilでない場合のみunwrapするので安全 } else { print("myOptional was found to be nil") // nilだった場合はこちらの処理が実行される }unwrapする前にnilかどうか判定するので安全な一方で、毎回unwrapしなくてはならないので面倒。そんなときは次の"Optional Binding"が有効。
let myOptioal: String? // Optional<String>で宣言 myOptional = nil // nilを代入 if myOptional != nil { let text1: String = myOptional! let text2: String = myOptional! let text3: String = myOptional! // 毎回unwrapする必要がある } else { print("myOptional was found to be nil") }3. Optional Binding
if statementで新たな定数にOptional型の変数を代入する。nilでない場合は
if let safeOptional = myOptional
の判定はtrueになり、nilの場合は判定の結果がfalseになる。ブロックの中では非OptionalのString型であるsafeOptional
が使えるのでunwrapは不要となるので便利。let myOptioal: String? // Optional<String>で宣言 myOptional = nil // nilを代入 if let safeOptional = myOptional { // myOptionalがnilでない場合はtrueになりここの処理が実行される let text1: String = safeOptional let text2: String = safeOptional let text3: String = safeOptional //条件の箇所で宣言したsafeOptionalは非OptionalのStringなのでunwrapは不要 } else { // myOptionalがnilの場合はfalseになりここの処理が実行される print("myOptional was found to be nil") }4. Nil Coalescing Operator
Optional型の変数の中身がnilの場合にデフォルト値を与えたい場合に有効です。
COALESCE
といえばSQLの関数にもありますね。"NULL以外の最初の引数を返す"というものです。
SwiftのNil Coalescing Operator
という演算子も同様の考え方が使えます。optional ?? defaultValue // optionalがnilでない場合はoptinalを、nilの場合はdefaultValueを返すlet myOptioal: String? // Optional<String>で宣言 myOptional = "Angela" // 文字列を代入 let text: String = myOptional ?? "I am the default value" print(text) // "Angela"が出力されるlet myOptioal: String? // Optional<String>で宣言 myOptional = nil // nilを代入 let text: String = myOptional ?? "I am the default value" print(text) // "I am the default value"が出力される5. Optional Chaining
これまではOptional型のStringなどを見てきましたがstructのインスタンスの場合はどうでしょうか。Optional型のユーザー定義のstructを格納する変数や定数が宣言されている場合にも安全にunwrapしたいです。これにはまた別の方法が用意されています。
struct MyOptional { var property = 123 func method() { print("I am the struct's method.") } } let myOptional: MyOptional? // Optional<MyOptional>で宣言 myOptional = MyOptional() // MyOptionalのインスタンスを代入 print(myOptional?.property) // 123が出力される myOptional?.method() // "I am the struct's method."が出力されるstruct MyOptional { var property = 123 func method() { print("I am the struct's method.") } } let myOptional: MyOptional? // Optional<MyOptional>で宣言 myOptional = nil // nilを代入 print(myOptional?.property) // myOptionalはnilなのでpropertyにはアクセスしない myOptional?.method() // myOptionalはnilなのでmethodは実行しないまとめ
Optional型はSwiftの便利な点であると同時に混乱することが多い箇所だと思うので、これらを状況によって使い分けることが大事ですね。
参考
https://developer.apple.com/documentation/swift/optional
https://www.udemy.com/course/ios-13-app-development-bootcamp/
- 投稿日:2020-05-18T19:07:54+09:00
【Xcode】差分(ファイル名の横のMとかRとか)が表示されない
- 投稿日:2020-05-18T15:24:34+09:00
[AndroidStudio][iOS] Flutterデバッグ中にHDの容量が食われてMacが落ちる問題について
発生した問題
Android StudioでFlutterアプリを実装中、iOSを実機に繋いでデバッグしていたらなんかのタイミングでMacのHDをめちゃくちゃ食うようになってしまいにはMacが落ちるという問題が発生した。
Macを再起動後、ゴミ箱にRecoverd filesが作成され大量の(A Document Being Saved By xcdevice)が。。。発生したバージョン
- Mac: macOS Catalina 10.15.4
- XCode: 11.4(11E146)
- AndroidStudio: 3.6.3
- Flutter: 1.17.0
- Dart: 2.8.1
解決策
以下のURLにたどり着いた。
https://apple.stackexchange.com/questions/387634/temporary-dyld-shared-cache-taking-a-lot-of-spaceXCodeを起動し、メニューの「Window」→「Devices and Simulators」をクリック。
接続しているiPhoneを選択し「Copying cache files from device」が表示されてるのでそのプロセスが完了すれば発生しなくなる。試してないが、XCodeをアップデートしたら治るのかも。。
- 投稿日:2020-05-18T12:20:12+09:00
[iOS] UIWebView APIが含まれているライブラリを調べる方法
前置き
本記事執筆時点の2020年5月現在、
新規アプリはUIWebView APIが含まれているとすでに受付停止されており、
アップデートは2020年12月から受付停止です。参考リンク:
[iOS] UIWebViewがいよいよヤバいらしい("ITMS-90809: Deprecated API Usage"メールが届いた件)
[速報] [iOS] UIWebViewが使えなくなる最終期限が告知されました使用ライブラリの中にUIWebView APIが含まれているかどうかの調査、
特に、手動で導入しているサードパーティ製ライブラリ(すなわちGitHub等でPublicな情報が出ていないもの)の調査は少々面倒かと思います。以下、私たちのチームでUIWebView APIが含まれているライブラリを調査した方法をシェアします
本題
Macのターミナルで、以下のコマンドを実行
nm {ライブラリ導入フォルダ}/{ライブラリ名}.framwork/{ライブラリ名} | grep UIWeb
および(または)
nm {ライブラリ導入フォルダ}/{ライブラリ名}.a | grep UIWeb
そうすると、該当ライブラリのシンボル内にUIWebViewに関連する箇所がある場合には以下の様に出力されます。
U OBJC_CLASS$_UIWebView
UIWebView APIが含まれていなければ、何も出力されません。
以上、簡単ですが、これから同様の調査をされる方に、何かの参考になればと思います。
- 投稿日:2020-05-18T10:08:30+09:00
【Alamofire】Module 'Alamofire' has no member named 'request' というエラーが出る
Alamofire.request(route).responseJSONDecodable { response in }これを書いて普通にAlamofireを使おうと思ったら
Module 'Alamofire' has no member named 'request'そんなん無いよと怒られました。
Alamofireは5系からの変更に注意
Alamofireは5系からメソッドの命名とかが結構変わっています。
こういう変更は勘弁してもらいたい。基本的に「Alamofire」と記述していた部分は「AF」に変わったみたいです。
上記の例だとAF.request(route).responseJSONDecodable { response in }こうすれば動くようになります。
- 投稿日:2020-05-18T03:14:53+09:00
【iOS】iOSアプリ開発入門~ レイアウト編2~
前回はStoryBoard上にボタンを配置する方法、ボタンの色変更方法について投稿しました。
レイアウト編1:https://qiita.com/euJcIKfcqwnzDui/items/ba37adae94e07c89d404今回はUIの配置やサイズの調整方法について解説していきます。
AutoLayoutとは
iOSアプリのUIの配置、サイズ指定には必ずといっていいほどAutoLayoutというXcodeならではの概念を使う必要があります。
ではAutoLayoutとは何でしょうか。
iPhone、iPadのを想像してもらえるとわかりますが機種によって画面の大きさが異なります。
参考:https://qiita.com/tomohisaota/items/f8857d01f328e34fb551この多岐にわたる画面サイズに適応させるための仕組みをAutoLayoutと言います。
何を言っているかイメージしづらいかと思います。
AutoLayoutで組んだレイアウトを一度シミュレータで見てみましょう。
前回使用したプロジェクトでMain.storyboardを開いてください。まず差をわかりやすくするためにstoryboard上でのViewサイズを変更します。
(Viewとは今白く表示されている端末画面のような部分ととりあえず認識してもらえればいいです。)
storyboardの下の方にある[View as:iPhone〜〜]となっている部分を選択し[Device]を選択してください。
すると端末の種類をプルダウン形式で選択できます。
これはstoryboard上でViewサイズを変更するメニューです。
今回は[iPhone 11 Pro]を選択してください。ViewのサイズがiPhone 11 Proのサイズになります。次に以下のように配置されているボタンをViewの右上に配置してください。
ドラッグアンドドロップで移動させることができます。
この状態でiPhone 11 Proのシミュレータで実行します。
(シミュレータの選択方法は以前説明しているので参照してください。)
ボタンの位置はどうでしょう?
storyboardと同じ位置に配置されています。
今度はこのままiPhone 11 Pro Maxのシミュレータで実行してください。
どうでしょうか?
ボタン右側に余白ができてしまったかと思います。
原因はiPhone 11 ProとiPhone 11 Pro Maxの端末サイズが異なるためです。iOSの座標系は画面の左上を原点として取り、右方向にX軸の正、下方向にY軸の正を取ります。
またその単位はdpです。
この座標の1dpあたりの大きさは端末毎に変わらず一定です。
AutoLayoutを使用しなければUIは配置された座標に表示されます。
この差により右側に余白ができてしまいます。
この差分を吸収するためにAutoLayoutを使用します。
AutoLayoutを設定する
それではAutoLayoutを使って右上にボタンを配置していきます。
ボタンを選択した状態で下の方にあるアイコンのうち真ん中のアイコンを選択します。
先程Viewサイズを変更したあたりの右の方です。
選択するとメニューが表示されます。
まずメニューの一番上に上下左右に数字が出ています。
これは上下左右にどれだけのマージンを取るかという設定です。
とりあえず右上に寄せたいので上と右に0と入力します。
真ん中あたりのマークが濃い赤にならなければマークをクリックして濃い赤に変えてください。
その後[Add 2 Constraints]をクリックします。
するとボタンの上と右に0dpのマージンが設定されます。
このようなUI設定するマージン等のことを制約(Constraint)と呼びます。
それではこの状態でiPhone 11 Pro、iPhone 11 Pro Maxでそれぞれ実行してください。
機種に依存せずボタンが右上に表示になったかと思います。この追加した制約はUIのsrotyboardに追加されています。
画面左の階層構造に
Button.top = Safe Area.top
とあります。
Safe Areaは少し難しいかもしれないのでとりあえずは大元となるビュー、画面自体というように認識しておいてください。
Buttonというのは配置しているボタンのことです。ボタンの名前を変えてあげると自動的に反映されます。
topというのはそのまま上部という意味です。
つまりこの制約は「Safe Areaとボタンの上部が同じ位置にある」ということを示します。
【補足】
- UIのリネーム方法:UIを選択した状態でもう一度クリックする
- 制約の意味
- top:上
- bottom:下
- leading:左
- trailing:右
設定したマージンを変更することもできます。一度試してみましょう。
ボタンのtopマージンを選択するとInspectorにtopマージンの設定が表示されます。
Attribute InspectorのConstraintの値が0になっています。
これが現在設定しているtopマージンの値です。
これを100に変更してみます。
topマージンに100dpが設定され、ボタンが100dp下方向に動いたかと思います。このようにして制約を追加し、端末間の差分をなくすための機能がAuto Layoutです。
複数のUIを配置しにマージンを設定する
実際のアプリではこのようなボタン1つではなく様々なUIが配置され、その分制約が増えAuto Layoutの設定は複雑になります。
こればかりは慣れるしかありません。
一度練習としてもう一つUIを配置してみます。Text Fieldを追加します。
(このテキストフィールドはキーボードから文字を入力するためのUIです)ボタンを追加したときのように[+]ボタンからUIのメニューを開きます。
[Text Field]を選択しビューの適当な位置にドラッグアンドドロップします。
するとビューにText Fieldが追加されました。
説明した通りこの状態では端末毎に位置が異なります。
制約を追加してあげましょう。
今回はボタンと同じ高さに左寄せで配置してあげます。
同様の手順でテキストフィールドを選択し、leadingマージン:0, topマージン:100と設定してあげます。
これでボタンと同じ高さにテキストフィールドを配置することができました。
ですが配置されたテキストフィールドを見ると小さくなっていませんか?
制約を追加すると自動的にサイズ調整がされるようです。
それではサイズの制約を追加してあげましょう。
制約追加のメニューに[Width]、[Height]の項目があります。
これらにチェックを入れ任意のサイズを設定します。
これでテキストフィールドのサイズ指定ができました。
もちろん同様の方法でボタン等他のUIでもサイズの制約を追加することができます。これで複数のUIを追加することができましたが、考えてみてください。
今回テキストフィールドの位置はボタンの位置と合わせるためtopマージンに100を設定しました。
ではもしボタンの位置を調整したいとなったときテキストフィールドのtopマージンも合わせてあげる必要があります。
今は2つなのでそれほど問題にはなりませんが、それが5個、10個となるとそれだけ手間になりますし修正漏れが発生する可能性があります。そこでテキストフィールドの高さを相対的に指定してあげます。
一度テキストフィールドのtopマージンを削除してください。topマージンを選択しキーボードからバックスペースを入力するだけです。
下図のようにテキストフィールドが赤くなっていればOKです。
余談ですがこの赤くなっている状態はAuto Layoutのエラーです。話が逸れるので次項で簡単に説明します。
次にcommandキーを押しながらボタンとテキストフィールドを選択します。
下の方にあるタブの右から4つ目を選択します。(先程制約を追加する際に選択したタブの左側)
このメニューからは選択した2つ以上のUIに相対的な制約を追加することができます。
今回はtopマージンを相対的に指定したいので[Top Edges]にチェックを入れ、0を入力し[Add 1 Constraint]を選択します。
これは選択された複数のUI間のtop間の差は0dpです、という制約です。
追加するとAuto Layoutのエラーが消えボタンとテキストフィールドのtopが等しくなりました。
ではこれで本当に相対的な制約を追加できたのか確認します。
前項で説明した手順でボタンのtopマージンを変更してください。どうですか?
一緒にテキストフィールドの位置も変わったのではないでしょうか?このようにして複数のUI間に制約を与えていき複雑なUIを組み上げていきます。
そのためそれぞれのUIが影響しあい、1つでも制約を間違えていると大きくレイアウトが崩れたりエラーが発生したりします。
本項冒頭でも言いましたがこれは慣れていくしかありません。
トライアンドエラーで頑張ってください。Auto Layoutのエラー
前項の途中でAuto Layoutのエラーが発生しました。
これについて簡単に解説します。
このエラーはtopマージンを削除したときに発生しました。
上の方に赤い矢印が出ていますがこれをクリックするとエラーの内容を確認できます。
エラー内容は以下です。
Need constraints for: Y position
Y座標に対する制約が必要ですよとのこと。Auto Layoutでは1つでも制約を追加すると完璧に位置を決定できるように制約を追加してあげる必要があります。
削除前はビューからマージンとしてtop:100dp、leading:0dpと指定していました。
その状態だと(x,y) = (0, 100)の位置に表示すればいい、ということがInterface Builderが認識してくれます。
ですが削除すると(x,y) = (0,?)という状態になりInterface Builderが理解できなくなったためエラーが発生しました。その後ボタンとテキストフィールドの間に制約を設定したため
ボタンとビューのtopマージン:100
=> ボタンとテキストフィールドのY座標の差:0
=> テキストフィールドのY座標:100
という流れでInterface Builderが理解できるようになりエラーがなくなったというわけです。エラーが発生したら足りてない制約はないか?どうすればInterface Builderが計算できるようになるかを考えていく必要があります。
最後に
今回は端末間の表示差をなくすためのAuto Layoutについて説明しました。
何度か言ってますがこれは習うより慣れろです。筋トレのようなものだと思って諦めてください。
Auto Layoutはアプリ開発の第一関門です。覚えてしまえば格段に楽になります。とはいえAuto Layoutの内容は量が多く、この連載だけでは説明しきれない部分が多くあります。
本を買って勉強するなりして身につけていきましょう。以上で今回の説明は終わりです。
次回はUIとSwiftの接続方法について説明してます。
SwiftとUIの接続編1:https://qiita.com/euJcIKfcqwnzDui/items/f794ee8b996e6d650027本連載ではプログラミング未経験からiOSアプリ開発が行えるようになることを目的としています。
今までの投稿をまとめていますのでこちらもご覧ください。
アジェンダ:https://qiita.com/euJcIKfcqwnzDui/items/0b480e96166e88945684
- 投稿日:2020-05-18T01:43:25+09:00
[Swift][Eureka]"onCellSelection"でButtonRowは怖くない
UIを実装するためのライブラリEurekaにはrowそのものがbuttonになっているButtonRowというものがあります。
onCellSelectionを使えばButtonRowに好きな処理を持たせることができます。<<< ButtonRow(){ $0.onCellSelection{_,_ in //ここに任意の処理を書く } }これでできます。
- 投稿日:2020-05-18T01:39:46+09:00
【Swift】RxSwiftでボタンのタップイベントを拾う
RxSwift
はボタンのタップイベントとTableView
CollecrionView
の実装が格段に楽になるので大好きです。
今回はボタンのタップイベントの実装をいくつかご紹介します。subscribe(_ on:)を使う
subscribe(_ on:)の説明
Subscribes an event handler to an observable sequence.
- parameter on: Action to invoke for each event in the observable sequence.
- returns: Subscription object used to unsubscribe from the observable sequence.
つまり監視できるもののイベントを拾えるのです。
これを使ってButton
のタップイベントを拾うとこんな感じになります。button.rx.tap.subscribe({ [weak self] _ in // ボタンタップでキックしたいアクションを記述 }).disposed(by: disposeBag)
[weak self]
は[unowned self]
でももちろんOKですが、私は管理が面倒なので[weak self]
を使うことが多いです。
循環参照一度起こすと結構調査が面倒だし、平気でクラッシュするから脳筋weak
しちゃうんですよね。。[weak self]と[unowned self]についてはこちらの記事が面白かったです
bind(to observers:)を使う
私は普段この実装方法を取り入れていることが多いです。
private func setButton() { button.rx.tap.bind(to: buttonTapBinder).disposed(by: disposeBag) } private var buttonTapBinder: Binder<()> { return Binder(self) { base, _ in base.button.isSelected = !base.button.isSelected } }
Binder
を使用しているので、循環参照は自ずと防がれるので結構使ってます。
subscribe
の実装もそうですが、どちらもボタンのタップだけではなく、他のイベントの監視もできるのでぜひ応用してみてください!RxSwiftに関する記事