iOS8アプリ拡張機能のチュートリアル
公開: 2022-03-11これまでに試したことのある人はほとんどいませんでしたが(これを見てください)、スマートフォンとモバイルOSの外観を定義したのは最初のiPhoneを搭載したAppleでした。 Appleは、ハードウェアとユーザーエクスペリエンスに驚異的な進歩を遂げました。 ただし、モバイルOSの動作方法や、スマートフォンアプリケーションの作成方法にも基準が設定されていることを忘れがちです。
アプリケーション間にコンクリートの壁を構築し、アプリケーションを完全に分離して互いに気付かないようにすることが、アプリケーションを安全に保ち、データを保護するための最良の方法でした。 すべてのアクティビティはiOSによって綿密に監視されており、アプリがその範囲外で実行できるアクションはほんの一握りでした。
「禁欲は最高の保護です!」 -でも、その面白さはどこにあるのでしょうか。
彼らにはしばらく時間がかかりました。 あなたが私に尋ねるなら長すぎるが、iOS8でAppleはいくつかの楽しみを持っていることに決めた。 iOS 8では、AppExtensionsと呼ばれる新しい概念が導入されました。 この新機能は、アプリケーション間の壁を壊すことはありませんでしたが、いくつかのアプリ間の穏やかでありながら具体的な接触を提供するいくつかの扉を開きました。 最新のアップデートにより、iOS開発者はiOSエコシステムをカスタマイズするオプションが提供され、この道も開かれることを望んでいます。
iOS 8アプリ拡張機能とは何ですか?どのように機能しますか?
簡単に言うと、iOS 8 App Extensionsは、アプリケーションを起動したり画面に表示したりせずに、アプリケーションを操作する新しい方法を提供します。
予想通り、Appleはそれらがすべてを把握していることを確認したので、アプリケーションが提供できる新しいエントリポイントはほんの一握りです。
- 今日(ウィジェットとも呼ばれます)-通知センターの[今日]ビューに表示される拡張機能は、簡単な情報を表示し、迅速なタスクの実行を可能にします。
- 共有-アプリがソーシャルネットワークやその他の共有サービス上のユーザーとコンテンツを共有できるようにする拡張機能。
- アクション-アクションシートにカスタムアクションボタンを作成して、ユーザーがホストアプリから発信されたコンテンツを表示または変換できるようにする拡張機能。
- 写真編集-ユーザーが写真アプリ内で写真やビデオを編集できるようにする拡張機能。
- ドキュメントプロバイダー-他のアプリがアプリによって管理されているドキュメントにアクセスできるようにするために使用される拡張機能。
- カスタムキーボード-システムキーボードを置き換える拡張機能。
アプリ拡張機能はスタンドアロンアプリではありません。 これらは、効率的で単一のタスクに集中することを目的としたアプリの拡張機能(ホストアプリと呼ばれる他のアプリからアクセスできます)を提供します。 それらには独自のバイナリ、独自のコード署名、および独自の要素セットがありますが、含まれているアプリのバイナリの一部としてAppStore経由で配信されます。 1つの(含む)アプリに複数の拡張子を付けることができます。 ユーザーが拡張機能を備えたアプリをインストールすると、iOS全体で利用できるようになります。
例を見てみましょう。ユーザーがSafariを使用して写真を見つけ、共有ボタンを押して、共有するアプリケーション拡張機能を選択します。 Safariは、拡張機能をロードして提示するiOSSocialフレームワークと「通信」します。 拡張機能のコードが実行され、システムのインスタンス化された通信チャネルを使用してデータが渡され、タスクが完了すると、Safariは拡張機能ビューを破棄します。 この直後、システムはプロセスを終了し、アプリケーションが画面に表示されることはありませんでした。 それでも、画像共有機能は完了しました。
iOSは、プロセス間通信を使用して、ホストアプリとアプリ拡張機能が連携できるようにする責任があります。 開発者は、拡張ポイントとシステムによって提供される高レベルのAPIを使用するため、基盤となる通信メカニズムについて心配する必要はありません。
ライフサイクル
アプリ拡張機能のライフサイクルは、iOSアプリとは異なります。 ホストアプリは、ユーザーのアクションへの応答として拡張機能のライフサイクルを開始します。 次に、システムはアプリ拡張機能をインスタンス化し、それらの間に通信チャネルを設定します。 拡張機能のビューは、ホストアプリのリクエストで受け取ったアイテムを使用して、ホストアプリのコンテキスト内に表示されます。 拡張機能のビューが表示されると、ユーザーは拡張機能を操作できます。 拡張機能は、ユーザーのアクションに応じて、タスクをすぐに実行/キャンセルするか、必要に応じてバックグラウンドプロセスを開始して実行することにより、ホストアプリのリクエストを完了します。 その直後に、ホストアプリは拡張機能のビューを破棄し、ユーザーはホストアプリ内の以前のコンテキストに戻ります。 このプロセスを実行した結果は、プロセスが完了するとホストアプリに返される可能性があります。 拡張機能は通常、ホストアプリから受信したリクエストを完了した後(またはバックグラウンドプロセスを開始して実行した後)に終了します。
システムはホストアプリからユーザーのアクションの拡張機能を開き、拡張機能はUIを表示し、いくつかの作業を実行し、データをホストアプリに返します(拡張機能のタイプに適している場合)。 拡張機能が実行されている間、含まれているアプリは実行されていません。
アプリ拡張機能の作成-Today拡張機能を使用した実践的な例
ウィジェットとも呼ばれるToday拡張機能は、通知センターのTodayビューにあります。 これらは、ユーザーに最新のコンテンツを提示する(気象条件を表示するなど)、または迅速なタスクを実行する(To Doリストアプリのウィジェットで実行したことをマークするなど)ための優れた方法です。 ここで、キーボードエントリはサポートされていないことを指摘する必要があります。
アプリからの最新情報(GitHubのコード)を表示するToday拡張機能を作成しましょう。 このコードを実行するには、プロジェクトのアプリグループを(再)構成したことを確認してください(開発チームを選択します。アプリグループ名は一意である必要があり、Xcodeの指示に従う必要があります)。
新しいウィジェットの作成
前に述べたように、アプリ拡張機能はスタンドアロンアプリではありません。 アプリ拡張機能を構築するための包含アプリが必要です。 含まれているアプリを入手したら、[ファイル]->[新規]->[Xcodeへのターゲット]に移動して、新しいターゲットを追加することを選択します。 ここから、TodayExtensionを追加するための新しいターゲットのテンプレートを選択します。
次のステップでは、製品名を選択できます。 これは、通知センターの[今日]ビューに表示される名前です。 このステップでも、SwiftとObjective-Cの間で言語を選択するためのオプションがあります。 これらの手順を完了すると、XcodeはTodayテンプレートを作成します。このテンプレートは、 Info.plist
ファイルとインターフェイスファイル(ストーリーボードまたは.xibファイル)を含むプリンシパルクラス( TodayViewController
という名前)のデフォルトのヘッダーファイルと実装ファイルを提供します。 Info.plist
ファイルは、デフォルトでは次のようになります。
<key>NSExtension</key> <dict> <key>NSExtensionMainStoryboard</key> <string>MainInterface</string> <key>NSExtensionPointIdentifier</key> <string>com.apple.widget-extension</string> </dict>
テンプレートによって提供されるストーリーボードを使用したくない場合は、 NSExtensionPrincipalClass
NSExtensionMainStoryboard
を追加します。
Todayウィジェットは次のようにする必要があります。
- コンテンツが常に最新であることを確認してください
- ユーザーの操作に適切に対応する
- パフォーマンスが良い(iOSウィジェットはメモリを賢く使用する必要があります。そうしないとシステムによって終了されます)
データと共有コンテナの共有
アプリ拡張機能とその包含アプリはどちらも、プライベートに定義された共有コンテナー内の共有データにアクセスできます。これは、包含アプリと拡張機能の間の間接的な通信の方法です。
Appleがこれらのものをとても「シンプル」にする方法が好きではありませんか? :)
NSUserDefaults
を介したデータの共有は単純で、一般的なユースケースです。 デフォルトでは、拡張機能とそれに含まれるアプリは別々のNSUserDefaults
データセットを使用し、互いのコンテナーにアクセスできません。 この動作を変更するために、iOSはアプリグループを導入しました。 含まれているアプリと拡張機能でアプリグループを有効にした後、 [NSUserDefaults standardUserDefaults]
を使用する代わりに、 [[NSUserDefaults alloc] initWithSuiteName:@"group.yourAppGroupName"]
を使用して同じ共有コンテナーにアクセスします。
ウィジェットの更新
コンテンツが常に最新であることを保証するために、Today拡張機能は、ウィジェットの状態を管理し、コンテンツの更新を処理するためのAPIを提供します。 システムはウィジェットのビューのスナップショットをキャプチャすることがあるため、ウィジェットが表示されると、ビューのライブバージョンに置き換えられるまで、最新のスナップショットが表示されます。 スナップショットが作成される前にウィジェットの状態を更新するには、 NCWidgetProviding
プロトコルへの準拠が重要です。 ウィジェットがwidgetPerformUpdateWithCompletionHandler:
呼び出しを受信したら、ウィジェットのビューを最新のコンテンツで更新し、更新の結果を説明するために次の定数のいずれかを使用して完了ハンドラーを呼び出す必要があります。

-
NCUpdateResultNewData
新しいコンテンツにはビューの再描画が必要です NCUpdateResultNoDate
ウィジェットを更新する必要はありませんNCUpdateResultFailed
更新プロセス中にエラーが発生しました
- (void)widgetPerformUpdateWithCompletionHandler:(void (^)(NCUpdateResult))completionHandler { // Perform any setup necessary in order to update the view. // If an error is encountered, use NCUpdateResultFailed // If there's no update required, use NCUpdateResultNoData // If there's an update, use NCUpdateResultNewData [self updateTableView]; completionHandler(NCUpdateResultNewData); }
ウィジェットが表示可能になるタイミングの制御
ウィジェットがいつ表示されるかを制御するには、 NCWidgetController
クラスのsetHasContent:forWidgetWithBundleIdentifier:
メソッドを使用します。 このメソッドを使用すると、ウィジェットのコンテンツの状態を指定できます。 ウィジェットまたはそれに含まれるアプリ(アクティブな場合)から呼び出すことができます。 このメソッドにNO
またはYES
フラグを渡して、ウィジェットコンテンツの準備ができているかどうかを定義できます。 コンテンツの準備ができていない場合、Todayビューを開いたときにiOSはウィジェットを表示しません。
NCWidgetController *widgetController = [[NCWidgetController alloc] init]; [widgetController setHasContent:YES forWidgetWithBundleIdentifier:@"com.your-company.your-app.your-widget"];
ウィジェットから含むアプリを開く
Todayウィジェットは、 openURL:completionHandler:
メソッドを呼び出して、含まれているアプリを開くように要求できる唯一の拡張機能です。 包含アプリがユーザーの現在のタスクのコンテキストで意味のある方法で開くようにするには、カスタムURLスキーム(ウィジェットと包含アプリの両方が使用できる)を定義する必要があります。
[self.extensionContext openURL:[NSURL URLWithString:@"customURLsheme://URLpath"] completionHandler:nil];
UIに関する考慮事項
ウィジェットを設計するときは、 UIVisualEffectView
クラスを利用してください。ぼやけた/鮮やかなビューは、 UIVisualEffectView
に直接追加するのではなく、 contentView
に追加する必要があることに注意してください。 ウィジェット( NCWidgetProviding
プロトコルに準拠)は、最後のviewWillDisappear:
からのビューの状態と一致させるために、 viewWillAppear:
にキャッシュされた状態をロードし、新しいデータが到着したときにスムーズに移行する必要があります。これは通常のビューの場合ではありません。コントローラ(UIはviewDidLoad
で設定され、 viewWillAppear
でアニメーションとデータの読み込みを処理します)。 ウィジェットは、タスクを実行するため、またはシングルタップで含まれているアプリを開くために設計する必要があります。 キーボードエントリはウィジェット内では使用できません。 これは、テキスト入力が必要なUIは使用しないことを意味します。
垂直方向と水平方向の両方で、ウィジェットにスクロールを追加することはできません。 より正確には、スクロールビューを追加することは可能ですが、スクロールは機能しません。 Today拡張機能のスクロールビューでの水平スクロールジェスチャは、通知センターによってインターセプトされ、Todayから通知センターへのスクロールが発生します。 今日の拡張機能内のスクロールビューを垂直方向にスクロールすると、今日のビューをスクロールすることによって中断されます。
テクニカルノート
ここでは、アプリ拡張機能を作成するときに覚えておくべきいくつかの重要な点を指摘します。
すべての拡張機能に共通の機能
次の項目は、すべての拡張機能に当てはまります。
sharedApplicationオブジェクトは立ち入り禁止です:アプリ拡張機能は、sharedApplicationオブジェクトにアクセスしたり、そのオブジェクトに関連するメソッドを使用したりすることはできません。
カメラとマイクは立ち入り禁止です:アプリ拡張機能はデバイスのカメラまたはマイクにアクセスできません(ただし、これはすべてのハードウェア要素に当てはまるわけではありません)。 これは、一部のAPIが使用できない結果です。 アプリ拡張機能の一部のハードウェア要素にアクセスするには、そのAPIがアプリ拡張機能で利用可能かどうかを確認する必要があります(上記のAPI可用性チェックを使用)。
ほとんどのバックグラウンドタスクは立ち入り禁止です。アプリ拡張機能は、アップロードまたはダウンロードを開始することを除いて、長時間実行されるバックグラウンドタスクを実行できません。これについては以下で説明します。
AirDropは立ち入り禁止です:アプリ拡張機能はAirDropを使用してデータを受信できません(ただし送信できます)。
バックグラウンドでのアップロード/ダウンロード
バックグラウンドで実行できるタスクの1つは、 NSURLSession object
を使用したアップロード/ダウンロードです。
アップロード/ダウンロードタスクが開始された後、拡張機能はホストアプリのリクエストを完了し、タスクの結果に影響を与えることなく終了できます。 バックグラウンドタスクの完了時に拡張機能が実行されていない場合、システムはバックグラウンドで含まれているアプリを起動し、アプリのデリゲートメソッドapplication:handleEventsForBackgroundURLSession:completionHandler:
が呼び出されます。
拡張機能がバックグラウンドNSURLSession
タスクを開始するアプリには、含まれているアプリとその拡張機能の両方がアクセスできる共有コンテナーが設定されている必要があります。
含まれているアプリとそのアプリ拡張機能ごとに異なるバックグラウンドセッションを作成してください(各バックグラウンドセッションには一意の識別子が必要です)。 一度に1つのプロセスのみがバックグラウンドセッションを使用できるため、これは重要です。
アクション対シェア
アクション拡張機能と共有拡張機能の違いは、実際には非常に似ているため、コーダーの観点からは完全には明確ではありません。 共有拡張ターゲット用のXcodeのテンプレートはSLComposeServiceViewController
を使用します。これは、ソーシャル共有に使用できる標準の作成ビューUIを提供しますが、必須ではありません。 共有拡張機能は、アクション拡張機能がSLComposeServiceViewController
から継承できるのと同じ方法で、完全にカスタム設計するためにUIViewControllerから直接継承することもできます。
これら2つのタイプの拡張機能の違いは、それらの使用方法にあります。 アクション拡張機能を使用すると、独自のUIを使用せずに拡張機能を構築できます(たとえば、選択したテキストを翻訳し、翻訳をホストアプリに返すために使用される拡張機能)。 共有拡張機能を使用すると、コメント、写真、ビデオ、オーディオ、リンクなどをホストアプリから直接共有できます。 UIActivityViewController
は、アクション拡張機能と共有拡張機能の両方を駆動します。共有拡張機能は上の行にカラーアイコンとして表示され、アクション拡張機能は下の行にモノクロアイコンとして表示されます(画像2.1)。
禁止されているAPI
NS_EXTENSION_UNAVAILABLE
マクロまたは使用不可の同様のマクロでヘッダーファイルにマークされたAPIは使用できません(たとえば、iOS8のHealthKitおよびEventKitUIフレームワークはどのアプリ拡張機能でも使用できません)。
アプリと拡張機能の間でコードを共有している場合、アプリ拡張機能で許可されていないAPIを参照しても、AppStoreからのアプリが拒否されることに注意する必要があります。 これに対処するには、共有クラスを階層にリファクタリングし、共通の親と異なるターゲットの異なるサブクラスを使用します。別の方法は、 #ifdef
チェックでプリプロセッサを使用することです。 組み込みのターゲット条件はまだないため、独自のターゲットを作成する必要があります。
これを行うもう1つの優れた方法は、独自の組み込みフレームワークを作成することです。 拡張機能に使用できないAPIが含まれていないことを確認してください。 組み込みフレームワークを使用するためのアプリ拡張機能を構成するには、ターゲットのビルド設定に移動し、「App-Extension-SafeAPIのみが必要」設定を「はい」に設定します。 Xcodeプロジェクトを構成する場合、ファイルのコピービルドフェーズで、組み込みフレームワークの宛先として「フレームワーク」を選択する必要があります。 「SharedFrameworks」の宛先を選択した場合、送信はAppStoreによって拒否されます。
下位互換性に関する注意
アプリ拡張機能はiOS8以降でのみ利用可能ですが、含まれているアプリを以前のiOSバージョンで利用できるようにすることができます。
Appleヒューマンインターフェイスコンプライアンス
アプリ拡張機能を設計するときは、AppleのiOSヒューマンインターフェイスガイドラインに留意してください。 含まれているアプリがサポートするデバイスに関係なく、アプリの拡張機能がユニバーサルであることを確認する必要があります。 アプリの拡張機能がユニバーサルであることを確認するには、Xcodeでターゲットデバイスファミリーのビルド設定を使用して、「iPhone / iPad」の値(ユニバーサルと呼ばれることもあります)を指定します。
結論
アプリ拡張機能は間違いなくiOS8で最も目に見える影響を及ぼします。デバイスの79%がすでにiOS 8を使用しているため(2015年4月13日にApp Storeで測定)、アプリ拡張機能はアプリが利用すべき素晴らしい機能です。 APIの制限と、拡張機能とそれに含まれるアプリの間でデータを共有する方法を組み合わせることで、Appleはセキュリティモデルを損なうことなく、プラットフォームに関する最大の不満の1つに対処できたようです。 サードパーティのアプリがデータを相互に直接共有する方法はまだありません。 これは非常に新しい概念ですが、非常に有望に見えます。