SwiftからTodoistのTodoを取得する
TodoistとTodoistAPIで、毎朝のタスク報告をするヾ(‘ω’)ノを見てSwiftでTodoist使って何かできないかなーと思い、とりあえずAPI使ってみました。
参考サイト Todoist API Documentation
事前準備
こちらにてClient IDとClient Secretを取得する
認証
今回はWebViewを使って、認証をおこなえるようにしました。
STEP1
まずhttps://todoist.com/oauth/authorize?client_id=<CLIENT_ID>&scope=<SCOPE>&state=<STATE>&redirect_uri=<REDIRECT_URI>
にリクエストを送ります。
パラメータは4つあります。
- client_id: 事前準備で取得したClient IDです
- scope: 権限の強さを指定します
- task:add, data:read, data:read_write, data:delete, project:delete
- state: 好きな文字列。CSRFから守るためとありました。
- redirect_uri: リダイレクト先 (Optional, DeveloperサイトからDefaultを設定することができる)
STEP2
リクエストを送信すると、https://todoist.com/Users/showLogin?<PARAMETERS>
が返ってきます。これは以下のようなGoogleかemailでログインすることができるビューです。
このビューをwebViewに表示しユーザーがログインできるようにします。
STEP3
ユーザーがログインしたら、以下の「同意する」のビューになります。 ここで同意するをクリックすると、STEP1で指定したリダイレクト先にリダイレクトされます。
このときに、パラメーターとしてstateとcodeの2つが添付されています。 このcodeが次に必要になるので、URLからcodeを取得します。
URLはhttps://<リダイレクト先>?state=<STATE>&code=<CODE>
という形をしているので、urlからパラメータ部分を抽出し、stateの文字数がわかっているのでそれをもとにcodeの部分だけ抜き出しました。
(もうちょっとスマートにやりたいんですけど何かうまい方法無いのかな。。)
func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool { let url = request.URL! if url.host == "<リダイレクト先のホスト>" { let query = url.query! // stateの長さに依存する. 12+(stateの長さ). stateをstateに設定したので今回は17. let code = query.substringFromIndex(advance(query.startIndex, 17)) // codeを利用してaccess_tokenを取得する処理 // -- <略> -- } return true }
STEP4
先ほど取得したcodeを利用してaccess_tokenを取得します。
https://todoist.com/oauth/access_token?client_id=<CLIENT_ID>&client_secret=<CLIENT_SECRET>&code=<CODE>
にPOSTリクエストを送信します。
ここでのパラメータは3つです。
- client_id: 事前準備で取得したClient ID
- client_secret: 事前準備で取得したClient Secret
- code: STEP3で取得したcode
これでaccess_tokenを取得できるので、大事に保存しましょう。
認証で引っかかった問題
STEP2でGoogleでログインしようとしたときにうまくリダイレクトしてくれませんでした。 メールならログインした後に以下の画面が表示されるのですが、Googleのときはローディングで一切進みません。
以下の画面を表示するために、Googleでログインが完了したタイミングでもう一度STEP1に戻ってリクエストを送信しました。 STEP1のリクエストですが、すでにログインしている場合はメールアドレス/パスワードを入力する画面ではなく、同意するかどうかの画面に飛びます。 なので、もう一度リクエストを送信して同意するかどうかの画面を表示するようにしました。
データ取得
タスクの取得
タスクのリストを取得するためにはhttps://todoist.com/API/v6/sync?token=<TOKEN>&seq_no=0&seq_no_global=0&resource_types=[\"items\"]
にリクエストを送信します。
パラメータは以下の4つです。
- token: アクセストークン
- seq_no: Sequence number. 初めは0, 2回目以降は前回に受け取ったものを利用する.
- seq_no_global: Global sequence number. 初めは0, 2回目以降は前回に受け取ったものを利用する.
- resource_types: 取得したい情報を入力。リスト形式で複数渡すことが可能。
- パラメータの種類は, projects, items, labels, notes, filters, reminders, locations, user, live_notifications, day_orders, allがある.
こんな感じで取得できました。
func getTasks() { let request = NSMutableURLRequest(URL: NSURL(string: getTasksURL)!) NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue(), completionHandler: { (res, data, error) in let dataString = NSString(data: data, encoding: NSUTF8StringEncoding) let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as! NSDictionary let tasks = jsonResult["Items"] as! NSArray for task in tasks { let task = task as! NSDictionary let id = task["id"] as! NSNumber let title = task["content"] as! String let date = task["due_date"] as! String println("id: \(id.stringValue), title: \(title), date: \(date)") } }) } // 出力結果 id: 71504480, title: today's task 2, date: Mon 01 Jun 2015 04:59:59 +0000 id: 71504414, title: today's task 1, date: Mon 01 Jun 2015 04:59:59 +0000 id: 71504481, title: tomorrow's task 1, date: Tue 02 Jun 2015 04:59:59 +0000
まとめ
よくわからないところで引っかかり、ググってもイマイチ情報が出てこないので時間とられました。 Documentのサンプルがうまく動かないとかは止めて欲しいですね。
サンプルとして全体像を載せておきます。 適当すぎですが、とりあえずは動くはず。。