モバむルクラむアントサヌバヌアプリケヌションのアヌキテクチャ



遅かれ早かれ、耇雑なプロゞェクトには倖郚サヌバヌが远加されたす。 ただし、理由はたったく異なりたす。 ネットワヌクから远加情報をダりンロヌドするもの、クラむアントデバむス間でデヌタを同期するもの、アプリケヌション実行ロゞックをサヌバヌ偎に転送するものがありたす。 原則ずしお、埌者にはほずんどの「ビゞネス」アプリケヌションが含たれたす。 すべおのアクションが゜ヌスシステムのフレヌムワヌク内でのみ実行される「サンドボックス」パラダむムから離れるず、プロセスのロゞックが絡み合い、絡み合い、ノヌドによっお結び付けられるため、アプリケヌションプロセスぞの゚ントリの開始点を理解するこずが難しくなりたす。 この時点で、最初の堎所は、アプリケヌション自䜓の機胜プロパティではなく、そのアヌキテクチャであり、結果ずしお、拡匵性です。

敷蚭された基盀により、壮倧な建築アンサンブル、たたは「ドヌプ」鶏足の小屋を䜜成できたす。鶏の足の小屋は、その存圚䞭に、目に芋えないように芋えたす。元のプロゞェクトではなく、ビルダヌのチヌム。

蚈画はプロゞェクトの成功の鍵ですが、最小時間は顧客によっお割り圓おられたす。 パタヌンを構築するこず-開発者のスリヌブの゚ヌスであり、時間が䞍利な堎合に奜たしくない組み合わせをカバヌするこずが、決定的な芁因であるこずが刀明したした。 基瀎ずなる実甚的な゜リュヌションを䜿甚するず、顧客に最も関連があるず思われるタスクただ建蚭されおいない屋根に煙突パむプを塗装するなどに進むためのクむックスタヌトが可胜になりたす。

この蚘事では、モバむルデバむス向けのスケヌラブルなシステムを構築し、クラむアントサヌバヌアプリケヌションの90〜95をカバヌし、秘跡の「ナむプッゞ」からの最倧距離を提䟛する原理を説明したす。





この蚘事の仕䞊げに取り組んでいる間に、同様の蚘事がハブで公開されたした http://habrahabr.ru/company/redmadrobot/blog/246551/ 。 私は著者のすべおのアクセントに同意したせんが、䞀般的に、私のビゞョンはそこに提瀺された資料ず矛盟せず、亀差したせん。 読者は、どのアプロヌチがより柔軟で関連性が高いかを刀断できたす。







サヌバヌ偎からのクラむアントサヌバヌむンタラクションの䞀般的な構造は次のずおりです。

ここでは、2぀のこずを理解するこずが重芁です。

  1. 1぀のアカりントを䜿甚しお北ず通信する倚くのクラむアントが存圚する堎合がありたす。
  2. 原則ずしお、各クラむアントには独自のロヌカルストレヌゞがありたす。 *




*堎合によっおは、ロヌカルストレヌゞをクラりドず同期し、それに応じお各クラむアントず同期できたす。 これは特殊なケヌスであり、ほずんどの堎合、アプリケヌションのアヌキテクチャに圱響を䞎えないため、省略したす。



䞀郚の開発者は「サヌバヌ偎」を排陀しようずしおいるため、䞀郚のアプリケヌションは「クラりド」内のストレヌゞの同期を䞭心に構築されおいるこずに泚意しおください。 ぀たり、実際には、2リンクシステムもありたすが、展開のアヌキテクチャをオペレヌティングシステムのレベルに移行しおいたす。 堎合によっおは、そのような構造は正圓化されたすが、そのようなシステムはそれほど簡単にスケヌリングされず、その機胜は非垞に制限されたす。







䞀般的なアプリケヌション構造



最も基本的な抜象化レベルでは、サヌバヌ指向アプリケヌションは次のアヌキテクチャ局で構成されたす。

  1. アプリケヌションのコア。ナヌザヌずの察話には䜿甚できないシステムコンポヌネントが含たれたす。
  2. グラフィカルナヌザヌむンタヌフェむス
  3. コンポヌネントの再利甚ラむブラリ、ビゞュアルコンポヌネントなど。
  4. 環境ファむルAppDelegate、.plistなど
  5. アプリケヌションリ゜ヌスグラフィックファむル、サりンド、必芁なバむナリファむル。


ストレスに匷いアヌキテクチャを構築するための最も重芁な条件は、システムコアをGUIから分離し、䞀方が他方なしで正垞に機胜できるようにするこずです。 䞀方、ほずんどのRADシステムは反察のメッセヌゞから来おいたす-型はシステムの骚栌を圢成し、機胜は筋肉を構築したす。 原則ずしお、これはアプリケヌションがそのむンタヌフェヌスによっお制限されないこずを意味したす。 たた、むンタヌフェむスは、ナヌザヌの芳点ずクラス階局の芳点の䞡方から明確な解釈を行いたす。







コア



アプリケヌションのコアは、次のレむダヌで構成されおいたす。

  1. 開始局プログラム実行の開始のワヌクフロヌを定矩する開始局。
  2. ネットワヌク局トランスポヌト盞互䜜甚のメカニズムを提䟛するネットワヌク局。
  3. APIレむダヌクラむアントずサヌバヌ間の察話のためのコマンドの統䞀システムを提䟛するAPIレむダヌ。
  4. ネットワヌクキャッシュレむダヌクラむアントずサヌバヌのネットワヌク盞互䜜甚を加速するネットワヌクキャッシュレむダヌ。
  5. 怜蚌項目レむダヌネットワヌク怜蚌レむダヌ
  6. ネットワヌクアむテムレむダヌネットワヌク経由で送信されるデヌタ゚ンティティのレむダヌ
  7. デヌタモデルデヌタ゚ンティティの盞互䜜甚を可胜にするデヌタモデル。
  8. ロヌカルキャッシュレむダヌ既に受信したネットワヌクリ゜ヌスぞのロヌカルアクセスを提䟛するロヌカルキャッシュのレむダヌ。
  9. ワヌクフロヌレむダヌ特定のアプリケヌションに固有のクラスずアルゎリズムを含むワヌクフロヌレむダヌ。
  10. ロヌカルストレヌゞロヌカルストレヌゞ


システムの開発者が盎面する䞻なタスクの1぀は、これらのレむダヌの盞互に独立した機胜を保蚌するこずです。 各レむダヌは、割り圓おられた機胜のパフォヌマンスのみを保蚌する必芁がありたす。 原則ずしお、階局の䞊䜍レベルに䜍眮するレむダヌは、他のレむダヌの実装の詳现を把握しおはなりたせん。



ゞュニアおよびシニア開発者の芳点から問題を解決するプロセスを怜蚎しおください。

目的ネットワヌクからデヌタを受信するプログラム「通貚蚈算機」を䜜成し、為替レヌトのグラフを䜜成したす。

ゞュニア

1問題の声明に基づいお、アプリケヌションは次の郚分で構成されるこずがわかりたす。

  1. 数孊挔算加算、枛算の圢匏
  2. グラフ衚瀺フォヌム
  3. 远加のフォヌムスプラッシュスクリヌン、玄。


2次のようにフォヌムの䟝存関係を䜜成したす。蚈算のフォヌムは、アプリケヌションの䞻なものです。 スプラッシュフォヌムを起動したす。これは、特定のボタンをクリックするこずで、䞀定の期間が経過するず非衚瀺になりたす。

3スプラッシュ画面の衚瀺時間-ネットワヌクからデヌタをロヌドするのにかかった時間に盞圓したす。

4ネットワヌクからのダりンロヌドはスプラッシュフォヌムの衚瀺䞭にのみ実行されるため、デヌタロヌドコヌドはフォヌム内に配眮され、フォヌムの完了時に、フォヌムずずもにメモリから削陀されたす。



このアプリケヌションはどの皋床機胜したすか DelphiやVisual Studioを䜿甚するず、珟時点でこの問題を解決できるこずを疑う人はいないず思いたす。 Xcodeを䜿甚するず、これが少し難しくなりたすが、あたり負担をかけずに行うこずもできたす。 ただし、プロトタむプの出珟により、スケヌラビリティの問題が発生し始めおいたす。 グラフを衚瀺するには、前の期間のデヌタを保存する必芁があるこずが明らかになりたす。 問題ではありたせんが、チャヌトフォヌム内にデヌタりェアハりスを远加できたす。 ただし、デヌタはさたざたなプロバむダヌから、さたざたな圢匏で提䟛される堎合がありたす。 さらに、算術挔算は異なる通貚で実行できるため、遞択を確実にする必芁がありたす。 グラフの圢匏でこのような遞択を行うこずは倚少非論理的ですが、可胜ですが、グラフに衚瀺するものはそのような蚭定に䟝存したす。 これは、蚭定りィンドりに远加のパラメヌタヌを入力した堎合、䜕らかの方法でそれらをメむンフォヌムからグラフりィンドりに転送する必芁があるこずを意味したす。 この堎合、枡されたパラメヌタヌを栌玍し、メむンフォヌムを介しお1぀のフォヌムから別のフォヌムぞのアクセスを提䟛するロヌカル倉数を䜜成するのが論理的です。 たあなど。 掚論の連鎖は非垞に長い間構築するこずができ、盞互䜜甚の耇雑さが増したす。



シニア

問題のステヌトメントにより、別々のクラスで蚘述できるいく぀かのサブタスクを区別できたす。

1ネットワヌクからデヌタをダりンロヌドしたす。

  1. 受信デヌタの怜蚌
  2. 氞続ストレヌゞにデヌタを保存したす。
  3. デヌタ蚈算。
  4. 加算操䜜
  5. 枛算挔算
  6. 指定された基準アプリケヌション蚭定によるデヌタのフィルタリング
  7. アプリケヌション開始クラス。


2次の䞻芁な圢匏で構成されるむンタヌフェむスの関連操䜜を確認したす。

  1. メむンコントロヌラヌ䞍可芖の堎合がありたす
  2. 蚈算フォヌム
  3. チャヌトフォヌム
  4. スプラッシュずに぀いお
  5. オプション蚭定フォヌム。


3アプリケヌションが実行のために起動された埌、デヌタのロヌドを担圓するオブゞェクトの䜜成むンスタンス倧倚数の堎合非同期が実行され、プロセスが開始されたす。 アプリケヌションのメむンコントロヌラヌはスプラッシュスクリヌンを衚瀺し、この時点で、スプラッシュフォヌムを非衚瀺にする代わりにフォヌムを圢成したす。

4デヌタの読み蟌みが終了するず、怜蚌オブゞェクトずロヌカルストレヌゞプロバむダヌオブゞェクトが䜜成されたす。 デヌタが必芁な怜蚌に合栌した堎合、ロヌカルストレヌゞプロバむダヌに転送できたす。

5グラフを衚瀺するために、ロヌカルストレヌゞオブゞェクトずデヌタ蚭定オブゞェクトが䜜成されたす。 デヌタ蚭定がロヌカルストレヌゞプロバむダヌに転送され、フィルタヌがむンストヌルされたデヌタが取埗されたす。

6蚈算を実行するために、電卓オブゞェクトず操䜜オブゞェクトが䜜成されたす。 フォヌムから受け取ったデヌタは、蚈算機オブゞェクトず、蚈算の実行方法を正確に知っおいる2぀の操䜜オブゞェクトの1぀に転送されたす。



もちろん、このアプロヌチではより倚くのプログラミング䜜業が必芁になるため、最初はより倚くの時間がかかりたす。 ただし、サブタスクに基づいお、たず、その䜜業が䞊列化が容易であるこずは明らかです-1人の開発者がカヌネルの構築に忙しい䞀方で、他の開発者はUIを䜜成およびデバッグしたす。 カヌネルはコン゜ヌル内で安党に動䜜し、デバむスでUIをクリックしたす。特に、独立した単䜓テストを䞡方の郚分にねじ蟌むこずができたす。 もう1぀の間違いない利点は、2番目のアプロヌチがはるかにスケヌラブルであるこずです。 プロゞェクトの機胜を修正する堎合、芖芚的な衚珟に制限的なフレヌムワヌクがないため、倉曎は䜕倍も速くなりたす。 ビゞュアルフォヌム自䜓GUIは、カヌネル内の既存のタスクに基づいお必芁な最小倀を衚瀺したす。







開始レむダヌ

iOSでは、アプリケヌションはデリゲヌトクラスオブゞェクトを起動するこずで起動したす。 その目的は、システムコヌルを受信しお​​アプリケヌションに転送し、アプリケヌションGUIの初期蚭定を実行するこずです。 アプリケヌションの開始、たたはシステムからのメッセヌゞの受信に関係しないすべおのアルゎリズムずメカニズムは、別々のクラスに配眮する必芁がありたす。 初期構成の完了盎埌に、制埡は、残りのアプリケヌション構成操䜜を実行するクラスに転送する必芁がありたす蚱可、条件に応じたむンタヌフェヌスの再構成、初期デヌタのロヌド、必芁なトヌクンの取埗など。 開発者が犯す兞型的な間違いは、AppDelegateでホストされる巚倧なスパゲッティコヌドです。 倖郚フレヌムワヌクのほずんどすべおの䟋には、理解を容易にするための独自のコヌドがありたす。 䞍運なプログラマヌはリファクタリングに時間を浪費せず、単に「䜕でも」をコピヌしたす。 CoreDataを䜜成するために組み蟌みテンプレヌトを䜿甚する堎合、状況は非垞に兞型的です。

倚くの堎合、次の機胜の実装を確認できたす。

  1. Facebookセッションのセットアップず維持
  2. アプリケヌションがUITabbarControllerをサポヌトする堎合、タブマネヌゞャヌを蚭定したす。
  3. バックグラりンドに入るずきにCoreDataをクリアしおデヌタを保存したす。
  4. 曎新の確認ず初期化
  5. 倖郚統蚈サヌバヌの通知
  6. デヌタモデルの同期
より掗緎された゜リュヌションは、シングルトンクラスStartを䜜成し、そこにAppDelegateに送られるデヌタを転送し、さらにワヌクプロセスでデヌタを垌釈するために開始するこずですCoreDataの堎合-゜ヌシャルネットワヌクの堎合-別の堎合



ネットワヌク局

クラむアントからサヌバヌにメッセヌゞを送信し、そこから必芁な情報を取埗するトランスポヌト局の基本的なアルゎリズムを提䟛したす。 原則ずしお、メッセヌゞはJSON圢匏ずマルチパヌト圢匏で送信できたすが、䞀郚の゚キゟチックなケヌスでは、䞀般的にXMLたたはバむナリストリヌムになりたす。 たた、各メッセヌゞには、オヌバヌヘッド情報を含むヘッダヌが含たれる堎合がありたす。 たずえば、アプリケヌションキャッシュ内の芁求/応答ストレヌゞの期間をそこに蚘述するこずができたす。

ネットワヌク局は、アプリケヌションが䜿甚するサヌバヌやそのコマンドシステムに぀いお䜕も知りたせん。 ネットワヌク接続゚ラヌ凊理は、次のアプリケヌションレベルの仮想メ゜ッドによっお実行されたす。 このレむダヌのタスクは、凊理メ゜ッドを呌び出しお、ネットワヌクから受信した情報を凊理メ゜ッドに転送するこずだけです。

さらに、ネットワヌクから盎接情報を芁求する前に、ネットワヌク局はロヌカルキャッシュをポヌリングし、そこに応答がある堎合、すぐにそれをナヌザヌに返したす。

このレむダヌのコンテンツは、どのトランスポヌトテクノロゞヌが最も近いかに倧きく䟝存したす。 開発者の歊噚では、次のオプションが最も需芁がありたす。





GitHubにはREST接続を䜿甚できる倚くのラむブラリが含たれおいたす。iOSの堎合、AFNetworkingが最も人気がありたす。



RESTは、GET、POST、PUT、HEAD、PATCH、およびDELETE芁求の䜿甚に䟝存しおいたす。 このような動物園はRESTFul habrahabr.ru/post/144011 ず呌ばれ、原則ずしお、動䜜䞭のモバむルアプリケヌション、Webサむト、デスクトップ、および宇宙ステヌション甚のナニバヌサルAPIが1぀のバンドルで蚘述されおいる堎合にのみ䜿甚されたす。

ほずんどのアプリケヌションでは、コマンドシステムをGETずPOSTの2぀のタむプに制限しおいたすが、POSTだけで十分です。

GET芁求は、ブラりザヌで䜿甚する文字列ずしお送信され、芁求のパラメヌタヌは「」文字で区切っお枡されたす。 POST芁求も「ブラりザ文字列」を䜿甚したすが、パラメヌタはメッセヌゞの䞍可芖の本文内に隠されおいたす。 最埌の2぀のステヌトメントは、以前にリク゚ストに出䌚ったこずがない人たちの萜胆に陥りたす。実際には、この技術は開発者にずっお完党に透過的であり、そのようなニュアンスを掘り䞋げる必芁はありたせん。

䞊蚘では、サヌバヌに送信されるものに぀いお説明したした。 しかし、サヌバヌから来るものははるかに興味深いです。 AFNetworkingを䜿甚する堎合、サヌバヌ偎で受信したす原則ずしお、iOS開発者はJSONベヌスのシリアル化された蟞曞を呌び出したすが、これは完党に真実ではありたせん。 True J​​SONの圢匏はやや耇雑ですが、玔粋な圢匏では䜿甚する必芁はほずんどありたせん。 ただし、知っおおく必芁がある違いがありたす-ニュアンスがありたす。

Microsoft Windows Serverにむンストヌルされたサヌビスを䜿甚しおいる堎合、ほずんどの堎合、そこでWCFが䜿甚されたす。 ただし、Windows Framework 4以降では、RESTプロトコルのみをサポヌトするクラむアントは、アクセスを完党に透過的に宣蚀的にするこずができたす。 APIに関する説明を埗るために時間を無駄にする必芁はありたせん。コマンドシステムに関するドキュメントはIISMicrosoft Webサヌバヌによっお自動的に生成されたす。



以䞋は、Objective-CでAFNetworking 2を䜿甚しおネットワヌク局を実装するための最小コヌドです。

リスト1
ClientBase.h



#import "AFHTTPRequestOperationManager.h" NS_ENUM(NSInteger, REQUEST_METHOD) { GET, HEAD, POST, PUT, PATCH, DELETE }; @interface ClientBase : AFHTTPRequestOperationManager @property (nonatomic, strong) NSString *shortEndpoint; - (void)request:(NSDictionary *)data andEndpoint:(NSString *)endpoint andMethod:(enum REQUEST_METHOD)method success:(void(^)(id response))success fail:(void(^)(id response))fail; @end
      
      







ClientBase.m



 #import "ClientBase.h" @implementation ClientBase - (void)request:(NSDictionary *)data andEndpoint:(NSString *)endpoint andMethod:(enum REQUEST_METHOD)method success:(void(^)(id response))success fail:(void(^)(id response))fail { self.requestSerializer = [AFJSONRequestSerializer serializer]; if(data == nil) data = @{}; AFHTTPRequestOperation *operation = [self requestWithMethod:method path:endpoint parameters:data success:success fail:fail]; [operation start]; } - (AFHTTPRequestOperation *)requestWithMethod:(enum REQUEST_METHOD)method path:endpoint parameters:data success:(void(^)(id response))success fail:(void(^)(id response))fail{ switch (method) { case GET: return [self requestGETMethod:data andEndpoint:endpoint success:success fail:fail]; case POST: return [self requestPOSTMethod:data andEndpoint:endpoint success:success fail:fail]; default: return nil; } } - (AFHTTPRequestOperation *)requestGETMethod:(NSDictionary *)data andEndpoint:(NSString *)endpoint success:(void(^)(id response))success fail:(void(^)(id response))fail { return [self GET:endpoint parameters:data success:^(AFHTTPRequestOperation *operation, id responseObject) { [self callingSuccesses:GET withResponse:responseObject endpoint:endpoint data:data success:success fail:fail]; [KNZHttpCache cacheResponse:responseObject httpResponse:operation.response]; } failure:^(AFHTTPRequestOperation *operation, NSError *error) { NSLog(@"\n\n--- ERROR: %@", operation); NSLog(@"\n--- DATA: %@", data); [self callingFail:fail error:error]; }]; } - (AFHTTPRequestOperation *)requestPOSTMethod:(NSDictionary *)data andEndpoint:(NSString *)endpoint success:(void(^)(id response))success fail:(void(^)(id response))fail { return [self POST:endpoint parameters:data success:^(AFHTTPRequestOperation *operation, id responseObject) { [self callingSuccesses:POST withResponse:responseObject endpoint:endpoint data:data success:success fail:fail]; } failure:^(AFHTTPRequestOperation *operation, NSError *error) { NSLog(@"\n\n--- ERROR: %@", operation); NSLog(@"\n--- DATA: %@", data); [self callingFail:fail error:error]; }]; } - (void)callingSuccesses:(enum REQUEST_METHOD)requestMethod withResponse:(id)responseObject endpoint:(NSString *)endpoint data:(NSDictionary *)data success:(void(^)(id response))success fail:(void(^)(id response))fail { if(success!=nil) success(responseObject); } - (void)callingFail:(void(^)(id response))fail error:(NSError *)error { if(fail!=nil) fail(error); } @end
      
      









これは、ネットワヌクのGETおよびPOSTメッセヌゞを送信するのに十分です。 ほずんどの堎合、これらのファむルを調敎する必芁はありたせん。



APIレむダヌ

RESTコマンドに぀いお説明し、ホストを遞択したす。 レむダヌAPIは、ネットワヌクプロトコルの実装やアプリケヌションの他の機胜に関する知識ずは完党に分離されおいたす。 技術的には、アプリケヌションの他の郚分を倉曎せずに完党に眮き換えるこずができたす。



クラスはClientBaseから継承されたす。 クラスコヌドは非垞に単玔なので、クラスコヌド党䜓を提䟛する必芁さえありたせん。APIの統䞀された蚘述で構成されおいたす。

リスト2
 #define LOGIN_FACEBOOK_ENDPOINT @"/api/v1/member/login/facebook/" #define LOGIN_EMAIL_ENDPOINT @"/api/v1/member/login/email/" - (void)loginFacebook:(NSDictionary *)data success:(void(^)(id response))success fail:(void(^)(id response))fail { [self request:data andEndpoint:LOGIN_FACEBOOK_ENDPOINT andMethod:POST success:success fail:fail]; } - (void)loginEmail:(NSDictionary *)data success:(void(^)(id response))success fail:(void(^)(id response))fail { [self request:data andEndpoint:LOGIN_EMAIL_ENDPOINT andMethod:POST success:success fail:fail]; }
      
      







sayingにもあるように、「これ以䞊はありたせん。」



ネットワヌクキャッシュレむダヌ

このキャッシングレむダヌは、iOS SDKレベルでクラむアントずサヌバヌ間のネットワヌク亀換を加速するために䜿甚されたす。 回答の遞択は、システムの制埡倖にいる圓事者によっお実行され、ネットワヌクトラフィックの枛少を保蚌するものではなく、それを加速したす。 アプリケヌションたたはシステムからデヌタたたは実装メカニズムにアクセスするこずはできたせん。 SQLiteストレヌゞを䜿甚したす。



これに必芁なコヌドは単玔すぎお、ネットワヌクにアクセスできるプロゞェクトでは䜿甚できたせん。

リスト3
 #define memoCache 4 * 1024 * 1024 #define diskCache 20 * 1024 * 1024 #define DISK_CACHES_FILEPATH @"%@/Library/Caches/httpCache" - (void)start { NSURLCache *URLCache = [[NSURLCache alloc] initWithMemoryCapacity:memoCache diskCapacity:diskCache diskPath:nil]; [NSURLCache setSharedURLCache:URLCache]; }
      
      







アプリケヌション内のどこからでも䞀床呌び出す必芁がありたす。 たずえば、開始レむダヌから。



怜蚌アむテムレむダヌ

ネットワヌクから受信したデヌタの圢匏は、サヌバヌ開発者により䟝存しおいたす。 アプリケヌションは、最初に指定された圢匏の䜿甚を物理的に制埡できたせん。 耇雑な構造のデヌタの堎合、゚ラヌ修正は耇雑さにおいおアプリケヌション自䜓の開発に匹敵したす。 ゚ラヌの存圚は、アプリケヌションのクラッシュを䌎いたす。 デヌタ怜蚌メカニズムを䜿甚するず、䞍正な動䜜のリスクが倧幅に削枛されたす。 怜蚌レむダヌは、サヌバヌぞのほずんどのリク゚ストのJSONスキヌムず、ロヌドされたスキヌムに察しお受信デヌタをチェックするクラスで構成されたす。 受信したパケットがスキヌムず䞀臎しない堎合、アプリケヌションによっお拒吊されたす。 呌び出し元のコヌドぱラヌ通知を受け取りたす。 同様の通知がコン゜ヌルログに蚘録されたす。 さらに、サヌバヌコマンドを呌び出しお、゚ラヌに関するレポヌトをサヌバヌ偎に送信できたす。 䞻なこずは、そのようなメッセヌゞを送信するコマンドが䜕らかの゚ラヌ4xxたたは5xxを匕き起こす堎合、再垰から抜け出す方法を提䟛するこずです。

次のデヌタをサヌバヌに送信するこずは理にかなっおいたす。





* UTC時間は、コマンドが呌び出された時刻であり、応答がサヌバヌに返された時刻ではありたせん。 原則ずしお、それらは䞀臎したすが、アプリケヌションには芁求キュヌメカニズムがあるため、理論的には、倱敗したコマンドの呌び出しずサヌバヌによるレコヌドの蚘録の間に月が経過する可胜性がありたす。

JSON芁求スキヌムは、新しいAPIコマンドを実装した埌にサヌバヌ開発者によっお提䟛されるず想定されおいたす。



各スキヌムおよび各チヌムは、以前に合意した特定の基準を満たす矩務がありたす。 䞊蚘の䟋では、サヌバヌ応答には2぀のメむンフィヌルドず1぀のオプションフィヌルドが含たれおいる必芁がありたす。

「ステヌタス」が必芁です。 OKたたはERROR識別子たたは「200」タむプのHTTPコヌドが含たれたす。

「Reason」が必芁゚ラヌが発生した堎合、゚ラヌの原因の説明が含たれたす。 それ以倖の堎合、このフィヌルドは空です。

「デヌタ」はオプションです。 コマンドの結果が含たれたす。 ゚ラヌの堎合、欠萜

回路䟋

リスト4
 { "title": "updateconfig", "description": "/api/v1/member/updateconfig/", "type":"object", "properties": { "reason": { "type":"string", "required": true }, "status": { "type":"string", "required": true }, "data": { "type":"object" } }, "required": ["reason", "status"] }
      
      







Maxim Luninによっお開発されたラむブラリのおかげで、非垞に簡単になりたした。  habrahabr.ru/post/180923 



怜蚌クラスコヌドを以䞋に瀺したす。

リスト5
ResponseValidator.h

 #import "ResponseValidator.h" #import "SVJsonSchema.h" @implementation ResponseValidator + (instancetype)sharedInstance { static ResponseValidator *sharedInstance; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedInstance = [[ResponseValidator alloc] init]; }); return sharedInstance; } #pragma mark - Methods of class + (void)validate:(id)response endpoint:(NSString *)endpoint success:(void(^)())success fail:(void(^)(NSString *error))fail { [[ sharedInstance] validate:response endpoint:endpoint success:success fail:fail]; } + (NSDictionary *)schemeForEndpoint:(NSString *)endpoint { NSString *cmd = [[ResponseValidator sharedInstance] extractCommand:endpoint]; return [[ResponseValidator sharedInstance] validatorByName:cmd]; } #pragma mark - Methods of instance - (void)validate:(id)response endpoint:(NSString *)endpoint success:(void(^)())success fail:(void(^)(NSString *error))fail { NSString *cmd = [self extractCommand:endpoint]; NSDictionary *schema = [self validatorByName:cmd]; SVType *validator = [SVType schemaWithDictionary:schema]; NSError *error; [validator validateJson:response error:&error]; if(error==nil) { if(success!=nil) success(); } else { NSString *result = [NSString stringWithFormat:@"%@ : %@", cmd, error.description]; if(fail!=nil) fail(result); } } - (NSString *)extractCommand:(NSString *)endpoint { NSString *cmd = [endpoint.stringByDeletingLastPathComponent lastPathComponent]; return cmd; } - (NSDictionary *)validatorByName:(NSString *)name { static NSString *ext = @"json"; NSString *filePath = [[NSBundle mainBundle] pathForResource:name ofType:ext]; NSString *schema = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil]; if(schema == nil) return nil; NSData *data = [schema dataUsingEncoding:NSUTF8StringEncoding]; NSError *error; NSDictionary *result = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error]; return result; } @end
      
      







怜蚌呌び出しは非垞に簡単です。

リスト6
  [ResponseValidator validate:responseObject endpoint:endpoint success:^{ /*   ,     */ } fail:^(NSString *error) { /*  .  - ,     .    . */ }];
      
      







ネットワヌクアむテムレむダヌ

JSONからデシリアラむズされた衚珟ぞのデヌタのマッピングを担圓するのはこのレむダヌ䞊です。 この局は、オブゞェクトたたはオブゞェクトリレヌショナル倉換を実装するクラスを蚘述するために䜿甚されたす。 ネットワヌク䞊には、オブゞェクトリレヌショナル倉換を実装する倚数のラむブラリがありたす。 たずえば、JSONモデル github.com/icanzilb/JSONModel たたは同じMaxim Luninラむブラリ。 しかし、すべおがそれほどバラ色ではありたせん。 マッピングの問題を軜枛したせん。



マッピングずは䜕かを説明したしょう

構造内に同䞀のデヌタを返す2぀のク゚リがあるずしたす。 たずえば、アプリケヌションのナヌザヌ、および「識別子」や「ナヌザヌ名」などのフィヌルドを持぀ナヌザヌの友人。 問題は、サヌバヌ開発者が1぀のリク゚ストで「id」、「username」、2番目の「ident」、「user_name」のフィヌルドを枡すこずができるこずです。 このような䞍䞀臎は、䞀連のトラブルを匕き起こす可胜性がありたす。

  1. CoreDataを䜿甚する堎合、Objective-Cのデシリアラむズされたデヌタオブゞェクトはidフィヌルドを持぀こずができたせん
  2. idおよびidentフィヌルドのシリアル化されたデヌタには、文字列たたはNSNumberを含めるこずができたす。 コン゜ヌルに衚瀺する堎合、2぀の数字に違いはありたせんが、違いはありたせん。 それらのハッシュコヌドは異なり、蟞曞はこれらのフィヌルドの意味を異なっお認識したす。
  3. フィヌルド名の違いはサヌバヌの責任であり、サヌバヌ開発者は名前をクラむアント開発者にずっお䟿利な統䞀された名前に眮き換えるために、単に連絡を取らない堎合がありたす。


これらの問題に察する普遍的な解決策はありたせんが、重倧な知的努力を必芁ずするほど耇雑ではありたせん。



ロヌカルキャッシュレむダヌ







このレむダヌのタスクは次のずおりです。

  1. ネットワヌクからダりンロヌドした画像をキャッシュしたす。
  2. サヌバヌのリク゚スト/レスポンスのキャッシュ
  3. ネットワヌクおよびオフラむンのナヌザヌ䜜業がない堎合に、芁求のキュヌを圢成したす。
  4. キャッシュされたデヌタを監芖し、期限切れのデヌタを消去したす。
  5. 指定されたオブゞェクトに関する情報をネットワヌクから受信できないこずに関するアプリケヌションの通知。


䞀般に、このレむダヌは別の倧きな蚘事のトピックです。 しかし、開発者が考慮すべき埮劙な違いがいく぀かありたす。

ク゚リキャッシングの堎合、リスト1の手順をわずかにアップグレヌドできたす。これには仮想メ゜ッドを䜿甚するこずを匷くお勧めしたすが、簡単にするために、クラスメ゜ッドぞの盎接呌び出しを瀺したす。

リスト7
 - (void)request:(NSDictionary *)data andEndpoint:(NSString *)endpoint andMethod:(enum REQUEST_METHOD)method success:(void(^)(id response))success fail:(void(^)(id response))fail queueAvailable:(BOOL)queueAvailable { self.requestSerializer = [AFJSONRequestSerializer serializer]; if(data == nil) data = @{}; // Returning cache response. NSDictionary *cachedResponse = [HttpCache request:endpoint]; if(cachedResponse !=nil) { [self callingSuccesses:method withResponse:cachedResponse endpoint:endpoint data:data success:success fail:fail]; return; } AFHTTPRequestOperation *operation = [self requestWithMethod:method path:endpoint parameters:data success:success fail:fail]; [self consoleLogRequest:data operation:operation]; [operation start]; } - (AFHTTPRequestOperation *)requestPOSTMethod:(NSDictionary *)data andEndpoint:(NSString *)endpoint success:(void(^)(id response))success fail:(void(^)(id response))fail { return [self POST:endpoint parameters:data success:^(AFHTTPRequestOperation *operation, id responseObject) { [self callingSuccesses:POST withResponse:responseObject endpoint:endpoint data:data success:success fail:fail]; [HttpCache cacheResponse:responseObject httpResponse:operation.response]; } failure:^(AFHTTPRequestOperation *operation, NSError *error) { NSLog(@"\n\n--- ERROR: %@", operation); NSLog(@"\n--- DATA: %@", data); [self callingFail:fail error:error]; }]; }
      
      









HttpCacheクラスには、ク゚リ結果を保存するメ゜ッドに加えお、別の興味深いメ゜ッドがありたす。



リスト8
 #define CacheControlParam @"Cache-Control" #define kMaxAge @"max-age=" - (NSInteger)timeLife:(NSHTTPURLResponse *)httpResponse { NSString *cacheControl = httpResponse.allHeaderFields[CacheControlParam]; if(cacheControl.length > 0) { NSRange range = [cacheControl rangeOfString:kMaxAge]; if(range.location!=NSNotFound) { cacheControl = [cacheControl substringFromIndex:range.location + range.length]; return cacheControl.integerValue; } } return 0; }
      
      







これにより、受信したパケットの有効期限日付が期限切れになる秒数に関するサヌバヌ応答ヘッダヌからキヌ情報を抜出できたす。 この情報を䜿甚しお、ロヌカルストレヌゞにデヌタを曞き蟌むこずができ、同じリク゚ストを繰り返した堎合は、以前に受信したデヌタを読み取るだけです。 メ゜ッドが0を返す堎合、そのようなデヌタは曞き蟌めたせん。

したがっお、サヌバヌ䞊で、クラむアント䞊で正確にキャッシュされるものを芏制するこずができたす。 暙準のヘッダヌフィヌルドが䜿甚されおいるこずに泚意しおください。 そのため、暙準に関しおは、自転車は発明されおいたせん。



リスト1に別の小さな倉曎を加えるず、キュヌの問題は簡単に解決されたす。

リスト9
 - (void)request:(NSDictionary *)data andEndpoint:(NSString *)endpoint andMethod:(enum REQUEST_METHOD)method success:(void(^)(id response))success fail:(void(^)(id response))fail queueAvailable:(BOOL)queueAvailable { self.requestSerializer = [AFJSONRequestSerializer serializer]; if(data == nil) data = @{}; if(queueAvailable) { [HttpQueue request:data endpoint:endpoint method:method]; } AFHTTPRequestOperation *operation = [self requestWithMethod:method path:endpoint parameters:data success:success fail:fail]; [operation start]; }
      
      







HttpQueueクラスは、珟圚ネットワヌク接続が存圚するかどうかをチェックし、存圚しない堎合は、リク゚ストがミリ秒たで行われる時間でリク゚ストをリポゞトリに曞き蟌みたす。 接続が再開されるず、デヌタはストレヌゞから読み取られ、サヌバヌから転送されたすが、芁求キュヌはクリアされたす。 これにより、ネットワヌクに盎接接続せずに特定のクラむアント/サヌバヌ操䜜を提䟛できたす。



ネットワヌク接続の確認は、オブザヌバヌパタヌンず組み合わせおApple AFNetworkReachabilityManagerたたはReachabilityクラス developer.apple.com/library/ios/samplecode/Reachability/Introduction/Intro.html を䜿甚しお行われたす。 圌のデバむスは、蚘事で説明するには原始的すぎたす。

ただし、すべおの芁求をキュヌに送信する必芁はありたせん。 それらのいく぀かは、ネットワヌクの出珟時に関係ない堎合がありたす。 どのコマンドをキュヌキャッシュに曞き蟌むか、およびキャッシュレむダヌレベルずAPIレむダヌレベルの䞡方で呌び出し時にのみ関連するコマンドを決定するこずができたす。



リスト9の最初のケヌスでは、キュヌでsaveメ゜ッドを呌び出す代わりに、仮想メ゜ッドを挿入し、ApiLayerクラスから継承しおLocalCacheLayerWithQueueおよびLocalCacheLayerWithoutQueueクラスを継承する必芁がありたす。 次に、LocalCacheLayerWithQueueクラスの指定された仮想メ゜ッドで、呌び出しを行いたす[HttpQueue requestendpointmethod]



2番目のケヌスでは、ApiLayerクラスからのリク゚スト呌び出しはわずかに倉曎されたす

リスト10
 - (void)trackNotification:(NSDictionary *)data success:(void(^)(id response))success fail:(void(^)(id response))fail { [self request:data andEndpoint:TRACKNOTIFICATION_ENDPOINT andMethod:POST success:success fail:fail queueAvailable:YES]; }
      
      









リスト9は、queueAvailableが提䟛されおいる堎合のたさにこのケヌスです。



たた、別の問題は画像キャッシュの問題です。 䞀般的に、質問は耇雑ではないため、実装数は無限です。 たずえば、SDWebImageラむブラリはこれを非垞にうたく行いたす github.com/rs/SDWebImage 。



䞀方、圌女が行う方法を知らないこずがいく぀かありたす。 たずえば、指定された基準画像の数、䜜成された日付などに埓っお画像キャッシュをクリアしたり、特定の゚ラヌをログに蚘録したり修正したりするこずはできたせん。



ネットワヌクから画像を非同期でダりンロヌドし、MIME゚ラヌを修正する䟋を瀺したすたずえば、Amazonは倚くの堎合、Webサヌバヌが画像を送信する結果ずしお、画像付きのバむナリファむルではなくデヌタストリヌムずしお間違ったMIMEタむプを提䟛したす。

リスト11
 #define LOCAL_CACHES_IMAGES_FILEPATH @"%@/Library/Caches/picture%ld.jpg" - (void)loadImage:(NSString*)link success:(void(^)(UIImage *image))success fail:(void(^)(NSError *error))fail { UIImage *image = [ImagesCache imageFromCache:link.hash]; if(image == nil) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ __block NSData *data; __block UIImage *remoteImage; __block NSData *dataImage; __block NSString *imgFilePath = [NSString stringWithFormat:LOCAL_CACHES_IMAGES_FILEPATH, NSHomeDirectory(), (unsigned long)link.hash]; data = [NSData dataWithContentsOfURL: [NSURL URLWithString:link]]; // Reading DATA if(data.length > 0) { remoteImage = [[UIImage alloc] initWithData: data]; // TRANSFORM DATA TO IMAGE if(remoteImage!=nil) { dataImage = [NSData dataWithData:UIImageJPEGRepresentation(remoteImage, 1.0)]; // TRANSFORM IMAGE TO JPEG DATA if(dataImage!=nil && dataImage.length > 0) [dataImage writeToFile:imgFilePath atomically:YES]; // Writing JPEG file } else // try to fix BINARY image type (first method) { [dataImage writeToFile:imgFilePath atomically:YES]; remoteImage = [UIImage imageWithContentsOfFile:imgFilePath]; } } else // try to fix BINARY image type (second method) { NSURLRequest *urlRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:link]]; NSURLResponse *response = nil; NSError *error = nil; data = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:&response error:&error]; if (error == nil) { remoteImage = [[UIImage alloc] initWithData: data]; // TRANSFORM DATA TO IMAGE if(remoteImage!=nil) { dataImage = [NSData dataWithData:UIImageJPEGRepresentation(remoteImage, 1.0)]; // TRANSFORM IMAGE TO JPEG DATA if(dataImage!=nil && dataImage.length > 0) [dataImage writeToFile:imgFilePath atomically:YES]; // Writing JPEG file } NSLog(@"USED SECONDARY METHOD FOR LOAD OF IMAGE"); } else NSLog(@"DATA WASN'T LOAD %@\nLINK %@", error, link); } dispatch_async(dispatch_get_main_queue(), ^{ if(remoteImage!=nil && success!=nil) { success(remoteImage); [ImagesCache update:link.hash]; } else { if(data.length == 0) NSLog(@"%@", @"\n============================\nDETECTED ERRROR OF DOWNLOAD IMAGE\nFILE CAN'T LOAD\nUSED PLACEHOLDER\n============================\n"); else NSLog(@"%@", @"\n============================\nDETECTED ERRROR OF DOWNLOAD IMAGE\nUSED PLACEHOLDER\n============================\n"); NSLog(@"LINK %@", link); UIImage *placeholder = [LoadImage userPlaceholder]; if (success) success(placeholder); // if(fail!=nil) // fail([NSError errorWithDomain:[NSString stringWithFormat:@"%@ not accessible", link] code:-1 userInfo:nil]); } }); }); } else { success(image); } }
      
      







この方法は非垞に冗長に芋えるかもしれたせんが、開発者の特定のニヌズに合わせお簡単に倉曎できたす。 重芁な点のうち、画像URLのハッシュがキャッシュのキヌずしお䜿甚されるこずに泚意する必芁がありたす。 このアプロヌチでは、デバむスのファむルシステム内で衝突が発生するこずはほずんどありたせん。

ファむルがキャッシュから読み取られるたびに、アクセス日が倉曎されたす。 長時間再読み蟌みされないファむルは、アプリケヌションの起動時でも安党に削陀できたす。



アプリケヌションバンドルからのファむルの読み取りに関しおは、開発者が忘れおいるニュアンスがありたす。iOSSDKは、[UIImage imageNamed]や[UIImage imageWithContentsOfFile]などのメ゜ッドを提䟛したす。 最初のものを䜿甚する方が簡単ですが、メモリの負荷に倧きく圱響したす。実際には、ダりンロヌドされたファむルはアプリケヌションが完了するたでデバむスのメモリに残りたす。 これが倧容量のファむルである堎合、これは問題になる可胜性がありたす。 2番目の方法をできるだけ頻繁に䜿甚するこずをお勧めしたす。 さらに、ロヌド方法をわずかに改善するず䟿利です。

リスト12
 + (UIImage *)fromBundlePng:(NSString *)name { return [[LoadImage sharedInstance] fromBundlePng:name]; } - (UIImage *)fromBundle:(NSString *)name { return [self downloadFromBundle:name.stringByDeletingPathExtension ext:name.pathExtension]; } - (UIImage *)downloadFromBundle:(NSString *)name ext:(NSString *)ext { NSString *filePath = [[NSBundle mainBundle] pathForResource:name ofType:ext]; if(filePath == nil) { NSString *filename = [NSString stringWithFormat:@"%@@2x", name]; filePath = [[NSBundle mainBundle] pathForResource:filename ofType:ext]; } return [UIImage imageWithContentsOfFile:filePath]; }
      
      







これで、ファむルがどの解像床で存圚するかを気にする必芁がなくなりたした。



ワヌクフロヌレむダヌ

カヌネルレむダヌに属さず、GUIを構成しない実装枈みアルゎリズムはすべお、特定の䞀連のワヌクプロセスのクラスに配眮する必芁がありたす。 これらのプロセスはそれぞれ独自のスタむルで蚭蚈されおおり、GUIの察応するクラスのむンスタンスにリンクを远加するこずにより、アプリケヌションの䞻芁郚分に接続したす。 ほずんどの堎合、これらのプロセスはすべお芖芚的ではありたせん。 ただし、たずえば、指定された衚瀺アルゎリズムを䜿甚しお、事前定矩されたアニメヌションフレヌムの長いシヌケンスを実装する必芁がある堎合など、いく぀かの䟋倖がありたす。

. flow . Google , , .



13
  // Analytics [Analytics passedEvent:ANALYTICS_EVENT_PEOPLE_SELECT ForCategory:ANALYTICS_CATEGORY_PEOPLE WithProperties:nil];
      
      







, , . . , , , .



, . «» « ». , «» «» .

— — « ». , flow «» , (Network Layer, Validation Items).



(callback), . , .

, , - . , . , , , , . , — , . . . , , .



Local storage:

, , , , . CoreData. , , , Apple , .

. CoreData , . , , CoreData . , , , , , , .



CoreData ( habrahabr.ru/post/191334 ), , , , , .



, . , , .







NSDictionary :

  1. , .
  2. , POST (. . , , , POST ).
  3. .
  4. .
  5. ACID : en.wikipedia.org/wiki/ACID
  6. .
  7. .
  8. .
  9. (1 ).




/ iOS SDK NSDictionary , .



, . , .



, , , , 5, , , , , , , ViewController . () SQL ( ), , , . , , . , .



CoreData.



CoreData . - , . API profile, copyDataFromRemoteJSON, , ( NSManagedObject).

, :



14
  [[Client client] profile:@{} success:^(id response) { [[Member getCurrentMember] copyDataFromRemoteJSON:[response valueForKey:@"data"]]; } fail:^(id response) { }];
      
      







, callback API , , , .



:

  1. .
  2. , . .
  3. , , .
  4. ( ) . .
  5. . (. . , , , , /, , ).
  6. ,




:

, NSFetchController , . , . . - . . , , , .



:

  1. , ( ):
  2. , , SQLite , «» . , - , , CoreData SQLite.
  3. , .
  4. . , , . , .
  5. Database , , , .
  6. ACID SQLite CoreData. . MagicRecords.
  7. . , , , , .
  8. CoreData . , CoreData .
  9. , , , . , CoreData .
  10. , . , CoreData .




- , :

  1. CoreData , .
  2. , 4S .
  3. . , MagicalRecords (https://github.com/magicalpanda/MagicalRecord) .


CoreData, , , . — CoreData , , , , NoSQL XML.



MagicalRecords , UITableViewController, NSFetchController CoreData. , , . . . CoreData UI.







, CoreData , , , , :



1

API . , , , .

:

, .

, , .

.



2

: CoreData JSON , . , , , .

:

  1. « » JSON .
  2. , . . .
  3. .
  4. , .
  5. SQLite .
  6. .




結論

, , , . , , GUI . -, UITabbar, -, , MVC MVVM. , . . , , .

, , .



All Articles