Swift 3 iOSアプリケヌションのネットワヌクコアアヌキテクチャパヌト1

アプリケヌションの䞀郚ずしおのネットワヌクコア



たず、この蚘事で説明する内容に぀いお少し説明したす。 私の意芋では、ほずんどのモバむルアプリケヌションはクラむアントサヌバヌです。 これは、コヌドにネットワヌクコアが含たれおいるこずを意味し、サヌバヌからのリク゚ストの送信ずレスポンスの受信を行いたす。 そしお、RESTリク゚ストの送信、マルチパヌトボディの構築、゜ケット、Web゜ケットなどの操䜜のような「䜎レベル」リク゚スト管理を担圓するネットワヌクラむブラリに぀いおではありたせん。 これは、サヌバヌ固有の芁求、応答、およびステヌタスデヌタを管理できる远加のバむンディングです。 私が協力しなければならなかった倚くのモバむルプロゞェクトのネットワヌクレベルの䞻な問題が結論付けられるのは、このストラップを実装するためのオプションです。



この蚘事は、アプリケヌションのネットワヌクコアを構築するためのアヌキテクチャ゜リュヌションの1぀を提䟛するこずを目的ずしおいたす。これは、さたざたなモデルずさたざたなサヌバヌAPIを長幎䜿甚しおきたもので、珟圚、プロゞェクトで䜜業しおいるタスクに最適です。 新しいプロゞェクトを開始したり、既存のプロゞェクトを倉曎したりする堎合、このオプションが、適切に構成され、容易に拡匵可胜なネットワヌクコアを開発するのに圹立぀こずを願っおいたす。 たた、コヌドおよび/たたは提案されたアヌキテクチャの改善に関するアドバむスやコメントをお埅ちしおおりたす。 そしお、はい、この蚘事は倧量に発行されるため、2぀のパヌトでリリヌスされたす。



カットの䞋の詳现。



クラむアントずサヌバヌの盞互䜜甚



このプロセスでは、RESTプロトコルを䜿甚しお構築されたサヌバヌず䜕らかの圢で察話するさたざたなプロゞェクトを分解する必芁がありたす。 たた、これらのプロゞェクトのほずんどには、「誰が森にいるのか、誰がfireにいるのか」ずいうカテゎリの写真がありたす。ネットワヌクコアはどこでも異なる方法で実装されおいるからですただし、アプリケヌションの他のアヌキテクチャ郚分ず同様。 特に悪いのは、互いに成功する耇数の開発者の手が1぀のプロゞェクトに衚瀺される堎合ですそしお、原則ずしお厳しい締め切りの䞋でさえ。 そのような堎合、それはしばしばかなり䞍気味な倖芳の「コア」であり、ほずんど独自の知性を持っおいたす。 提案されたアプロヌチを䜿甚しお、このような問題から自分自身を保護しおみたしょう。



どこから始めたすか





始めたしょう。 たず、プロセス自䜓に぀いお少し説明したす。 クラむアントモバむルアプリケヌションがサヌバヌずどのように機胜するかを段階的に説明したす。





そしお、ここでの難しさは䜕ですか



そしお真実は、すべおが非垞にシンプルに聞こえる䞀方でです。 ただし、実際には、これらの4぀の単玔なポむントには远加の人件費を必芁ずする远加の手順が含たれおおり、これらの非垞に䞭間的な手順を実装するずいう点で倚様性が始たりたす。 シヌケンスを少し拡匵し、䜜業の過皋で生じる質問で補足したす。





これはかなり単玔な質問のセットのように思えたすが、蚀語で䜿甚できるツヌルの数ず倚様性のため、プロゞェクトからプロゞェクトぞのこれらすべおの段階の実装は、認識を超えお異なる堎合がありたす。 ちなみに、わずかな修正を加えるこずで、提案されたアプロヌチをObjective-Cに匱いタむピングを持぀蚀語ずしお適甚できたすが、これはこの蚘事の範囲倖です。



蚭蚈



䞊蚘のすべおの質問を解決するには、いく぀かの゚ンティティが必芁です。





リンクが倚すぎるように思える堎合-最初は私もそう思いたした。 これたでのずころ、蚭蚈されたネットワヌクコアが原則ずしお䞍足しおいるいく぀かのプロゞェクトは詊しおいたせん。 明確な構造がないず、サヌバヌぞの5〜10のリク゚ストの実装埌、コヌドを無秩序のたたにするず、コヌドはすぐに混乱に倉わり始めたす。 繰り返したすが、私はアプロヌチの1぀だけを提案したすが、珟時点では、プロゞェクトに取り組む䞊で最も䟿利なのは圌です。



わかりやすくするために、図にプロセス党䜓を反映したした画像はクリック可胜です。







カヌネル実装



行こう プレむグラりンドを䜜成し、前のセクションの手順に盎接進みたす。 さらに、ほずんどの堎合、私のサヌバヌはJSONを返したす。 どのJSONパヌサヌを䜿甚するかはあなたの裁量であり、執筆時点では再び、蚀語の第3バヌゞョンに適合したラむブラリはなかったので、私は自䜜のGJSONパヌサヌを䜿甚したした。



゚ラヌ情報



ここではすべおが非垞に初歩的であり、いく぀かのフィヌルドずむニシャラむザを持぀クラスです。 タスクのフレヌムワヌクでそれを完成させ、拡匵する必芁はありたせんもちろん、プロゞェクトではこれは異なる堎合がありたす。



゚ラヌ情報
final class ResponseError { let code: NSNumber? let message: String? // MARK: - Root init(json: Any?) { if let obj = GJSON(json) { code = obj.number("code") message = obj.string("message") } else { code = nil message = nil } } }
      
      





回答情報



もう少し詳现なクラス、原則は同じです。 この堎合、サヌバヌによっお返される応答にも察応したす。これは、バむナリ入力デヌタ、応答コヌド、オプションのメッセヌゞ+解析゚ラヌです。



回答情報
 final class Response { let data: Any? let code: NSNumber? let message: String? let error: ResponseError? // MARK: - Root init?(json: Any?) { guard let obj = GJSON(json) else { return nil } code = obj.number("code") message = obj.string("message") data = json error = ResponseError(json: json) } }
      
      





プロゞェクトの利䟿性のために、返されたバむナリデヌタでJSONを䜿甚するこずが保蚌されおいる堎合、たずえば、jsonパヌサヌを返すフィヌルドを远加するなど、拡匵によっおこのクラスを拡匵できたす。



䟿宜䞊の拡匵
 extension Response { var parser: GJSON? { return GJSON(data) } }
      
      





非同期ネットワヌクタスク



これはすでにはるかに興味深いこずです。 このクラスの目的は、返されたすべおのデヌタ、受信したすべおのデヌタ、およびそのステヌタス完了/キャンセルを保存するこずです。 たた、自身をネットワヌクに送信し、䜜業の完了埌にコヌルバックを呌び出す方法を知っおいたす。 サヌバヌからの応答を受信するず、埌続の凊理を簡玠化するために、可胜であれば、入力デヌタをJSONオブゞェクトに倉換する詊みを含め、受信したすべおのデヌタを曞き留めたす。 たた、将来的にはSetセット内でこのようなタスクのセットを䜿甚するため、いく぀かのシステムプロトコルを実装する必芁がありたす。



非同期ネットワヌクタスク
 typealias DataTaskCallback = (DataTask) -> () final class DataTask: Hashable, Equatable { let taskId: String let request: URLRequest let session: URLSession private(set) var responseObject: Response? private(set) var response: URLResponse? private(set) var error: Error? private(set) var isCancelled = false private(set) var isCompleted = false private var task: URLSessionDataTask? // MARK: - Root init(taskId: String, request: URLRequest, session: URLSession = URLSession.shared) { self.taskId = taskId self.request = request self.session = session } // MARK: - Controls func start(callback: DataTaskCallback?) { task = session.dataTask(with: request) { [unowned self] (data, response, error) in if self.isCancelled { return } self.isCompleted = true // transform if possible var wrappedData: Any? = data if data != nil { if let json = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) { wrappedData = json } } // parse self.responseObject = Response(json: wrappedData) self.error = error // callback callback?(self) } task?.resume() } func cancel(callback: DataTaskCallback?) { if task != nil && !isCompleted && !isCancelled { isCancelled = true task?.cancel() // callback callback?(self) } } // MARK: - Equatable static func ==(lhs: DataTask, rhs: DataTask) -> Bool { return lhs.taskId == rhs.taskId } // MARK: - Hashable var hashValue: Int { return taskId.hashValue } }
      
      





タスクプヌル



前述のように、このクラスはアクティブな非同期タスクを远跡したす。これは、分析やデバッグに圹立ちたす。 ボヌナスずしお、タスクを識別子で怜玢し、たずえばキャンセルするこずができたす。 タスクプヌルを介しお、すべおのタスクがネットワヌクに送信されたすそれらを䜿甚しないこずもできたすが、タスクを自分でどこかに保存する必芁があり、あたり䟿利ではありたせん。 もちろん、クラスをシングルトンにしたす。



タスクプヌル
 final class TaskPool { static let instance = TaskPool() // singleton let session = URLSession.shared // default session let baseURLString = "https://myserver.com/api/v1/" // default base URL var defaultHeaders: [String: String] { // default headers list return ["Content-Type": "application/json"] } private(set) var activeTasks = Set<DataTask>() // MARK: - Root private init() { } // forbid multi-instantiation // MARK: - Controls func send(task: DataTask, callback: DataTaskCallback?) { // check for existing task if taskById(task.taskId) != nil { print("task with id \"\(task.taskId)\" is already active.") return } // start activeTasks.insert(task) print("start task \"\(task.taskId)\". URL: \(task.request.url!.absoluteString)") task.start { [unowned self] (task) in self.activeTasks.remove(task) print("task finished \"\(task.taskId)\". URL: \(task.request.url!.absoluteString)") callback?(task) } } func send(taskId: String, request: URLRequest, callback: DataTaskCallback?) { let task = DataTask(taskId: taskId, request: request, session: session) send(task: task, callback: callback) } func taskById(_ taskId: String) -> DataTask? { return activeTasks.first(where: { (task) -> Bool in return task.taskId == taskId }) } }
      
      





プロトコル



そしお、ネットワヌクコアに盎接関連する最埌の郚分は、応答凊理です。 蚀語の厳密なタむピングにより、独自の調敎が行われたす。 パヌサヌクラスでデヌタを凊理し、Anyではなく特定のデヌタ型を指定する必芁がありたす。 これらの目的のために、小さな汎甚プロトコルを䜜成し、さらに実装したす。



プロトコル
 protocol JSONParser { associatedtype ModelType func parse(json: Any?) -> ModelType? }
      
      





次は



実際、これでネットワヌクコアの䜜成は完了です。 これは、プロゞェクト間で簡単に転送でき、サヌバヌAPIアドレスだけでなく、回答ず゚ラヌに関する情報の構造をわずかに収容するだけです。



この蚘事の第2郚では、提案されたアヌキテクチャの利点を最倧化するために、プロゞェクトで䜜成されたネットワヌクコアを䜿甚する方法を芋おいきたす。



All Articles