[AWS] API Gateway に iOS SDK からつなぎたい

2018/10/19

こんにちは。きんくまです。
前回設定したAPIはブラウザのJSからアクセスしました。
>> [AWS] API Gateway で Authorization AWS_IAM のときに JavaScript SDKからアクセスする

今回はiOSからつなぎたいと思います。
いくつかはまりどころがあったので、それのメモみたいな感じです。

CocoaPodsでライブラリをインストール

CocoaPodsでライブラリをインストールします。
公式のチュートリアルだとそれと合わせてAWS Amplify CLIを入れてねとなっていました。

Amplify CLIを少しやってみたところ、これはコマンドラインで複数のAWSのサービスを操作して、目的のバックエンドを作成するやつでした。これを使うことでAWSにそこまで詳しくなくてもユーザー認証とかプッシュ通知とかすぐにできる感じのようです。Google Firebaseの対抗ということでしょうかね?ページのデザインも似てますし。
>> AWS Amplify

今回は特にサーバー側をCLIからは操作しないので、入れません。
でCocoaPodsで入れるライブラリとしては、AWSのベースとなるAWSCoreと、API Gateway用のAWSAPIGatewayとなります。

Podfile

# platform :ios, '9.0'

target 'SamplePod' do
  use_frameworks!

  pod 'AWSCore', '~> 2.6.13'
  pod 'AWSAPIGateway', '~> 2.6.13'
  
end

バージョンについて。あとから出てくるAPI Gatewayで書き出されたPodfileにはもっと低いバージョンの2.5.3が書いてあったのですが、それでやったらいろいろとうまくいかなかったので新しいバージョンにしています。(2018/10月時点)

インストール

pod install

Lambdaのソース

前回特に書かなかったのですが、今回は出力の部分があるのでLambdaのソースを載せておきます。
基本チュートリアル通りなのですが、おまけで2桁の数字をランダムで作って lucky というプロパティで返しています。

'use strict';
var days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
var times = ['morning', 'afternoon', 'evening', 'night', 'day'];

console.log('Loading function');

exports.handler = function(event, context, callback){
    let name = event.name === undefined ? 'you' : event.name;
    let city = event.city === undefined ? 'World' : event.city;
    let time = times.indexOf(event.time) < 0 ? 'day' : event.time;
    let day = days.indexOf(event.day) < 0 ? null : event.day;

    let greeting = 'Good ' + time + ', ' + name + ' of ' + city + '. ';
    if(day){
        greeting += 'Happy ' + day + '!';
    }

    console.log('Hello: ', greeting);
    let num = Math.floor(Math.random() * 100);
    let luckyStr = 'lucky num is ' + num;

    callback(null, {
        'greeting': greeting,
        'lucky': luckyStr
    })
};

API Gatewayのモデルを作る

レスポンスされるときにモデルを作っておかないと、zipで吐き出されるソースでうまくいかなかったので先に出力モデルを作ります。ここはJSでは必要ないところでした。

GetStartedLamdaIntegrationResult

{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "title": "GetStartedLamdaIntegrationResultModel",
  "type": "object",
  "properties": {
    "greeting": { "type": "string" },
    "lucky": {"type": "string"}
  }
}

さきほどLambdaで返した greeting と lucky が入っています。

レスポンスに設定する

さきほどのモデルをPOSTのレスポンスに設定します。デフォルトではEmptyというふうになっていました。

ソースをダウンロード

ソースをダウンロードしてXcodeのプロジェクトに追加しておきます。

ソースを入れたら、チュートリアルにあるようにBridging-headerを設定します。SwiftからObjective-Cを利用できるようになりました。

で、ここでハマったところだったのですが、AWSModelの子供クラスは全て @objcMembers というのをつけてあげないとうまくいきませんでした。
>> API Gateway iOS SDK empty body?

今回だとこんな感じになりました。吐き出されたソースにあったAWSModelの子供クラスは Empty, SPGetStartedLambdaIntegrationUserInput, SPGetStartedLamdaIntegrationResultがありました。

@objcMembers
public class SPGetStartedLambdaIntegrationUserInput : AWSModel {

swiftから利用してみる

ようやく準備が整いました。swiftからリクエストしてみます。

前回ハマったので今回は大丈夫だったのですが、Cognitoで認証してからリクエストしました。

import UIKit
import AWSCore

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        
        let credentialsProvider = AWSCognitoCredentialsProvider(
            regionType: AWSRegionType.APNortheast1,
            identityPoolId: "ap-northeast-1:XXXXXXXXX")
        let configuration = AWSServiceConfiguration(
            region: AWSRegionType.APNortheast1, credentialsProvider: credentialsProvider)
        AWSServiceManager.default()?.defaultServiceConfiguration = configuration
        
        if credentialsProvider.identityId == nil {
//           print("id not found")
            credentialsProvider.getIdentityId().continueWith { [weak self](task) -> Any? in
                guard task.error != nil else {
                    print("Error: " + task.error!.localizedDescription)
                    return task
                }
//                let cognitoId = task.result!
//                print("result: \(cognitoId)")
//                if let id = credentialsProvider.identityId {
//                    print("ispopulate? -> \(id)")
//                }
                self?.testPost()
                return task
            }
        }else{
            testPost()
        }
    }

    func testPost(){
        let client = SPGetStartedLambdaIntegrationAPIClient.default()
        guard let model = SPGetStartedLambdaIntegrationUserInput() else {
            return
        }
        model.callerName = "Mike"
        client.cityPost(day: "Tuesday", time: "afternoon", city: "Hokaido", body: model)
            .continueWith { (task) -> Any? in
                //print("task \(task)")
                self.showResult(task: task)
                return nil
        }
    }
    
    func showResult(task:AWSTask<SPGetStartedLamdaIntegrationResult>){
        if let error = task.error {
            print("Error: \(error)")
            return
        }
        guard let result = task.result else {
            return
        }
        if let greeting = result.greeting {
            print("greeting: \(greeting)")
        }
        if let lucky = result.lucky {
            print("lucky: \(lucky)")
        }

    }
}

出力

greeting: Good afternoon, Mike of Hokaido. Happy Tuesday!
lucky: lucky num is 51

うまくいきました! 前回ハマってしまった認証のところがわかっていたので、比較的スムーズにできました。
ところどころハマるところがあってググる必要があるのですが、基本的にAWSのドキュメントを順番に読んでいけば詳しく書いてあるので、なんとかなるのではないかと思いました。ではでは。

LINEで送る
Pocket

自作iPhoneアプリ 好評発売中!
フォルメモ - シンプルなフォルダつきメモ帳
ジッピー電卓 - 消費税や割引もサクサク計算!

LINEスタンプ作りました!
毎日使える。とぼけたウサギ。LINEスタンプ販売中! 毎日使える。とぼけたウサギ

ページトップへ戻る