20200628のiOSに関する記事は3件です。

FlutterでWeb,Android,iOSの3プラットフォームから同じコードでS3に写真をアップロードする

概要

6月にWeb向けのImagePickerが出ていたので、ネイティブアプリとブラウザアプリを同じソースコードでビルドして、どのデバイスでもファイルアップロード出来るのかというのを試しました。
AndroidとiOSに加えてWebでもPickerが同じ処理で使えるようになったのは結構画期的だなと思ってのメモです。内容はクライアント側に重点を置いています。

1. パッケージをインポートする。

pubspec.yamlにimage_pickerとimage_picker_for_web、amazon_cognito_identity_dart_2を追加する。
私が試したバージョンは以下です。

image_picker: ^0.6.7
image_picker_for_web: ^0.1.0+1
amazon_cognito_identity_dart_2: ^0.1.14

2. 画像取得処理を書く。thenを使用してますが、awaitでも問題ありません。

import 'package:image_picker/image_picker.dart';
~
~
var picker = ImagePicker();
picker.getImage(source: ImageSource.gallery)
    .then((PickedFile value) {
      // ここから必要に応じて後続の処理を呼び出す
    });

3. アップロード処理を書く。

こちら( https://pub.dev/packages/amazon_cognito_identity_dart_2#signing-requests )のページのFor S3 Uploadsをベースにしますが、PickedFileはlengthが取れないので少し改造します。PolicyやSigV4はそのままでmain()だけ変更します。
私の場合はuploadImageとして以下のようにしました。

  Future<String> uploadImage(PickedFile file) async {
    final _credentials = CognitoCredentials(ID_POOL_ID, _userPool);
    final _cognitoUser = CognitoUser(_username, _userPool);
    final authDetails =
    AuthenticationDetails(username: _username, password: _passwd);
    CognitoUserSession _session;
    try {
      _session = await _cognitoUser.authenticateUser(authDetails);
    } catch (e) {
      print(e);
      return null;
    }
    await _credentials.getAwsCredentials(_session.getIdToken().getJwtToken());
    final uri = Uri.parse(_s3Endpoint);
    final req = http.MultipartRequest("POST", uri);
    final multipartFile = http.MultipartFile.fromBytes('file', await file.readAsBytes(),contentType: MediaType('application', 'octet-stream'));
    logger.d('Policy.fromS3PresignedPost');
    var uuid = Uuid();
    String filename = 'test.jpg';
    final policy = Policy.fromS3PresignedPost(
        filename,
        _backetName,
        150,
        _credentials.accessKeyId,
        10000000,
        _credentials.sessionToken,
        region: REGION);
    final key = SigV4.calculateSigningKey(
        _credentials.secretAccessKey, policy.datetime, REGION, 's3');
    final signature = SigV4.calculateSignature(key, policy.encode());
    req.files.add(multipartFile);
    req.fields['key'] = policy.key;
    req.fields['acl'] = 'public-read';
    req.fields['X-Amz-Credential'] = policy.credential;
    req.fields['X-Amz-Algorithm'] = 'AWS4-HMAC-SHA256';
    req.fields['X-Amz-Date'] = policy.datetime;
    req.fields['Policy'] = policy.encode();
    req.fields['X-Amz-Signature'] = signature;
    req.fields['x-amz-security-token'] = _credentials.sessionToken;
    try {
      final res = await req.send();
      logger.d('res:${res.toString()}');
      await for (var value in res.stream.transform(utf8.decoder)) {
        logger.d(value);
      }
      return filename;
    } catch (e, stacktrace) {
      logger.e(e);
      logger.e(stacktrace);
    }
    return null;
  }
}

ID_POOL_IDや_userPoolは各環境に合わせて定義してください。
fromS3PresignedPostへ渡すパラメータmaxFileSizeは一旦10Mとしているので必要に応じて変更してください。
リンク先と主に異なるのは、final file = File(path.join('/path/to/my/folder', 'square-cinnamon.jpg'));PickedFileに置き換わっていて、
streamからMultipartFileを生成する処理

final stream = http.ByteStream(DelegatingStream.typed(file.openRead()));
final multipartFile = http.MultipartFile('file', stream, length,
      filename: path.basename(file.path));


bytesからMultipartFileを生成する処理

final multipartFile = http.MultipartFile.fromBytes('file', await file.readAsBytes(),contentType: MediaType('application', 'octet-stream'));

になっております。目的としてはlengthが取れないからbytesでのリクエストにしました。
クライアント側はこんな感じです。

他に

  • Cognitoのユーザプール作成
  • IDプール作成
  • IAM追加
  • S3のCORS設定
  • iOSのNSPhotoLibraryUsageDescription

が必要です。特にS3のCORS設定はブラウザからのみアップロードできない問題に直面するので、ハマりポイントとして記載します。(その他は調べれば出てくると思うので他の方の記事をご参照ください。)

4. サーバ側番外編 S3でCORSの設定

こちら( https://docs.aws.amazon.com/ja_jp/sdk-for-javascript/v2/developer-guide/cors.html )のCORS設定例を参考にS3のバケット>アクセス権限>CORSの設定で設定してください。AllowedOriginだけ各環境に合わせて変える必要があります。
ローカル環境からの試しであればアスタリスク*で設定すると早いです。

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
  <CORSRule>
    <AllowedOrigin>https://example.org</AllowedOrigin>
    <AllowedMethod>HEAD</AllowedMethod>
    <AllowedMethod>GET</AllowedMethod>
    <AllowedMethod>PUT</AllowedMethod>
    <AllowedMethod>POST</AllowedMethod>
    <AllowedMethod>DELETE</AllowedMethod>
    <AllowedHeader>*</AllowedHeader>
    <ExposeHeader>ETag</ExposeHeader>
    <ExposeHeader>x-amz-meta-custom-header</ExposeHeader>
  </CORSRule>
</CORSConfiguration>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Unity で Automatically manage signing を行う

はじめに

Unity & Xcode でビルドを作成する際に毎回忘れてしまう署名関連の備忘録として残しておこうと思います。

Manual or Auto

『Certificates, Identifiers & Profiles』の署名関係のファイルなどを Xcode だけで出来るようになっています。
以前までは自前でポータルサイトにログインしてそれぞれのファイルを作成していましたが、今はそれが不要になっています。
エンタープライズ版の場合は * アスタリスクでどのアプリでも利用することができたのですが、今はそれを行う必要がなく Unity を利用する場合は Preferences と PlayerSettings に設定するだけです。

注意

Automatically manage signing を行う場合にポータルで同じ Bundle ID のプロビジョニングプロファイルなどの作成はしないようにしてください。もし、同じ Bundle ID が存在すると競合してしまいエラーになります。

Unity 側の設定

Unity 側には二箇所設定する項目があります。

Preferences の設定

まずは Preferences ですが『Xcode Default Settings』という設定がありますが、それぞれ以下のような値を設定します。

  • Automatically Sign にチェック
  • Signing Team Id にポータルの Team ID を指定
  • iOS/tvOS の Manual は利用しないのでそのまま

image.png

Team ID はポータルのアカウントで『Membership』に記載されています。

image.png

PlayerSettings の設定

次はプロジェクトごとの PlayerSettings です。
『PlayerSettings > Other Settings > Identification』で以下のような値を設定します。

  • Bundle Identifier に Bundle ID
  • Signing Team ID に Team ID を指定
  • Automatically Sign にチェック

image.png

Xcode Default Settings の値と同じにします。
まぁ、設定しなくてもデフォルト設定が利用されると思うのですが…。

ビルド

Unity の設定を行いビルドを行います。
生成された Xcode プロジェクトを開いて『Signing & Capabilities』の設定を見るとちゃんと設定がされています。

image.png

ポータル側も以下のように証明書、プロファイルがそれぞれ登録されています。

『Certificates,Identifiers & Profiles > Certificates』にはビルドを実行した端末が登録されています。
image.png

『Certificates,Identifiers & Profiles > Identifiers』には『XC <Bundle ID>』という ID が登録されます。
例えば『com.aaa.BBB』とした場合『XC com aaa BBB』という感じの ID です。
image.png

おわりに

とりあえず毎回忘れてしまう署名関連の処理。
Unity を使えば Unity の設定だけで済みますのでかなり楽になります。

一度 Manual ではなく Auto でビルドすることを検討してみてください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

iOS アプリ起動時に "The Google Mobile Ads SDK was initialized without an application ID" で強制終了する場合に必要な対応

概要

iOS アプリ起動直後に以下のようなログを出力して強制終了する件について、原因と対応方法をまとめました。

*** Terminating app due to uncaught exception 'GADInvalidInitializationException', reason: 'The Google Mobile Ads SDK was initialized without an application ID. Google AdMob publishers, follow instructions at 
https://googlemobileadssdk.page.link/admob-ios-update-plist to set a valid application ID. Google Ad Manager publishers, follow instructions at https://googlemobileadssdk.page.link/ad-manager-ios-update-plist.'

対応方法

ログに含まれている URL を開き、そこに書かれている記載に従い、 Info.plist の修正をする必要があります。

Google AdMob

Google AdMob を利用している場合には、以下リンク先のスタートガイドの記載に従い、 Info.plistGADApplicationIdentifier キーと、それに対応する値( AdMob アプリ ID )を追加します。
https://googlemobileadssdk.page.link/admob-ios-update-plist

追加するキーと値の例
<key>GADApplicationIdentifier</key>
<string>ca-app-pub-3940256099942544~1458002511</string>

Google Ad Manager

Google Ad Manager を利用している場合には、以下リンク先のスタートガイドの記載に従い、 Info.plistGADIsManagerApp キーと、それに対応する値 YES を追加します。
https://googlemobileadssdk.page.link/ad-manager-ios-update-plist

追加するキーと値の例
<key>GADIsAdManagerApp</key>
<true/>

この問題の背景

上記2つのスタートガイドの両方に記載されているとおり、 Google Mobile Ads SDK のバージョン 7.42.0 以降では、 Info.plist に必要な値を設定しておかないとアプリが強制終了します。

アプリの Info.plist に AdMob アプリ ID を設定する必要があることについては、 Google Mobile Ads SDK のバージョン 7.42.0リリースノートにも書かれています。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む