iOS 14 の App Clips について

iOS 14 の App Clips に関して調べたことのまとめ

資料一覧

資料から重要そうなところを抜粋

概要

f:id:daisuke-t-jp:20200918091805p:plain (画像は Apple のもの)

App Clips とはアプリ全体をダウンロードしなくても、ユーザがアプリの必要な機能だけをすばやく取得し、使用できる仕組み。

アプリが、ユーザに「瞬間的なエクスペリエンスを提供」する場合は App Clips の採用を検討する。

たとえばコーヒーショップアプリが AppStore にあるとすると、ユーザはそのアプリを使ってドリンクを注文したり、お気に入りのドリンクを保存したり、クーポンを収集したり、特別オファーを取得したりできる。

対照的に、App Clips はドリンクを注文する機能のみを提供する。ユーザがコーヒーショップを通り過ぎると、システムは Siri からの位置ベースの提案をデバイスに表示する。

ユーザーは提案をタップし、システム提供の App Clips カード(上記の図の中央で示されている)で App Clips を起動し、すぐにドリンクを注文できる。

ユーザはどこから App Clips を見つけて起動できるか?

https://developer.apple.com/documentation/app_clips/developing_a_great_app_clip

ユーザが App Clips を見つけて、起動する方法は以下がある。

  • 物理的な場所での NFC タグまたはビジュアルコードのスキャン
  • Siri から場所ベースの候補をタップする
  • Map アプリでリンクをタップする
  • Webサイトでスマートアプリバナーをタップする
  • メッセージアプリで誰かが共有したリンクをタップする

App Clip Codes

https://developer.apple.com/app-clips/

App Clips にアクセスするためのビジュアルコード。

見た目も美しく、はっきりしている、ユーザが App Clips を見つけるための最良の方法となる。

この App Clips のコードを作成するツールは 2020年後半に提供される予定。

優れた App Clips の設計

https://developer.apple.com/design/human-interface-guidelines/app-clips/overview/#designing-a-great-app-clip

  • 必要な機能だけ。ユーザが目の前のタスクを処理することに集中できるように機能は限定する。
  • App Clips のみで価値があり、タスクを完結できるようにする。App Clips をサービスの宣伝に使用しないように。
  • わかりやすく、直線的にタスクを実行できるようにインターフェースを提供する。App Clips にはタブバー、複雑なナビゲーション、設定は含めることはできない。目の前のタスクを簡潔なインターフェースでユーザが実行できるようにする。
  • 小さいほど良い。小さいほどユーザはすばやくダウンロードでき、実行できる。できるかぎり不要なアセット、コードを削除する。
  • Apple Pay のサポートを検討する。支払い情報の入力は、長くて間違いが起こりやすい作業のため Apple Pay が使用できると良い。
  • アカウント作成を要求しない。ユーザがすぐタスクを完了できるように、アカウント作成はタスク完了後にユーザに促す。
  • App Clips からアプリの移行をスムーズにする。アプリ全体をダウンロードすると、以前の App Clips はアプリに置き換えられる。ユーザが App Clips を使用していた時のエクスペリエンスをアプリでも提供する。

App Clips のライフサイクル

https://developer.apple.com/documentation/app_clips#overview

  • ユーザがアプリをインストールしない場合、システムは一定期間操作がないと、 App Clips を自動的に削除する。
  • ユーザが App Clips に対応するアプリをインストールすると、 App Clips がアプリに置き換わる。

App Clips の開発

https://developer.apple.com/documentation/app_clips/creating_an_app_clip

アプリのプロジェクトに App Clips のターゲットを追加する。

App Clips の信頼性の検証をするために Web サーバ構築も必要になる。

考察

以下、個人的な考察。

  • App Clips は一定期間操作されないと、システムにより削除される。
    • この期間が不明だが、App Clips を気にいって繰り返し使うユーザは、App Clips の入手の手間を嫌って、どこかのタイミングで AppStore から完全なアプリを入手することなりそう。
    • App Clips で良い体験をユーザが感じれば、アプリ本体のダウンロードに繋がる。
  • 開発時点で機能が分離できていないコードでは、App Clips 用に機能提供することのコストが大きくなるため、プロジェクトは疎結合の状態が望ましい。

iOS / Android 用のローカライズ文字列ファイルを生成するツール「strgen」

iOS / Androidローカライズ文字列ファイルを一括で生成するツールstrgenPython)を作った。

PyPI に登録してあるので、 pip でインストールできます。

概要

多言語情報を記載した CSV と、設定を記載した YAML ファイルを入力として、以下のファイルを出力する。

  • iOS 用ファイル
    • 各言語用の Localized.strings
    • ローカライズのキーが定義されている Swift ファイル(LocalizedStrings.swift
  • Android 用ファイル
    • 各言語用の strings.xml

strgen を使うメリット

一元管理できる

iOS / Androidローカライズファイルのソースが、単一の CSV になり、多言語が一元管理できる。

多言語更新のミスが発生しにくい

ツールで生成したファイルをプロジェクトに反映させるだけなので、ファイル(Localized.strings, strings.xml)を直接編集するのに比べ、ミスが発生しにくい。

iOSローカライズの効率がよくなる

hello というキーでローカライズ文字列が存在する場合、 iOS はキーを文字列で指定してローカライズ文字列を得るので、以下のようになる。

NSLocalizedString("hello", comment: "")

しかし、これは存在していないキーも指定できる問題がある。

たとえば、以下のようにキー指定をミスした場合、ビルド時には分からず、ミスがわかるのは実行時に確認した時だ(ローカライズ文字列が正しく表示されないので)

NSLocalizedString("hellow", comment: "")

この問題の対応として strgen は iOS の多言語ファイルを生成する時に、一緒にキーを列挙した swift ファイル(LocalizedStrings.swit)も作成する。

import Foundation

class LocalizableStrings {

    enum Key: String {
        case hello = "hello"
    }

}

この swift ファイルからキー定義を参照するようにすると、ローカライズのコーディングの際に、ビルド時点でミスに気付けるので、効率がよくなる。

NSLocalizedString(LocalizableStrings.Key.hello.rawValue, comment: "")

strgen を試してみる

サンプルで動作を確認できます。

インストール

まず、 pip で strgen をインストールします。

$ pip install strgen

これで、コマンドラインから strgen を実行できるようになる。

$ which strgen
/usr/local/bin/strgen

サンプルを動作させてみる

  1. プロジェクトを clone します。
    • $ git clone https://github.com/daisuke-t-jp/strgen
  2. カレントディレクトリを clone したプロジェクト内の strgen/sample フォルダに変更します。
  3. 以下のコマンドを実行。
    • $ strgen strgen.yml
  4. build フォルダを確認します。

使い方

そのほかの使い方、YAML ファイルの仕様などは、README を参照してください。

https://github.com/daisuke-t-jp/strgen

Python パッケージ作成に関する情報まとめ

Python パッケージ作成、PyPI(パイ・ピーアイ) への登録などに役立つ情報を、まとめる。

setup.py だけ使用するパターン、setup.cfg も使用するパターン、__init__.py に多くの役割を持たせるパターン/ほとんど役割を持たせないパターン、などパッケージ作成に関してさまざま選択肢があるので、いろいろな例を見ておくと良い。

関連公式ドキュメント

参考情報

参考になる Python パッケージ(OSS

パッケージのインストールテスト

パッケージを作ったらインストールして試してみる

開発用

開発中のパッケージは、こちらの方法でインストールしてテストする。

$ python setup.py develop

# もしくは

$ pip install -e .

配布用

# 配布用パッケージの作成
# これで dist/ 以下にパッケージ(tar.gz)ができる
$ python setup.py sdist

# 配布用パッケージのインストール
$ pip install dist/*

パッケージ情報の確認

$ pip show <パッケージ名> 

アンインストール

$ pip uninstall <パッケージ名>

PyPI への登録

登録するためのツール twine が入っていないならインストールする。

$ pip install twine

登録。

$ twine upload --repository pypi dist/*

iOS でパラパラ漫画のような見た目のインジケーターを作った

everysing というカラオケのアプリで、読み込み中に表示されるインジケーターがパラパラ漫画のようになっていて、キュートな感じがしてよい。(マイクやヘッドフォンのアイコンが表示される)

f:id:daisuke-t-jp:20200610220213g:plain:w250



この UI を再現したく、パラパラとアニメするインジケータを作ってみた。


↓こんな感じに画像を設定すれば、パラパラアニメで表示される。

import ParaParaIndicator


class ViewController: UIViewController {
    
    let paraparaIndicator = ParaParaIndicator()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
        // Images for flipping 
        paraparaIndicator.images = [
            UIImage(named: "camel")!.withRenderingMode(.alwaysTemplate),
            UIImage(named: "deer")!.withRenderingMode(.alwaysTemplate),
            UIImage(named: "elephant")!.withRenderingMode(.alwaysTemplate),
            UIImage(named: "giraffe")!.withRenderingMode(.alwaysTemplate),
            UIImage(named: "kangaroo")!.withRenderingMode(.alwaysTemplate),
            UIImage(named: "mouse")!.withRenderingMode(.alwaysTemplate),
            UIImage(named: "rhino")!.withRenderingMode(.alwaysTemplate),
        ]
        
        
        // Indicator frame
        paraparaIndicator.frame.size.width = 100
        paraparaIndicator.frame.size.height = 100     
        paraparaIndicator.center = self.view.center
        
        // Tint color
        paraparaIndicator.tintColor = .cyan
        
        // Flip interval
        paraparaIndicator.timeInterval = 0.3
        
        // Hide when stopped
        paraparaIndicator.hidesWhenStopped = true
        
        // Start animation
        paraparaIndicator.startAnimating()
        
        // Stop animation
        // paraparaIndicator.stopAnimating()
        
        self.view.addSubview(paraparaIndicator)
    }
    
}

なお UIActivityIndicatorView Like で扱えるように、以下のメソッドを実装し、インターフェースを似せてある。

  • hideWhenStopped
  • isAnimating
  • startAnimating
  • stopAnimating

macOS 10.15 から有効な EndpointSecurity API(SystemExtension.framework)

https://developer.apple.com/jp/system-extensions/

macOS 10.15 から、従来のカーネル拡張(KEXT)を使わずに、ユーザーランドで動作するソフトでシステムイベントを監視(おそらくイベントの許可/禁止も)できるようになった。



https://developer.apple.com/contact/request/system-extension/

まず Apple に SystemExtension.framework / EndpointSecurity API の使用リクエストを出し、許可される必要がある。


https://developer.apple.com/documentation/endpointsecurity

f:id:daisuke-t-jp:20200531152712p:plain:w400

プロジェクトで Endpoint Security API を使用するには、以下の2つをリンクする。

  • SystemExtension.framwork
  • libEndpointSecurity.tdb


f:id:daisuke-t-jp:20200531152915p:plain:w400

entitlements に com.apple.developer.endpoint-security.client = YES を設定する。

AppleAPI リクエストが許可されていないと、アプリがただしく起動しない。



従来の KEXT を使用してシステム監視(アンチウィルスソフトなど)する場合より SystemExtension.framework を使用するメリット

  • Kernel extension で機能を実現した場合、ソフトはカーネルランドで実行されるため、ソフトの処理が遅いと OS 全体も遅くなってしまうなど OS の安定性に関わる現象が起きる。その分、実装もシビアになる。
    • ユーザランド実行になるとここが、そこまでシビアではなくなる?
  • Kernel extension 由来のセキュリティ問題がある場合、カーネル空間でその問題は発生するので OS デベロッパApple)、ソフトデベロッパの責務の線引きが不明瞭になる。
    • カーネルランド、ユーザランド、それぞれのセキュリティレベルが線引きされた状態で、ソフトウェア開発ができる。



不明な点

https://developer.apple.com/documentation/endpointsecurity/client?language=objc

  • Endpoint Security API のサンプルコードをみると panic() 関数を呼んでいる
    • ユーザランドで動作するソフトだが、作法として kernel panic を発生させて良いのだろうか?
  • Kernel extension ではデバッグをするとき、同じモデル・OS の mac 2台を用意して、片方の mac で panic を発生させて、その状態でもう片方の mac で接続してデバッグ・・・という方法があったが、そのあたりのデバッグ方法もしやすい方に変わっているのだろうか

GitHub Action の Private / Public リポジトリの制限

GitHub Actionsの支払いについて - GitHub ヘルプ

Public リポジトリ

GitHub Actions の制限はない

Private リポジトリ

GitHub Actions の実行時間とストレージに制限がある。

アカウントに存在するすべての Private リポジトリの実行時間、ストレージ使用量の合算が制限の対象。個々のリポジトリごとに制限が計算されるわけではない。

  • Free アカウントの場合
    • ストレージ : 500 MB
    • 実行時間 : 2,000分(月)
  • Pro アカウントの場合
    • ストレージ : 1 GB
    • 実行時間 : 3,000分(月)

Private リポジトリにおける GitHub の Actions の使用状況(利用時間とストレージ使用量)はユーザアカウントから確認できる。

GitHub Actions の使用状況を表示する - GitHub ヘルプ

制限を超えて使用したい場合は、料金支払いが必要になる。

まとめ

  • Public リポジトリ
    • Action の実行時間、ストレージは気にしなくて良い
  • Private リポジトリ
    • Action の実行時間、ストレージを気にする必要がある
    • 実行時間を気にするならば Action 内の処理で適切にタイムアウト時間を設定する必要もある
    • タイムアウト待ち時間で、実行時間が思わず消費される可能性があるからだ

GitHub ワークフローで環境変数、シークレットな情報を使用する

これは 2020/05 時点の情報をもとにした記事なので、最新では事情が変わっている可能性を留意すること。

ワークフロー内で使用できる環境変数

環境変数の利用 - GitHub ヘルプ

たとえば GITHUB_REPOSITORY(所有者とリポジトリの名前 octocat/Hello-World など)がある。

API キーなどシークレット情報の扱い

暗号化されたシークレットの作成と保存 - GitHub ヘルプ

ある Web API を呼び出すのに必要な API キーがあったとして、それをコード中にそのまま記載してしまうと API キーの漏洩になってしまう。これを避けるため、シークレット情報は GitHub に設定して秘匿して使用することができる。

シークレット情報は Organization / Repository ごとに設定することができる。

設定したシークレット情報は、以下のようにワークフローで参照することができる。

steps:
  - shell: bash
    env:
      SUPER_SECRET: ${{ secrets.SuperSecret }}
    run: |
      example-command "$SUPER_SECRET"