LoginSignup
2
1

More than 5 years have passed since last update.

Swift3でAWS Kinesis Firehoseを導入してハマったところ

Last updated at Posted at 2016-11-28

ユーザーのログを集めたい

iOSのクライアントの動きをログとして集積したいので、その方法としてAWSのCognitoでTemporary Credentialsを各クライアントに発行して、Kinesis Firehoseに集めたいデータを渡してS3に保存することにしました。

Kinesis Firehose(以下Firehose)を使うとKinesis Stream(従来のKinesis: 以下Kinesis)と異なり、いきなりS3やRed Shiftにデータを保存できるのが特徴です。

参考にしたサイト

  • AWSのiOS developer guide
    これはKinesisとFirehoseのふたつに関して実装手順が書いてありますがObjective-Cで記述されていました。Objective-Cには馴染みがあまりなかったのでこれだけでは実装完了できませんでした。

  • classmethodさんの記事
    コンソール画面での手順も書いてあり、これに沿っていけばKinesisの導入まではすぐいけそうです。ただし、コードはSwift3で書かれていないので自分で修正しないといけない点がありました。

ハマったところ

1, ユーザーロールに紐づく権限周り(IAM)

1, Cognitoの権限

Cognitoにはデフォルトの権限に加えてFirehoseにアクセスする権限(最低でもPutRecord, PutRecordBatch)を付与します。CognitoのFederated IdentitiesのほうでEdit Identity Poolを選択して権限を確かめます。

Screen Shot 2016-11-28 at 23.31.24.png
(FirehoseはTokyoリージョンにまだ対応していないのでCognitoもFirehoseもバージニアにしました。)

Authenticated RoleとUnauthenticated Roleに設定している権限を見ると、以下のようなポリシーがデフォルトで入っていました。

デフォルト.
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "mobileanalytics:PutEvents",
                "cognito-sync:*",
                "cognito-identity:*"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}

これにIAM画面で以下のようなポリシーがあるので追加しました。

AmazonKinesisFirehoseFullAccess.
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "firehose:*"
            ],
            "Effect": "Allow",
            "Resource": "*"
        }
    ]
}

2, Firehoseの権限

これはデフォルトで準備されるやつに

{
            "Effect": "Allow",
            "Action": [
                "firehose:PutRecord",
                "firehose:PutRecordBatch"
            ],
            "Resource": [
                "arn:aws:firehose:us-east-1:account-id:deliverystream/delivery-stream-name"
            ]
        }

を付け加えました。

IAM Simulatorで確かめながらやると確実です。

2, iOSのsubmitAllRecords() のところ

Firehoseに関する記事は少なくてSwift3ではどうすればいいのかわかりにくかったです。
こんな感じで実装してうまくいきました。実際はこれらをシングルトンにまとめました。

AppDelegate.swift

import AWSCore
import AWSCognito
import AWSKinesis

////

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

    ////
    configureAWSCognito()

    ////

    return true
}

func configureAWSCognito() {
    // Settings for AWSCognito
    let credentialsProvider = AWSCognitoCredentialsProvider(regionType: .usEast1, identityPoolId: "us-east-1:00000000-xxxx-0000-0000-xxxxxxxxxxxx")
    let configuration = AWSServiceConfiguration(region:.usEast1, credentialsProvider:credentialsProvider)

    AWSFirehoseRecorder.register(with: configuration, forKey: "FirehoseName")

    AWSServiceManager.default().defaultServiceConfiguration = configuration
    updateIdentifier()
}

func updateIdentifier() {
    // Settings for AWSCognito
    let credentialsProvider = AWSCognitoCredentialsProvider(regionType: .usEast1, identityPoolId: "us-east-1:00000000-xxxx-0000-0000-xxxxxxxxxxxx")
    credentialsProvider.getIdentityId().continue({ (task: AWSTask!) -> Any! in
        if (task.error != nil) {
            print("Error: " + task.error!.localizedDescription)
        }
        else {
            // the task result will contain the identity id
            self.cognitoId = task.result as? String
            // print("CognitoId: ")
            // print(task.result as? String)
        }
        return nil
    })
}


データを送る場所.swift

    func record(data: [Data]) {
        guard  let firehoseRecorder = AWSFirehoseRecorder.default() else { return }

        let tasks: [AWSTask] = data.map { firehoseRecorder.saveRecord($0, streamName: "FirehoseName") }

        let task = AWSTask(forCompletionOfAllTasks: tasks).continue(successBlock: { (task: AWSTask<AnyObject>) in

            return firehoseRecorder.submitAllRecords()
        })

        task.continue({ [weak self] (task: AWSTask<AnyObject>) in
            switch task.error {
            case let .some(error):
                print("Error: \(error)")
            case .none:
                print("FirehoseRecorded")
            }

            return nil
        })
    }

特にハマったのは参考にしていたページの

AWSTask(forCompletionOfAllTasks: tasks).continueWithSuccessBlock { task -> AnyObject? in
    recorder.submitAllRecords()
}.continueWithBlock { task -> AnyObject? in
    if let err = task.error {
        NSLog("Error: [%@]", err)
    }
    return nil
}

の部分を

let task = AWSTask(forCompletionOfAllTasks: tasks).continue(successBlock: { (task: AWSTask<AnyObject>) in

    return firehoseRecorder.submitAllRecords()
})

task.continue({ [weak self] (task: AWSTask<AnyObject>) in
    switch task.error {
    case let .some(error):
        print("Error: \(error)")
    case .none:
        print("FirehoseRecorded")
    }

    return nil
})

とcontinueを分割した点です。

これでS3に欲しいログをためられるようになりました。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1