kumamotone’s blog

iOS/Android アプリエンジニアです https://twitter.com/kumamo_tone

App Clipsの起動方法の設定とテストについて

kumamotone.hatenadiary.jp

上記の続きになります。

デフォルトの起動方法の設定

App Clip は単独でリリースできず、常に対応する本体アプリが必要で、アプリの一部としてApp Store Connectに提出します。App Store Connectでは、新しいアプリバージョンのページで、Default App Clip Experience を設定し、Clip Cardに次のメタデータを提供する必要があります。

  • ヘッダー画像
  • アプリのクリップの詳細情報を提供するサブタイトル
  • アクションボタンをタップしたときに呼ばれるアクション

設計指針については、Human Interface Guidelines を参照してください。

Advanced App Clip Experience

Webサイトの Smart App Banner や Message アプリの共有リンクからの呼び出しは、前述の Default App Clip Experience が呼び出されます。

しかし、NFCタグなどをサポートしたい場合は、 Advanced App Clip Experience を設定する必要があります。呼び出しURLを設定し、App Store Connectで呼び出しと呼び出しURLの両方をAdvanced App Clip Experienceとして設定します。

次のような場合に、Advanced App Clip Experienceを設定します。

  • NFC タグやビジュアルコードからの呼び出しを含む、すべての可能な呼び出しをサポートするようにしたい場合
  • 物理的な場所に関連付ける必要がある場合
  • 複数の事業者が利用する場合

Advanced App Clip Experience を設定するには、呼び出しURLを登録する必要があります。また、Advanced App Clip Experience ごとに異なる画像やメタデータを指定することもできます。

Advanced App Clip Experience に設定すべきURL

Advanced App Clip Experience に登録すべきURLの数は、ユースケースによって異なります。たとえば、App Clipが常に同じ画面を表示する場合、1つ登録すれば十分でしょう。

通常の呼び出し(WebのSmart App Banner、Messageアプリの共有リンク)以外の呼び出しをサポートしたくない場合は、Advanced App Clip Experienceを作成する必要すらありません。その他のユースケースでは、逆にAdvanced App Clip Experienceを設定することが最も重要な場合があります。

一般的には、登録するURLはできるだけ少なくして、できるだけ汎用的なURLを登録してください。

次に、最もふさわしい接頭辞を持つURLを考えます。

たとえば、https://example.com/menu を URL として設定すると、https://example.com/menu/lunchhttps://example.com/menu/dinner などの同じURLで始まるすべてのURLをカバーすることができます。

ただし、ビジネスが複数の物理的な拠点を持つ場合は、1 つまたは複数の物理的な拠点用に それぞれ Advanced App Clip Experience を設定し、各拠点ごとに異なるヘッダー画像、メタデータ、および呼び出し URL を使用することができます。

(例: https://example.com/location1https://example.com/location2 など)

Advanced App Clip Experiences の作成

App Store Connect で Advanced App Clip Experienceを作成するには、呼び出し URL を登録する必要があります。異なるドメインの URL を使用している場合は、アプリとApp Clipの両方のターゲットの Associated Domains Entitlement に適切なドメインエントリを追加して、登録した URL を認証します(参考: Associated Domains Entitlement の追加)。

次に、App Clipと完全なアプリの両方のコードで、登録した URL に応答します。最後に、App Clipが物理的な場所に関連する機能を提供している場合は、Advanced App Clip Experience を物理的な場所に関連付けます(参考: ユーザーの位置情報の確認)。

Smart App Banner とリンクから呼び出せるようにする

WebサイトのSmart App Banner から、 App Clipを起動できるようにしましょう(注釈:Smart App Banner というのは、下記画面にもある、スマホサイトの上部に、アプリへの誘導を表示するアレ)。サイトにSmart App Bannerがすでに設置されている場合は、既存のmetaタグに app-clip-bundle-id=appClipBundleID 属性を追加し、その値にApp Clipのbundle identifierを使用します。

Smart App Bannerを表示したくない場合は、次のHTML metaタグを各ページに追加します。

<meta name="apple-itunes-app" content="app-id=myAppStoreID, app-clip-bundle-id=appClipBundleID, affiliate-data=myAffiliateData, app-argument=myAppArgument">

プレースホルダを適切な値に置き換えます。metaタグのcontent属性には、app-clip-bundle-idとapp-idの両方が含まれていることに注意してください。app-idを含めることで、iOS 13以前のデバイスでアプリを開いたり、プロモーションしたりできるようになります。

f:id:kumamotone:20200625025453p:plain
https://developer.apple.com/documentation/app_clips/configuring_your_app_clip_s_launch_experience

Smart App BannerをWebサイトに設置するには、Apple App Site Association ファイルを設定し、Webサイトのドメインを関連付けられたドメインのリストに追加する必要があります(参考: Associated Domains Entitlement の追加)。

App Clipでは、NSUserActivity オブジェクト経由で、呼び出し元の Smart App Bannerを設置したWebサイトのURLを取得できます。

Web サイトにSmart App Bannerを追加するには、Advanced App Clip Experience は必ずしも必要ありません。しかし、Smart App Bannerを表示できるWebサイトであれば、どのようなWebサイトにも設定することができます。

Advanced App Clip Experience の一部としてバナーの URL を登録したとします。このとき、バナーからApp Clipを起動すると、関連するClip Cardが表示されます。

一方、Default App Clip Experienceのみを設定した場合、バナーからApp Clipを起動すると、Default App Clip ExperienceのClip Cardが表示されます。

App Clip の呼び出しの設定

アプリとApp Clipのどちらもが、スムーズな起動を提供するために、呼び出しURL に応答して、UIを更新する必要があります。

たとえば、複数の物理的な場所を持つビジネスがあったとします。

https://example.com (何のパラメータもないURL)を設定した場合、起動後にユーザーにリストから物理的な場所を選択してもらうこともできますが、操作回数が増えてしまいます。呼び出し URL を適切に設定し、追加の URL パラメータを呼び出しに渡すことで、余計な操作を減らし、ユーザーの体験を良くすることができます。

アプリとApp Clipの両方について、

  1. Associated Domains capability を有効にし、App Clipを起動する各ドメインのエントリを追加します。上記の例では、example.comを追加します。
  2. ユーザーがある場所から別の場所に切り替えた場合に備えて、保存されていないデータを永続化します。
  3. 追加のパラメータを含む呼び出し URL を使用します(例:https://example.com/location1https://example.com/location2 など)。

起動時には、URL に応答して、UIを場所に合わせて更新します。

マップアプリからの呼び出しや「Siriからの提案」からの位置情報ベースのサジェストでは、App Clip Experienceに登録したURLが呼び出しURLとして使用されます。呼び出しURLのプレフィックスとしてのみ使用する場合でも、App Clipとアプリの両方でこのURLを処理できるようにしておく必要があります。

たとえば、Advanced App Clip Experience の1つとして https://example.com/menu を登録したとします。

その場合、NFC タグからの呼び出しには、https://example.com/menu/dinner/item/1234https://example.com/menu/dinner/item/5678 のような URL を使用し、 https://example.com/menu は使用しないかもしれません。

そのような場合でも、App Clipとアプリは https://example.com/menu は扱えるようにする必要があります。

呼び出しURLへのアクセス

App Clip が起動されるタイミングで、NSUserActivityオブジェクトが起動時のライフサイクル関数に渡ってきます。

任意の状態とデータを永続化し、起動時にNSUserActivityオブジェクトにアクセスするために、以下のそれぞれの場合に対応する必要があります。

  • SwiftUIの場合

    • SwiftUIのライフサイクル関数を実装します
    • たとえば、onContinueUserActivity(_:perform:) を使用します。
  • UIKit + UISceneDelegate の場合

    • UISceneDelegateで定義されている関数を実装します。
    • たとえば、scene(:willConnectTo:options:)、scene(:willContinueUserActivityWithType:)、scene(_:continue:)などを実装します。
  • UIKit + UIApplicationDelegate の場合

    • UIApplicationDelegateで定義されている関数を実装します。
    • application(:didFinishLaunchingWithOptions:)ではNSUserActivityオブジェクトにアクセスできないので、必ずapplication(:continue:restorationHandler:)を実装してください。

起動時に、呼び出しの型が NSUsuserActivityTypeBrowsingWeb であることを確認してから、App Clip を起動した URL にアクセスします。

次のコードは、呼び出しURLから情報を抽出する関数です。

func respondTo(_ activity: NSUserActivity?) {
        
    // 異常なデータのための guard 文
    guard activity != nil else { return }
    guard activity!.activityType != NSUserActivityTypeBrowsingWeb else { return }
    guard let incomingURL = activity?.webpageURL else { return }
    guard let components = NSURLComponents(url: incomingURL, resolvingAgainstBaseURL: true) else { return }

    // App Clipに渡されたURLに基づいてUIを更新
}

ユーザーの位置情報の検証

物理的な場所で呼び出す App Clipを作成する場合、タスクを実行する前に、システムがユーザーの場所を検証する必要がある場合があります。

f:id:kumamotone:20200625031813p:plain
https://developer.apple.com/documentation/app_clips/responding_to_invocations

App Clipは、素早く起動するために、軽量なアルゴリズムでユーザーが特定の場所にいることを検証します。

  1. App Clip の Info.plist に NSAppClip という Dictionary 型のキーを追加します。
  2. NSAppClipRequestLocationConfirmation という Bool 型のキーを追加し、値をtrueにします。このエントリはアプリのInfo.plistには追加する必要はありません。 アプリをインストールしてApp Clipを置き換えたとき、App Clipがすでにユーザーの位置情報を確認できていた場合は、アプリもユーザーの位置情報を確認できるようになります。
  3. 位置情報をApp Clipに提供します。この情報を取得するには、App Clipを起動するURLにIDを含めて、そのIDを使用してデータベース内のビジネスの位置情報を検索することができます。または、App Clipを起動する URL に位置情報自体をエンコードして含めることもできます。
  4. 位置情報を使って、半径500mまでのCLCircularRegionオブジェクトを作成し、confirmAcquired(in:completionHandler:) に渡します。

Info.plist に NSAppClipRequestLocationConfirmation キーを追加すると、App Clipの呼び出し時に表示されるClip Cardには、App Clipが自分の位置情報を確認できることをユーザーに伝える追加のnoteが含まれています。この権限はデフォルトで有効になっていますが、ユーザーはClip Cardのnoteをタップすることで無効にすることができます。

次のコードは、App Clipを起動すると、ユーザーの位置情報を検証します。ユーザーがデバイス上の位置情報サービスの権限を拒否した場合も含めた、すべての可能性に合わせてUIを更新するようにしてください。

import UIKit
import AppClip
import CoreLocation

class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    
    var window: UIWindow?
    
    // 該当するすべてのライフサイクル関数で verifyUserLocation(_:) を呼ぶ
    func verifyUserLocation(_ activity: NSUserActivity?) {
        
        // 異常なデータのための guard 文
        guard activity != nil else { return }
        guard activity!.activityType != NSUserActivityTypeBrowsingWeb else { return }
        guard let payload = activity!.appClipActivationPayload else { return }
        guard let incomingURL = activity?.webpageURL else { return }

        // CLRegion オブジェクトの作成
        guard let region = location(from: incomingURL) else {
            // ここで解析エラーに対応してください。
            return
        }
        
        // 呼び出しが期待した場所で発生したことを検証
        payload.confirmAcquired(in: region) { (inRegion, error) in
            guard let confirmationError = error as? APActivationPayloadError else {
                if inRegion {
                    // NFCタグの位置が、ユーザーの位置と一致した場合
                } else {
                    // NFCタグの位置が、ユーザーの位置と一致しなかった場合
                    // (例)だれかが NFC タグを移動したとき
                }
                return
            }
            
            if confirmationError.code == .doesNotMatch {
                // スキャンしたURLがApp Clipに登録されていなかった場合
            } else {
               // ユーザーが位置情報へのアクセスを拒否したか、
               // 呼び出し元がNFCタグやビジュアルコードではなかった場合
            }
        }
    }

    func location(from url:URL) -> CLRegion? {
        let coordinates = CLLocationCoordinate2D(latitude: 37.334722,
                                                 longitude: 122.008889)
        return CLCircularRegion(center: coordinates,
                                radius: 100,
                                identifier: "Apple Park")
    }
}

App Clip の起動のテスト

環境変数を追加して App Clip の呼び出しをローカルでデバッグしたり、TestFlightでテストするための呼び出しを設定したりします。

App Clipを作成する際には、Advanced App Clip Experienceに登録したURLにApp Clipが応答できるように、可能性のあるすべての呼び出しをテストすることが重要です。

デバッグ時のローカルテスト

App Clip のデバッグ時に呼び出しをテストするには、以下の工程を行います。

  1. Xcodeで、[Product] > [Scheme] > [Edit Scheme] を選択し、App Clip の Scheme を選択します。
  2. [Run] を選択します。
  3. Argument タブで、新しい環境変数を追加し、名前を _XCAppClipURL とし、その値をテストするURLに設定し、変数を有効にします。プロジェクトにApp Clip Targetを追加すると、Xcodeはこの環境変数を追加します。
  4. テストしたいURLのドメインが、Associated Domains Entitlement に追加されていることを確認します(参考: Associated Domains Entitlement の追加)。
  5. NSUserActivity オブジェクトから構成したテスト URL にアクセスするために、App Clipをビルドして実行します。

TestFlight を使った呼び出しURLのテスト

新しいバージョンのApp Clipを App Store Connect に提出し、TestFlight でテスターが利用できるようにすると、テスト用に最大 3 つの異なる呼び出しを設定できます。

TestFlightアプリでは、設定された3つの呼び出しのいずれかからApp Clipを起動し、App Clipが期待通りに応答するかどうかを検証します。TestFlight アプリからApp Clipを起動した場合、Clip Cardは表示されないことに注意してください。

出典

続き

kumamotone.hatenadiary.jp