読者です 読者をやめる 読者になる 読者になる

Swiftで遊ぼう! on Hatena

あしたさぬきblogでやってた初心者オヤジのiOSプログラミング奮闘記がHatenaに来ました

Swiftで遊ぼう! - 704 - Table View 2

さて、TableViewの勉強をしっかりやろうと思っていたのですが...

Developing iOS 9 Apps with Swift - Free Course by Stanford on iTunes U

非常に重要なフレームワークが手に入らないんです(T_T)

Twitter Framework」が何処を探してもありません。これが無いとプロジェクトを動かすことができないんです。

フレームワークの扱い方の勉強はできました。Xcodeを使ってワークプレスの扱いも分かったのですが、非力な私はTwitter frameワークを自力で再現することができません(T_T)

いくつかTwitterを扱うフレームワークが用意されていますが、それをパール先生の講義に使えるようにモディファイする力が無いため、Table Viewの講義はデモを実線することができず、眺めるだけになりました。

眺めていても重要な説明がいくつもあります。

まず、フレームワークで扱うメソッドをどのように実装されているのか理解できませんが、使い方は説明しているので、そこから説明を聞きます。

検索したいTweetを探すためのString型のsearchTextを用意します。

var searchText: String? {
    didSet {
        tweets.removeAll()
        searchForTweets()
        title = searchText
    }
}

tweetsはTweetした内容をArray型の入れ籠のことです。「[Array]()」なので、searchTextに検索文字が入力される度にArrayをリセットして、searchForTweets()メソッドが実行されます。

searchForTweets()メソッドは、フレームワークで重要なメソッドですが、これを利用するために、searchTextを使った「twitterRequest」変数を用意します。

private var twitterRequest: Twitter.Request? {
    if let query = searchText where !query.isEmpty {
        return Twitter.Request(search: query + " -filter:retweets", 
                               count: 100)
    }
    return nil
}

Twitter.Requestも実装できないフレームワークです。このイニシャライザーを使ってリツイートは除外してツイートを100個ダウンロードするリクエストを作ります。

このリクエストを使ってフェッチするので、メインになるsearchForTweets()メソッドは次のようになります。

func searchForTweets() {
    if let request = twitterRequest {
        request.fetchTweets( handler: ([Tweet]) -> Void )
    }
}

「handler: ([Tweet]) -> Void」の所が青くなっています。ここをダブルクリックするとクロージャーに変化します。

func searchForTweets() {
    if let request = twitterRequest {
        request.fetchTweets({ ( [Tweet] ) in
            code
        })
    }
}

このように変化して[Tweet]とcodeが青くなります。そしてこれをトレーリングクロージャに変更してやります。

func searchForTweets() {
    if let request = twitterRequest {
        request.fetchTweets { ( [Tweet] ) in
            code
        }
    }
}

[Tweet]に「newTweets」を与えて、コードを書きます。ここでsearchTweets()メソッドはマルチスレッドでバックグラウンドに入るasyncメソッドなので必ず結果が出るとメインキューに戻す必要があります。

func searchForTweets() {
    if let request = twitterRequest {
        request.fetchTweets { newTweets in
            dispatch_async(dispatch_get_main_queue()) {
                if !newTweets.isEmpty {
                    self.tweets.insert(newTweets, atIndex: 0)
                }
            }
        }
    }
}

ここで問題になるのが「self」です。これだと結果をヒープに残ってしまいます。画面が消えてもメモリーに残るので、いらなくなったら勝手に消えてしまうweakSelfに変更する必要があります。これも基本的な内容なので必ず理解する必要があります。

func searchForTweets() {
    if let request = twitterRequest {
        request.fetchTweets { [ weak weakSelf = self ] newTweets in
            dispatch_async(dispatch_get_main_queue()) {
                if !newTweets.isEmpty {
                    weakSelf?.tweets.insert(newTweets, atIndex: 0)
                }
            }
        }
    }
}

これで完璧! いえいえこれだけじゃ駄目です。このリクエストの実行に時間がかかって、別のリクエストをしてしまっても、古い結果が表示されてしまうというマルチスレッドで基本的な問題の対処をする必要があります。

private var lastTwitterRequest: Twitter.Request?

func searchForTweets() {
    if let request = twitterRequest {
        request.fetchTweets { [ weak weakSelf = self ] newTweets in
            dispatch_async(dispatch_get_main_queue()) {
                if request == weakSelf?.lastTwetterRequest {
                    if !newTweets.isEmpty {
                        weakSelf?.tweets.insert(newTweets, atIndex: 0)
                    }
                }
            }
        }
    }
}

ポール先生の説明は素晴らしいです。実際にデモで確認できないのが辛いのですが、滅茶苦茶重要な話題でした。

今日はここまで。