【Swift】TwitterKitを使ってTwitterクライアントのサンプルを作る その2【4.2】

【Swift】TwitterKitを使ってTwitterクライアントのサンプルを作る その2【4.2】

この記事は、【Swift】TwitterKitを使ってTwitterクライアントのサンプルを作る その1【4.2】の続きとなります。

モールス信号でつぶやける自作Twitterクライアント「TwitMorse」のiOS版を片手間に開発しています。
食わず嫌いしていたxcodeのストーリーボードと格闘しつつ、iOSアプリ開発の自習をしつつ、軽いサンプルをつくったので、動くところまでできたものを紹介します。
なお、途中書き落とした部分があるかと思いますので、うまくいかない部分はコメントで質問してくださると幸いです。



ViewControllerとTimelineViewControllerにコードを追加する

さて、ここで待ちに待ったコードです。

ViewControllerは最初に立ち上がり、Twitter認証を行う画面となります。Twitter認証が済んでいる場合にホームタイムラインへ遷移させる、というロジックもここで書いてしまいます。

ViewController.swift

import UIKit
import TwitterKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // すでにログイン済みか
        if !hasAlreadyTwitterAuth() {
        self.navigationItem.title = "login"
        let logInButton = TWTRLogInButton { (session, error) in
            if let unwrappedSession = session {
                let alert = UIAlertController(title: "Logged In",
                                              message: "User \(unwrappedSession.userName) has logged in",
                    preferredStyle: UIAlertController.Style.alert
                )
                alert.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil))
                //self.present(alert, animated: true, completion: nil)
                self.performSegue(withIdentifier: "timeline", sender: session)
            } else {
                NSLog("Login error: %@", error!.localizedDescription);
            }
        }
        logInButton.center = self.view.center
        self.view.addSubview(logInButton)

        } else {
            // 既にログインしていればtimelineへ
            self.performSegue(withIdentifier: "timeline", sender: nil)
        }
    }

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        let session = sender as? TWTRSession
        let dest = segue.destination as! TimelineViewController
        dest.title = session?.userName
        dest.userId = session?.userID
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    /**
     * Twitter 認証済みかどうか
     */
    func hasAlreadyTwitterAuth() -> Bool {
        if TWTRTwitter.sharedInstance().sessionStore.hasLoggedInUsers() {
            return true
        }
        return false
    }

}

このコードのポイントを少しだけ説明します。

  • let logInButton = TWTRLogInButton以下はTwitter認証用のボタンを定義しています。更にlogInButton.center = self.view.centerとしてあげた後、self.view.addSubview(logInButton)として、プログラムからViewに対してログインボタンを貼り付けております。
    • TWTRLogInButtonはTwitterKitから提供されたものです。これの中身を記述してログイン失敗時の処理などを書きます
  • func hasAlreadyTwitterAuth() -> BoolはDocに記載してあるとおり、既に認証済みかどうかを判別します
    • 既に認証済みであれば”timeline”というidentifierを持つsegueで次の画面に遷移することになります

続いて、TimelineViewControllerにコードを追加。
プロジェクトディレクトリで右クリック、new File → swift fileを選択してTimelineViewControllerと名付けます。

[追記]

右上のこいつidentity inspectorっていうんだね・・・

TimelineViewControllerをTimelineViewと紐付ける必要があります。
Main.storyboardを開き、TimelineViewを選択、右側のペインからidentity inspectorを選び、
Custom ClassにTimelineViewControllerと指定してあげてください

TimelineViewController.swift

import UIKit
import TwitterKit
/**
* - reference
* https://qiita.com/ktanaka117/items/e721b076ceffd182123f ←deprecated
* https://o-tyazuke.hatenablog.com/entry/2016/11/23/123748
*/
class TimelineViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var tableView: UITableView!
var rightBarButton: UIBarButtonItem!
// テーブル表示用のデータソース
var tweets: [TWTRTweet] = [] {
didSet {
tableView.reloadData()
}
}
var prototypeCell: TWTRTweetTableViewCell?
var userId: String?
private let refreshControl = UIRefreshControl()
override func viewDidLoad() {
super.viewDidLoad()
// 投稿ボタン
rightBarButton = UIBarButtonItem(
title: "tweet",
style: .plain,
target: self,
action: #selector(tappedRightBarButton(_:))
)
self.navigationItem.rightBarButtonItem = rightBarButton
tableView = UITableView(frame: self.view.bounds)
tableView.delegate = self
tableView.dataSource = self
tableView.refreshControl = refreshControl
refreshControl.addTarget(self, action: #selector(refresh), for: .valueChanged)
prototypeCell = TWTRTweetTableViewCell(style: .default, reuseIdentifier: "cell")
tableView.register(TWTRTweetTableViewCell.self, forCellReuseIdentifier: "cell")
self.view.addSubview(tableView)
loadTweets()
// バックボタン消しておく
self.navigationItem.hidesBackButton = true
}
func loadTweets(){
TwitterAPI.getHomeTimeline(user: userId, tweets: {
twttrs in
for tweet in twttrs {
print(tweet)
self.tweets.append(tweet)
}
}, error: {
error in
print(error.localizedDescription)
})
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tweets.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! TWTRTweetTableViewCell
let tweet = tweets[indexPath.row]
cell.configure(with: tweet)
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let tweet = tweets[indexPath.row]
prototypeCell?.configure(with: tweet)
if let height: CGFloat = TWTRTweetTableViewCell.height(for: tweet, style: .regular, width: self.view.bounds.width, showingActions: true){
return height
}else{
return tableView.estimatedRowHeight
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
@objc func tappedRightBarButton(_ sender: UIBarButtonItem!){
let composer = TWTRComposer()
composer.show(from: self, completion: {
result in
if (result == TWTRComposerResult.cancelled){
print("tweet composetion cancelled")
}else{
self.loadTweets()
self.tableView.reloadData()
print("sending tweet!!")
}
})
}
@objc func refresh(sender: UIRefreshControl) {
// ここに通信処理などデータフェッチの処理を書く
// データフェッチが終わったらUIRefreshControl.endRefreshing()を呼ぶ必要がある
self.loadTweets()
self.tableView.reloadData()
self.refreshControl.endRefreshing()
}
}

ポイント
– viewDidloadメソッド内のrightBarButton = UIBarButtonItemでNavigationBarの右側につぶやき用モーダルを表示する機能をセットしています
– @objcはSwiftコードをObjective-Cから呼び出せるようにするための修飾子
– action: #selector(tappedRightBarButton(_:)で指定する際に必要
– 他にもっとマシな方法があると思われるが、ググったりした結果これをつけないとメソッドを認識してくれない
– let composer = TWTRComposer()でTwitterKitの投稿モーダルを設定している
– その他RefreshControllerを実装しているが、こいつはまだ動きません・・・(調査中)

と、こんな感じで中途半端な状態ではありますが、とりあえずTwitterKitを使ってのホームタイムライン表示・投稿機能は以上で実装完了。
本来であればStoryBoardからTimelineViewControllerへ参照を追加するべきだと思うのですが、そのあたりのノウハウがないのと、コピペでできるようにするためにコード上でTimelineViewのセルの設定を行っています。

iOS開発初心者なので、誰か教えてください・・・


timeline セグエ遷移の追加

iOSにまだ詳しくないのでセグエ遷移という言い方が正しいのかわかっていません。が、とにかく、認証済みであればTwitterのホームタイムラインを表示する画面に遷移する、という動きを実現したいと思います。
上述してあるTimelineViewControllerに向けて遷移させます。

ViewControllerを選択、controlキーを押しながらTimelineViewへドラッグアンドドロップ

ViewControllerを選択、controlキーを押しながらTimelineViewへドラッグアンドドロップ
Manual SegueのShowを選択。

StoryBoadSegueでIdentifierにtimelineと入力

StoryBoardSegueでIdentifierにtimelineと入力。
この辺は画像を参考に追加していただけると幸いです。

iPhoneをつないで実行してみる

昔は実機デバッグをするのに年間100ドル払ってApple Developer Programに登録する必要がありましたが、いつの間にか実機デバッグが無料でできるようになっていてびっくり。というか、最初からそうしろよ、と心の中でツッコミつつ実行。

ViewControllerが立ち上がり、ログインボタンが動く
ViewControllerが立ち上がり、ログインボタンが動く

ログインボタンを押すとTwitterアプリに遷移

TwitterKitによるTwitter認証
TwitterKitによるTwitter認証が入るので下までスクロールして連携

Twitterアプリがインストールされていない場合の画面が開くが気にしないで認証

Twitterアプリがインストールされていない場合の画面が開くが気にしないで認証。原因は後日調査。

TwitterKitによるホームタイムライン
下にリツイート用のViewが出てしまっているが、後々対応することとして一旦放置。

右上のtweetボタンを押すと出る投稿モーダル
右上のtweetボタンを押すと出る投稿モーダル。TwitterKitにあるComposerを使っている。

なお、ここに書いてあるサンプルコードでは投稿したあとにTableViewを更新する処理を加えていますが、なぜか機能しません。原因は後日調査・・・。

もう一回アプリを立ち上げると、認証が済んでいるのでホームタイムライン画面に遷移してくれる。
この時にホームタイムラインを取得し直すので、投稿した内容「ほげー」がちゃんと投稿されている事を確認。

以上、雑にTwitterクライアントのサンプルを作った感想

依然からAndroidで出しているTwitMorseのiOS版を作りたいと思っていました。ただTwitterクライアントアプリのサンプルが軒並みDeprecatedされていたり、実際に動かすと動かない状態のものも多い、かつ情報が古いなどの問題がありました。
今回はそれらをすべて一個一個調査して、内容を最新のものに置き換えつつとりあえずTwitterKitを使ってTwitterクライアントのサンプルが動くところまで実装してみました。XcodeのUIが一昔まえに比べると変更点があったり、TwitterAPIの仕様変更などがあって普通の日本語のTwitterクライアントサンプルブログなどは軒並み機能していませんでした。
Swiftでのアプリ開発は何度か挑戦したことがありますが、いつもStoryBoardで躓いて結局なぁなあになっていました。
今回はSwiftコード側ですべてUIを追加していくという手法をとったので、そこまでStoryBoardに悩まされませんでしたが、Androidアプリ開発に比べると、文章で説明できない直感的な操作が多かったりして、iOSアプリ開発って通りで情報が古くなっていったり、広まったりしないんだなぁと感じました。

今現在はこのサンプルをベースにして、認証部分だけはTwitterKitを使い、あとの画面は結局新しく作り直しています。Composerなどの投稿機能もカスタマイズする方法がわからず・・・。

ただ、なんとなくですが、StoryBoardによるUI部品配置などもできるようになり、今では独自の投稿画面を実装できたり、iOSアプリ開発も少しずつ楽しくなってきています。iOS版TwitMorseのリリースまでまだ開発は続けます。

githubにあげておきました


参考資料