この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
Lambda を MBaaS として使う
先日米国ラスベガスで開催された『re:Invent 2014』にて発表されたAWS Lambdaに関するエントリを12/01から12/25まで毎日1本ずつ書いていくアドベントカレンダー『AWS Lambda Advent Calendar 2014』。このエントリは5日目の内容となります。
先日4日目のエントリはj3tm0t0さんの『AWS Lambda を CoffeeScript で書いてみた』でした。
本日5日目は、Lambda API を iOS アプリから直接呼び出すという内容です。
Lambda API を使うと、Lambda ファンクションをモバイルから直接呼び出すことができたり、Lambda ファンクションの取得や登録などといったことも可能です。Lambda ファンクションを呼び出すというのを始めに想像しますが、結構試されている記事も多いので、今回は Lambda ファンクションの情報を取得し、iOS アプリに表示するという処理を実装してみたいと思います。
AWS Mobile SDK は未対応
Lambda API の呼び出しは AWS Mobile SDK を使えば楽勝!と言いたいところですが、現在(2014/12/05)の時点では Lambda は未対応です。
それでもどうしても iOS から呼んでみたい!ということで、今回は Lambda のクライアントのクラスを自作しました。実装は少し長いので、Lambda のクラス実装部分だけ載せます。
AWSLambdaModel.h
#import "AWSNetworking.h"
#import "AWSModel.h"
@class AWSLambdaGetFunctionRequest;
@class AWSLambdaGetFunctionResponse;
@class AWSLambdaFunctionConfiguration;
@class AWSLambdaFunctionCodeLocation;
@interface AWSLambdaGetFunctionRequest : AWSRequest
@property (nonatomic, strong) NSString *functionName;
@end
@interface AWSLambdaGetFunctionResponse : AWSModel
@property (nonatomic, strong) AWSLambdaFunctionConfiguration *configuration;
@property (nonatomic, strong) AWSLambdaFunctionCodeLocation *code;
@end
@interface AWSLambdaFunctionConfiguration : AWSModel
@property (nonatomic, strong) NSString *functionName;
@property (nonatomic, strong) NSString *functionARN;
@property (nonatomic, strong) NSString *configurationId;
@property (nonatomic, strong) NSString *runtime;
@property (nonatomic, strong) NSString *role;
@property (nonatomic, strong) NSString *handler;
@property (nonatomic, strong) NSString *mode;
@property (nonatomic, strong) NSString *codeSize;
@property (nonatomic, strong) NSString *functionDescription;
@property (nonatomic, strong) NSString *timeout;
@property (nonatomic, strong) NSString *memorySize;
@property (nonatomic, strong) NSString *lastModified;
@end
@interface AWSLambdaFunctionCodeLocation : AWSModel
@property (nonatomic, strong) NSString *location;
@property (nonatomic, strong) NSString *repositoryType;
@end
AWSLambdaModel.m
#import "AWSLambdaModel.h"
@implementation AWSLambdaGetFunctionRequest
+ (NSDictionary *)JSONKeyPathsByPropertyKey {
return @{@"functionName" : @"FunctionName"};
}
@end
@implementation AWSLambdaGetFunctionResponse : AWSModel
+ (NSDictionary *)JSONKeyPathsByPropertyKey {
return @{@"configuration" : @"Configuration",
@"code" : @"Code"};
}
+ (NSValueTransformer *)configurationJSONTransformer {
return [NSValueTransformer mtl_JSONDictionaryTransformerWithModelClass:[AWSLambdaFunctionConfiguration class]];
}
+ (NSValueTransformer *)codeJSONTransformer {
return [NSValueTransformer mtl_JSONDictionaryTransformerWithModelClass:[AWSLambdaFunctionCodeLocation class]];
}
@end
@implementation AWSLambdaFunctionConfiguration : AWSModel
+ (NSDictionary *)JSONKeyPathsByPropertyKey {
return @{@"functionName" : @"FunctionName",
@"functionARN" : @"FunctionARN",
@"configurationId" : @"ConfigurationId",
@"runtime" : @"Runtime",
@"role" : @"Role",
@"handler" : @"Handler",
@"mode" : @"Mode",
@"codeSize" : @"CodeSize",
@"functionDescription" : @"Description",
@"timeout" : @"Timeout",
@"memorySize" : @"MemorySize",
@"lastModified" : @"LastModified"};
}
@end
@implementation AWSLambdaFunctionCodeLocation : AWSModel
+ (NSDictionary *)JSONKeyPathsByPropertyKey {
return @{@"repositoryType" : @"RepositoryType",
@"location" : @"Location"};
}
@end
AWSLambda.h
#import "AWSService.h"
#import "AWSLambdaModel.h"
@class BFTask;
@interface AWSLambda : AWSService
@property (nonatomic, strong, readonly) AWSServiceConfiguration *configuration;
+ (instancetype)defaultLambda;
- (instancetype)initWithConfiguration:(AWSServiceConfiguration *)configuration;
// Lambdaファンクションの情報の取得
- (BFTask *)getFunction:(AWSLambdaGetFunctionRequest *)request;
@end
AWSLambda.m
@implementation AWSLambda
// ...省略...
#pragma mark - Service method
- (BFTask *)getFunction:(AWSLambdaGetFunctionRequest *)request {
return [self invokeRequest:request
HTTPMethod:AWSHTTPMethodGET
URLString:@"/2014-11-13/functions/{FunctionName}/"
targetPrefix:@""
operationName:@"GetFunction"
outputClass:nil];
}
@end
この他に、次のような修正を行います。
- lambda-2014-11-11.json の追加
- AWSServiceType に AWSServiceLambda を追加
- AWSEndpoint#initWithRegion:service:useUnsafeURL: に分岐処理を追加
Lambda ファンクションの実装
今回はとりあえず取得できるか試したかったので、 Lambda ファンクションはかなり簡単なものです。
console.log('Loading event');
exports.handler = function(event, context) {
context.done(null, 'Hello, iOS Client!');
};
iOS アプリ側の実装
もろもろ用意ができたところで、最後に呼び出すところの実装です。もちろん Cognito で認証します!
ViewController.m
- (void)getFunction {
// Cognito認証
AWSCognitoCredentialsProvider *provider
= [AWSCognitoCredentialsProvider credentialsWithRegionType:AWSRegionUSEast1
accountId:@"xxxxxxxxxxxx"
identityPoolId:@"us-east-1:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
unauthRoleArn:@"arn:aws:iam::xxxxxxxxxxxx:role/Cognito_SampleAppUnauth_DefaultRole"
authRoleArn:@"arn:aws:iam::xxxxxxxxxxxx:role/Cognito_SampleAppAuth_DefaultRole"
logins:nil];
// Lambdaファンクションを取得する
AWSLambdaGetFunctionRequest *request = [AWSLambdaGetFunctionRequest new];
request.functionName = @"helloMobileClient";
AWSLambda *lambda = [AWSLambda defaultLambda];
[[lambda getFunction:request] continueWithBlock:^id(BFTask *task) {
if (task.error) {
NSLog(@"エラー : %@", task.error);
} else {
NSLog(@"成功 : %@", task.result);
AWSLambdaGetFunctionResponse *response = task.result;
dispatch_async(dispatch_get_main_queue(), ^{
AWSLambdaFunctionConfiguration *configuration = response.configuration;
NSString *result = [NSString stringWithFormat:@"configurationId = %@\nfunctionName = %@\nfunctionDescription = %@\nfunctionARN = %@\nhandler = %@\ncodeSize = %@\nmemorySize = %@\nmode = %@\nlastModified = %@\nrole = %@\nruntime = %@\ntimeout = %@", configuration.configurationId, configuration.functionName, configuration.functionDescription, configuration.functionARN, configuration.handler, configuration.codeSize, configuration.memorySize, configuration.mode, configuration.lastModified, configuration.role, configuration.runtime, configuration.timeout];
self.label.text = result;
});
}
return nil;
}];
}
アプリを実行してログを見てみると、取得出来てるはずです!
まとめ
AWS Mobile SDK が対応していないけどどうしても試したい!っていう方はぜひ参考にしていただければと思います。クラスの設計自体は既存のサービスクラスと同じように実装しているので、今後もし AWS Mobile SDK 側がアップデートで Lambda を対応したとしても、利用するクラスの実装コードはきっとそのままで動くはずです!多分w
ちなみに AWS Mobile SDK の実装ソースをちょっと解読できたので、個人的にもタメになりました。
明日6日目はyoshidashingoさんによる「既存のアーキテクチャのどこかを置き換えてみる」です。お楽しみに!