20200401のiOSに関する記事は7件です。

iOSアプリのバージョニング方針草案

目的

バージョニングのプラクティス化

前提環境

iOSApp/MacApp開発、CI/CDが構築されている。

iOSのバージョニング概要

iOSのバージョン管理では、以下の二つが利用されます。

  • CFBundleShortVersionString
  • CFBundleVersion

詳しくはこちらに書いてあります。

一言で説明すると
CFBundleShortVersionString: ユーザーから見えるバージョン。セマンティックバージョニングが推奨。2.3.0のようなピリオド区切りでメジャー、マイナー、パッチを管理する。
CFBundleVersion: アプリの内部バージョンでユーザーからは見えない。しかし、AppStoreConnectへバイナリをアップロードする際に、前のビルドよりも数値が上がっている必要がある。

となります。

草案

1. CFBundleShortVersionStringはセマンティックバージョニングで手動管理

こちらは、プロダクトの意図に合わせて変更する必要があるので、手動でplistを弄って変更を行いコミットします。

他に考えられる案

CIを前提にしているので、git tagをトリガーにデプロイフロー→gitのバージョンタグを拾ってCFBundleShortVersionStringにセット。のようなフローも考えられます。

ただ、2.0.0のようなタグを打ってバイナリをアップデートし終わった後に問題が発覚すると、修正し終わった後に前のタグを消して同じタグを新しいコミットに付けるか、もしくは2.0.1のようなパッチバージョンで新しいタグをつけるかしかないので、開発側にある程度パッチバージョンレベルの決定権がないと難しそうです。

2. CFBundleVersionはCIでの自動管理に任せる

要件

この内部バージョンでまず必須なのは
バイナリアップロード時に前のバージョンより大きいこと

そして、あると便利なのが
問題が発生した時にコミットを一意に特定できること

だと考えています。前者はApple側の仕様ですが、後者はこのようなケースが考えられます。

「ユーザーからエラー報告が上がってきたが、ユーザーから見えているバージョンと内部番号は必ずしも一致しないので(リリースはひとつだがビルドが複数存在するケース)、内部番号を使ってどのコミット/ビルドで問題が発生していたのか一意に特定して追跡したい。」

上記の要件を踏まえて「毎回数値が増えていて一意で特定可能」であれば良いので、バイナリアップデートごとにまめに手で数字を増やしても実現可能なのですが、かなり面倒でミスの入り込む余地がありそうなので、CI/CDでの自動管理を提案したいです。

提案手法

自動デプロイフローの際に、CI/CDでのビルド番号をCFBundleVersionにセットする。

TravisCIではTRAVIS_BUILD_NUMBERCircleCIではCIRCLE_BUILD_NUMとして環境変数でビルド番号が取得でき、特にBitriseではステップとしてSet Xcode Project Build Numberというステップを用意しており、plistのパスを指定するだけでCIのバージョン番号を自動設定してくれます。

このCI/CDのビルド番号は、毎回インクリメントされ、かつ、ビルド番号からビルドを特定でき、クローンしているgitのコミットハッシュを特定する事が可能なので、先ほど上げた2つの条件を満たしていると言えます。

ただ1つ問題として、内部バージョンはアーカイブ後に毎回捨てられ、コミットには載らないので気になる人もおられるかもしれません。

まとめ

今回はこのようにCFBundleShortVersionStringはセマンティックバージョニングでプロダクトに応じて手動管理し、CFBundleVersionは手元では一切触らず、CI/CDのビルド番号をセットする自動管理に任せる」というバージョニング方針を提案させて頂きました。

「~な理由でこうした方が良いよ!」「これは~な理由でやめた方が良いよ!」など、ご意見是非コメントいただけると助かります。

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

Flutter環境構築 - 1(Flutter SDKインストール編)

はじめに

Flutterの開発環境構築の手順について解説していきます。
今回は「Flutter SDKインストール編」です!
今後「Xcodeインストール編」「Android Studioインストール編」「VS codeインストール編」も随時執筆していく予定です。
さらにその後には「シミュレーターでの動作確認編」「アプリ実行編」なども執筆予定ですのでしばらくお待ちいただけると幸いです。
また今回の解説では、私自身がmacOSを使用しているということもあり、macOSでの手順解説となります。
Windows・Linuxを使用されている方は別途、Flutter公式サイトのWindows版Linux版 などをご参照下さい。

「Flutter SDKインストール編」でやること

本記事では、以下の項目を行います。

  • Flutter SDKをインストールする
  • Flutterコマンドが常に使えるようにする

システム要件

まずFlutterをインストールして実行するには、以下の要件を満たしている必要があります。

  • オペレーティングシステム(OS): macOS(64-bit)
  • ディスク容量: 2.8 GB (IDE /ツールのディスク容量は含まれません)
  • ツール: 以下のコマンドラインツールが使用できること。
    • bash
    • curl
    • git 2.x
    • mkdir
    • rm
    • unzip
    • which
    • zip

この条件が揃っていましたら、早速環境構築していきましょう!

Flutter SDKをインストールする

1.Flutter公式サイト右上の「Get started」をクリック

スクリーンショット 2020-04-01 15.18.47.png

2.installページにきたらmacOSを選択

スクリーンショット 2020-03-29 23.41.45.jpg

3.Get the Flutter SDK 欄のインストールボタンをクリックし、Flutter SDKをインストール

スクリーンショット 2020-03-29 23.41.56.png

4.完了したら、インストールしたzipファイルをクリックして解凍

Macでは基本「Downloads」の中にインストールされていると思います。
解凍できたら、次項目からターミナルにてコマンドを実行していきます。

5.ホームディレクトリに移動

cd

6.「development」ディレクトリを作成

mkdir development

7.作成した「development」ディレクトリに移動

cd development

8.「flutter」ファイルを「development」ディレクトリの中に移動

ユーザー名のところは各自のユーザー名を入力して下さい。
また、インストールされたflutterファイルの場所によってこちらは書き換えて下さい。

mv /Users/ユーザー名/Downloads/flutter ./

9.「flutter」ファイルに移動

cd flutter

Flutterコマンドが常に使えるようにする

1.Flutterをパスに常に追加する

vi ~/.bash_profile

「i」をタイプして挿入モードにし、export PATHの先頭に以下コードを記入します。
ユーザー名は各自ユーザー名を記述して下さい。
パスの区切りの「:」を忘れないようにして下さい。

/Users/ユーザー名/development/flutter/bin:

コードの全体(例)

export PATH="/Users/ユーザー名/development/flutter/bin:~/.rbenv/shims:/usr/local/bin:$PATH"

escキーで挿入モードを終了します。

編集が完了したら、「:wq」を入力しEnterで編集を終了させます。

:wq

2.Flutterコマンドが実行できるようにする

source ~/.bash_profile

3.Flutterコマンドを実行

flutter

するとコマンドが実行され、「Welcome to Flutter!」と表示されます。
スクリーンショット 2020-03-30 0.07.08.png

これでFlutterコマンドが実行できるようになりました!

最後に

以上で「Flutter SDKインストール編」は終了となります。お疲れ様でした。

またその次のステップとして
「Xcodeインストール編」「Android Studioインストール編」「VS codeインストール編」
の方も随時投稿していきます。よろしくお願いいたします。

最後までご覧下さりありがとうございました。

参考サイト[Flutter公式サイト]

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

MonacaからCustom Schemeプラグインがリリースされてた

Monacaより「Custom Schemeプラグイン」がリリースされていました。
https://ja.monaca.io/headline/#entry_5e835238e78885e357efb480

これは何?

以下記事の通り、WKWebViewでは、file://スキームのローカルリソース(wwwディレクトリ配下のファイル)にXHRでアクセスすることができません。それを解決するためのプラグインの様です。
https://qiita.com/kishisuke/items/d201790aeae2129ff2e3#file%E3%82%B9%E3%82%AD%E3%83%BC%E3%83%A0%E3%81%B8%E3%81%AE%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B920200323%E8%BF%BD%E8%A8%98

「このプラグインを利用することで、WKWebView上においても、ローカルに配置されているリソースへのAjaxアクセスが可能となります。」とあります。実際にインストールしてみると、確かにローカルリソース(wwwディレクトリ配下のファイル)にXHRでアクセスできました。

動きを見てみる

デバッグしてみたところ、cordova-plugin-wkwebview-file-xhrの様にXHRをフックしているわけではなく、wwwディレクトリ配下のファイル全体をカスタムスキームを通じてアクセスする手法を取っているようです。
例えば、index.htmlは以下の様なURLになっています。

monaca-app://monaca.io/www/index.html

カスタムスキームではXHRのアクセス制限がないためローカルリソースにアクセスできる様になるわけです。cordova-plugin-wkwebview-file-xhrと異なり、プラグインの初期化を待たずにローカルリソースにアクセスできるため、よりベターな手法です。

なお、カスタムスキームを使ってローカルリソースにアクセスしているということは、WKURLSchemeHandlerを使っているはずです。このAPIはiOS11以降で追加されたため、このプラグインはiOS11以降でしか使用できないものと思われます。(実際に、iOS10ではアプリを起動することができませんでした)

なお、残念ながらこのプラグインはpublicなnpmレジストリで公開されていません。(2020/4/1時点)
Monaca LocalKitMonaca CLIで開発を行っている場合は、ローカル環境でnpm installを実行する際に一度package.jsonのdependenciesからmonaca-plugin-customschemeを削除する必要があります。

余談

ちなみに、Ionicが提供するWKWebViewのEngineもカスタムスキームを使ってローカルリソースにアクセスできます。
MonacaのCustom Schemeプラグインはcordova-plugin-wkwebview-engineと併用できる点が良いですね。次のメジャーバージョンでCordova本体にWKWebViewが統合されるので、

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

1つ以上のProvisioning Profileがインストールできないとは

Provisioning Profileをインストールして、ダブルクリックしたら、

Failed to install one or more provisioning profiles on the device.
Please ensure the provisioning profile is configured for this device. If not, please try to generate a new profile.

というエラーが出たときの対処法について。

原因

最近Apple Developer上で登録Deviceの整理をしたりしませんでしたか。
あるいはProvisioning Profileの整理など。

登録情報がなにかしら変更されて、
Provisioning Profileの上書きができなくなってしまっている状態です。

対処法

すでにMacに保存しているProvisioning Profileを削除して、
再読み込みをしてあげれば良いです。

削除すべきProvisioning Profileを知る

Signing&CapabilitiesのProvisioning Profileの
インフォメーションアイコンをタップして表示される「PROV」画像のとこ、
実はdrop&dragできるProvisioning Profileのファイルになってます。

これを一旦デスクトップにでもうつして、ファイル名をメモしておきます。
例: 1234ab45-abcd-1234-abcd-abcdef1234ghi.mobileprovision

Provisioning Profileを削除する

Terminalでroot directoryにて、対象ディレクトリに移動して、

$ cd Library/MobileDevice/Provisioning\ Profiles

対象の.moblieprovisionファイルがあるか確認し、

$ cat 1234ab45-abcd-1234-abcd-abcdef1234ghi.mobileprovision

削除します。

$ rm 1234ab45-abcd-1234-abcd-abcdef1234ghi.mobileprovision

Provisioning Profileを更新

Apple Developerからインストールしたものをダブルクリックしてもいいですし、
Automatically auto signingを一旦OFF -> 再度ONにするだけで更新されます。

以上です。

ちなみに

Xcode encountered an error

というエラーが出る場合、Xcode11.2.1の既存バグなので、
Xcodeをアップデートしましょう。

https://stackoverflow.com/questions/59016207/xcode-encountered-an-error-in-xcode-11-2-1

参考

https://conocode.com/troubleshooting/provisioning-profile-reload/

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

stripe-iosでクレジットカード情報のZIPを非表示にしたい

stripe-iosとは、クレジットカードで決済を行うiOSアプリを作成する上で、
必須と言っても過言ではない素晴らしいSDKです。

※stripe-ios githubページより。
https://github.com/stripe/stripe-ios

ただ現在、クレジットカードの情報として入力する項目が、

  • 番号
  • 有効期限
  • セキュリティコード(CVC)
  • ZIP

の4種類がデフォルトになっています。
ちょっと前までデフォルトはZIP以外の3つだったはずなのに。

てかZIPってなに!!!!!?????

と無能な自分は思ってしまったので、これはそんな悩みを解決する記事です。

ZIPとは

参考: https://www.shineray-ryokou.com/post/zip-code
参考記事をまとめると、以下です。

  • ZIP Codeとは、「郵便番号」です。
  • 別名、Postal Code。
  • ZIP Codeの目的: セキュリティ強化のため。
  • 日本のクレジットカードにはZIP Codeが存在しない。

ZIP Codeを非表示に

iOSアプリの公開範囲が日本のみの場合、ZIP Codeは入力不要ですし、
入力項目として表示するのも不要です。
では非表示にしましょう。

var cardTextField = STPPaymentCardTextField(frame: CGRect(
            x: 0,
            y: 0,
            width: UIScreen.main.bounds.height,
            height: 44.0))

...

cardTextField.postalCodeEntryEnabled = false

以上です。ZIP Codeは別名Postal Codeなので。

その他参考

https://qiita.com/k-boy/items/c99cc681c29a1b250b4a

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

Flutterで環境構築からHelloWorld & スプラッシュまでやってみた。【HelloWorld&スプラッシュ編】

お久しぶりです。
多忙な日々を送っていたらもう4月になってました。

今更感がすごいのですが前回の環境構築編からそのまま放置状態だったので
今回は簡単なスプラッシュ〜Helloworldを作成していきましょう。

画像を呼び出せるように設定する。

スプラッシュスクリーンで画像を表示しようと思うのでpubspec.yamlにて下記の設定を追加します。

pubspec.yaml
flutter:

  # The following line ensures that the Material Icons font is
  # included with your application, so that you can use the icons in
  # the material Icons class.
  uses-material-design: true

  # To add assets to your application, add an assets section, like this:
  # ↓この部分を追加する↓
  assets:
    - assets/images/logo.png

プロジェクトディレクトリ配下にassets/imagesディレクトリを作成しlogo.pngを配置してください。

main.dartを修正する。

すでにプロジェクトディレクトリ内の/libの中にmain.dartというファイルがあるかと思います。
main.dartを下記のようにしてスッキリさせませう。

main.dart
import 'package:flutter/material.dart';
import 'package:my_app/splash.dart';

void main() => runApp(MyApp());

スプラッシュ画面の作成

今回実装するスプラッシュではAppbarを利用しませんので記述しません。
3秒間スプラッシュ画面を表示してその後メインスクリーン(例ではAppScreen)に遷移するという流れです。
.thenを利用したいので非同期処理であるFutureを利用しています。

通常は画面遷移の場合Navigator.pushを利用するのですが、Navigator.pushでメインスクリーンへ遷移した場合スプラッシュ画面に戻れてしまいます。
そのため、画面を上乗せするのではなく、置き換えをするNavigator.pushReplacementを利用します。
FadeTransitionも利用していますが公式ドキュメントを読んでいただくかYoutubeの「Widget of the Week」にて説明していますのでそちらを参考にしていただければと思います。

https://api.flutter.dev/flutter/widgets/FadeTransition-class.html

splash.dart
import 'package:flutter/material.dart';
import 'package:flutter/animation.dart';
import 'package:my_app/app_screen.dart';

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'スプラッシュ',
      home: Splash(),
    );
  }
}

class Splash extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _SplashState();
}

class _SplashState extends State<Splash> with TickerProviderStateMixin {
  AnimationController _controller;
  Animation<double> _animation;
  @override
  void initState() {
    super.initState();
    Future.delayed(Duration(milliseconds: 3000)).then((value) =>
        Navigator.pushReplacement(
            context, MaterialPageRoute(builder: (context) => AppScreen())));
    _controller = AnimationController(
        duration: const Duration(milliseconds: 1000), vsync: this);
    _animation = CurvedAnimation(parent: _controller, curve: Curves.easeIn);
    _controller.forward();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Center(
          child: FadeTransition(
            opacity: _animation,
            child: Image.asset('assets/images/logo.png'),
          ),
        ),
      ),
    );
  }
}

HelloWorldを表示する為だけのスクリーンを作成する。

app_screen.dart
import 'package:flutter/material.dart';

class AppScreen extends StatefulWidget {
  static const String routeName = '/app';

  @override
  _AppScreenState createState() => _AppScreenState();
}

class _AppScreenState extends State<AppScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text('ホームスクリーン'),
        ),
        body: Center(child: Text("HelloWorld")));
  }
}

設定した画像がフェードで表示された後に一方通行の画面遷移をしてあとにHelloWorldが表示されたと思います!
おめでとうございます!!!:v_tone2::v_tone2::v_tone2:

hello.png

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

独自プロパティリスト(plist)を利用する

iOSアプリで独自plist(プロパティリスト)を作って使いたい時のメモ。
※よくある、APIのURLとかその他アプリに特化した設定などに使用
※他の方がわかりやすく書いてくれているのもあるのでメモ程度

plistの作成

まずはお目当てのplistを作成します。

  • File→New→File(cmd+N)で新規作成画面を表示
  • Property Listを選択、名前(今回はApp.plist)を決めて作成

作成できたら、適当に値を設定しましょう。
今回はよくあるURLを記載します

作ったplistの読み込み

先程作ったApp.plistを読み込むコードが以下になります。

var property: Dictionary<String, Any> = [:]
// App.plistのパス取得
let path = Bundle.main.path(forResource: "App", ofType: "plist")
// App.plistをDictionary形式で読み込み
let configurations = NSDictionary(contentsOfFile: path!)
if let datasourceDictionary: [String : Any]  = configurations as? [String : Any] {
    property = configurations
}
// キーを指定して、値の取得
let url = property["url"] as! String
print(url)

データ取得部分も含めてクラス化

読み込み、データ取得を毎度書くのは無駄よってことで
クラス化します。

App.swift
class App {
    var property: Dictionary<String, Any> = [:]
    init() {
        // App.plistのパス取得
        let path = Bundle.main.path(forResource: "App", ofType: "plist")
        // App.plistをDictionary形式で読み込み
        let configurations = NSDictionary(contentsOfFile: path!)
        if let datasourceDictionary: [String : Any]  = configurations as? [String : Any] {
            property = datasourceDictionary
        }
    }

    /// 指定されたキーの値を取得する
    /// - Parameter key: plistのキー
    func getString(_ key: String) -> String? {
        guard let value: String = property[key] as? String else {
            return nil
        }
        return value
    }
}

さて、これで利用できるようになりました。
では実際に呼び出してみましょう。

var app = App()
print(app.getStirng("url"))

Appクラスをシングルトンにしてみる

シングルトンにすることで、読み込みを一回だけにしてあとはインスタンス再利用する形にします。
以下コードを追加

static let shared: App = {
    let instance = App()
    return instance
}()

全体

App.swift
class App {
    var property: Dictionary<String, Any> = [:]
    static let shared: App = {
        let instance = App()
        return instance
    }()
    private init() {
        // App.plist取得
        let path = Bundle.main.path(forResource: "App", ofType: "plist")
        let configurations = NSDictionary(contentsOfFile: path!)
        if let datasourceDictionary: [String : Any]  = configurations as? [String : Any] {
            property = datasourceDictionary
        }
    }

    /// 指定されたキーの値を取得する
    /// - Parameter key: plistのキー
    func getString(_ key: String) -> String? {
        guard let value: String = property[key] as? String else {
            return nil
        }
        return value
    }
}

さて、これで利用方法を変更します。

var app = App.shared
print(app.getStirng("url"))

これでアプリ独自プロパティも怖くない!!
次回は、これを環境別に設定できるようにしたいと思います。

参考

Swift で簡単なシングルトンの実装方法
Swiftでplistを読み込む

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