- 投稿日:2020-04-01T23:19:47+09:00
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_NUMBER
、CircleCIではCIRCLE_BUILD_NUM
として環境変数でビルド番号が取得でき、特にBitriseではステップとしてSet Xcode Project Build Number
というステップを用意しており、plistのパスを指定するだけでCIのバージョン番号を自動設定してくれます。このCI/CDのビルド番号は、毎回インクリメントされ、かつ、ビルド番号からビルドを特定でき、クローンしているgitのコミットハッシュを特定する事が可能なので、先ほど上げた2つの条件を満たしていると言えます。
ただ1つ問題として、内部バージョンはアーカイブ後に毎回捨てられ、コミットには載らないので気になる人もおられるかもしれません。
まとめ
今回はこのように「
CFBundleShortVersionString
はセマンティックバージョニングでプロダクトに応じて手動管理し、CFBundleVersion
は手元では一切触らず、CI/CDのビルド番号をセットする自動管理に任せる」というバージョニング方針を提案させて頂きました。「~な理由でこうした方が良いよ!」「これは~な理由でやめた方が良いよ!」など、ご意見是非コメントいただけると助かります。
- 投稿日:2020-04-01T22:07:17+09:00
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」をクリック
2.installページにきたらmacOSを選択
3.Get the Flutter SDK 欄のインストールボタンをクリックし、Flutter SDKをインストール
4.完了したら、インストールしたzipファイルをクリックして解凍
Macでは基本「Downloads」の中にインストールされていると思います。
解凍できたら、次項目からターミナルにてコマンドを実行していきます。5.ホームディレクトリに移動
cd6.「development」ディレクトリを作成
mkdir development7.作成した「development」ディレクトリに移動
cd development8.「flutter」ファイルを「development」ディレクトリの中に移動
ユーザー名のところは各自のユーザー名を入力して下さい。
また、インストールされたflutterファイルの場所によってこちらは書き換えて下さい。mv /Users/ユーザー名/Downloads/flutter ./9.「flutter」ファイルに移動
cd flutterFlutterコマンドが常に使えるようにする
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で編集を終了させます。
:wq2.Flutterコマンドが実行できるようにする
source ~/.bash_profile3.Flutterコマンドを実行
flutterするとコマンドが実行され、「Welcome to Flutter!」と表示されます。
これでFlutterコマンドが実行できるようになりました!
最後に
以上で「Flutter SDKインストール編」は終了となります。お疲れ様でした。
またその次のステップとして
「Xcodeインストール編」「Android Studioインストール編」「VS codeインストール編」
の方も随時投稿していきます。よろしくお願いいたします。最後までご覧下さりありがとうございました。
参考サイト[Flutter公式サイト]
- 投稿日:2020-04-01T20:25:11+09:00
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 LocalKitやMonaca CLIで開発を行っている場合は、ローカル環境でnpm install
を実行する際に一度package.jsonのdependenciesからmonaca-plugin-customschemeを削除する必要があります。余談
ちなみに、Ionicが提供するWKWebViewのEngineもカスタムスキームを使ってローカルリソースにアクセスできます。
MonacaのCustom Schemeプラグインはcordova-plugin-wkwebview-engineと併用できる点が良いですね。次のメジャーバージョンでCordova本体にWKWebViewが統合されるので、
- 投稿日:2020-04-01T18:38:36+09:00
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.mobileprovisionProvisioning 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/
- 投稿日:2020-04-01T18:16:34+09:00
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なので。
その他参考
- 投稿日:2020-04-01T14:34:59+09:00
Flutterで環境構築からHelloWorld & スプラッシュまでやってみた。【HelloWorld&スプラッシュ編】
お久しぶりです。
多忙な日々を送っていたらもう4月になってました。今更感がすごいのですが前回の環境構築編からそのまま放置状態だったので
今回は簡単なスプラッシュ〜Helloworldを作成していきましょう。画像を呼び出せるように設定する。
スプラッシュスクリーンで画像を表示しようと思うのでpubspec.yamlにて下記の設定を追加します。
pubspec.yamlflutter: # 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.dartimport '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.dartimport '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.dartimport '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が表示されたと思います!
おめでとうございます!!!
- 投稿日:2020-04-01T09:33:43+09:00
独自プロパティリスト(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.swiftclass 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.swiftclass 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"))これでアプリ独自プロパティも怖くない!!
次回は、これを環境別に設定できるようにしたいと思います。参考