[iOS][Swift] SocialAccountKit を使った iOS 11 以降の Twitter と Facebook API へのアクセス

  • 6
    いいね
  • 0
    コメント

はじめに

iOS 11 になって Twitter や Facebook への投稿や API にアクセスするために利用していた Account.framework と Social.framework が使えなくなった。 Apple は、 Twitter と Facebook が提供している公式の SDK の利用をを推奨している。これらを使うのが正しい方法なのだろう。でも、その際の既存のソースコードの修正がとても面倒臭い。

そのため、iOS 10.3 までのソースコードを可能なかぎり少ない修正で、かつ Account.framework と Social.framework の代替ができる SocialAccountKitSwift を作成した。 このフレームワークを使用すれば、従来とほぼ同じような感じで Twitter や Facebook の API にアクセスできる。

何ができるの

以下のような iOS 10.3 までの Social.framework を使った Twitter に投稿するソースコードが、

Social.framework
if SLComposeViewController.isAvailable(forServiceType: SLServiceTypeTwitter) {
  if let slc = SLComposeViewController(forServiceType: SLServiceTypeTwitter) {
    slc.completionHandler = {
      (result: SLComposeViewControllerResult) -> Void in
      switch (result) {
        case .done:
          print("tweeted")
        case .cancelled:
          print("tweet cancel")
      }
    }
    present(slc, animated: true, completion: nil)
  }
}

次のように書き換えられる。 修正箇所が少なくって、とても楽チン。

SocialAccountKitSwift.framework
let accountType = SAKAccountType(.twitter)
if SAKComposeViewController.isAvailable(for: accountType) {
  if let slc = SAKComposeViewController(for: accountType) {
    slc.completionHandler = {
      (result: SAKComposeViewControllerResult) -> Void in
      switch (result) {
        case .done(let json): // API の Response を JSON 形式で受け取れる
          print("tweeted")
        case .cancelled:
          print("tweet cancel")
        case .error(let error): // Error も受け取れる
          print(error.localizedDescription)
      }
    }
    present(slc, animated: true, completion: nil)
  } 
}

同様に、 Twitter の API にアクセスするコードは、

Social.framework
let requestURL = URL(string: "https://api.twitter.com/1.1/statuses/home_timeline.json")!
if let request = SLRequest(forServiceType: SLServiceTypeTwitter, requestMethod: .GET, url: requestURL, parameters: [:]) {
  request.account = self.account
  request.perform(handler: { (responseData, urlResponse, error) in
    if let jsonData = responseData {
      dump(jsonData)
    }
  })
}

以下のように書き換え可能である。

SocialAccountKitSwift.framework
let requestURL = URL(string: "https://api.twitter.com/1.1/statuses/home_timeline.json")!
if let request = try? SAKRequest(forAccount: self.account, requestMethod: .GET, url: requestURL, parameters: [:]) {
  request.perform(handler: { (responseData, urlResponse, error) in
    if let jsonData = responseData {
      dump(jsonData)
    }
  })
}

ざっくり説明すると、 AC と SL 接頭辞で始まるコードが SAK 接頭辞に置き換わっただけ。ACAccount も SAKAccount になった。開発者が自分でアカウント管理の仕組みを実装しない限り、大きな手間はないと思う。

SocialAccountKit の使い方

自作アプリに組み込むまでの手順を説明するよ。
ほとんどが OAuth 認証のための準備に費すんだけどね。

SocialAccountKitSwift.framework の作成

  1. GitHub にアクセスして、ソースコードを clone するか zip 形式でダウンロードする。
  2. Xcode で Project を開き、Target を SocialAccountKitSwiftFatBinary を選択し、 Build を実行する。
  3. SocialAccountKitSwift.framework が作成されたフォルダが表示されるので、${HOME}/Library/Frameworks などにコピーする。

API Key と API Secret の入手と設定

アカウント管理のために、Twitter と Facebook から API Key と API Secret を入手する必要がある。どちらか一方しか利用しないなら、必要なサービスの API Key と API Secret を取得すれば良い。

Twitter を使う場合

  1. Twitter Application Management ページにアクセスする。
  2. Create New App を実行して、アプリケーションを登録する。
  3. Keys and Access Tokens タブを選択し、 Consumer KeyConsumer Secret の文字列をコピーする。
  4. Xcode Project 内の Twitter.plistCosumerKeyConsumerSecret にペーストする。
Twitter.plist
<plist version="1.0">
<dict>
  <key>ConsumerKey</key>
  <string>YOUR CONSUMER KEY</string>
  <key>ConsumerSecret</key>
  <string>YOUR CONSUMER SECRET</string>
</dict>
</plist>

Facebook を使う場合

  1. 開発者向け Facebook ページにアクセスする。
  2. 右上の マイアプリ から 新しいアプリを追加 を選択し、アプリを登録する。
  3. ダッシュボード画面の アプリIDapp secret の文字列をコピーする。
  4. Xcode Project 内の Facebook.plistAppIDAppSecret にペーストする。
Facebook.plist
<plist version="1.0">
<dict>
  <key>AppID</key>
  <string>YOUR APP ID</string>
  <key>AppSecret</key>
  <string>YOUR APP SECRET</string>
  <key>Permissions</key>
  <array>
    <string>public_profile</string>
    <string>email</string>
    <string>manage_pages</string>
    <string>publish_pages</string>
    <string>publish_actions</string>
    <string>user_posts</string>
    <string>user_friends</string>
  </array>
</dict>
</plist>

Permissions の値を調整したい場合は、アクセス許可のリファレンス - Facebook >ログイン を参照してね。
Permission の値は認証後に変更できないので気を付けよう。また、 Facebook の承認が必要な値もあるので注意しよう。

さらにもう一手間、 OAuth 認証の設定をするよ。

  1. プロダクト画面で Facebook ログイン を追加する。
  2. クライアントOAuth設定 画面を開く。
  3. クライアントOAuthログイン埋め込みブラウザOAuthログインはい に変更する。

Facebook の設定は、いろいろと複雑なので、先ずは OAuth 認証でアカウント作成ができるかどうかを確認してから、プログラムの開発を進めると良い。

API Key と API Secret の確認

取得した API Key と API Secret を使って SocialAccountKitDemo を実行してアカウント登録してみよう。

  1. Twitter.plist あるいは Facebook.plist に入手した API Key と API Secret を設定する。
  2. Xcode の Target を SocialAccountKitDemo に変更して Run を実行する。
  3. アプリが起動したなら、画面右上の ボタンをタップする。
  4. アカウント管理画面に切り替わるので、右上の Edit ボタンをタップする。
  5. Add New Account をタップする。
  6. Twitter / Facebook の認証画面に切り替わるので、 ID とパスワードを入力する。
  7. 認証が無事完了したら、アカウント管理画面に戻るので、右上の Done ボタンをタップする。
  8. 左上の × ボタンをタップしてアカウント画面を閉じる。

上記一連の操作でエラーが起きなければ、取得した API Key と API Secret が正しく使えるのが確認できた。

それと、デモアプリの左上のボタンをタップすると、登録したアカウントの一覧が表示される。選択すれば、タイムラインの情報を画面に表示するよ。また、画面左下のボタンをタップすると、投稿画面が表示されるので、自分のタイムラインに文章を書き込めるよ。試してみてね。

自作アプリに組み込む

Xcode での設定

Embedded Binaries に作成した SocialAccountKitSwift.framework を登録する。

アカウント管理機能の実装(必須)

iOS 10.3 までは、 iOS の 設定 アプリ内で Twitter と Facebook のアカウント管理機能が提供されていた。 iOS 11 以降では、それらの機能が削除されている。そのため、アプリ毎にアカウントを管理する必要がある。

このアカウント管理の仕組みを提供するのが、 SAKAccountViewController クラスである。 そのため、SocialAccountKitSwift を利用するなら、必ずこのクラスを呼び出す仕組みを実装しなければならない。

たとえば、UIBarButtonItem から Twitter のアカウント管理画面を呼び出すには、以下のようなコードを実装する。

SAKAccountViewController
func manageAction(_ sender: UIBarButtonItem) {
  let accountType = SAKAccountType(.twitter)
  let viewController = SAKAccountViewController(accountType: accountType)
  present(viewController, animated: true, completion: nil)
}

SAKAccountViewController クラスは、アカウントの登録と削除のみの機能を提供する。もし独自のアカウント管理画面を実装したいなら、 SocialAccountKit フォルダ内の AccountViewController.swift を参考にすると良い。

アカウントの取得

登録したアカウント情報は次のようなコードで取得できる。 例は Twitter の場合。

class MyClass
{
  let store = SAKAccountStore.shared
  var accounts = [SAKAccount]()

  func getAccounts() {
    let accountType = SAKAccountType(.twitter)
    store.requestAccessToAccounts(with: accountType, completion: {
      [unowned self] (granted: Bool, error: Error?) in
      guard granted, error == nil else { return }
      if let accounts = self.store.accounts(with: self.accountType) {
        self.accounts = accounts
      }
    })
  }
}

上記のコードでも分かるように、可能なかぎり従来の Account.framework からの修正を少なくなるようにしている。この場合は、ACAccountStore に対応する SAKAccountStore である。違いは、 SAKAccountStore は Singleton なだけ。
それと、requestAccessToAccounts() の第一引数が、SAKAccountType になった点かな。

API へのアクセス

iOS 10.3 までは、Social.framework の SLRequest クラスを利用すれば、Twitter や Facebook の API にアクセスできた。同様に、SocialAccountKitSwift でも SAKRequest クラスとして機能を提供している。

以下のコードでは、Twitter のタイムライン上の情報を 20件取得する。 エラー処理は省略。

SAKRequest
let account: SAKAccount = self.accounts.first!
let accountType = SAKAccountType(.twitter)
let requestURL = URL(string: "https://api.twitter.com/1.1/statuses/home_timeline.json")!
let parameters: [String:Any] = [
  "count" : 20
]
let request = try? SAKRequest(forAccount: account, requestMethod: .GET, url: requestURL, parameters: parameters)
request.perform(handler: {
  [unowned self] (data, response, error) in
  guard error == nil, let data = data else { return }
  if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 {                                                  
    let json = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [Dictionary<String,Any>]
    dump(json)
  }
})

SLRequest() と SAKRequest() の違いは、第一引数に SAKAccount を取っている所である。また、例外を投げるのも異なる。この点に関しては、エラー処理の設計がイマイチなのが原因である。

これらについて注意すれば、既存のソースコードのちょっとした修正で、iOS 11 でも API へのアクセスが可能になる。

タイムラインへの投稿

Twitter や Facebook のタイムラインへの投稿も、 SAKComposeViewController クラスを利用して可能である。これも、SLComposeViewController クラスとほぼ同じ使い方である。違いは、第一引数に、SAKAccountType を渡す点である。この点に気を付ければ、従来のソースコードの修正も簡単に実施できるだろう。

SAKComposeViewController
func composeAction(_ sender: UIBarButtonItem) {
  let accountType = SAKAccountType(.twitter)
  if SAKComposeViewController.isAvailable(for: accountType) {
    let viewController = SAKComposeViewController(for: accountType)
    viewController.completionHandler = {
      [unowned self] (result: SAKComposeViewControllerResult) -> Void in
      switch result {
        case .cancelled:
          print("cancelled")
        case .done(let json): // XXX: 'json' may be 'nil'.
          dump(json)
        case .error(let error):
          print(error.localizedDescription)
      }
    }
    present(viewController, animated: true, completion: nil)
  }
}

ダウンロード

Xcode Project 一式 は、 github から入手可能。
スクリーンショットもあるので、そちらも参考にしてね。

おわりに

SocialAccountKitSwift は、 iOS 10.3 までの Account.framework と Social.framework を使ったソースコードをできるだけ少ない手間で iOS 11 以降でも動作させるためのフレームワークだ。手間を惜しまないのであれば、 Twitter と Facebook が提供している公式の SDK を利用して対応するのが正解なのだろう。

私は、Account.framework と Social.framework を既存のアプリで結構使っているので、そのためだけに 公式 SDK を使って対応するのが面倒だと思った。なので、SocialAccountKitSwift を作ったのだ。 自作フレームワークを作るまでは大変だが、作った後の対応は段違いに楽だ。

それと、このフレームワークを使う利点は、困ったらソースコードを見て自分で改良できるところだろう。 OAuth 認証のコードも RFC 5849 を読んで実装したので、自作アプリで使う際の参考にもなるだろう。他のサービスへの対応も考慮した設計にしたつもりだが、どうだろうか。