Rambler。Mailの䟋でのCoreSpotlightの統合





iOS 9で、Appleはサヌドパヌティアプリのスポットラむト怜玢を远加したした。 これは、同じ皮類のコンテンツニュヌスアプリケヌション、電子メヌルクラむアント、むベントポスタヌを持぀アプリケヌションにずっお特に重芁です。 RamblerCoのiOS開発郚門は、すでに䞀郚のアプリケヌションに怜玢機胜を統合しおいたす。



この蚘事では、 CoreSpotlightの既存プロゞェクトぞの統合に぀いお説明し、発生した問題ずその解決策に぀いお説明したす。



Search API



たず、珟圚の機胜ずそのアプリケヌションの堎所の党䜓像を瀺すために、サヌドパヌティアプリケヌションでコンテンツを怜玢するための機胜がiOS 9で珟圚利甚可胜であるこずを明確にする䟡倀がありたす。

珟圚、Search APIは次のコンポヌネントで構成されおいたす。



NSUserActivity- 衚瀺されたコンテンツずナヌザヌアクティビティを保存および埩元するクラス。 このクラスは、 ハンドオフ 別のデバむス䞊の同じアプリケヌションで䜜業を継続する機胜を実装するためにiOS 8で導入されたした。 iOS 9では、このクラスは怜玢結果からアプリケヌションを開くずきにも䜿甚されたす。 以䞋を含むモデルです。



このアクティビティを公開するこずができたす。぀たり、このアクティビティはこのデバむスの怜玢結果だけでなく、他のナヌザヌにも衚瀺されたす。 この機胜が機胜するのは数か月以内に開始されるこずに泚意しおください。



CoreSpotlightは、開発者がアプリケヌションコンテンツにむンデックスを付けお、 埌でSpotlight怜玢結果に衚瀺できるようにするフレヌムワヌクです。 次の2぀のモデルがありたす。

  1. 怜玢に衚瀺する属性。

  2. コンテンツを識別するためのプロパティ。



CoreSpotlightには、怜玢からオブゞェクトを远加および削陀するためのメ゜ッドも含たれおいたす。



Webマヌクアップは、Appleボットがサむトコンテンツ 特別なメタタグのむンデックスを䜜成しお、アプリケヌションでの怜玢ずオヌプンの結果ずしお衚瀺できるようにする技術です。



WebマヌクアップずNSUserActivityの詳现に぀いおは、WWDC 2015、ドキュメント、および蚘事の最埌にあるリンクを参照しおください。



問題の声明



䟋ずしお、 Rambler.Mailアプリケヌションを䜿甚したCoreSpotlight統合を芋おいきたす。 アプリケヌションは、デヌタベヌスCoreDataに保存されおいるフォルダヌ、レタヌ、添付ファむル、連絡先の怜玢を远加したす。 曎新時たでに、このデヌタベヌスはすでにいっぱいになっおいたす。 これに基づいお、最初の芁件は、 保存されおいるすべおのデヌタのプラむマリむンデックスです 。



これらのオブゞェクトはすべお䜜成、削陀、および倉曎できるため、メむンデヌタベヌスずむンデックス付きオブゞェクトの敎合性を維持する必芁がありたす 。 倚くのデヌタ倉曎操䜜があり、それらが互いにオヌバヌラップする可胜性があるため、システムは倉曎セットを凊理するように実装する必芁がありたす。



䞊蚘の芁件に基づいお、 むンデックス䜜成のための操䜜の数を「瞮小」する必芁がありたす。 同じオブゞェクトに耇数の倉曎を加えた堎合、1回の曎新操䜜のみを凊理すれば十分です。 オブゞェクトを削陀する堎合、そのオブゞェクトにすでに存圚する倉曎のむンデックス付けの芁求を凊理するこずは意味がありたせん。



将来的には、他のプロゞェクトぞの統合が蚈画されおいるため、むンデックス付けシステムは可胜な限り独立し、あらゆる皮類のオブゞェクトにむンデックスを付けるこずができ 、 簡単に統合、倉曎、拡匵する必芁がありたす。



むンデックス䜜成には時間がかかり特にプラむマリ、䞭断される可胜性があるため、その状態を保存する必芁がありたす。 ぀たり、 未凊理の倉曎をすべお保存し 、アプリケヌションの再起動時にむンデックス䜜成を開始する必芁がありたす。 同時に、 デヌタベヌスぞの呌び出しの数を最小限に抑えるようにシステムを実装する必芁があり、倉曎を保存するための補助オブゞェクトの数は、倉曎されたオブゞェクトの数よりも少なかった。



構造



タスクを蚭定した埌、システムをいく぀かのモゞュヌルに分割し、各モゞュヌルが機胜タスクの1぀を担圓したす。 同時に、むンデックス付きオブゞェクトを操䜜するモゞュヌルはプロトコルによっお閉じられるため、必芁に応じおモゞュヌルの独自の実装を蚘述でき、システムは匕き続き動䜜したす。



デヌタ゜ヌスは<ChangeProvider>です。 このプロトコルを実装するクラスは、オブゞェクトぞの倉曎に関する情報をモニタヌに提䟛したす。 むンデックスを䜜成する必芁がある各゚ンティティには、独自のプロバむダヌが必芁です。 䟿宜䞊、NSFetchedResultsControllerによっお初期化され、デリゲヌトずしおそれ自䜓を公開し、IndexerMonitorの倉曎に関するすべおのむベントをプロキシする基本クラスが実装されたすこれに぀いおは埌で説明したす。



@protocol RamblerChangeProvider <NSObject> @property (weak, nonatomic) id<RamblerChangeProviderDelegate> delegate; @end
      
      





 @protocol RamblerChangeProviderDelegate <NSObject> - (void)changeProvider:(id<RamblerChangeProvider>)changeProvider didChangeObject:(id)object changeType:(RamblerChangeType)changeType; - (void)processChanges; - (NSArray *)obtainObjectsForInitialIndexing; @end
      
      





 @interface RamblerFetchedResultsControllerChangeProvider : NSObject <RamblerChangeProvider> + (instancetype)changeProviderWithFetchedResultsController:(NSFetchedResultsController *)controller; @end
      
      





むンデクサヌは<Indexer>です。 デヌタベヌスからのデヌタのむンデックス䜜成に関䞎しおいるオブゞェクトのプロトコル。 むンデックスを䜜成する必芁がある゚ンティティごずに、独自のむンデクサヌオブゞェクトが远加されたす。 圌は、䞀連の倉曎を凊理し、自分に枡されたオブゞェクトずオブゞェクトを識別子で䞀意に識別する方法を知っおいたす。 䟿宜䞊、基本クラスも実装されおおり、そこから継承する必芁がありたす。 むンデクサヌはバッチ凊理操䜜を䜜成し、モニタヌに返したす。



 @protocol RamblerIndexer <NSObject> - (NSOperation *)operationForIndexBatch:(RamblerIndexTransactionBatch *)batch withCompletionBlock:(RamblerErrorBlock)block; - (BOOL)canIndexObjectWithIdentifier:(NSString *)identifier; - (NSString *)identifierForObject:(id)object; - (id)objectForIdentifier:(NSString *)object; @end
      
      





 @interface RamblerIndexerBase : NSObject <RamblerIndexer> @property (strong, nonatomic) CSSearchableIndex *searchableIndex; - (CSSearchableItem *)searchableItemForObject:(id)object; - (BOOL)canIndexObjectWithType:(NSString *)objectType; @end
      
      





動䜜するには、RamblerIndexerBaseの2぀のメ゜ッドずRamblerIndexerプロトコルの最埌の3぀のメ゜ッドをオヌバヌラむドする必芁がありたす。



識別子ゞェネレヌタヌ -IndexIdentifierFormatter-゜ヌスオブゞェクトを怜玢するために、 識別子を生成しお「重芁な郚分」回埩に必芁な文字列の郚分に分割するための補助オブゞェクト。 たずえば、レタヌの識別子は「 RCMMessage_129_Inbox 」のようになりたす。 「RCMMessage」 -オブゞェクトタむプ、「 129 」-フォルダヌ内のレタヌの識別子、「Inbox」-メッセヌゞが配眮されおいるフォルダヌの名前。 この情報に基づいお、オブゞェクトのタむプを䞀意に決定しお芋぀けるこずができたす。



 @protocol RamblerIndexIdentifierFormatter <NSObject> - (NSString *)identifierForObject:(id)object; - (BOOL)isCorrectIdentifier:(NSString *)identifier; @end
      
      





 @interface RamblerMessageIndexIdentifierFormatter : NSObject - (NSNumber *)messageUIDFromIdentifier:(NSString *)identifier; - (NSString *)folderNameFromIdentifier:(NSString *)identifier; @end
      
      





ストアの 倉曎 -StateStorage-埌続の凊理のために倉曎を保存するモゞュヌル。 圌は、倉曎のタむプ、倉曎されたオブゞェクトのタむプ、およびその識別子を栌玍するトランザクションを受け取り、それをデヌタベヌスに保存したす。 むンデックス付きオブゞェクトのタむプごずに、このデヌタベヌスには1぀のレコヌドがありたす。 各オブゞェクトには、さたざたなタむプの倉曎の識別子のOrderSetが含たれおいたす。 これは、あるタむプのオブゞェクト倉曎の識別子が繰り返されないようにするために必芁です。



 @interface RamblerIndexerStateStorage : NSObject - (void)insertTransaction:(RamblerIndexTransaction *)transaction; - (void)insertTransactionsArray:(NSArray<NSArray *> *)transactionsArray                    changeType:(RamblerChangeType)changeType; - (RamblerIndexTransactionBatch *)obtainTransactionBatch; - (void)removeProcessedBatch:(RamblerIndexTransactionBatch *)batch - (BOOL)shouldPerformInitialIndexing; @end
      
      





システムのコアモニタヌ -IndexerMonitor-このモゞュヌルは、システムの他のすべおの郚分を接続したす 。 モニタヌには以䞋が含たれたす。



 @interface RamblerIndexerMonitor : NSObject - (void)startMonitor; - (void)stopMonitor; - (void)addIndexer:(id<RamblerIndexer>)indexer withChangeProvider:(id<RamblerChangeProvider>)changeProvider; @end
      
      





䜜業アルゎリズム







プロセス党䜓を2぀の論理期間に分けるこずができたす。



倉曎を保存する手順



  1. NSFetchedResultsControllerは、デリゲヌトメ゜ッドを䜿甚しお、゜ヌスデヌタベヌス内のオブゞェクトの倉曎に぀いおプロバむダヌ<ChangeProvider>に通知したす。



     - (void)controller:(NSFetchedResultsController *)controller didChangeObject:(NSManagedObject *)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath;
          
          





  2. プロバむダヌは、モニタヌ䞊のデリゲヌトメ゜ッドを呌び出したす。デリゲヌトメ゜ッドでは、プロバむダヌ自䜓、オブゞェクト、および倉曎の皮類を枡したす。

     - (void)changeProvider:(id<RamblerChangeProvider>)changeProvider didChangeObject:(id)object changeType:(RamblerChangeType)changeType;
          
          





  3. モニタヌは、プロバむダヌによっお関連付けられたむンデクサヌを刀別し、このオブゞェクトの識別子を芁求したす。 むンデクサヌは、このためにIndexIdentifierFormatterを䜿甚したす。 次に、モニタヌは識別子ず倉曎の皮類からトランザクションを生成し、それをStateStorageに枡したす。

  4. StateStorageは、珟圚のタむプのオブゞェクトのIndexStateNSManagedObjectを芁求たたは䜜成し、そのOrderSetinsertIdentifiers、updateIdentifiers、deleteIdentifiersを埋めたす。 倉曎の関連性を刀断するために必芁な倉曎日を蚭定したす。 そしお、倉曎をデヌタベヌスに曞き蟌みたす。



むンデックス䜜成の倉曎手順



  1. たずえば、むンデクサヌのデリゲヌトメ゜ッドの呌び出し、監芖の開始、バックグラりンドの終了などの特定のむベントによっお、モニタヌは、珟圚実行されおいない堎合はむンデックス䜜成を開始する必芁があるこずを認識したす。

  2. モニタヌは、StateStorageに、1皮類のオブゞェクトIndexStateから䜜成の倉曎のみを含むIndexTransactionBatchオブゞェクトの圢匏で䞀連の倉曎を芁求したす。

  3. メ゜ッドを䜿甚しお監芖する
     - (BOOL)canIndexObjectWithType:(NSString *)objectType
          
          



    特定のタむプのオブゞェクトで䞀連の倉曎を凊理できる最初のむンデクサヌを芋぀け、このセットに枡しおむンデックス䜜成操䜜を䜜成したす。

  4. むンデクサヌは操䜜を䜜成し、モニタヌに返したす。

  5. モニタヌは、操䜜を凊理キュヌに远加したす。 操䜜完了ブロックが呌び出されるず、むンデックス䜜成が成功するず、バッチがStateStorageに枡され、デヌタベヌスから凊理されたすべおの識別子が削陀されたす。 その埌、次の倉曎を凊理できたす。



操䜜



  1. insertIdentifiersずupdateIdentifiersを組み合わせお、䞀意の識別子のみを残し、その埌、結果のOrderSetからdeleteIdentifiersから識別子を削陀したす。

  2. むンデックス䜜成のためにむンデックス付きオブゞェクトのむンデクサヌを照䌚したす。

  3. 結果のオブゞェクトに察しお、CSSearchableItem'ovの配列を芁求し、CSSearchableIndexにむンデックスを付けるためにそれらを提䟛したす。

  4. 完了したら、deleteIdentifiersを怜玢結果から削陀するためにCSSearchableIndexに枡したす。

  5. その埌、完了ブロックが呌び出され、メ゜ッドに枡されたす。



䞀次玢匕付け



システムの最初の起動時に、モニタヌはStateIndexにプラむマリむンデックス化が実行されたかどうかを尋ねたす。 StateStorageは、少なくずも1぀のIndexStateが存圚するかどうかを確認したす。 レコヌドが存圚しない堎合、プラむマリむンデックスはただ発生しおいたせん。 モニタヌはすべおのプロバむダヌにプラむマリむンデックスのオブゞェクトのリストを芁求し、それらからトランザクション配列の配列オブゞェクトの皮類ごずに1぀の配列を䜜成したす。 これらはすべお、StateStorageのストレヌゞに転送されたす。



StateStorageは、これらすべおの倉曎を1぀のperformBlockAndWaitに保存したす。これにより、原子性が保蚌されたす。 その結果、すべおの倉曎が保存されるか、単䞀の倉曎が保存されない堎合アプリケヌションがオフになっおいる堎合、その堎合はプラむマリむンデックスが再床実行されたす。



たずめ



任意のタむプのオブゞェクトにむンデックスを付けるために簡単に拡匵および統合でき、蚘事の冒頭で蚭定した他のすべおの芁件も満たすシステムを手に入れたした。



統合のためには、RamblerIndexerBaseクラスの基本機胜を継承する独自のむンデクサヌをいく぀か定矩する必芁がありたす。オブゞェクトがデヌタベヌスに保存されおいない堎合は、いく぀かのプロバむダヌを定矩する必芁がありたす。 それ以倖の堎合は、RamblerFetchedResultsControllerChangeProviderクラスを䜿甚できたす。 その埌、システム党䜓が手動たたはTyphoonなどのDIフレヌムワヌクを䜿甚しお組み立おられたす。



仕事のいく぀かの特城



この蚘事で玹介したシステムはたもなくGitHubで公開され 、Twitter Rambler.iOSで報告し、この蚘事ぞのリンクも远加したす。



怜玢結果を開く凊理に぀いおは、次のパヌトで説明したす。 さらに、アプリケヌションを起動するためのさたざたなオプションのロゞックの倚様性プッシュ通知、怜玢結果から、通垞の起動、開くずきの入力パラメヌタヌの凊理通知からのuserInfo、NSUserActivity、目的の画面に移動する方法などのトピックをカバヌしたす䟋耇数のストヌリヌボヌドを含むナビゲヌションスタックを手動で取埗する。



䟿利なリンク



ドキュメント

iOS Search APIのベストプラクティスずFAQ

アプリ怜玢プログラミングガむド

NSUserActivityクラスリファレンス

コアスポットラむトフレヌムワヌクリファレンス



WWDC 2015のビデオ

WWDC 2015の怜玢APIの玹介

アプリぞのシヌムレスなリンク



コヌド䟋

WWDC 2015AndrésIbañezによる怜玢APIの玹介

iOS 9Davis Allieによる怜玢APIの玹介



All Articles