iOS App Extensionsについて知る必要があるすべて





App ExtensionsはiOS 8に登場し、ユーザーにとってシステムをより柔軟で強力かつ手頃な価格にしました。 アプリケーションは、通知センターにウィジェットとして表示したり、写真の写真に独自のフィルターを提供したり、新しいシステムキーボードを表示したりできます。 同時に、ユーザーデータとシステムのセキュリティが維持されました。 App Extensionsの機能の特徴については、以下で説明します。



Appleは常に、アプリケーションを相互に慎重に分離しようとしてきました。 これは、ユーザーの安全を確保し、データを保護するための最良の方法です。 各アプリケーションには、アクセスが制限されたファイルシステム内の個別の場所が与えられます。 App Extensionsを使用すると、アプリケーションを起動したり画面に表示したりすることなく、アプリケーションと対話できます。 したがって、その機能の一部は、ユーザーが他のアプリケーションまたはシステムと対話するときに利用できます。



App Extensionsは、含むアプリケーション( Containing App)とは独立して実行される実行可能ファイルです。 単独では、含まれているアプリでのみ、App Storeに公開できません。 すべてのApp Extensionsは特定のタスクを1つ実行し、タイプに応じてiOSの1つの領域のみに関連付けられます。 例:カスタムキーボード拡張機能は標準キーボードを置き換えるためのもので、写真編集拡張機能は写真内の写真を編集するためのものです。 App Extensionsには現在25種類があります。



Life Extension App Extension



ユーザーがApp Extensionを起動するために使用するアプリケーションはHost Appと呼ばれます。 ホストアプリはApp Extensionライフサイクルを起動し、ユーザーアクションに応答してリクエストを送信します。









たとえば、Facebook Share Extensionを使用してPhotosから写真を共有する場合、FacebookはContaining Appであり、PhotosはHost Appです。 この場合、ユーザーが[共有]メニューで選択すると、PhotosはFacebook共有拡張機能のライフサイクルを開始します。







App Extensionとの相互作用









汎用コード:動的フレームワーク



Containing AppとApp Extensionが同じコードを使用する場合は、動的フレームワークに配置する必要があります。



たとえば、写真編集拡張機能は、包含アプリケーションの一部のフィルターを使用するカスタム写真編集アプリケーションに関連付けることができます。 良い解決策は、これらのフィルターの動的フレームワークを作成することです。



これを行うには、新しいターゲットを追加して、 Cocoa Touch Frameworkを選択します。







名前( ImageFiltersなど )を指定すると、ナビゲーターパネルに、作成されたフレームワークの名前を持つ新しいフォルダーが表示されます。



フレームワークがApp Extensionsで利用できないAPIを使用しないことを確認する必要があります。





App Extensionsでこのリストのいずれかを使用すると、App Storeに公開されたときに拒否されます。



[ 全般]のフレームワーク設定で [アプリ拡張機能APIのみを許可する]の横にあるチェックボックスをオンする必要があります。







フレームワークコードでは、Containing AppおよびApp Extensionsで使用されるすべてのクラス、メソッド、およびプロパティはpublic



なければなりません。 フレームワークを使用する必要がある場合は、 import



実行します。



 import ImageFilters
      
      





データ交換:アプリグループ



Containing AppとApp Extensionには、ファイルシステムの独自のセクションがあり、それらにのみアクセスできます。 Containing AppとApp Extensionに読み取りおよび書き込みアクセスの共通コンテナーを持たせるには、それらのアプリケーショングループを作成する必要があります。



App GroupはApple Developer Portalで作成されます:







右上隅にある[+]をクリックし、表示されるウィンドウで必要なデータを入力します。







次に進む->登録->完了



含まれているアプリの設定で、[ 機能 ]タブに移動し、アプリグループをアクティブにして、作成されたグループを選択します。







同様にApp Extensionの場合:







これで、Containing AppとApp Extensionはコンテナーを共有します。 次に、読み取りと書き込みの方法について説明します。



UserDefaults



少量のデータを交換するには、 UserDefaults



を使用すると便利ですUserDefaults



の名前を指定するだけです。



 let sharedDefaults = UserDefaults(suiteName: "group.com.maxial.onemoreapp")
      
      





NSFileCoordinatorおよびNSFilePresenter



ビッグデータの場合、 NSFileCoordinator



は読み取り/書き込みの一貫性を確保するのにより適しています。 これにより、複数のプロセスが同時にアクセスできる可能性があるため、データの破損を回避できます。



共有コンテナのURLは次のように取得されます。



 let sharedUrl = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.maxial.onemoreapp")
      
      





記録:



 fileCoordinator.coordinate(writingItemAt: sharedUrl, options: [], error: nil) { [unowned self] newUrl in do { let data = try NSKeyedArchiver.archivedData(withRootObject: self.object, requiringSecureCoding: false) try data.write(to: newUrl, options: .atomic) } catch { print(error) } }
      
      





読書:



 fileCoordinator.coordinate(readingItemAt: sharedUrl, options: [], error: nil) { newUrl in do { let data = try Data(contentsOf: newUrl) if let object = try NSKeyedUnarchiver.unarchivedObject(ofClass: NSString.self, from: data) as String? { self.object = object } } catch { print(error) } }
      
      





NSFileCoordinator



が同期して動作することをNSFileCoordinator



価値があります。 一部のファイルは一部のプロセスによって占有されますが、他のファイルは解放されるまで待機する必要があります。



Containing Appがデータの状態を変更するタイミングをApp Extensionに知らせたい場合は、 NSFilePresenter



使用されます。 これは、実装が次のように見えるプロトコルです。



 extension TodayViewController: NSFilePresenter { var presentedItemURL: URL? { let sharedUrl = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.maxial.onemoreapp") return sharedUrl?.appendingPathComponent("Items") } var presentedItemOperationQueue: OperationQueue { return .main } func presentedItemDidChange() { } }
      
      





presentedItemOperationQueue



プロパティは、ファイルを変更するときのコールバックに使用されるキューを返します。 presentedItemDidChange()



メソッドは、プロセス(この場合はContaining App)がデータの内容を変更したときに呼び出されます。 低レベルの書き込み呼び出しを使用して直接変更が行われた場合、 presentedItemDidChange()



呼び出されません。 NSFileCoordinator



を使用した変更のみNSFileCoordinator



ます。



NSFileCoordinator



オブジェクトを初期化するとき、特に何らかのファイル操作を開始する場合は、 NSFilePresenter



オブジェクトを渡すNSFileCoordinator



お勧めします。



 let fileCoordinator = NSFileCoordinator(filePresenter: self)
      
      





そうしないと、 NSFilePresenter



はこれらの操作に関する通知を受け取り、同じスレッドで作業しているときにデッドロックを引き起こす可能性があります。



データの状態の監視を開始するには、対応するオブジェクトでaddFilePresenter(_:)



メソッドを呼び出す必要があります。



 NSFileCoordinator.addFilePresenter(self)
      
      





後で作成されたNSFileCoordinator



オブジェクトNSFileCoordinator



、このNSFilePresenter



オブジェクトについて自動的に認識し、そのディレクトリの変更について通知します。



データステータスの監視を停止するには、 removeFilePresenter(_:)



使用しremoveFilePresenter(_:)







 NSFileCoordinator.removeFilePresenter(self)
      
      





コアデータ



データを共有するには、SQLiteを使用し、それに応じてコアデータを使用できます。 共有データを処理するプロセスを管理できます。 含むアプリとアプリ拡張機能の間で共有されるコアデータを構成するには、 NSPersistentContainer



サブクラスを作成し、データストアアドレスを返すdefaultDirectoryURL



メソッドをオーバーライドします。



 class SharedPersistentContainer: NSPersistentContainer { override open class func defaultDirectoryURL() -> URL { var storeURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.maxial.onemoreapp") storeURL = storeURL?.appendingPathComponent("OneMoreApp.sqlite") return storeURL! } }
      
      





AppDelegate



persistentContainer



プロパティを変更します。 プロジェクトの作成時に[ コアデータ使用する]の横にあるチェックボックスを選択すると、自動的に作成されます。 次に、 SharedPersistentContainer



クラスのオブジェクトを返します。



 lazy var persistentContainer: NSPersistentContainer = { let container = SharedPersistentContainer(name: "OneMoreApp") container.loadPersistentStores(completionHandler: { (storeDescription, error) in if let error = error as NSError? { fatalError("Unresolved error \(error), \(error.userInfo)") } }) return container }()
      
      





あとは、 .xcdatamodeldをApp Extensionに追加するだけです。 ナビゲーターパネルで.xcdatamodeldファイルを選択します。 [ファイルインスペクター][ ターゲットメンバーシップ]セクションで、 [アプリ拡張機能]の横にあるチェックボックスをオンにします。







したがって、Containing AppとApp Extensionは、同じストレージに対してデータを読み書きでき、同じモデルを使用できます。



App Extensionからの包含アプリケーションの起動



ホストアプリがApp Extensionリクエストを送信すると、 extensionContext



提供されます。 このオブジェクトにはopen(_:completionHandler:)



メソッドがあり、これを使用してContaining Appを開くことができます。 ただし、この方法はすべての種類のApp Extensionで使用できるわけではありません。 iOSでは、Today ExtensionおよびiMessage Extensionによってサポートされています。 iMessage拡張機能は、Containing Appを開くためにのみ使用できます。 Today Extensionで別のアプリケーションを開くと、App Storeに送信するために追加の確認が必要になる場合があります。



App Extensionからアプリケーションを開くには、Containing AppでURLスキームを定義する必要があります。







次に、App Extensionから次の図を使用してopen(_:completionHandler:)



メソッドを呼び出します。



 guard let url = URL(string: "OneMoreAppUrl://") else { return } extensionContext?.open(url, completionHandler: nil)
      
      





open(_:completionHandler:)



メソッドを呼び出すこれらのタイプのApp Extensionsには使用できませんが、方法もあります。 ただし、App Storeでチェックインすると、アプリケーションが拒否される可能性があります。 メソッドの本質は、 openURL



呼び出しを受け入れるUIApplication



ができるまで、 UIResponder



オブジェクトのチェーンをUIResponder



することです。



 guard let url = URL(string: "OneMoreAppUrl://") else { return } let selectorOpenURL = sel_registerName("openURL:") var responder: UIResponder? = self while responder != nil { if responder?.responds(to: selectorOpenURL) == true { responder?.perform(selectorOpenURL, with: url) } responder = responder?.next }
      
      





今後のアプリ拡張機能



App ExtensionsはiOSの開発に多くをもたらしました。 徐々に、より多くの種類のApp Extensionsが登場し、それらの機能は発展しています。 たとえば、iOS 12 SDKのリリースでは、通知でコンテンツ領域とやり取りできるようになりました。



このように、Appleはこのツールの開発を続けており、そのツールは将来について楽観的な見方を示しています。



便利なリンク:



公式文書

iOSアプリとアプリ拡張機能間でデータを共有する

iOS 8アプリ拡張機能開発のヒント



All Articles