Swiftのprotocolおぼえがき

connpass.com

「Swift実践入門」刊行記念 Tech Talks #swiftjn を聞きに行って、知らないこといっぱいありそうだったので、この本で学んだことをおぼえがきとして残しておきます。

本の裏にも書いてあることを引用しますが、「Swiftは簡潔な言語ですが、その言語仕様を理解し、正しく使うことはけっして容易ではありません。」という言葉、まさしくその通りって感じてます。

『Swift実践入門』の著者の石川さんも言ってましたが、

大事なことです。

せっかくSwiftで書くならSwiftらしいコードで書けるようになりたい。そのためのインプットとして「Swift実践入門」を教科書代わりに使ってみようと思います。

Swift実践入門 ── 直感的な文法と安全性を兼ね備えた言語 (WEB+DB PRESS plus)

Swift実践入門 ── 直感的な文法と安全性を兼ね備えた言語 (WEB+DB PRESS plus)

めんどくさがりやなので、14章から読み始めればOK問題ない、と思ってたけど、いざ読んでみるとしっかり理解していない箇所が結構ありそうなので、とりあえず第7章のprotocolから読みつつ、playgroundで書いたりして遊んでみます。

protocolの基本

ぼんやり知ってたことの復習。ストアドプロパティとかコンピューテッドプロパティという呼び方知らなかった。

protocol SomeProtocol{
    var title:String { get }
    func someMethod(string: String) -> Void
    nonmutating func nonmutatingSomeMethod(string: String) -> Void
    mutating func mutatingSomeMethod(string: String) -> Void
}

struct SomeStruct1 : SomeProtocol {
    //ストアドプロパティ
    internal var title: String
    func someMethod(string: String) {
//        self.title = string //=> エラー
    }
    nonmutating func nonmutatingSomeMethod(string: String) -> Void{
//        self.title = string //=> エラー
    }
    mutating func mutatingSomeMethod(string: String) -> Void{
        self.title = string
    }
}

struct SomeStruct2 : SomeProtocol {
    //コンピューテッドプロパティ
    internal var title: String {
        return "default"
    }
    func someMethod(string: String) {
//        self.title = string //=>エラー
    }
    mutating func mutatingSomeMethod(string: String) -> Void{
//        self.title = string //=>エラー
        print("nasi")
    }
    nonmutating func nonmutatingSomeMethod(string: String) -> Void{
//        self.title = string //=>エラー
    }
}

class SomeClass : SomeProtocol {
    var title:String = ""
    func someMethod(string: String) -> Void{
        self.title = string
    }
    func nonmutatingSomeMethod(string: String) -> Void{
        self.title = string
    }
    func mutatingSomeMethod(string: String) -> Void{
        self.title = string
    }
}

let struct1 = SomeStruct1.init(title: "aaa")
struct1.title
//struct1.title = "hoge" //#=>エラー
//struct1.mutatingSomeMethod(string: "hoge") //#=> struct1をletで生成しているのでエラー
struct1.title


let struct2 = SomeStruct2.init()
struct2.title
//struct2.title = "hoge" //#=> コンピューテッドプロパティなのでエラー

let someClass = SomeClass.init()
someClass.title = "hoge"

associatedtype を使った抽象化

適当なサンプル出したくて消費税とかで書いてみたけど、実装が同じで意味なかった😢

protocol TaxProtocol {
    associatedtype AssociatedType
    var tax: AssociatedType { get }
    func calculate(price: Int) -> AssociatedType
}

// 実装からAssociatedTypeを自動で定義する方法
struct JapaneseTax: TaxProtocol {
    var tax: Float
    func calculate(price: Int) -> Float{
        return floor(Float(price) + Float(price) * self.tax)
    }
}

let japaneseTax = JapaneseTax(tax: 0.08)
japaneseTax.calculate(price: 20100)


// typealiasを使ってAssociatedTypeを定義する方法
struct KoreanTax: TaxProtocol {
    typealias AssociatedType = Float
    var tax: AssociatedType
    func calculate(price: Int) -> AssociatedType{
        return floor(Float(price) + Float(price) * self.tax)
    }
}

let koreanTax = KoreanTax(tax: 10.0)
let taxPrice = koreanTax.calculate(price: 20100)


// ネスト型AssociatedTypeを定義する方法(使いみちどんな時...?)
struct ItarianTax: TaxProtocol {
    struct AssociatedType {}
    
    var tax: AssociatedType
    func calculate(price: Int) -> AssociatedType{
        return AssociatedType()
    }
}

protocol extension によるデフォルト実装 / extension where Self : を使った制約

Tackleって🎣の装備品?をまとめてタックルと言います。

KamipはFishingTackleとBloggerというprotocolを持っているけど、HogeFisherはFishingTackleだけ持っている例。これも適当な思いつきで読みながら進めたので、FishingTackleがwriteDiaryを持っている謎仕様ですが気にしない。HogeとかFooとかで書いたほうがよかったな..。

protocol FishingTackle {
    var rod: String { get }
    var reel: String { get }
    var line: String { get }
    var lure: String { get }
}

protocol Blogger {
    var blogUrl: String { get }
}

extension FishingTackle {
    var list: String {
        return [
            "rod: \(self.rod)",
            "reel: \(self.reel)",
            "line: \(self.line)",
            "lure: \(self.lure)",
        ].joined(separator: "\n")
    }
    
    func printList() -> Void {
        print(self.list)
    }
}

extension FishingTackle where Self : Blogger {
    func writeDiary() -> Void{
        // 日記を書く処理
    }
}

struct Kamip : FishingTackle, Blogger {
    var rod: String
    var reel: String
    var line: String
    var lure: String
    var blogUrl: String
}

let kamip = Kamip(
    rod: "Daiwa presso-ltd ags ml",
    reel: "Daiwa 15luvias 2004",
    line: "SanyoNylon GT-R PINK-SELECTION 2lb",
    lure: "Jackall tearo 1.6g",
    blogUrl: "http://kamip.net/"
)

print(kamip.list)
kamip.printList()
print(kamip.blogUrl)
kamip.writeDiary()


struct HogeFisher: FishingTackle {
    var rod: String
    var reel: String
    var line: String
    var lure: String
}

let hogeFisher = HogeFisher(rod: "", reel: "", line: "", lure: "")
print(hogeFisher.list)
hogeFisher.printList()
//hogeFisher.writeDiary() //#=> Bloggerじゃないのでエラーになる

自分で書くと色々試せるのでとてもよい。

次は

標準ライブラリのプロトコルを使う設計が自然にできたら素敵そうだけど、そもそも、標準ライブラリのプロトコル何があるかInputしないと。 classstruct の違いも理解が足りなそうなので、次は第5章を読もう。ジェネリクスとかは怖いなー怖いなー。

参考

Swift実践入門 ── 直感的な文法と安全性を兼ね備えた言語 (WEB+DB PRESS plus)

Swift実践入門 ── 直感的な文法と安全性を兼ね備えた言語 (WEB+DB PRESS plus)

2017/2/24 #swiftjn 「Swift実践入門」刊行記念 Tech Talks - Togetterまとめ

remoteConfig.fetchWithExpirationDuration の callback の呼び出しタイミングがとても遅い件

FirebaseのremoteConfigを試してたのですが、以下の ...のcallbackが呼ばれるのがとっても遅い。

remoteConfig.fetchWithExpirationDuration(NSTimeInterval(1)) {...}

controllerの処理が終わったらcallbackが動き出す感じ。なんでだろう。

理想は func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) ここでリモートのデータを取得したかったのだけど出来そうもない。普通に自家製APIつくって同期処理のリクエスト作って dispatch_semaphore_wait 使ってやればやりたいことは実現できそうなのだけど、 remoteConfig 使えなくてつらい。

firebase.google.com

dispatch_semaphore_createを使った別スレッドの処理を待つ書き方

同期処理というのかな?並列処理とか苦手だなぁ。

print("hoge-1")
let semaphore = dispatch_semaphore_create(0)
print("hoge-2")
let queue = dispatch_get_main_queue()
print("hoge-3")
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
    print("hoge-4")
    sleep(10)
    print("hoge-5")
    dispatch_semaphore_signal(semaphore)
    print("hoge-6")
})
print("moge-1")
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)
print("moge-2")

結果は

hoge-1
hoge-2
hoge-3
hoge-4
moge-1
hoge-5
hoge-6
moge-2

iOS - 非同期処理dispatch_queueのまとめ - Kamanii Square

bundle install 時に使われるrubyのバージョンがおかしい

gem list

ここに bundler がない場合におかしくなる様子

なので

gem install bundler

を行い、

rbenv rehash

してから再度

bundle install --path vendor/bundle

するといい感じになる

Sierraで rails c すると「uninitialized constant Mysql2::Client::SECURE_CONNECTION (NameError)」

新しいPC(Sierra)で既存Railsアプリケーションを構築中に rails c したら以下のようなエラーがでる

uninitialized constant Mysql2::Client::SECURE_CONNECTION (NameError)

mysqlがインストールされていないと思って brew install mysql して直るだろうと思ってインストールしてみて再度 rails c すると以下のようなエラーがでる

Library not loaded: /usr/local/opt/mysql/lib/libmysqlclient.20.dylib (LoadError)

つらい パニック

しかしggって調べてたらピンときて mysql5.6 が必要なのに mysql5.7 がインストールされてるのが原因っぽい

ので一旦、インストール済みの mysql5.7 をアンインストール brew uninstall mysql してから、mysql5.6 をインストールする brew install homebrew/versions/mysql56

そしてすでに bundle install しているファイルを消す必要がある

私の場合は rm -rf ./vendor/bundle で消える

削除が完了したら再度 bundle install --path vendor/bundle

おういえ。

bundle install したら eventmachine でエラーが起こる

bundle install --path vendor/bundle したときに以下のエラーで困った時

An error occurred while installing eventmachine (1.0.3), and Bundler cannot continue.
Make sure that `gem install eventmachine -v '1.0.3'` succeeds before bundling.

以下を実行して予めビルドオプションを設定しておくとよい。

bundle config build.eventmachine --with-cppflags=-I/usr/local/opt/openssl/include

内容は cat ~/.bundle/config ここに記載される。

xcode7で謎エラー

xibを表示するとなぞのエラーが出ていたのでいろいろ調べた結果、

Failed to update auto layout status. ... the agent crashed

xcodeを再起動したら直った件

『 failed to update auto layout status the agent crashed』とかでググってる人は試してみるといいかも