iOSアプリケーションAviasales.ruの開発。 空港選択画面

iOS用のAviasales.ruアプリケーションを作成する際、多くの興味深いタスクがありました。 その1つは、出発地と目的地を選択するための便利なメカニズムの実装です。 この投稿では、この問題をどのように解決したかと、iOS SDKのどの機能がこれに使用されたかを簡単に説明したいと思います。







おそらく、あなたはこの記事を少し前に見たことがあるでしょう。 前回間違った方法で書いたのはそれだけです-そしてそれは出版物から削除されました。 そして、私は本当に資料が消えたくありません。 あなたがすでに彼を見たのであれば-彼に注意を払ってはいけません。



目的地の空港選択画面は、3つの部分に分かれています。最も近い空港、ユーザーが以前に選択した空港のリスト、および空港名の入力行です。



最寄りの空港の機能は2段階で実装されます。最初に、アプリケーションはユーザーに許可を求めた後、デバイスの座標を学習します。 ところで、実際に私たちが興味を持っているものの位置情報データの要求に明確化を追加できます。 これを行うには、CLLocationManagerの目的プロパティを使用できます。



locationManager.purpose = @”   ”;
      
      









ユーザーの正確な座標は必要ありません。近くにある都市を知るだけで十分です。 したがって、ジオロケーションの精度が低下する可能性があります。



 locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers;
      
      





2番目の段階-受信した座標はサーバーに送信され、ユーザーが関心を持っている空港を決定します。 たとえば、サンクトペテルブルクの住民は、プルコヴォに加えてフィンランドのラッペーンランタに招待されています。



検索履歴は、データベースに保存されている最後の5つの選択されたアイテムです(コアデータでSQLiteを使用し、複雑なものはありません)。



最も興味深い部分に移りましょう-ユーザーが入力した行による空港と都市の検索です。 検索は3つの段階で機能します。

  1. 人気のある空港のリストで完全に一致するものを探しています。
  2. 何も見つからない場合は、同じリストで不正確な一致を探します。
  3. ユーザーが3文字以上を入力した場合、ユーザーは検索クエリをサーバーに送信して人気のない空港を見つけることができます。


順番に。



世界の空港、それは非常に多く-約1万であることが判明しました。 しかし、本当に人気があるのは(ユーザーが検索クエリで一定の規則で使用するもの)わずか1.5万人です。 最初にユーザーが目的の都市をできるだけ早く選択できるように、最初にこのアプリケーション内の人気のある目的地のリストを提供することにしました。 これらの空港に関する情報を保存するために、コアデータでSQLiteも使用します( コアデータをアプリケーションプリロードする方法に関するHabrの記事があります )。 起動すると、アプリケーションは空港に関するデータをデータベースから配列に読み取ります。



 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSManagedObjectContext* context = [[ASCoreDataManager sharedInstance] currentManagedObjectContext]; NSArray *dbAirports = [ASAirport MR_findAllInContext:context]; @synchronized(self){ _airports = dbAirports; } });
      
      





Grand Central Dispatchメカニズムを使用して、バックグラウンドスレッドでこの操作を実行します。 マルチスレッド化された操作中に安全なメモリアクセスを確保するには、synchronizedを介した呼び出しが必要です。



MR_findAllInContext



メソッドとは何ですか?」と尋ねます。 これはMagicalRecordライブラリの機能です。 実際、Core Dataエンジン自体はスレッドセーフではありません。 実際には、フェッチ読み取り要求が異なるスレッドから送信されると、アプリケーションがクラッシュする可能性があります。 これは、スレッドごとに個別のNSManagedObjectContext



を使用することで解決されます。すべてのスレッドがpersistentStoreCoordinator



を持ちます。 MagicalRecordライブラリは、これらすべてのコンテキストの調整に役立ちます。



まず、 [NSManagedObjectContext MR_contextForCurrentThread]



メソッド(上記のコードのcurrentManagedObjectContext



メソッドで使用)を実装します。このメソッドは、 currentManagedObjectContext



れたスレッドのコンテキストを返します。 第二に、MagicalRecordにはコードの標準ブロック上に多くの命を救うラッパーがあります。たとえば、さまざまなNSManagedObjectModel



NSPersistentStoreCoordinator



NSManagedObject



およびその子孫の作成は、必要なパラメーターを設定するだけで1行で実行できます。



人気のある空港のデータが更新される場合があります。人気のある目的地もあれば、逆に関連性が低くなるものもありますし、名前が変わるものもあります。 そのため、アプリケーションは時々gzipで圧縮されたJSONファイルをサーバーからダウンロードし、そこからデータがバックグラウンドストリームでデータベースを更新します。 まず、エントリのデータベースをクリアします。



 [ASAirport MR_truncateAllInContext:context];
      
      





次に、新しいデータを書き込みます。



 NSManagedObjectContext* context = [[ASCoreDataManager sharedInstance] currentManagedObjectContext]; for (APIAirport *airport in airportsArray) { ASAirport *initialAirport = [ASAirport MR_createInContext:context]; //       } [context MR_save];
      
      







検索に戻りましょう。アプリケーションは空港に関するデータをメモリに読み込み、ユーザーが検索文字列に文字を入力するのを待ちます。 新しい各文字を入力すると、プログラムはデータ配列をバイパスし、ユーザーが入力した行で名前が正確に始まる空港を検索します。 何も見つからない場合は、配列の繰り返し走査が開始され、検索文字列からのレーベンシュタイン距離が小さい名前が選択されます。 これは、ユーザーがタイプミスをした場合、または単に都市の名前のつづり方を知らない場合に役立ちます。







特にあいまい一致を探している場合は、検索プロセスにかなりの時間がかかる場合があります。 したがって、 NSOperation



クラスに実装されます。 これにより、ブロックの単純な非同期実行よりも重要な利点が得られます。検索プロセスがまだ完了しておらず、ユーザーが既に検索文字列を変更している場合、操作を中止できます。 実際には、次のようになります。



メイン関数を実装するとき、ループの各反復後に、操作がキャンセルされたかどうかを確認します。



 for (ASAirport *currentAirport in initialAirports) { if (self.isCancelled) { return; } //    }
      
      





関係がなくなったら操作をキャンセルします。



 [_searchOperation cancel];
      
      





そして、貴重なリソースを消費することなく、すぐにそれをやめます。



ユーザーが人気のある空港のリストで目的の空港を見つけることができなかった場合、ユーザーはいつでもサーバーにリクエストを送信できます。 例:ナズランを探しています-カザンを見つけます(ナズランは人気のない空港で、ローカルデータベースにはありません。カザンはレーベンシュタインに最も近い空港です)。







サーバーからの応答を待ってから2秒間、幸せなNazranが安いチケットを見つけて飛び立つことができます!



ご清聴ありがとうございました!



PS

iOS用 Aviasalesアプリ。

また、 Androidアプリもあります。



All Articles