Swifterを自分のアプリに組み込む方法


[@k-boyさんの記事](https://qiita.com/k-boy/items/c83a0505d12cc8e7d4b9)を参考にSwifterを自分のアプリに組み込んでみたのですが、色々とつまづいたので備忘のため手順を残しておきます。

# 使用したバージョン
- Xcode ： 10.3
- Swifter ： 1.7.0


# 1. 自分のアプリにCocoaPodでSwifterをインストールする
 - 自分のアプリディレクトリに移動し、```$ pod init```を実行する
 - Podfileが作成されるので```pod 'Swifter', :git => 'https://github.com/mattdonnelly/Swifter.git'```を追記する
 - ```$ pod install``` を実行する

``` 編集後のPodfile
# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'

target 'MyTweetTool' do
  # Comment the next line if you don't want to use dynamic frameworks
  use_frameworks!

  # Pods for MyTweetTool
  pod 'Swifter', :git => 'https://github.com/mattdonnelly/Swifter.git'
  pod 'SwiftGifOrigin', '~> 1.7.0'
end
```


# 2. Twitter Developersで自分のアプリを登録する

Twitter Developersに自分のアプリを登録します。
細かい手順は割愛しますが[@tdknさんの記事](https://qiita.com/tdkn/items/521686c240b0c5bc6207)が参考になります。

登録が完了し、最終的に下記のように自分のConsumer API keysが確認できればOKです。

![スクリーンショット 2019-08-19 0.27.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/200963/ad88fc30-d745-947b-53fc-8a870fc7058b.png "スクリーンショット 2019-08-19 0.27.55.png")


# 3. Custom URL Schemeを設定する

SwifterはOAuthにてAPIの認証を行うため、一時的にブラウザを開きます。
開いた先から自分のアプリに戻って来られるようにCustome URL Schemeを設定します。

- Info.plistに自分のCFBundleURLTypes定義を追加する

``` 追加する要素
    <key>CFBundleURLTypes</key>
    <array>
        <dict>
            <key>CFBundleURLName</key>
            <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
            <key>CFBundleURLSchemes</key>
            <array>
                <string>swifter-{Consumer API key}</string>
            </array>
        </dict>
    </array>
```

※CFBundleURLSchemesにswifter-{Consumer API key}とありますが、{Consumer API key}には先程のTwitter DevelopersのAPI keyを入力します

- AppDelegate.swiftでapplication:openURL:options:をオーバーライドする

```
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
        return Swifter.handleOpenURL(url, callbackURL: URL(string: "swifter-{Consumer API key}://")!)
}
```

※Swifterのコードを呼び出すので```import SwifteriOS```が必要です


# 4. Swifterを使ってtweetを投稿する

自分が作成したのはテキスト入力と投稿ボタンだけのシンプルなUIのアプリで画面構成は下記のようになります。

![スクリーンショット 2019-08-19 0.54.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/200963/3291aa79-6f0a-5dc1-fa64-ad1ded54628f.png "スクリーンショット 2019-08-19 0.54.55.png")

説明よりも実際のコードを紹介します。

``` ViewController.swift
import UIKit
import SwifteriOS
import SafariServices

class ViewController: UIViewController, SFSafariViewControllerDelegate {

    @IBOutlet weak var tweetMsgArea: UITextView!
    private let appStatus = AppStatus()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    }

    @IBAction func tweetPushed(_ sender: Any) {
        tweet()
    }
    
    func tweet() {
        let TWITTER_CONSUMER_KEY = "{Consumer API key}"
        let TWITTER_CONSUMER_SECRET = "{Consumer API secret key}"
        
        // load from UserDefaults
        let tokenKey = self.appStatus.twitterTokenKey
        let tokenSecret = self.appStatus.twitterTokenSecret
        
        if tokenKey == nil || tokenSecret == nil {
            let swifter = Swifter(consumerKey: TWITTER_CONSUMER_KEY, consumerSecret: TWITTER_CONSUMER_SECRET)
            
            swifter.authorize(
                withCallback: URL(string: "swifter-{Consumer API key}://")!,
                presentingFrom: self,
                success: { accessToken, response in
                    print(response)
                    guard let accessToken = accessToken else { return }
                    self.appStatus.twitterTokenKey = accessToken.key
                    self.appStatus.twitterTokenSecret = accessToken.secret
                    self.tweet()
            }, failure: { error in
                print(error)
            })
            
        } else {
            let swifter = Swifter(consumerKey: TWITTER_CONSUMER_KEY, consumerSecret: TWITTER_CONSUMER_SECRET, oauthToken: tokenKey!, oauthTokenSecret: tokenSecret!)
            
            swifter.postTweet(status: tweetMsgArea.text, success: { response in
                print(response)
            }, failure: { error in
                print(error)
            })
        }
    }
}

class AppStatus {
    var userdefault = UserDefaults.init(suiteName: "app_status")!
    
    var twitterTokenKey : String? {
        get {
            if let token : String = userdefault["token_key"] {
                return token
            } else {
                return nil
            }
        }
        
        set {
            userdefault["token_key"] = newValue
        }
    }
    
    var twitterTokenSecret : String? {
        get {
            if let secret : String = userdefault["token_secret"] {
                return secret
            } else {
                return nil
            }
        }
        
        set {
            userdefault["token_secret"] = newValue
        }
    }
}

extension UserDefaults {
    subscript<T: Any>(key: String) -> T? {
        get {
            if let value = object(forKey: key) {
                return value as? T
            } else {
                return nil
            }
        }
        set(_newValue) {
            if let newValue = _newValue {
                set(newValue, forKey: key)
            } else {
                removeObject(forKey: key)
            }
            synchronize()
        }
    }
}
```

ポイントとしてはUserDefaultsからアクセストークンを取得し、取得できなかった場合は```Swifter.authorize()```を呼び出してアクセストークンを取得します。
既に取得できていた場合はそのまま```Swifter.postTweet()```で入力された内容をTweetします。

# 5. トラブルシュート

## dyld: Library not loaded: ... Reason: image not foundと出る場合

Embedded BinariesにSwifteriOSが設定されていないことが原因。
下記のように設定を行う。

![スクリーンショット 2019-08-19 11.57.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/200963/d6b29dd4-8caf-a186-a1dd-a0e404b4e63a.png "スクリーンショット 2019-08-19 11.57.33.png")