fastlaneで複数の国のメタデータを一括更新
アプリの申請や証明書周りを自動化するfastlaneを試してみました。
インストール
インストールはgemを利用します。
gem install fastlane
fastlaneの初期化
インストールが終わったらfastlaneの初期化を行います。
プロジェクトのフォルダで以下コマンドを実行します。
fastlane init
そうするとAppleIDを聞かれるので答えます。
今回は既にあるアプリに適用したので、iTunesconnectからメタデータをダウンロードしています。
ダウンロードしたファイルはfastlaneフォルダ以下に入ります。
fastlaneでメタデータをアップロード
fastlaneを使ったメタデータの更新を試してみます。
fastlane/metadata/en-USフォルダのrelease_notes.txtを更新します。
次にプロジェクトのルートフォルダで以下コマンドを打ちます。
fastlane deliver
すると以下のようにアップロードを開始してくれます。
iTunesconnectにアクセスすると先程更新したReleaseNoteが反映されている事が分かります。
ファイルを用意するだけでメタデータの更新ができるので、複数国対応しているアプリのリリースも非常に楽になりそうです。
【Swift】Chartsを使って色々なグラフを描画してみる
Chartsというライブラリを試して色々なグラフを使ってみました。
インストール
CocoaPodsでインストールしました。
target 'MyApp' do use_frameworks! pod "Charts" end
棒グラフ
import UIKit import Charts class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() var rect = view.bounds rect.origin.y += 20 rect.size.height -= 20 let barChartView = BarChartView(frame: rect) let entry = [ BarChartDataEntry(x: 10, y: 30), BarChartDataEntry(x: 20, y: 20), BarChartDataEntry(x: 30, y: 40), BarChartDataEntry(x: 40, y: 10), BarChartDataEntry(x: 50, y: 30) ] let set = [ BarChartDataSet(values: entry, label: "Data") ] barChartView.data = BarChartData(dataSets: set) view.addSubview(barChartView) } }
バブルチャート
import UIKit import Charts class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() var rect = view.bounds rect.origin.y += 20 rect.size.height -= 20 let chartView = BubbleChartView(frame: rect) let entries = [ BubbleChartDataEntry(x: 1, y: 10, size: 30), BubbleChartDataEntry(x: 2, y: 60, size: 10), BubbleChartDataEntry(x: 3, y: 20, size: 20), BubbleChartDataEntry(x: 4, y: 40, size: 30), BubbleChartDataEntry(x: 5, y: 30, size: 40), BubbleChartDataEntry(x: 6, y: 10, size: 20) ] let set = BubbleChartDataSet(values: entries) chartView.data = BubbleChartData(dataSet: set) view.addSubview(chartView) } }
ロウソク足
import UIKit import Charts class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() var rect = view.bounds rect.origin.y += 20 rect.size.height -= 20 let chartView = CandleStickChartView(frame: rect) let entries = [ CandleChartDataEntry(x: 1, shadowH: 12, shadowL: 10, open: 11, close: 11), CandleChartDataEntry(x: 2, shadowH: 20, shadowL: 15, open: 18, close: 17), CandleChartDataEntry(x: 3, shadowH: 30, shadowL: 24, open: 26, close: 28), ] let set = CandleChartDataSet(values: entries, label: "Test") chartView.data = CandleChartData(dataSet: set) view.addSubview(chartView) } }
横棒グラフ
データは棒グラフ同様、BarChartDataを使います。
import UIKit import Charts class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() var rect = view.bounds rect.origin.y += 20 rect.size.height -= 20 let chartView = HorizontalBarChartView(frame: rect) let entry = [ BarChartDataEntry(x: 1, y: 30), BarChartDataEntry(x: 2, y: 20), BarChartDataEntry(x: 3, y: 40), BarChartDataEntry(x: 4, y: 10), BarChartDataEntry(x: 5, y: 30) ] let set = BarChartDataSet(values: entry, label: "Data") chartView.data = BarChartData(dataSet: set) view.addSubview(chartView) } }
折れ線グラフ
import UIKit import Charts class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() var rect = view.bounds rect.origin.y += 20 rect.size.height -= 20 let chartView = LineChartView(frame: rect) let entries = [ BarChartDataEntry(x: 1, y: 30), BarChartDataEntry(x: 2, y: 20), BarChartDataEntry(x: 3, y: 40), BarChartDataEntry(x: 4, y: 10), BarChartDataEntry(x: 5, y: 30) ] let set = LineChartDataSet(values: entries, label: "Data") chartView.data = LineChartData(dataSet: set) view.addSubview(chartView) } }
円グラフ
import UIKit import Charts class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() var rect = view.bounds rect.origin.y += 20 rect.size.height -= 20 let chartView = PieChartView(frame: rect) let entries = [ PieChartDataEntry(value: 10, label: "A"), PieChartDataEntry(value: 20, label: "B"), PieChartDataEntry(value: 30, label: "C"), PieChartDataEntry(value: 40, label: "D"), PieChartDataEntry(value: 50, label: "E") ] let set = PieChartDataSet(values: entries, label: "Data") chartView.data = PieChartData(dataSet: set) view.addSubview(chartView) } }
レーダーチャート
import UIKit import Charts class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() var rect = view.bounds rect.origin.y += 20 rect.size.height -= 20 let chartView = RadarChartView(frame: rect) let entries = [ RadarChartDataEntry(value: 40), RadarChartDataEntry(value: 30), RadarChartDataEntry(value: 20), RadarChartDataEntry(value: 40), ] let set = RadarChartDataSet(values: entries, label: "Data") chartView.data = RadarChartData(dataSet: set) view.addSubview(chartView) } }
点グラフ
import UIKit import Charts class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() var rect = view.bounds rect.origin.y += 20 rect.size.height -= 20 let chartView = ScatterChartView(frame: rect) let entries = [ BarChartDataEntry(x: 1, y: 10), BarChartDataEntry(x: 2, y: 15), BarChartDataEntry(x: 3, y: 9), BarChartDataEntry(x: 4, y: 13), ] let set = ScatterChartDataSet(values: entries, label: "Data") chartView.data = ScatterChartData(dataSet: set) view.addSubview(chartView) } }
まとめ
今回は簡単に一通りのグラフに触れてみました。
どれも同じようなインターフェースな為、一つ実装すれば他のものも同じように作れそうです。
【iOS】指紋認証を使ってみる
iPhone5sから登場した指紋認証ですが、まだちゃんと使った事がなかったので試してみました。
指紋認証可能化どうかはLAContextクラスのcanEvaluatePolicyメソッドを使います。 LAContextを使うためにはLocalAuthenticationをimportする必要があります。
import LocalAuthentication class ViewController: UIViewController { override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) let context = LAContext() var error: NSError? if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) { // 指紋認証可能 } } }
認証はevaluatePolicyメソッドを使います。
引数には認証画面での説明テキストも入れます。
import LocalAuthentication class ViewController: UIViewController { override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) let context = LAContext() var error: NSError? if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) { context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: "パスコードロックに使う") { success, error in } } } }
起動すると以下のようにパスコードを求められる画面になります。
.deviceOwnerAuthenticationWithBiometricsでなく.deviceOwnerAuthenticationを使った場合
シミュレータで試したところ以下のようにパスコードを求められました。
【iOS】Smile-Lockでパスコードロック機能を実現する
Smile-Lockというライブラリを試してみました。
URL見るとリクルートライフスタイルで出しているライブラリなんですね。
インストール
CocoaPodsでインストールしました。
target 'MyApp' do use_frameworks! pod 'SmileLock' end
使い方
下のようにWindowやViewに貼り付ける事で利用します。
import UIKit import SmileLock class ViewController: UIViewController { override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) let kPasswordDigit = 6 let contentView = PasswordContainerView.createWithDigit(kPasswordDigit) if let window = view.window { let baseView = UIView(frame: window.bounds) baseView.addSubview(contentView) contentView.center = baseView.center window.addSubview(baseView) } } }
実行すると下のような表示になります。
パスコードを入力した時の動作はプロトコルに記述します。
passwordInputComplete:input:
は入力が完了した時に呼ばれます。
extension ViewController: PasswordInputCompleteProtocol { func passwordInputComplete(_ passwordContainerView: PasswordContainerView, input: String) { print(input) } func touchAuthenticationComplete(_ passwordContainerView: PasswordContainerView, success: Bool, error: NSError?) { } }
パスコードが違う場合のシェイクはwrongPasswordメソッドを使います。
入力のクリアだけしたい場合はclearInputメソッドを使えば良さそうです。
extension ViewController: PasswordInputCompleteProtocol { func passwordInputComplete(_ passwordContainerView: PasswordContainerView, input: String) { passwordContainerView.wrongPassword() } func touchAuthenticationComplete(_ passwordContainerView: PasswordContainerView, success: Bool, error: NSError?) { } }
それとPasswordContainerViewのプロパティーを触る事で少しだけデザインを変更できます。
ボタンの色を変えるtintColor、ハイライト時の色を変えるhighlightedColor、曇りガラス風デザインに変更できるisVibrancyEffectなどがあります。
タッチIDが使える場合は自動的にViewにタッチボタンが表示されます。
押すとタッチIDを求める表示になります。
指紋認証が完了するとdelegateのtouchAuthenticationComplete
メソッドが呼ばれます。
extension ViewController: PasswordInputCompleteProtocol { func passwordInputComplete(_ passwordContainerView: PasswordContainerView, input: String) { } func touchAuthenticationComplete(_ passwordContainerView: PasswordContainerView, success: Bool, error: NSError?) { print(success) } }
今回は触れないのですが、PasswordUIValidationというクラスを使ったアプローチもあるようです。
THPinViewControllerとの比較
THPinViewControllerより自由度が高いのが良さそうでした。
THPinViewControllerは良く使っているんですが、「パスコード正解時は自動的にdissmissされる為、確認ページへ遷移させるのが難しい」みたいな問題があってそれが解決するだけでも良さそうです。
それと指紋認証を標準で対応してくれているのもありがたいです。
FirebaseRemoteConfigでA/Bテストを試してみる
Firebaseを使う準備
まずは下サイトで新しいプロジェクトを作成します。
プロジェクトを作成すると以下のような画面になるのでiOS アプリに Firebase を追加
を選びます。
情報を入力してアプリを追加します。
次画面に移動するとGoogleService-Info.plist
というファイルのダウンロードが開始します。
このファイルは後で使います。
次はXcodeでプロジェクトを作成します。
SingleViewApplicationを選択します。
次にFirebaseをインストールします。
Podsファイルを作成して以下のように修正して下さい。
platform :ios, ’10.0’ target 'MyApp' do use_frameworks! pod 'Firebase/Core' pod 'Firebase/RemoteConfig' end
先程ダウンロードしたGoogleService-Info.plist
をプロジェクトに追加します。
最後にAppDelegate.swift
へFirebase初期化処理を追加します。
これでFirebaseを使えるようになりました。
import UIKit import Firebase @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { FIRApp.configure() return true } }
FirebaseRemoteConfigを使ってみる
続けてFirebaseRemoteConfigを使ってみます。
Firebaseの管理画面に戻り、左メニューのRemote Configを選択します。
そのページで最初のパラメータを追加
ボタンを押してキーと値を追加します。
最後に変更を公開
ボタンを押して追加したパラメータを有効化します。
設定したパラメータの取得方法は以下の通りです。
import UIKit import Firebase @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? let remoteConfig = FIRRemoteConfig.remoteConfig() func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { FIRApp.configure() remoteConfig.fetch { [weak self] status, error in print(status.rawValue) print(error) self?.remoteConfig.activateFetched() // これでパラメータを取得できる print(self?.remoteConfig["test_key"].stringValue) // → "test_value" } return true } }
FirebaseRemoteConfigでA/Bテストをする
最後にFirebaseRemoteConfigを使ったA/Bテストの方法を見ていきます。
まず管理画面上の条件を追加
ボタンから条件を追加します。
条件はUser in random percentile
にして、好きな割合を入力します。
保存すると、2種類の値を設定できるようになります。
これらは先程設定した割合に応じて出しわけられます。
余談ですが、条件は推定地域などを選ぶ事ができます。
国に応じて出し分けをしたい場合などに有効そうです。
最後にFirebaseAnalyticsの設定を行います。
サイドメニューのAnalyticsタブのユーザープロパティーを選択して下さい。
新しいユーザーのプロパティー
ボタンからプロパティーをセットします。
次はXcode上でユーザーのプロパティーをセットします。
FIRAnalytics.setUserPropertyString
を使ってプロパティーのセットを行って下さい。
import UIKit import Firebase @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? let remoteConfig = FIRRemoteConfig.remoteConfig() func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { FIRApp.configure() remoteConfig.fetch { [weak self] status, error in self?.remoteConfig.activateFetched() FIRAnalytics.setUserPropertyString(self?.remoteConfig["test_key"].stringValue, forName: "my_property") } return true } }
これで実装は完了です。
Analytics上でプロパティー毎の数字を見る事ができるので、これを使って値の出し分けの成果を見ます。
その他
FirebaseRemoteConfigはとてもおもしろかったのですが、コホート分析でプロパティーによるフィルターが使えないのが少し痛かったです。
アクティブ率をKPIにする事が多いので、ここでA/Bテストを多用したかったです。
この辺りはGoogleAnalyticsのカスタムディメンションを使ったほうがうまくいくかもしれません。
GoogleAnalyticsだと下のように見ることができます。
参考URL
R.swiftでNSLocalizedStringも取れるようになっていたので試してみる
R.swiftがいつの間にかNSLocalizedStringにも対応されていたので動かしてみました。
R.swiftとは
R.swiftとはStoryboardや画像などの取得を便利にするライブラリです。
Storyboardの取得はUIStoryboard(name: "Main", bundle: nil)
のように文字列で指定しますが、R.swiftを使うとR.storyboard.main
と変数として取得する事ができます。
以前記事を書いたので、宜しければ併せてご参照下さい。
R.swiftを使ってStoryboard名や画像名のTypoを0にする - Qiita
準備
CocoaPodsを使ってインストールします。
platform :ios, ’10.0’ target 'MyApp' do use_frameworks! pod “R.swift” end
次にBuild PhasesでRun scriptを追加して"$PODS_ROOT/R.swift/rswift" "$SRCROOT"
と入力します。
この状態ビルドすればR.generated.swift
が生成されるので、プロジェクトに追加します。
これでRという構造体を通してStoryboardなどを取得できるようになりました。
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() R.storyboard.main.instantiateInitialViewController() } }
NSLocalizedStringを取得してみる
MyString.strings
を追加して、以下のようにテキストを記入しました。
"MyText" = "Hello!";
ビルドするとR.generated.swift
に先程の文字が追加されていました。
print(R.string.myString.myText()) // → Hello!
MyString2.strings
ファイルを作って、"myText2" = "Hello world!";
を追加したら以下のようになりました。
print(R.string.myString.myText()) // → Hello! print(R.string.myString2.myText2()) // → Hello world!
複数言語対応も試してみます。
MyString.strings
を英語用と日本語用を作って、英語用には"MyText" = "Hello!";
、日本語用には"MyText" = "こんにちは";
を追加します。
この状態で起動すると、シミュレータの言語設定に合わせた文字を出力してくれます。
print(R.string.myString.myText()) // → Hello! or こんにちは
【Swift】WKWebViewのスクロール速度を変更する
WKWebViewのスクロール速度を変更する方法を調べてみました。 まずはViewControllerにwebViewを貼り付けます。
import UIKit import WebKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() let webView = WKWebView(frame: view.bounds) webView.load(URLRequest(url: URL(string: "https://google.com")!)) view.addSubview(webView) } }
起動するとSafariなどと同様のスクロール速度です。
webViewのscrollViewのdecelerationRateにUIScrollViewDecelerationRateNormalをセットします。
import UIKit import WebKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() let webView = WKWebView(frame: view.bounds) webView.load(URLRequest(url: URL(string: "https://google.com")!)) webView.scrollView.decelerationRate = UIScrollViewDecelerationRateNormal // 追加 view.addSubview(webView) } }
これで起動すると、スクロールがかなり早くなります。
スクロール速度はUIScrollViewDecelerationRateFastとUIScrollViewDecelerationRateNormalが用意されています。
decelerationRateはCGFloatなので直接値を指定する事もできます。
ちなみに0.1だと指を話した瞬間に止まるような動きになります。
webView.scrollView.decelerationRate = 0.1
それぞれの値は下のようになっていました。
print(webView.scrollView.decelerationRate) // 0.9893243312 print(UIScrollViewDecelerationRateNormal) // 0.998 print(UIScrollViewDecelerationRateFast) // 0.99