目的と手順の説明
今回は、私の最新のGmailを誰から受信したのか?を取得することを目的にするよ〜〜〜〜〜。
1.Gmail APIを有効にする
2.認証画面を作成
3.OAuth2.0を利用して、クライアントアプリにトークンを受けとる
4.Gmail APIを叩く
下の記事を読んでから、この記事を読み進めることをおすすめします。
一番分かりやすい OAuth の説明
1.Gmail APIを有効にする
(もしかしたら、1より先に2をしなあかんかったかもしれん。。。)
Gmail APIを有効
アプリを公開しないなら、全てを記述する必要がないので5分ぐらいで終わります。
認証情報の隣にある認証情報を作成というボタンをクリックしてOuthクライアントIDを選択する
→アプリケーションの種類をiOSにする→バンドルIDはXcodeで作成したアプリのBoundleIdentifierをコピペする。
2.認証画面を作成
認証画面は、クライアントアプリがアクセストークンを作成する時に、エンドユーザーに対して認可する権限を与える画面です。この画面自体プログラマーが作る必要がありません。もうすでに用意されている。
認証画面の途中でスコープ
を聞かれると思います。そのとき、__制限付きのスコープのhttps://mail.google.com/__を選択する。(今回はメール情報を取得するから)
テストユーザーも追加しておく
これでサーバーサイドの設定はしゅーーーりょーーーーーーーーう!!
これから、Xcodeで作業します。
3.OAuth2.0を利用して、クライアントアプリにトークンを受けとる
OAuthを利用する準備
この記事みながらセットアップした。
ライブラリのインストールにCocoaPodsを使用します。
ターミナルを開けて〜〜〜〜〜
cd アプリのディレクトリ
アプリのディレクトリに移動します。
pod init
ポッドファイルを作成。
pod 'GoogleAPIClientForREST/Gmail', '~> 1.3.0'
pod 'GTMAppAuth'
podファイルに上記を追加して
pod update
アップデートしたら、大丈夫だぁ〜〜〜〜〜
アクセストークンをリクエスト
今回使う変数を全て定義するぞ〜
その前に、したのインポートを忘れずに
import AppAuth
import GoogleAPIClientForREST
import GTMAppAuth
private let scopes = ["https://mail.google.com/"]
private let kClientID = "クライアントID"
private let kRedirectURL = URL.init(string: "リダイレクトURL"+":/oauthredirect")!
private let configuration = GTMAppAuthFetcherAuthorization.configurationForGoogle()
private var authorization: GTMAppAuthFetcherAuthorization?
private let userId=Gメールアドレス
scopes
2の認証画面の設定で使用したスコープをいれる。
kClientIDとkRedirectURL
google.com/api
上のリンクから認証情報のOAuth2.0クライアントIDに追加されているアプリの編集画面に行けばみれる!
クライアントID
とiOSのURLスキーム
あります。
kClientID=クライアントID
__kRedirectURL=iOSのURLスキーム+":/oauthredirect"__です
(:/oauthredirect"を忘れずに! 俺は忘れていた。)
認証画面を表示させる
private func showAuthorizationDialog() {
let request = OIDAuthorizationRequest.init(configuration: configuration,
clientId: self.kClientID,
scopes: scopes,
redirectURL: kRedirectURL,
responseType: OIDResponseTypeCode,
additionalParameters: nil)
let appDelegate: AppDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.currentAuthorizationFlow=OIDAuthState.authState(byPresenting: request, presenting: self, callback: { (authState, err) in
if let error = err {
NSLog("\(error)")
} else {
if let authState = authState {
self.authorization = GTMAppAuthFetcherAuthorization.init(authState: authState)
GTMAppAuthFetcherAuthorization.save(self.authorization!, toKeychainForName: "authorization")
}
}
})
}
軽く説明していきます。
let request = OIDAuthorizationRequest.init(configuration: configuration,
clientId: self.kClientID,
scopes: scopes,
redirectURL: kRedirectURL,
responseType: OIDResponseTypeCode,
additionalParameters: nil)
認証画面を表示するためのサーバーに対するリクエストを表しています。
responseType:は応答のタイプを表しています。
additionalParameters:はクライアントの追加の許可パラメーターです。今回はないのでnilを渡します。
let appDelegate: AppDelegate = UIApplication.shared.delegate as! AppDelegate
AppDelegateにアクセスしている理由は後ほどわかります。
appDelegateに変数を置いている理由は、異なるSwiftファイル間で変数を共有するためです。
AppDelegateに宣言した変数は、どこのViewControllerでも共有できる。
appDelegate.currentAuthorizationFlow=OIDAuthState.authState(byPresenting: request, presenting: self, callback: { (authState, err) in
if let error = err {
NSLog("\(error)")
} else {
//OIDAuthStateからGTMAppAuthFetcherAuthorizationを作成します。
if let authState = authState {
self.authorization = GTMAppAuthFetcherAuthorization.init(authState: authState)
GTMAppAuthFetcherAuthorization.save(self.authorization!, toKeychainForName: "authorization")
}
}
})
上記のコードで認証要求を実行します。
appleDelegateクラスにcurrentAuthorizationFlowをOIDExternalUserAgentSession?型で宣言しておく。
より詳しく知りたいかたは下のリンクに飛べ〜〜〜〜〜〜〜〜
README.md
// Serialize to Keychain
GTMAppAuthFetcherAuthorization.save(_authorization, toKeychainForName: kGTMAppAuthExampleAuthorizerKey)
// Deserialize from Keychain
let authorization = GTMAppAuthFetcherAuthorization.init(fromKeychainForName: kGTMAppAuthExampleAuthorizerKey)
// Remove from Keychain
GTMAppAuthFetcherAuthorization.removeFromKeychain(forName: kGTMAppAuthExampleAuthorizerKey)
一番上のコードでキーチェーンを利用して、ログイン状態を保存します。
いくつもログインしたい場合は、名前を変えて保存したらいいだけだから、すごい便利ですね。
したの記事でキーチェーンの理解をしておくといいかももも桃もお
キーチェーンの記事
ここまでで、認証完了!!
4.Gmail APIを叩く
やっと最後の章になります。長かった〜、、、、
コード全体
@objc func message(_ senfder:UIButton){
let query = GTLRGmailQuery_UsersMessagesList.query(withUserId: userId)
let service = GTLRGmailService()
service.authorizer = GTMAppAuthFetcherAuthorization.init(fromKeychainForName: "authorization")
service.executeQuery( query,
delegate: self,
didFinish: #selector(callback) )
}
@objc func callback( ticket : GTLRServiceTicket,
finishedWithObject response : GTLRGmail_ListMessagesResponse,
error : NSError? ){
if let error = error {
print("メッセージリストの取得に失敗しました。")
print(error)
return
}
print("メッセージリストの取得に成功しました。")
guard let message=response.messages?.first else {return}
guard let messageid=message.identifier else {return}
guard let url=URL(string: "https://gmail.googleapis.com/gmail/v1/users/\(userId)/messages/\(messageid)") else {return}
var urlRequest=URLRequest(url: url)
guard let token=authorization?.authState.lastTokenResponse?.accessToken else {return}
urlRequest.addValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
let session=URLSession(configuration: URLSessionConfiguration.default)
let task=session.dataTask(with: urlRequest){
(data,urlResponse,err) in
if let err=err {
print("失敗しました\(err)")
}
do{
let responseJson=try JSONDecoder().decode(res.self, from: data!)
responseJson.payload.headers.forEach {
if($0.name=="From"){
print($0.value)
}
}
}catch(let err){
print("失敗した\(err)")
}
}.resume()
}
コードの解説
説明ながなる〜〜〜〜〜〜〜〜疲れた〜〜〜〜〜〜〜〜
@objc func message(_ senfder:UIButton){
let query = GTLRGmailQuery_UsersMessagesList.query(withUserId: userId)
let service = GTLRGmailService()
service.authorizer = GTMAppAuthFetcherAuthorization.init(fromKeychainForName: "authorization")
service.executeQuery( query,
delegate: self,
didFinish: #selector(callback) )
}
messageメソッドはボタンが押されたら呼ばれるメソッドです。
詳しく見ていきます。
let query = GTLRGmailQuery_UsersMessagesList.query(withUserId: userId)
GTLRGmailQuery_UsersMessagesList.queryはユーザーのメールボックス内のメッセージを一覧表示する命令です。useIdはGmailアドレスです。
let service = GTLRGmailService()
service.authorizer = GTMAppAuthFetcherAuthorization.init(fromKeychainForName: "authorization")
GTLRGmailServiceはGmailAPIクエリを実行するための作業人みたいな感じだと思います。
この作業する人が本人かどうか調べます。authorizerに、認証完了している情報をキーチェーンを用いて、GTMAppAuthFetcherAuthorizationを生成して代入する。これは、認証許可している人かどうか調べるためです。
service.executeQuery( query,
delegate: self,
didFinish: #selector(callback) )
作業人が本人だったら作業開始します。
その時、callback関数の引数に結果が帰っていきます。
callbackの中身を具体的に見ていきます。
@objc func callback( ticket : GTLRServiceTicket,
finishedWithObject response : GTLRGmail_ListMessagesResponse,
error : NSError? ){
if let error = error {
print("メッセージリストの取得に失敗しました。")
print(error)
return
}
print("メッセージリストの取得に成功しました。")
guard let message=response.messages?.first else {return}
guard let messageid=message.identifier else {return}
guard let url=URL(string: "https://gmail.googleapis.com/gmail/v1/users/\(userId)/messages/\(messageid)") else {return}
var urlRequest=URLRequest(url: url)
guard let token=authorization?.authState.lastTokenResponse?.accessToken else {return}
urlRequest.addValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
let session=URLSession.shared
session.dataTask(with: urlRequest){
(data,urlResponse,err) in
if let err=err {
print("失敗しました\(err)")
}
do{
let responseJson=try JSONDecoder().decode(res.self, from: data!)
responseJson.payload.headers.forEach {
if($0.name=="From"){
print($0.value)
}
}
}catch(let err){
print("失敗した\(err)")
}
}.resume()
}
if let error = error {
print("メッセージリストの取得に失敗しました。")
print(error)
return
}
引数のerrorに値が入っていたら、エラーを表示し、returnでcallback関数を抜けます。
guard let message=response.messages?.first else {return}
guard let messageid=message.identifier else {return}
response.messagesは,配列でいーーーっぱい、messageIDとthreadIDが格納されています
今回最初のメッセージが取得したいのでresponse.messages?.firstで配列の最初のメッセージ情報を取得します
ほんでもって,message.identifierでメッセージIDを取得します.
このメッセージIDを用いて,URLを生成します.
guard let url=URL(string: "https://gmail.googleapis.com/gmail/v1/users/\(userId)/messages/\(messageid)") else {return}
var urlRequest=URLRequest(url: url)
guard let token=authorization?.authState.lastTokenResponse?.accessToken else {return}
urlRequest.addValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
先ほどのmessageidを用いてURLを生成します。
その後、URLRequest型を定義します。
URLRequest型は通信のリクエストを表現する型です。HTTPリクエストのURL、ヘッダ、メソッド、ボディなどの情報を持ちます。
ヘッダにはアクセストークンが必要です。
(urlRequest.httpMethod="GET"を途中に書いても問題ないと思います。)
ヘッダにアクセストークンを追加します。
forHTTPHeaderFieldについては、通信に関するお話でよくわからないです。( i _ i )
また、勉強しておきます。
Appleのドキュメントに詳しく乗っています。
let session=URLSession.shared
session.dataTask(with: urlRequest){(data,urlResponse,err) in
if let err=err {
print("失敗しました\(err)")
}
do{
let responseJson=try JSONDecoder().decode(res.self, from: data!)
responseJson.payload.headers.forEach {
if($0.name=="From"){
print($0.value)
}
}
}catch(let err){
print("失敗した\(err)")
}
}.resume()
URLSessionクラスは,URL経由でのデータの送受信に使います。
リクエストはタスクと呼ばれます。
詳しく知りたい方はしたの記事を参考に
【Swift】URLSessionまとめ
URLSession.sharedはURLSessionクラスにデフォルト値が設定されたインスタンスです。
if let err=err {
print("失敗しました\(err)")
}
通信が完了して、エラーが帰ってきたらエラーを表示します
do{
let responseJson=try JSONDecoder().decode(res.self, from: data!)
responseJson.payload.headers.forEach {
if($0.name=="From"){
print($0.value)
}
}
}catch(let err){
print("失敗した\(err)")
}
通信が完了してdataの中に一番最新のメール情報に関するJSONファイルがあるのでパースします。
JsonDecoder().decode()でSwiftの型に変換しています。
今回、誰から連絡きたのかが知りたいのでforEachでループして探しています。。。
resは以下のようです。
struct res: Codable{
var payload:Playload
struct Playload:Codable {
var headers:[Header]
}
struct Header:Codable {
var name:String
var value:String
}
}
誰から、連絡きていたのか?を記事に載せようとしたけど、しょーもない人だったのでやめときます。。
感想
初心者なのでわからないことばかりですが、少しでもお力になれれば幸いです。