はじめに
その2の続きです。その2までで Swift で書けると思いますがもっといい感じにかくための応用編です。
プロパティ
プロパティのよく使う小技
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
class ViewController: UIViewController { @IBOutlet weak var tableView: UITableView! { // willSetもある didSet { tableView.delegate = self tableView.dataSource = self } } let label: UILabel = { let label = UILabel() label.textAlignment = .center label.adjustsFontSizeToFitWidth = true return label }() } |
willSet
は使ったことない。
プロトコル
Swift といえばプロトコル!プロトコルを多用すれば Swift っぽい!(はず)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
protocol Fuga { var foo: String { get } func fugafuga() func piyo() } // デフォルト実装 extension Fuga { func piyo() { print("piyo") } } class Hoge: Fuga { var foo: String = "Foo" func fugafuga() { print(foo) } } let hoge = Hoge() hoge.piyo() // piyo hoge.fugafuga() // Foo |
デリゲート
よく使うデリゲート!!!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
/// 弱参照にするために参照型のAnyObjectにする(これないと循環参照を起こすので危険!) protocol HogeDelegate: AnyObject { // 複数のhogeを扱う場合を考慮して第一引数でHogeを渡す func hoge(_ hoge: Hoge, didPiyo piyo: String) func hoge(_ hoge: Hoge, didFoo foo: String) } // デフォルト実装。厳密には違うけどObjective-Cでいうoptionalのような感じにできる extension HogeDelegate { // 空実装 func hoge(_ hoge: Hoge, didFoo foo: String) { } } class Hoge { // weakにしないと循環参照を起こすので危険! weak var delegate: HogeDelegate? func piyo() { delegate?.hoge(self, didPiyo: "piyo") } func foo() { delegate?.hoge(self, didFoo: "foo") } } class Fuga { let hoge = Hoge() init() { hoge.delegate = self } } // extensionで分けると見やすいしSwiftっぽい extension Fuga: HogeDelegate { func hoge(_ hoge: Hoge, didPiyo piyo: String) { print(piyo) } } |
同値比較
1 2 3 4 5 6 7 |
struct Hoge: Equatable { let piyo: String let foo: String } let hoge1 = Hoge(piyo: "p", foo: "f") let hoge2 = Hoge(piyo: "p", foo: "f") |
上記の hoge1
と hoge2
を hoge1 == hoge2
のように比較したい場合プロパティがすべて Equatable
に準拠していれば Equatable
をつけるだけで比較できる
1 2 3 4 5 6 7 8 9 10 |
struct Hoge: Equatable { let piyo: String let foo: String } let hoge1 = Hoge(piyo: "p", foo: "f") let hoge2 = Hoge(piyo: "p", foo: "f") let hoge3 = Hoge(piyo: "pp", foo: "ff") print(hoge1 == hoge2) // true print(hoge1 == hoge3) // false |
これでもいける
1 2 3 4 5 6 7 8 |
struct Hoge { let piyo: String let foo: String } func == (lhs: Hoge, rhs: Hoge) -> Bool { return lhs.piyo == rhs.piyo && lhs.foo == rhs.foo } |
こうでもいい
1 2 3 4 5 6 7 8 |
struct Hoge { static func == (lhs: Hoge, rhs: Hoge) -> Bool { return lhs.piyo == rhs.piyo && lhs.foo == rhs.foo } let piyo: String let foo: String } |
列挙型
CaseIterable
に準拠させれば allCases
全パターンを配列で取得できる。
1 2 3 4 5 |
enum Hoge: CaseIterable { case num1, num2, num3, num4, num5 } Hoge.allCases // [.num1, .num2, num3, .num4, num5] |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
enum Hoge { case foo case fuga case piyo(String) } let hoge = Hoge.piyo("Piyo") switch hoge { case .foo, .fuga: break case .piyo(let text): print(text) } |
上記のように piyo
の String
を得たい場合は下記のように書ける。
1 2 3 |
if case .piyo(let text) = hoge { print(text) } |
カスタムイニシャライザ!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
enum Hoge { case foo case piyo init?(value: String) { if value.contains("piyo") { self = .piyo return } if value.contains("foo") { self = .foo return } return nil } } let hoge1 = Hoge(value: "ApiyoAA") // Optional(Hoge.piyo) let hoge2 = Hoge(value: "AfooAA") // Optional(Hoge.foo) let hoge3 = Hoge(value: "AAA") // nil |
カスタムクラスのプリント
下記のようにカスタムクラスを作ってプリントするとほぼ情報がない。。。
1 2 3 4 5 6 7 8 9 |
class Hoge { private let piyo: String init(piyo: String) { self.piyo = piyo } } let hoge = Hoge(piyo: "fuga") print(hoge) // <モジュール名>.Hoge |
CustomStringConvertible
プロトコルを適用することによりいい感じにプリントできる。
1 2 3 4 5 6 7 8 |
extension Hoge: CustomStringConvertible { var description: String { return "piyo: \(piyo)" } } let hoge = Hoge(piyo: "fuga") print(hoge) // piyo: fuga |
エラー
独自のエラーを定義する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
enum HogeError: Error { case foo case piyo } extension HogeError: LocalizedError { var errorDescription: String? { switch self { case .foo: return "Fooです" case .piyo: return "Piyoです" } } } func hoge(_ text: String) throws { if text.lowercased().contains("foo") { throw HogeError.foo } if text.lowercased().contains("piyo") { throw HogeError.piyo } print(text) } do { try hoge("AAFOOA") } catch { // fooエラーでこっち通る print(error.localizedDescription) // Fooです } do { // エラーにならない try hoge("AAA") // AAA } catch { print(error.localizedDescription) } |
recoverySuggestion
とかもあるらしい。使ったことはない。。。
参考:Errorをいい感じにUIAlertControllerで表示する
Codable
JSON データをいい感じに扱ってくれる Codable
!!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
struct Hoge: Codable { let resultCode: Int let resultMessage: String let piyo: String let fuga: Int } extension Hoge: CustomStringConvertible { var description: String { return "resultCode: \(resultCode), resultMessage: \(resultMessage), piyo: \(piyo), fuga: \(fuga)" } } let json = """ {"resultCode": 0, "resultMessage": "OK", "piyo": "PiyoPiyo", "fuga": 10} """ let jsonData = json.data(using: .utf8) let hoge = try! JSONDecoder().decode(Hoge.self, from: jsonData!) print(hoge) // resultCode: 0, resultMessage: OK, piyo: PiyoPiyo, fuga: 10 let data = try! JSONEncoder().encode(hoge) print(String(data: data, encoding: .utf8)!) // {"resultCode":0,"piyo":"PiyoPiyo","resultMessage":"OK","fuga":10} |
共通ぽい result
をわけてみる
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
struct Hoge: Codable { let result: HogeResult let piyo: String let fuga: Int init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) self.piyo = try values.decode(String.self, forKey: .piyo) self.fuga = try values.decode(Int.self, forKey: .fuga) self.result = try .init(from: decoder) } } struct HogeResult: Codable { let code: Int let message: String enum CodingKeys: String, CodingKey { case code = "resultCode" case message = "resultMessage" } } let json = """ {"resultCode": 0, "resultMessage": "OK", "piyo": "PiyoPiyo", "fuga": 10} """ let jsonData = json.data(using: .utf8) let hoge = try! JSONDecoder().decode(Hoge.self, from: jsonData!) print(hoge) // resultCode: 0, resultMessage: OK, piyo: PiyoPiyo, fuga: 10 // 戻すときが変わるので注意!! let data = try! JSONEncoder().encode(hoge) print(String(data: data, encoding: .utf8)!) // {"result":{"resultCode":0,"resultMessage":"OK"},"fuga":10,"piyo":"PiyoPiyo"} |
decode
のとこは decodeIfPresent
ってのもあるけどあんま違いわかってない。。。戻り値がオプショナル型かどうか??
キーがスネークケースの場合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
struct Hoge: Codable { let resultCode: Int let resultMessage: String let piyo: String let fuga: Int } extension Hoge: CustomStringConvertible { var description: String { return "resultCode: \(resultCode), resultMessage: \(resultMessage), piyo: \(piyo), fuga: \(fuga)" } } let json = """ {"result_code": 0, "result_message": "OK", "piyo": "PiyoPiyo", "fuga": 10} """ let jsonData = json.data(using: .utf8) let decoder = JSONDecoder() decoder.keyDecodingStrategy = .convertFromSnakeCase // これ! let hoge = try? decoder.decode(Hoge.self, from: jsonData!) print(hoge) // resultCode: 0, resultMessage: OK, piyo: PiyoPiyo, fuga: 10 let encoder = JSONEncoder() encoder.keyEncodingStrategy = .convertToSnakeCase // これ! let data = try! encoder.encode(hoge) print(String(data: data, encoding: .utf8)!) // {"result_code":0,"piyo":"PiyoPiyo","result_message":"OK","fuga":10} |
その他は下記参考
Result
Result
これも便利!すごい見やすくなる気がする。
たまにみかけるこういうメソッド(API リクエストとかで多い)。これどういうときにエラーでどういうときに成功なのかがいまいちわかりにくい! (String に値があれば成功?エラーが nil なら成功?)
1 2 3 4 5 6 7 8 9 |
func hoge(text: String, completion: @escaping (String?, HogeError?) -> Void) { if text.lowercased().contains("foo") { completion(nil, .foo) } if text.lowercased().contains("piyo") { completion(nil, .piyo) } completion(text, nil) } |
Result
を使うとこんな感じで成功・失敗がわかりやすい!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
func hoge(text: String, completion: @escaping (Result<String, HogeError>) -> Void) { if text.lowercased().contains("foo") { completion(.failure(.foo)) } if text.lowercased().contains("piyo") { completion(.failure(.piyo)) } completion(.success(text)) } hoge(text: "Piyo") { result in switch result { case .success(let text): print("Success! text: \(text)") case .failure(let error): print("Failure! error: \(error.localizedDescription)") } } |
さいごに
これで今後 Swift を忘れても1〜3見れば思い出せるはず!(たぶん。。。)
コメント