20200729のiOSに関する記事は8件です。

[swift5]Protocolの作成方法

実装のポイント

Protocolの作成方法を5つの項目に分けて言語化及び要約してアウトプットしていきます。
初学者の方(私も初学者ですがw)はProtocolに関してわからない部分が多いと思いますが、実装の流れを掴む事ができればコードは書ける(Xcodeのおかげw)と思いますので実装の流れに注目して下さい!

今回はViewControllerNextViewControllerの2画面が存在し、
NextViewControllerからViewControllerへメソッドを委任したいと思います。

環境

swift5
Xcode 11

①Protocolの作成

まずnextViewControllerで下記コードのようにProtocolの定義と、Protocolで使用するメソッドの宣言を行います。今は使用するメソッドの規則だけでOKです。

NextViewController.swift
protocol プロトコル名ProtocolDelegate {
  //規則のみ決める
  func メソッド名(委任したい値: )
}

//プロトコルを変数に代入して宣言(? = nil NG)
var 変数名: プロトコル名ProtocolDelegate?

②他クラスで発動(戻るボタンpushで発動の場合)

今回は戻るボタンpushで発動する場合のコードを記述します。

NextViewController.swift
@IBAction func backButton(_ sender: Any) {
  //delegateメソッドを任せたクラスで発動
  で宣言した変数名.メソッド名( 委任したい値: (変数名) )

  //ボタンを押して戻る処理
  dismiss(animated: true, completion: nil)
}

③使用(委任)するクラスにProtocolで宣言

ViewControllerのclass宣言箇所に①で宣言したプロトコル名ProtocolDlegateを記述。

するとエラーが発生するので'Fix'を選択してXcodeの補完に従い、④のようにメソッドを作成する。

ViewController.swift
class ViewController: UIViewController,プロトコル名ProtocolDelegate {

}

④Protocolで書かれたメソッドを記述

③で自動補完されるメソッド。

ViewController.swift
func で宣言した規則のメソッド名(委任したい値: ) {
}

⑤NextViewControllerからViewControllerへ値を受け取る場合

ViewController.swift
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {  
  //変数nextVCに委任したい側のコントローラー名を代入し値を渡す
  let nextVC = segue.destination as! NextViewController

   //委任完了コード
   nextVC.delegate = self
}

最後に

もし誤っている箇所や疑問がある場合はコメント欄にて受け付けておりますのでご連絡お待ちしております。是非参考にしてください!

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

Xcode 11でビルドしたアプリをiOS 14 betaシミュレータ上で動かす方法

最初の1分ほどの手順で、それ以降はXcode 11からiOS 14 betaシミュレータを直接選び、アプリ(iOS 13 SDK)を実行することが可能になります。

何か問題や気付いたことがありましたらコメントで教えていただけると助かります。

準備

  • macOS Catalina 10.15.6
  • Xcode 12 beta 3
  • Xcode 11.5

:link: More Downloads for Apple Developers
このApple公式ダウンロードサイトからXcode 12 betaとXcode 11のxipファイルをダウンロードしてインストールしています。
ビルドを行うXcodeバージョンは11.5でなくてもOKです(Xcode 11.6が現時点で最新)。
App Store版のXcodeでは試していないため有効かわかりません。

xipファイルから解凍した各Xcodeは以下の通りに配置しリネームしてあります。
* Xcode 12 beta 3: /Applications/Xcode12-beta3.app
* Xcode 11.5: /Applications/Xcode.app

各Xcodeの初回起動時にCommand Line Toolsをインストールするダイアログが表示されるので、各Xcodeのバージョンに対応するCommand Line Toolsをインストールしておいてください。

手順

1. Xcode 12 betaを起動

Command Line Toolsのバージョンを指定します。

sudo xcode-select -s /Applications/Xcode12-beta3.app

(別法でメニューから指定することもできます Preference > Locations > Command Line Tools)

2. beta版シミュレータアプリを起動

新規プロジェクトでも何でもいいので、iOSアプリを実行しbeta版シミュレータアプリを起動します。

Screen Shot 2020-07-29 at 18.30.02.png

Screen Shot 2020-07-29 at 18.13.17.png

3. Xcode 12 betaを終了

このとき、beta版シミュレータアプリは起動させておきます。

4. Xcode 11を起動

Command Line Toolsのバージョンを指定します。

sudo xcode-select -s /Applications/Xcode.app

これで先程起動させたbeta版シミュレータが、より古いバージョンのXcodeから選択&実行できるようになりました :tada:

既存のプロジェクトでは以下の画像のようになります。

Screen Shot 2020-07-29 at 18.35.04.png

別法

:link: Test iOS Build from Production Xcode on Beta iOS Simulators
別の方法として、Xcode 11でビルドしてiOS 13のシミュレータ上にあるアプリの保存場所をActivity Monitorアプリで特定。それをドラッグ・アンド・ドロップでiOS 14 betaのシミュレータの画面上に移すという方法が紹介されていました。これは少し面倒くさいですね。

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

AutoLayoutで使えそうなUIViewのConstraintsExtension

Contraint用Extensionがおすすめ

コードベースで書いた経験が少なかったため、Constraintをどうするか調べていたところ、とても参考になる記事がありました。
一部自分で手を加えましたが、とても使い勝手が良いです。

extension UIView{
    func anchor(top: NSLayoutYAxisAnchor? = nil,
                left: NSLayoutXAxisAnchor? = nil,
                bottom: NSLayoutYAxisAnchor? = nil,
                right: NSLayoutXAxisAnchor? = nil,
                paddingTop: CGFloat = 0,
                paddingLeft: CGFloat = 0,
                paddingBottom: CGFloat = 0,
                paddingRight: CGFloat = 0,
                width: CGFloat? = nil,
                height: CGFloat? = nil) {

        translatesAutoresizingMaskIntoConstraints = false

        if let top = top {
            topAnchor.constraint(equalTo: top, constant: paddingTop).isActive = true
        }

        if let left = left {
            leftAnchor.constraint(equalTo: left, constant: paddingLeft).isActive = true
        }

        if let bottom = bottom {
            bottomAnchor.constraint(equalTo: bottom, constant: -paddingBottom).isActive = true
        }

        if let right = right {
            rightAnchor.constraint(equalTo: right, constant: -paddingRight).isActive = true
        }

        if let width = width {
            widthAnchor.constraint(equalToConstant: width).isActive = true
        }

        if let height = height{
            heightAnchor.constraint(equalToConstant: height).isActive = true
        }
    }

    func center(inView view: UIView, yConstant: CGFloat? = 0) {
        translatesAutoresizingMaskIntoConstraints = false
        centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: yConstant!).isActive = true
    }

    func centerX(inView view: UIView, topAnchor: NSLayoutYAxisAnchor? = nil, paddingTop: CGFloat? = 0) {
        translatesAutoresizingMaskIntoConstraints = false
        centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true

        if let topAnchor = topAnchor {
            self.topAnchor.constraint(equalTo: topAnchor, constant: paddingTop!).isActive = true
        }
    }

    func centerY(inView view: UIView, leftAnchor: NSLayoutXAxisAnchor? = nil, paddingleft: CGFloat? = nil, constant: CGFloat? = 0) {
        centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: constant!).isActive = true

        if let leftAnchor = leftAnchor, let padding = paddingleft {
            self.leftAnchor.constraint(equalTo: leftAnchor, constant: padding).isActive = true
        }
    }

    func setDimensions(width: CGFloat, height: CGFloat) {
        translatesAutoresizingMaskIntoConstraints = false
        widthAnchor.constraint(equalToConstant: width).isActive = true
        heightAnchor.constraint(equalToConstant: height).isActive = true
    }

    func addConstraintsToFillView(_ view: UIView) {
        translatesAutoresizingMaskIntoConstraints = false
        anchor(top: topAnchor, left: view.leftAnchor, bottom: bottomAnchor, right: view.rightAnchor)
    }
}

参考記事
https://medium.com/@suj9763/auto-layout-extension-in-swift-programmatically-fe1e7cca13e5

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

【個人開発】FlutterとFirebaseでサボり防止アプリを作った

先日、個人開発アプリをリリースしました!やったね!!!

審査も通りまして、現在App StoreとPlay Storeで配信されております。

App Store

Play Store

せっかくなので、利用した技術のメリットと注意点をまとめておこうと思います。

作ったアプリ

これです。

広報用ページも作ってみたのでリンクを貼っておきます。

広報用ページはスマホで見る用にレイアウトしてます。

監視し合う会 〜お互いサボらないようにみんなで作業しよう〜

英語名: Procrastinators Watch Mutually

kanshi_feature_1024_500.png

解決したい課題

やらなきゃいけないことがあるのに、なかなか取り組めずにグダグダしてしまう経験がみなさんあると思います。

でも、会社や学校で「今はこれをやる時間です」と管理されたり、誰かと一緒に「今からやるぞ!」って決めると、取り組めますよね。

そういう「誰かと一緒」「時間を管理される」という状況をアプリ上で再現して、作業に取り組みやすくするためのアプリです。

解決方法

  • 一緒に作業してるメンバーと作業内容が表示される
  • タイマーで全員同時に作業時間と休憩時間を指定される(49分作業12分休憩)
  • ちゃんと取り組めたかどうか自己申告し、それに対して褒めたり罰したりできる
    • 褒められたいし、自分に嘘をつきたくないので、作業に取り組むモチベーションアップ
  • 罰された人は社会貢献活動で償うまでアプリを利用できない
    • 社会貢献活動とは動画広告視聴のことで、広告主・広告配信業者・アプリ制作者に貢献できる
    • サボりに対し、有意義な軽い「罰」を用意することで、作業に取り組むモチベーションアップ

そんなアプリです。

監視し合う会 〜お互いサボらないようにみんなで作業しよう〜

ということは、広告の実装が必要ですね。

また、ユーザーの入力した内容が他者から見えるので、プライバシーポリシーや利用規約で不適切な入力を防止し、違反者にはミュートやBANをできるようにする必要があります。

そのためにはログイン機能でユーザーを識別する必要もありそうですね。

では技術方面を見ていきましょう。

Flutter

フロントエンドはFlutterを使います。

https://flutter.dev/

Flutterのメリット

最大のメリットはなんと言っても、AndroidとiOSの両アプリをそれぞれ作らなくて済むことです。

また、同じメリットを持つフレームワークは他にもありますが、Flutterが今最も勢いがあるフレームワークと言えると思います。コミュニティも活発で情報が得やすいです。

スクリーンショット 2020-07-27 15.01.59.png

あのGoogleがかなり力を入れて作っているという安心感もありますね。

今後はWebや、Mac、Windows用のネイティブアプリも作れるようになるということで、スマホアプリに限らず広範囲にわたるクロスプラットフォーム開発が可能になっていくようです。

Webは2020年7月27日現在、既にかなりのレベルでアプリが作れます。

開発環境

Flutterの開発環境はAndroid Studioを使いました。

VSCodeでもいいと思います。

Flutter Inspectorの見やすさや新規プロジェクトの作りやすさでAndroid Studioを選びましたが、それも半年以上前のことなので、今のVSCodeと比較するとどうかわかりません。

静的解析

Flutter開発をする際は静的解析を有効にするようにしましょう。

これはほぼ必須だと思います。

行儀がよく見やすい、ルールの統一されたコードを書くことが出来ます。

詳しくはこちら。

https://dart.dev/guides/language/effective-dart

https://medium.com/flutter-jp/analysis-b8dbb19d3978

状態管理手法

Provider + ChangeNotifier パターンを採用しました。

2020年7月27日現在、Flutter公式が最もおすすめしている方法です。

Flutter開発を始めると状態管理手法に悩むことになると思いますが、まずはこれでいいと思います。

他の方法については、こちらの記事を参考にしてもらえると嬉しいです。

Flutterの状態管理いろいろ比較 〜グローバル変数StateからBLoCパターンまで〜

なお、リンク先記事を書いた後で登場した

  • StateNotifier
  • Riverpod
  • Cubit

などの方法については記事内で触れていません。興味ある方はお調べください。

筆者自身の次のアプリはRiverpod+ChangeNotifierパターンで作ろうと思っています。

多言語対応

英語・日本語・中国語の3言語に対応させました。

多言語対応の方法はこちらの公式解説のAn alternative class for the app’s localized resourcesを参考にしつつ、オレオレ手法でやってます。

公式オススメの方法は、自動コード生成を含んでおり、それと静的解析ツールがバッティングして警告を吐きまくり、静的解析の例外設定をしてみたらそこにバグがあり例外設定が効かず…というトラブルに見舞われたので、それ以来オレオレでやってます。今はどうなんでしょう。

筆者のオレオレ手法は、自動コード生成がなく、コード補完も効いて
L10n.of(context).title
のように欲しい文言を得られるので、悪くないと思ってます。

ここでは詳しく解説しませんが、そのうち記事にするかもしれません。

なお、もちろん、後述のプライバシーポリシーやスクリーンショット、ストア上のタイトルや説明文も全て3言語分用意してます。ちょっと大変。

Firebase

バックエンドにはFirebaseを使っています。

ログイン・データベース・サーバーサイドロジック・広告・プライバシーポリシーページの用意を全てFirebaseに頼っています。様様ですね。

下記に出てくるfirebase関係のパッケージを使うにはfirebase_coreパッケージが必要になるのでご注意ください。

Authentication

Google、Facebookアカウントを使ったログインを実装しました。

firebase_authパッケージを使います。

ただし、iOSアプリの場合はサードパーティログインにAppleを加えることが必須なので、iOS版に限りAppleログインも実装しました。

Appleに関してはこちらの記事が大変参考になりました。

[Flutter] Sign in with Apple を Cloud Functions 無しで実装する

Androidアプリの方は、Androidのapkファイルの署名に使った証明書のハッシュをFirebaseやFacebookに伝えておく必要があるのでご注意ください。

デバッグ時にデバッグ用証明書のハッシュを渡していても、リリース時にはリリースに使った鍵の証明書のハッシュを伝える必要があります。筆者はこれを忘れていて、リリース直後にAndroid版で一切ログインできない事故を起こしました。

Firestore

cloud_firestoreパッケージを使います。

Firestoreの使い方・考え方についてはこちらの動画シリーズがとてもわかりやすいので一通り見ておくといいと思います。

Get to know Cloud Firestore

従来のRDBMSとは結構異なる考え方が推奨されてます。

「データの正規化を頑張るより、重複を許した方が良い」とか「リアルタイム接続をデフォルトにして一件ずつフェッチするのを例外的な場合に限定しよう」とか。

「クエリ一回でも、読み込んだドキュメントの件数に応じて料金がかかる」なんかは結構重要です。

セキュリティルールとテスト

Firestoreで重要になるのがセキュリティルールです。

アクセス権限の管理だけではなく、サーバーサイドバリデーションもセキュリティルールを使って行います。

筆者はセキュリティルールとそのテストはAndroid StudioではなくVSCodeで書いています。

テストコードをTypeScriptで書いているのですが、Android Studioではシンタックスハイライトや補完が効かないので。

セキュリティルールだけ書いていくとゴチャゴチャしてきて管理が大変なので、やはりテストとセットで書いていくのが良いと思います。

その際はこちらの記事を参考にしてもらえたらとても嬉しいです。

VSCodeデバッガを使ってCloud FunctionsとFirestoreの連携テスト(TypeScript)

2020年6月のバージョンで、セキュリティルール内で一時変数が使えるようになったりして、どんどん便利になっていますね。

New improvements to Firestore Security Rules

Functions

Firestoreの中身をどうしてもサーバーサイドから操作したい時は、Cloud Functionsを使います。

「どうしても」と書いたのは、Firestoreの操作をする時はまず第一にクライアントのSDKから操作することを考えるべきだからです。

Firestore SDKはオフラインサポートなどいろいろうまいことやってくれるので、積極的に利用しましょう。

先程の動画シリーズの一つですが、これが参考になります。

https://www.youtube.com/watch?v=oDvdAFP6OhQ

また、Functionsを使う必然性がある場合でも、呼び出し可能関数を作るのはできるだけ避けましょう。

理由はこちらの記事が参考になります。

CQRSを学ぶついでにCloud FunctionsとFirestoreを連動させる時の小技も学ぶ

Web APIの操作に慣れているとつい同様にAPIを作って叩きたくなりますが、耐えましょう。クライアントからの操作で完結するものは完結させます。

じゃあどうするかというと、Cloud Functionsには、さまざまな「トリガー」が用意されています。

  • Firebase Analyticsに新しいイベントが送られた時
  • Firebase Authentication で新しいユーザーが作られた時
  • Firestore上でドキュメントが削除されたとき

などなど、たくさんあります。

これらに応答してFunctionsが起動するようにしましょう。

ただし、Functions上の各関数は一回のトリガーに対して複数回起動することがあるので、「冪等」にしておくといいです。「冪等」というのは、「2回以上実行しても結果が変わらない」ということです。

今回のアプリでは、ユーザーがきちんと退室せずに放置した場合に退室処理を走らせるためにFunctionsを利用しています。

hosting

サーバー上にファイルを置いておき、特定のURLでアクセスできるようになります。

一昔前だったらレンタルサーバーを有料で借りないとこういうことはできませんでしたが、便利な時代になりました。

今回は、プライバシーポリシーを掲載したページや、広報用ページを置くために利用しています。

開発環境

FunctionsもVSCodeで書いています。TypeScriptで書いているのですが、Android Studioだと補完やシンタックスハイライトが効かないからです。

Functionsのテスト

公式記事が大いに参考になります。

Cloud Functions の単体テスト

また、こちらは上に既に貼ったのと同じ私の記事ですが、再掲します。参考にしてもらえると嬉しいです。

VSCodeデバッガを使ってCloud FunctionsとFirestoreの連携テスト(TypeScript)

AdMob

このアプリは広告が出るので、firebase_admobパッケージを使って広告を出しています。

ただ、このパッケージは何かと使いにくいので、色々注意が必要です。

筆者がこのパッケージについてあれこれ嘆いている様子がわかるツイッター検索結果がこちらです。

ツイッター検索

ストア申請のために

アプリが動くようになったら、ストアで配信したいですよね。

ここでもいろいろ障壁があります。

ミュート機能・通報機能

このアプリはユーザーが入力した表示名やタスク内容が他人から見えるため、「ユーザー生成コンテンツ」に該当すると思われます。

この場合、少なくともApp Storeに掲載されるためには、不適切な内容を投稿するユーザーに対して対策を施しておく必要があります。Play Storeについては未調査ですが、おそらく同様だと思います。

具体的には、ユーザーをミュートや報告する機能が必要です。

詳しくはApple公式のこちらが参考になります。

App Store Reviewガイドライン - 1.2 ユーザー生成コンテンツ

プライバシーポリシー

個人情報をどう扱うか、というものです。

すべてのアプリで必須のはずです。一切情報収集しない場合でもその旨を記述したプライバシーポリシーが必要だと思われます。

Firebase Analyticsを使っている場合は情報収集していることになるので、その旨の記述が必要ですね。

AdMobを使っている場合はユーザーに合わせた広告を出すために情報収集してますので、それについても記述が必要です。

詳しくはこちらを御覧ください。

App Store Reviewガイドライン - 5.法的事項

とはいえプライバシーポリシーなんてどう書いたら良いかよくわからないものです。

検索すると書き方の解説や雛形がヒットしますので、参考にするといいと思います。

申請用スクショ撮影

ストアで申請するためにはアプリのスクリーンショットが必須です。

これが案外面倒です。スクショとして欲しい画面が、アプリを実際に操作して簡単に作れるとは限らないからです。

それが複数端末分必要になります。

また、多言語対応している場合はその数だけ必要になります。

4画面*4端末*3言語とすると48枚必要になりますね。手動で撮っては結構な手間です。

これについては私のこちらの記事を参考にしてもらえると嬉しいです。

Flutterアプリのスクショを(極力)自動で撮る on AndroidStudio

署名について

署名周りも、アプリ配信時の結構なハードルになると思います。

これも自分の記事で恐縮ですが、概要をまとめておりますので参考にしてもらえたら嬉しいです。

Android・iOSアプリを配信する時の署名について整理する

終わりに

技術周りをまとめておこうと思って書いてみたら、自分の過去記事へのリンクが多くなってしまいました。

まあ、難所を突破するたびにその結果を記事にまとめるようにしていたので、それらへのリンクが多くなるのは必然か……。。。。

ということで、皆様、アプリ開発ライフを楽しみましょう!

サボり防止アプリもよろしくおねがいします!これで時間管理しながらアプリ開発してくれたらメチャクチャ嬉しいです!

監視し合う会 〜お互いサボらないようにみんなで作業しよう〜

App Store

Play Store

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

Fastlaneを使って、Firebase App Distributionを自動化する

Fabric終了に伴い、Firebase App Distributionを用いてアプリを配布しているプロジェクトは数多くあると思います。
手動で配布することもできるのですが、その場合、
1. ipaファイル作成
2. Firebaseコンソールに移動
3. ファイルのアップロード
4. テスターの指定
などと毎回同じ作業をするのは面倒です。

そこで個人プロジェクトでこちらの作業をfastlaneコマンド一発でできるようにしたので、その時の手順を記します。

今回解説するのは
- fastlaneのセットアップ
- fastlaneによるipa作成、アップロード方法
です。

Installing fastlane

まずはHomebrew経由でfastlaneをインストール

brew install fastlane

プロジェクトのルートディレクトリで下記コマンドを実行。
fastfileが作成され、こちらに後ほど配布するためのコードを記述します。

fastlane init

プラグインを追加

fastlane add_plugin firebase_app_distribution

ローカルマシンでFirebaseにログインできるようにする

ドキュメントに従います

Firebase CLIをダウンロード

curl -sL https://firebase.tools | bash

以下コマンドで確認

firebase login

firebase-toolsのインストール

まずはNode.jsをダウンロードします。
https://nodejs.org/ja/download/

プロジェクトのルートディレクトリで以下コマンドを入力。package.jsonが作成されます。

npm init

これでfirebase-toolsをインストールすることができます。

npm install firebase-tools

以下のパスはgitignoreに追記。

node_modules/

fastfileへの記述

platform :ios do
  desc "deploy DEV on Firebase App Distribution"
  lane :distribution do
    # .ipaファイルの作成
    gym(
      workspace: "hoge.xcworkspace",
      scheme: "hoge",
      export_method: "ad-hoc",
      export_options: {
        method: 'ad-hoc',
        teamID: 'hoge',
        provisioningProfiles: {
          'bundle identifier name' => 'provisioning profile name'
        }
      }
    )

    # firebase app distributionに.ipaアップロード
    firebase_app_distribution(
      app: "1:1234567890:ios:0a1b2c3d4e5f67890",
      testers: "emailadress@gmail.com, emailadress2@gmail.com",
      release_notes: "hoge",
      firebase_cli_path: "node_modules/.bin/firebase"
    )
  end
end

firebase_app_distributionについて

appの引数にはFirebaseのプロジェクト設定画面にあるアプリIDを指定。(プロジェクトIDじゃないよ)

Provisioning Profileの作成

Dev環境かつAutoでやってたので、appleのdeveloperサイトから手動で作成する必要があった。
Dev環境用のProvisioning Profileを作成し、その名前をFastfileに記述。
ダウンロードしたら、起動して、キーチェーンに登録までする。

実行

fastlane distributionで実行できます。
実行したら、Firebaseコンソール -> プロジェクト -> App Distributionで新たなバイナリーができているかを確認しましょう。

参考

https://firebase.google.com/docs/app-distribution/ios/distribute-fastlane?hl=ja
https://qiita.com/fuwamaki/items/a9745e1242e24430eb4c#fastlane%E3%82%92%E7%94%A8%E3%81%84%E3%81%9Ffirebase-app-distribution%E3%82%A2%E3%83%83%E3%83%97%E3%83%AD%E3%83%BC%E3%83%89%E6%96%B9%E6%B3%95

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

Codemagic でモバイルアプリの CI/CD を始めましょう

この記事は、Codemagic の運営元である Nevercode Ltd の依頼で作成しました。2020/7/6 に公開された オリジナルの英語記事の日本語訳に加え、冒頭に私のコメントを、末尾におすすめの Codemagic 関連の日本語記事へのリンクを列挙しています。


Codomagic は、今年の2月に発表された Flutter UX 調査チームによる Q4 2019 の調査結果において、開発者が CI/CD で採用しているサービスのトップ3に挙げられており、(Codemagic、 Github Actions、 GitLab)、その中でも満足度が最も高かったことが示されています。

Codomagic は、Google と提携して Flutter 専門の CI/CD サービスとして開始されただけあって、Flutter を手厚くサポートしています。また、以下の翻訳で示されているように、Flutter に加えて Android ネイティブ、 iOS ネイティブ、React Native のサポートが追加されましたので、既存のそれらのアプリの資産がある場合でも、Codomagic をすべてのアプリに対応する統一した選択肢として検討できるようになりました。

また、プライベートリポジトリでもクレジットカード登録なしで使える無料枠が用意されていますので、試用を開始する敷居がとても低いです。

公式のドキュメンテーション公式ブログの内容も、英語ではありますが、まずまず充実していると言えるのではないでしょうか。

あらたに Flutter プロジェクトを始める際の CI/CD サービスの選定作業については、Codemagic の試用から始めるのがひとつの無難な選択肢でしょう。

以下が、オリジナルの英語記事の日本語訳です。


codemagic-blog-getting-started-with-codemagic.ecdfe0547c7678263bedb56f1533acb13784617e42a96812891fe87bd3b2e03a.png

2018 年にロンドンで開催された Flutter Live おいて、 Nevercode は Google と提携し、Flutter アプリ専用の CI/CD ソリューションとして Codemagic を立ち上げました。Codemagic を使えば、Flutter アプリを辛い設定作業をせずにテストしてリリースできます。

Codemagic は Flutter の扱いに熟達していますが、今やそれだけにとどまりません。Flutter アプリでないネイティブアプリでも同等に扱えるように、codemagic.yamlファイルを使用して、ネイティブの AndroidiOS アプリ、および React Native アプリの設定とビルドができるようになりました。スクリプトをより詳細に制御したい場合は、カスタムスクリプトを実行したりカスタムワークフローを作成できます。高品質なアプリをさらに迅速に開発できるようになりました。

ここでは、Codemagic を使えば、Flutter アプリ、Android ネイティブ アプリ、 iOS ネイティブ アプリ、React Native アプリの CI/CD をいかに簡単に設定できるのかを、その手順を説明を通じてご紹介いたします。

必要条件

Flutter アプリ、iOS ネイティブアプリ、Android ネイティブアプリ、React Native アプリを Codemagic 上でセットアップするには、いくつかのものが必要です。

  1. もちろん、Flutter アプリ、iOS ネイティブアプリ、Android ネイティブアプリ、React Native アプリとそのテストコードが必要です。そして、それらのアプリは GitHub、GitLab、または Bitbucket でホストされていることを念頭に置いています。
  2. App Store や Play Store に公開したい場合は、証明書や Provisioning Profile のようなコード署名を用意します。
  3. ビルドレポートやアーティファクトを送信する場合は、Slack のワークスペースを用意します。

以上です。これでアプリに CI/CD を追加する準備ができました。では、実際に設定することを通じて、基本的なことを説明していきましょう。なお、コード署名と公開作業についてはこのチュートリアルでは対象とせず、別途説明とさせていただきます。

Codemagic にアカウント登録

最初の一歩は、codemagic.ioウェブサイトにアクセスし、GitHub、GitLab、または Bitbucket のアカウントで Codemagic のアカウント登録をすることです。これで、そのサービスのすべてのリポジトリーを Codemagic アカウントに統合して扱えるようになります。

image2.png

アプリケーション ダッシュボード ( Applications Dashboard )

サインインすると、Codemagic のダッシュボードにアプリのリポジトリーの一覧が表示されます。一覧をフィルターにかけたり検索したりして、ビルドしたいアプリを見つけることができます。初回のアクセスでは、以下のように表示されるでしょう。

app_not_configured.png

ビルドのワークフローを設定するには、Set up buildボタンをクリックします。

設定がすでに済んで、まだビルドを開始していない場合は、このように表示されます。

app_configured.png

Finish build setup をクリックして設定を終え、ビルドを開始できます。


ちなみに、以前に Codemagic 上でビルドしたアプリがある場合は、以下のような表示になります。

app_built.png

Start new build をクリックして以前の設定でビルドを開始したり、Settings(歯車のアイコン)をクリックして変更したりできます。


ビルドの設定

Set up buildボタンをクリックすると、このページが表示されます。ここでは、プロジェクトの種類を選択できます。

viimane.png

この一覧から適切なもの選択するだけです。選択すると、ワークフローの設定画面に移動します。

Flutter の設定

このページでは、以下のような UI で Flutter アプリのビルドワークフローを設定できます。

workflow_config_flutter.png

ビルドワークフローをより詳細に制御したい場合は、Download configuration ボタンをクリックして codemagic.yamlファイルを使用できます。

yaml_config.png

このcodemagic.yamlファイルを使用して、ワークフローを変更できます。プロジェクトのルートディレクトリに置いて、VCS にコミットするだけです。リポジトリーにこのファイルがあれば、Codemagic が自動的に検出します。

Start your first build ボタンをクリックして、Flutter アプリのビルドをすぐに開始できます。ワークフローをまだ変更していない場合は、以下の一連のステップのデフォルトのワークフローが使用されます。

default_workflow.png

ビルドマシンの準備、ソースコードのチェックアウト、Flutter の依存関係のインストール、テストの実行、アプリのビルド、アーティファクトの公開といった一連のステップがあります。これが基本的な CI/CD のワークフローに必要なものです。各ステップをクリックして展開すると、Codemagic はそれぞれのステップのログを表示します。

その他の設定

Flutter アプリ以外のプロジェクト(Android ネイティブアプリ、iOS ネイティブアプリ、React Native アプリ、または Flutter Module)をビルドする場合は、codemagic.yamlファイルを使用してワークフローを設定する必要があります。選択したプロジェクトの種類ごとにテンプレートが用意されています。

Android ネイティブ用の設定

ビルドレポート

ビルドが終了すると、Codemagic のダッシュボードに、ビルド時間、ビルドステータス、コミットなどの概要とそれらの詳細が以下のように表示されます。

build_report.png

これに加えて、デフォルトでは iOS と Android アプリの両方の、ビルドのステータスとアーティファクトがメールで送信されます。メールの内容は以下のようになります。

email.jpg

Slack の設定をすることで、Slack チャンネルにビルドレポートを投稿させることもできます。

以上です!なにも設定しなくても Flutter アプリに CI/CD サポートを追加できました。これが Codemagic のマジックです。

ビルドの失敗

ここまでは、すべてが完璧に動作しているハッピーパスのシナリオを説明してきましたが、現実の世界では必ずしもそうなるとは限りません。ビルドに失敗したり、テストに失敗したり、調査が必要な問題が発生することもあります。Codemagic は、各段階においてそれが成功したか失敗したかに関わらず、詳細なログを提供しています。これらのログは失敗したビルドをデバッグするのに非常に便利です。以下の画面は、ユニットテストが失敗していて、ビルドの失敗のすべての詳細がログに記録されているものです。

image3.png

見ての通り、インテグレーションテストとウィジェットテストは成功していますが、ユニットテストが失敗しています。これを修正することでビルドを修正できました。テストが失敗すると、それを知らせるメールもビルドログへのリンク付きで届きます。

ビルドの失敗についてもっと詳しく知るには、モバイルアプリの CI ビルドが失敗してしまう、よくある 12 個の設定間違い をお読みください。

デフォルトワークフローとカスタムワークフローの比較

デフォルトのビルドワークフローには CI/CD ワークフローに必要となる基本的なものが含まれていますが、ほとんどの場合、ビルドプロセスの一部になんらかのカスタマイズを加える必要があります。例えば、ビルドステータスを Slack や他のコミュニケーションチャンネルに送信したり、アーティファクトを Fabric や HockeyApp などの他のサービスに発行したい場合があります。これらのシナリオを実現するためには、カスタムワークフローを作成し、カスタムスクリプトを実行します。それでは、プロジェクトの必要に応じてワークフローをカスタマイズする方法を見ていきましょう。

Build フェーズ

Codemagic における Flutter アプリの Build フェーズには、ビルドプロセスを定義するためのさまざまな手段があります。どのブランチをビルドするか、そして何のイベントをきっかけにしてビルドするかを選択することから始めます。

build_triggers.png

次に、Godemagic では、以下のようなもっと詳細な設定も可能です。

  1. Flutter のバージョン
  2. アプリをビルドする設定。debug や release など
  3. iOS や Android、またはその両方といったプラットフォームの選択
  4. Xcode のバージョン
  5. カスタムビルドの引数

デフォルトのワークフローでは、以下の設定が選択されています。

build_default.png

とは言え、ビルドプロセスはデフォルトの動作をプロジェクトの必要に応じて変更することで、いつでもカスタマイズできます。Xcode の異なるバージョンや異なるリリース設定でアプリのビルドが、上記の画面のオプションの変更、さらにカスタムワークフローによって可能です。

Codemagic のもう一つの大きな特徴として、すべてのフェーズの前にカスタムスクリプトを追加できます。これにより、開発者は各フェーズの前後で任意のコマンドを実行できます。カスタムスクリプトを設定するには、単純に Build、Test、Publish などの各フェーズの上部にある + ボタンをクリックします。ここでは、Build フェーズの前に配置するスクリプトの例を示します。

カスタムスクリプトを追加

Pre-build scriptに、プロジェクト固有の依存関係のインストールなど、任意のコマンドを追加できます。

Test フェーズ

Codemagic の Test フェーズでは、開発者はテストの有効化と無効化を切り替えられます。プロジェクトで実行する Flutter テストの種類を選択できますし、テスト中に使用する追加の引数の指定もできます。プロジェクトで Flutter Driver tests を使用している場合は、使用するエミュレータの種類も指定できますし、AWS Device Farm の恩恵も受けられます。

こちらで、Flutter のテストに関する詳細をご覧いただけます。

Codemagic の Test セクション

とは言え、デフォルトの動作を無効にして、カスタマイズしたテストコマンドを使用することも可能です。例えば、コマンドラインから UI テスト(訳注: Flutter のインテグレーションテスト) だけを実行したい場合は、次のコマンドを pre-test フェーズに追加します。

flutter drive --target=test_driver/main.dart

これにより、デフォルトのテストワークフローを使用する代わりに、このコマンドが実行されます。

Publish フェーズ

Codemagic の Publish フェーズでは、ビルドレポートやアーティファクトの公開のための様々な設定が必要です。デフォルトでは、あなたが GitHub/Bitbucket/GitLab にメールアドレスを提供している場合は、メールアドレスだけが設定されています。

このフェーズでは、その他にも以下のものを明示的に設定する必要があります。

  1. iOS と Android の両方のコード署名に必要な情報をアップロード
  2. Slack の webhook を設定
  3. 公開のための、Google Play や App Store Connect の詳細を入力

Codemagic の Publish セクション

これらに加えて、カスタムスクリプトを書いて、FabricHockeyApp などのサードパーティサービスにアーティファクトをデプロイすることもできます。

スクリプトの大きな力を利用して Codemagic でカスタムワークフローを設定し、アプリのビルドプロセスをより強固なものにできます。

多数のワークフロー

Codemagic では、異なる設定やバージョンのソフトウェアの要件を満たすために、多数のワークフローを作成できます。既存のワークフローを複製して名前を変更することで、新しいワークフローを作成できます。以下の手順です。

  1. App settingsに移動します。(訳注: https://codemagic.io/apps ページで、それぞれのプロジェクトの歯車アイコンをクリックして移動できます)
  2. Workflow settings セクションの、Duplicate workflow をクリックします。複製されたワークフローは、アプリ名の下の一覧に "Default Workflow (Copy)" として表示されます。ワークフローを選択し、名前を変更します。

ワークフローの複製

上の画面では、デフォルトのワークフローの名前を "Custom Workflow" に変更しています。異なる Xcode のバージョンやデバッグ用設定やリリース用設定での Flutter アプリのテストなど、多くのワークフローを作成できます。

まとめ

Codemagic は、Flutter アプリ専用の公式の CI/CD ソリューションとして始まりましたが、今ではすべてのモバイルプロジェクトをこの最速の CI/CD に迎え入れています。Codemagic のマジックを使えば、設定なしで Flutter アプリのビルド、テスト、公開ができますし、カスタムワークフローを使えば環境を制御してビルドを実行できます。Android ネイティブアプリ、iOS ネイティブアプリ、React Native アプリをお持ちの場合は、codemagic.yaml ファイルを使用するだけで、発進の準備が完了です!

ハッピー ビルディング!

お役立ちリンクと参考文献

以上が翻訳です。

お役立ち日本語記事へのリンク

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

Xcodeシミュレーター管理コマンド

こちらが、Xcodeがローンチしたシミュレーターを管理するために実行できるコマンドです。

これらのコマンドは、あなたの端末で実行してください。この記事は、あなたが iPhone 11 Pro Max と名付けられたシミュレーターを実行していると仮定しています。

時刻を変更する

xcrun simctl status_bar "iPhone 11 Pro Max" override --time '9:41'

セルラーネットワークを変更する

Wi-Fiに切り替える

xcrun simctl status_bar "iPhone 11 Pro Max" override --dataNetwork 'wifi' 

Wi-Fiの信号強度を制御する

xcrun simctl status_bar "iPhone 11 Pro Max" override --wifiBars 0
xcrun simctl status_bar "iPhone 11 Pro Max" override --wifiBars 1
xcrun simctl status_bar "iPhone 11 Pro Max" override --wifiBars 2
xcrun simctl status_bar "iPhone 11 Pro Max" override --wifiBars 3

3G に切り替える

xcrun simctl status_bar "iPhone 11 Pro Max" override --dataNetwork '3g'

4G に切り替える

xcrun simctl status_bar "iPhone 11 Pro Max" override --dataNetwork '4g'

LTE に切り替える

xcrun simctl status_bar "iPhone 11 Pro Max" override --dataNetwork 'lte'

セルラーネットワークの信号強度を変更する

xcrun simctl status_bar "iPhone 11 Pro Max" override --cellularBars 0
xcrun simctl status_bar "iPhone 11 Pro Max" override --cellularBars 1
xcrun simctl status_bar "iPhone 11 Pro Max" override --cellularBars 2
xcrun simctl status_bar "iPhone 11 Pro Max" override --cellularBars 3
xcrun simctl status_bar "iPhone 11 Pro Max" override --cellularBars 4

明/暗 モードを切り替える

シミュレーター内

  • シミュレーターの設定アプリケーションを開く
  • Developer を開く

article_01

  • Dark Appearances をオンにする

article_02

現在シミュレーターはダークモードになっています

article_03

Xcodeインターフェース内

  • シミュレーターでアプリケーションを起動する
  • Xcode プログラムツールバーで Environmental overrides のアイコンを探す
  • Interface Style の隣のスイッチをオンにする

article_04

バッテリーの状態を変更する

xcrun simctl status_bar "iPhone 11 Pro Max" override --batteryState 'charging'
xcrun simctl status_bar "iPhone 11 Pro Max" override --batteryState 'charged'
xcrun simctl status_bar "iPhone 11 Pro Max" override --batteryState 'discharging'

バッテリー残量を変更する

xcrun simctl status_bar "iPhone 11 Pro Max" override --batteryLevel 0
xcrun simctl status_bar "iPhone 11 Pro Max" override --batteryLevel 100

シミュレータに画像をアップロードする

シミュレータのウィンドウに直接画像ファイルをドラッグ&ドロップすることができます。

プッシュイベントのシミュレーション

まずは、ユーザーの通知を許可した状態で空白のiOSプロジェクトを開始します。iOSのターゲットのバンドルIDを、com.demo.notification として仮定します。

payload.apns ファイル

payload.apns という名前の空のファイルを作成します。このファイルは、通知の送信先となるアプリのターゲットとその通知内容をシミュレーターに指示します。

この例では、Hello, World というシンプルなメッセージを、バンドルIDが com.demo.notification のアプリに送信するとします。

//payload.apns
{
    "Simulator Target Bundle": "com.demo.notification",
    "aps": {
        "alert": {
            "body": "ネコノヒー ???",
            "title": "こにちわ!"
        }
    }
}

このファイルをシミュレーターにドラッグアンドドロップします

  1. シミュレータ上でアプリを実行します

  2. 通知の許可を承認します

  3. このファイルをFinderからシミュレータにドラッグアンドドロップします

article_05

キャリア名を変更

( iPhone 8 Plus という名前のシミュレーターを実行していると仮定)

xcrun simctl status_bar "iPhone 8 Plus" override --operatorName 'ネコ Mobile' --cellularMode active

article_06

スクリーンショット

キーボード上の Command-s キーを押すことができます。スクリーンショットはデスクトップ上に保存されます。

動画を録画

xcrun simctl io booted recordVideo appVideo.mov

このコマンドは現在実行中のシミュレーターの動画をスクリーンレコード(画面録画)し、 control-C を押すとファイル appVideo.mov を保存します

マウスカーソルを表示

defaults write com.apple.iphonesimulator ShowSingleTouches 1

GUI app

Xcodeシミュレータを操作するのに役立つGUIアプリがあります

twostraws/ControlRoom: A macOS app to control the Xcode Simulator.

https://stackoverflow.com/a/60853266
https://stackoverflow.com/a/57989733
https://stackoverflow.com/a/41141801
xcrun simctl status_bar "iPhone 8 Plus" override --help

こちらのウェブページにアクセスすると、私の公開されているQiitaの記事のリストをカテゴリー別にご覧いただけます。

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

たった14行でiOSアプリ内のリンクをリッチに表示する方法

FacebookやTwitterでURLを投稿すると、リンク先のタイトルや画像を自動で取得してカードのようなUIとして表示されるのを見たことがあるでしょうか。そんな機能を簡単に実装できるApple標準のフレームワークがXcode11以降で使えるようになりました。

例えばYoutubeのリンク

Youtube動画の場合、リンクをアプリに埋め込むとそのまま再生できました。

LinkPresentation framework

URL からメタデータを取得し、アプリ内でリッチなリンクコンテンツをいい感じに表示してくれるAppleのフレームワーク。iOS 13.0以降から利用可能
https://developer.apple.com/documentation/linkpresentation

実装ステップ

利用箇所でまずフレームワークをインポートしておきましょう。

import LinkPresentation

URLからメタデータを取得する

startFetchingMetadata(for:completionHandler:)をコールしてメタデータの取得を開始します。

let metadataProvider = LPMetadataProvider()
let url = URL(string: "https://youtu.be/hMgcaYd2BZM")! // リッチに表示したいURL
metadataProvider.startFetchingMetadata(
    for: url,
    completionHandler: { metadata, error in
    // Make use of fetched metadata.
})

completionHandlerはバックグラウンドキューで実行されます。
クロージャー内でUIを操作する場合には、メインスレッドで行うようにしましょう。

メタデータを専用Viewに受け渡す

LPMetadataProviderから受け取ったメタデータをLPLinkViewの初期化時に渡します。

metadataProvider.startFetchingMetadata(
    for: url,
    completionHandler: { metadata, error in
        if error != nil { return }
        guard let metadata = metadata else { return }
        DispatchQueue.main.async { // メインディスパッチキューで実行
            let linkView = LPLinkView(metadata: metadata) // メタデータを渡す
            self.previewView.addSubview(linkView) // Storyboard上に配置した任意のUIView
            // Sizing
        }
})

今回は、生成したLPLinkViewのインスタンスをStoryboard上に配置したpreviewViewaddSubviewしました。
また、前述のとおり、completionHandler内のUIの更新は、DispatchQueue.mainで実行しましょう。

サイズを決定する

サイズを指定したら、sizeToFit()で任意のサイズにレイアウトを合わせましょう。

linkView.frame.size = self.previewView.frame.size
linkView.sizeToFit()

今回はlinkViewのサイズをSuperviewに揃えましたが、任意のCGSizeを設定しても◎
これにて完成です。

ソースコード全体

let metadataProvider = LPMetadataProvider()
let url = URL(string: "https://youtu.be/hMgcaYd2BZM")!
metadataProvider.startFetchingMetadata(
    for: url,
    completionHandler: { metadata, error in
        if error != nil { return }
        guard let metadata = metadata else { return }
        DispatchQueue.main.async {
            let linkView = LPLinkView(metadata: metadata)
            self.previewView.addSubview(linkView)
            linkView.frame.size = self.previewView.frame.size
            linkView.sizeToFit()
        }
})

ぴったり14行✌️?

まとめ

iOS14のリリースを(おそらく)9月に控え、最新OSを2,3世代サポートしているアプリは、やっとiOS13以降のフレームワークを本格的に導入していけることと思います。
LinkPresentationは たった十数行でアプリのUXを向上できるフレームワークなので、ぜひ活用していきたいですね。

このフレームワークについては、2019年のWWDCビデオもあります。
ビデオの長さは6分前後なので、気軽に見られておすすめです。
今回の記事は触れないローカルキャッシュの取得や共有時の実装のポイントに触れられています。
https://developer.apple.com/videos/play/wwdc2019/262/

以上です :tada:
ありがとうございました。

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