Swift Developer中級者への道
正直わからないことしかない初学者だけど、中級者になりたい。
わからないと思ったことを調べて、ここにまとめる。
スレッド
ネットでよく、UIの描画や更新はメインスレッドで行わないといけないってみるけど、メインスレッドで行わなければいけない理由はなんだろう?
※スレッドについての詳しい説明なんかは、ネットを調べた方がたくさん出てくるので割愛。
iOSの並行処理やマルチスレッドの話なんかはmixiの研修資料がすごくわかりやすい。
iOS13以降、メインスレッドではなくバックグラウンドスレッドでUI変更するとアプリがクラッシュし、エラーを吐くらしい。
マルチスレッドの時は、メインスレッドにキューとして入れて対応する。この時に DispatchQueue.main.sync
を使用する。
参考→swift初心者がiOS13対応でメインスレッド以外でUI更新をしてクラッシュさせてしまった話
なるほど、よくみる DispatchQueue.main.sync
等はメインスレッド使用を示すものだったのか。
DispatchQueue.main.sync
と DispatchQueue.main.async
- DispatchQueue.main.sync:同期処理
処理の途中でこれがあると、他の処理がストップされてアプリが止まる(デッドロック) - DispatchQueue.main.async:非同期処理
Observerパターン
デザインパターンの一種で、オブジェクトが状態変化した際に通知させるようにするもの。
protocol PropertyObserver : class {
func willChange(propertyName: String, newPropertyValue: Any?)
func didChange(propertyName: String, oldPropertyValue: Any?)
}
final class TestChambers {
// プロトコルに準拠した変数定義
weak var observer:PropertyObserver?
private let testChamberNumberName = "testChamberNumber"
// newValとoldValは予約語
var testChamberNumber: Int = 0 {
// protocolで定義した関数の中身を実装
willSet(newValue) {
observer?.willChange(propertyName: testChamberNumberName, newPropertyValue: newValue)
}
didSet {
observer?.didChange(propertyName: testChamberNumberName, oldPropertyValue: oldValue)
}
}
}
// こっちはプロトコルの適合の例
final class Observer : PropertyObserver {
func willChange(propertyName: String, newPropertyValue: Any?) {
if newPropertyValue as? Int == 1 {
print("Okay. Look. We both said a lot of things that you're going to regret.")
}
}
func didChange(propertyName: String, oldPropertyValue: Any?) {
if oldPropertyValue as? Int == 0 {
print("Sorry about the mess. I've really let the place go since you killed me.")
}
}
}
var observerInstance = Observer()
var testChambers = TestChambers()
testChambers.observer = observerInstance
testChambers.testChamberNumber += 1
【参考】
StoreKit
App内課金やApp Storeとやり取りするフレームワーク
【できること】
- App内課金
- 広告ネットワークのアトリビューション
広告に金するAppのインストールを検証する - Apple Music
ユーザーがApple Musicを利用可能かチェックし、サブスクリプションを提案する - おすすめとレビュー
サードパーティのコンテンツをおすすめとして表示し、ユーザーがAppの評価とレビューを行えるようにする
クロージャ
スコープ内の変数や定数を保持したひとまとまりのこと。再利用可能。
関数を定義する時にはfuncが必要だが、クロージャにはクロージャ式という定義があり、名前が不要だったり、型推論によって型の記述が省略可能だったり関数よりも軽量化して定義できる*。
書き方
処理が一行しかない場合は return
の省略が可能。
{(引数) -> 戻り値の型 in
処理
}
Closure構文をClosure型として宣言された変数に代入することも可能。
let c: (Int) -> Void
// cへclosureを代入
c = { (a: Int) -> Void in
print(a)
}
c(10)
typealiasを用いたClosureの型宣言
複雑になりがちなClosure構文の可読性を上げるために、typealiasがよく使われる。
typealias 型の名前 = (引数の型,...) -> (返り値の型)
以下、通常のClosure文をtypealiasを使用して書き直した例
// "Int型を引数としてとり、Double型"を返すClosure型を返り値として、Float型の引数aを取る関数func2の定義
let func2: (Float) -> ((Int) -> Double) = { a in
return { (b) -> Double in
return Double(a) + Double(b)
}
}
// func2の使い方 //
let myClosure: (Int) -> Double = func2(10)
let value = myClosure(20) // val = 10 + 20 が代入される
typealias Closure2Type = (Int) -> Double
let func2: (Float) -> Closure2Type = { a in
return { (b) -> Double in
return Double(a) + Double(b)
}
}
// func2の使い方 //
let myClosure: Closure2Type = func2(10)
let value = myClosure(20) // val = 10 + 20 が代入される
Closureオブジェクトをプロパティとして持つ場合の注意点
あるクラスのプロパティとしてClosureオブジェクトを持たせる際に、Closureプロパティからselfを参照すると循環参照に陥る可能性がある。
この循環参照を避けるためには、片方の参照をstring(強参照)からweak(弱参照)に変更する。
class ClosureSample {
var name: String = ""
private(set) var printNameClosure: (() -> ())!
init() {
setup()
}
private func setup() {
printNameClosure = {
print("name = \(self.name)")
}
}
}
参考にあるmixiのこの説明&図がわかりやすい
*https://qiita.com/Howasuto/items/eba4cb4b6d3e6bde0a17
【参考】
Escaping Closure
クロージャをパラメータの1つとして受け取る関数を実行する際に、@escaping
をパラメータTypeの前につける。
つまりクロージャがスコープを抜けても存在し続ける時に、@escaping
が必要になる。
具体例
- クロージャがスコープ外で参照される時
- クロージャを非同期的に実行する時
【参考】
Optional型
Optional型とは何かは他サイトを見れば大体わかる。ここでは疑問に思ったことだけ。
as? String と as String?の違い
let any: Any = "sample" as Any
// any(not Optional)を無理やりString?(Optional)に変換しようとしているためError
let text1 = any as String? // 'Any' is not convertible to 'String?'
// もしanyがStringに変換できなければnilが返る
let text2 = any as? String
// これもOK
let text3 = any as? String?
コメント
通常コメント
/*
* 通常コメント
*/
// 通常コメント
ドキュメントコメント
Quick HelpのDescription欄に出てくるようになる。Markdownも記述可能
/// ドキュメントコメント
ライブラリ関連
- SDWebImage:画像URLをUIImageViewで画像を管理するためのツール。キャッシュをとってこれる。
- VerticalCardSwiper:横スワイプできるカードを作成するライブラリ
- PKHUD:ローディングのグルグル実装
- Alamofire:通信関係
- DTradientButton:ボタンの背景をいい感じにしてくれる
- ChameleonFramework/Swift:色をいい感じにしてくれる
- AVFountdation:音を鳴らすときに必要なライブラリ
- UITextFieldDelegate:キーボードをしまうためのもの(?)
A set of optional methods to manage the editing and validation of text in a text field object.
Delegate
参考
hogehoge.delegate = self
は自分自身(例えばtextField)をViewContorllerに渡しているという意味か。
画面遷移
self.present(hoge, animated: true, completion: nil)
これがわからんと思ったけど、画面遷移する一手段だった。(?)
例) ログインしていなかったらログイン画面に飛ばす処置
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let loginViewController = storyboard.instantiateInitialViewController()
loginViewController.modelPresentationStyrle = .fillScreen
self.present(loginViewController, animated: true, completion: nil)