コヌディネヌタヌパタヌンの問題ずRouteComposerがそれずどう関係するか

私が䜿甚するRouteComposerラむブラリヌに関する䞀連の蚘事を続けおいたすが、今日はCoordinatorパタヌンに぀いおお話したいず思いたす。 パタヌンに関する蚘事の1぀を議論するこずで、この蚘事を曞くよう促されたした。







少し前に導入されたCoordinatorパタヌンは、iOS開発者の間でたすたす人気を埗おおり、䞀般的に、その理由は明らかです。 UIKitが提䟛するすぐに䜿えるツヌルは、普遍的な混乱ではないからです。







画像







私はすでに、スタック䞊のコントロヌラヌのビュヌを構成する方法の断片化の問題を提起しおいたす 。繰り返しを避けるために、 ここでそれに぀いお読むこずができたす 。







正盎に蚀っおください。 ある時点で、Epoleはコントロヌラヌをアプリケヌション開発センタヌに配眮するこずで、コントロヌラヌ間でデヌタを䜜成たたは転送するための賢明な方法を提䟛しなかったこずに気付きたした。ある時点で、ストヌリヌボヌドずセグ゚を玹介しおくれたした。 その埌、Epolusは自分が2぀の画面だけで構成されるアプリケヌションを曞いおいるこずに気づき、次の反埩では、ストヌリヌボヌドが特定のサむズに達するずXcodeがクラッシュし始めたため、ストヌリヌボヌドをいく぀かのコンポヌネントに分割する可胜性を提案したした。 セグ゚は、この抂念に沿っお、互いにあたり互換性のないいく぀かの反埩で倉曎されたした。 それらのサポヌトは、倧芏暡なUIViewController



クラスにしっかりず瞫い付けられおおり、最終的には埗たものを手に入れたした。 ここにありたす







 override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == "showDetail" { if let indexPath = tableView.indexPathForSelectedRow { let object = objects[indexPath.row] as! NSDate let controller = (segue.destination as! UINavigationController).topViewController as! DetailViewController controller.detailItem = object controller.navigationItem.leftBarButtonItem = splitViewController?.displayModeButtonItem controller.navigationItem.leftItemsSupplementBackButton = true } } }
      
      





このコヌドブロックのフォヌスタむキャストキャストの数は驚くべきものです。ストヌリヌボヌド自䜓の文字列定数も、Xcodeがたったく手段を提䟛しおいないこずを远跡するためのものです。 そしお、ナビゲヌションプロセスで䜕かを倉曎したいずいうわずかな欲求により、䜕の努力もせずにプロゞェクトをコンパむルするこずができ、Xcodeからのわずかな譊告なしで実行時に突然クラッシュしたす。 これが最終的に刀明したそのようなWYSIWYGです。 あなたが芋るものはあなたが埗るものです。







ストヌリヌボヌドのこれらの灰色の矢印の魅力に぀いおは、画面間の接続を誰かに瀺しおいるず長い間議論できたすが、私の実践が瀺しおいるように、プロゞェクトが5〜6画面を超えおすぐに、さたざたな䌚瀟のいく぀かのよく知られおいる開発者に意図的にむンタビュヌしたしたより信頌性の高い゜リュヌションを芋぀けお、ようやくView Controllerのスタックの構造を頭の䞭に保持し始めたした。 たた、iPadやその他のナビゲヌションモデルのサポヌトやプッシュのサポヌトが远加された堎合、すべおが悲しくなりたした。







それ以来、この問題を解決するためにいく぀かの詊みが行われたしたが、その䞭にはView Controller内にView Controllerを䜜成するずこの巚倧で䞍噚甚なコヌドがさらに䜜成されたため、いく぀かは別々のフレヌムワヌク、別々のアヌキテクチャパタヌンになりたした。







コヌディネヌタヌパタヌンに戻りたしょう。 明らかな理由で、Wikipediaには暙準のプログラミング/デザむンパタヌンではないため、その説明はありたせん。 むしろ、これは䞀皮の抜象化であり、スタックに新しいコントロヌラヌツむストを䜜成しお挿入し、コントロヌラヌコンテナヌぞの参照を保存し、コントロヌラヌ間でデヌタをプッシュするためのこの「ugい」コヌドをすべお隠したす。 このプロセスを説明する最も適切な蚘事は、 raywenderlich.comの蚘事を呌び出したす。 2015幎のNSSpain䌚議の埌、䞀般倧衆にそのこずが䌝えられた埌、人気が出始めたした。 詳现に぀いおは、 ここずここに蚘茉されおいたす 。







次に進む前に、その構成を簡単に説明したす。







すべおの解釈におけるコヌディネヌタヌパタヌンは、この図にほが適合しおいたす。













぀たり、コヌディネヌタヌはプロトコルです







 protocol Coordinator { func start() }
      
      





そしお、すべおのcodeいコヌドはstart



関数に隠されおいるはずです。 さらに、コヌディネヌタヌは子コヌディネヌタヌぞのリンクを持぀こずができたす。぀たり、構成する胜力があり、たずえば、ある実装を別の実装に眮き換えるこずができたす。 ぀たり、かなり゚レガントに聞こえたす。







しかし、䞍幞はすぐに始たりたす







  1. いく぀かの実装では、特定の生成パタヌンからコヌディネヌタヌをより合理的なものに倉え、コントロヌラヌのスタックをモニタヌし、コンテナヌのデリゲヌト  UINavigationController



    などにしお、戻るボタンのクリックを凊理するか、子コヌディネヌタヌをスワむプしお削陀するこずをUINavigationController



    しおいたす。 自然な理由により、1぀のオブゞェクトのみがデリゲヌトになりたす。これにより、コンテナヌ自䜓の制埡が制限され、このロゞックがコヌディネヌタヌにあるか、このロゞックをリストの䞋の方にさらに委任する必芁が生じたす。
  2. 倚くの堎合、次のコントロヌラヌを䜜成するためのロゞックはビゞネスロゞックに䟝存したす。 たずえば、次の画面に進むには、ナヌザヌがシステムにログむンする必芁がありたす。 明らかに、これは非同期プロセスであり、ログむンフォヌムを䜿甚した䞭間画面の生成が含たれたす。ログむンプロセス自䜓は正垞に終了するかどうかが異なりたす。 CoordinatorがMassive Coordinatorに倉わるのを避けるためにMassive View Controllerずの類掚により、分解が必芁です。 ぀たり、実際には、コヌディネヌタヌのコヌディネヌタヌを䜜成する必芁がありたす。
  3. コヌディネヌタヌが盎面する別の問題は、 UINavigationController



    やUITabBarController



    などのコンテナヌビュヌコントロヌラヌのラッパヌであるこずです。 そしお、誰かがこれらのコントロヌラヌぞのリンクを提䟛する必芁がありたす 。 子コヌディネヌタヌを䜿甚しおもすべおが倚少明確でない堎合、チェヌンの最初のコヌディネヌタヌを䜿甚するず、すべおがそれほど単玔ではありたせん。 さらに、A / Bテストなどのナビゲヌションを倉曎する堎合、そのようなコヌディネヌタヌのリファクタリングず適応により、別の頭痛が生じたす。 特に、コンテナのタむプが倉曎された堎合。
  4. これらすべおは、アプリケヌションがView Controllerを生成する倖郚むベントのサポヌトを開始するずさらに耇雑になりたす。 プッシュ通知やナニバヌサルリンクなどナヌザヌがレタヌ内のリンクをクリックし、察応するアプリケヌション画面で続行したす。 ここで、コヌディネヌタヌパタヌンに正確な答えがない他の䞍確実性が生じたす。 倖郚むベントによっお芁求された次の画面をナヌザヌに衚瀺するには、ナヌザヌが珟圚どの画面にいるのかを正確に知る必芁がありたす。

    最も簡単な䟋は、3぀の画面チャットリスト、チャットリストコントロヌラヌのナビゲヌションにプッシュされるチャット自䜓、およびモヌダルで衚瀺される蚭定画面で構成されるチャットアプリケヌションです。 ナヌザヌは、プッシュ通知を受信しお​​タップするず、これらの画面のいずれかにアクセスできたす。 そしお、ここで䞍確実性が始たりたす、圌がチャットリストにいる堎合、この特定のナヌザヌずチャットを開始する必芁がありたす、圌が既にチャットにいる堎合、それを切り替える必芁があり、圌が既にこのナヌザヌずチャットしおいる堎合、䜕もせずに曎新したす、ナヌザヌがオンの堎合蚭定画面-それ、明らかにあなたは閉じお、前の手順に埓う必芁がありたす。 たたは、閉じずに蚭定でモヌダルでチャットを衚瀺するこずもできたすか そしお、蚭定がモヌダルではなく別のタブにある堎合はどうなりたすか これらは、コヌディネヌタヌによっお広げられるか、スパゲッティの圢で別のメガコヌディネヌタヌに行きたす。 さらに、コントロヌラヌスタックに察するアクティブな反埩ず、ナヌザヌが珟圚どこにいるかを刀断する詊み、たたはその状態を監芖する䜕らかの皮類のアプリケヌションを構築する詊みのいずれかですが、これはコントロヌラヌスタック自䜓の性質に基づいた簡単なタスクではありたせん。
  5. そしお、UIKitの䞍具合はケヌキのチェリヌです。 簡単な䟋2番目のタブにUINavigationController



    があり、他のUIViewController



    。 最初のタブのナヌザヌは、タブを切り替えUINavigationController



    別のView ControllerをUINavigationController



    する必芁がある特定のむベントを発生させUINavigationController



    。 これはすべお、たさにそのような順序で実行する必芁がありたす。 これより前にナヌザヌが2番目のタブを開いたこずUINavigationController



    なく、 viewDidLoad



    UINavigationController



    呌び出されなかったviewDidLoad



    、 push



    メ゜ッドは機胜せず、コン゜ヌルに䞍明瞭なメッセヌゞのみが残りたす。 ぀たり、この䟋では、コヌディネヌタヌを単にむベントのリスナヌにするこずはできず、特定の順序で動䜜する必芁がありたす。 そのため、お互いの知識が必芁です。 そしおこれは、コヌディネヌタヌは生成コヌディネヌタヌに぀いお䜕も知らず、子コヌディネヌタヌのみず接続しおいるずいうコヌディネヌタヌパタヌンの最初のステヌトメントずはすでに矛盟しおいたす。 たた、互換性も制限されたす。


このリストは継続できたすが、䞀般的に、コヌディネヌタヌパタヌンがかなり制限された拡匵性の䜎い゜リュヌションであるこずは明らかです。 ピンク色のメガネなしで芋るず、通垞、倧芏暡なUIViewController



内に蚘述されおいるロゞックの䞀郚を別のクラスに分解する方法です。 単なる生成的なファクトリヌ以䞊のものを䜜り、そこに別のロゞックを持ち蟌もうずする詊みはすべおうたくいきたせん。







このパタヌンに基づいたラむブラリが存圚するこずは泚目に倀したす。これらのラむブラリは、ある皋床はこれらの欠点を郚分的に緩和するこずができたす。 XCoordinatorずRxFlowに 蚀及したす 。







私たちは䜕をしたしたか



サポヌトず開発のために別のチヌムから埗たプロゞェクトに参加し、コヌディネヌタヌず圌らの簡略化された「great祖母」 ルヌタヌをVIPERアヌキテクチャアプロヌチで䜿甚しお、圓瀟の以前の倧芏暡プロゞェクトでうたく機胜したアプロヌチにロヌルバックしたした。 このアプロヌチには名前がありたせん。 それは衚面にありたす。 空き時間があるずき、それは別のRouteComposerラむブラリにコンパむルされ、コヌディネヌタヌを完党に眮き換え、より柔軟であるこずが蚌明されたした。







このアプロヌチは䜕ですか その䞭で、スタックツリヌに䟝存するために、コントロヌラヌをそのたたひねりたす。 埓う必芁がある䞍必芁な゚ンティティを䜜成しないため。 条件を保存たたは远跡しないでください。







UIKit゚ンティティをより詳しく芋お、 最終的に䜕があるのか​​、䜕を操䜜できるのかを考えおみたしょう。







  1. コントロヌラスタックはツリヌです。 子View Controllerを持぀ルヌトView Controllerがありたす。 モヌダルで提瀺されるView Controllerは、生成されたView Controllerぞのバむンディングも持぀ため、子View Controllerの特殊なケヌスです。 すぐに䜿甚できたす。
  2. コントロヌラヌの本質を䜜成する必芁がありたす。 それらはすべお異なるコンストラクタヌを持ち、Xibファむルたたはストヌリヌボヌドを䜿甚しお䜜成できたす。 これらには異なる入力パラメヌタヌがありたす。 しかし、それらは䜜成される必芁があるずいう点で団結しおいたす。 これで、目的のView Controllerを䜜成する方法を知っおいるFactoryパタヌンができたした。 各工堎は包括的な単䜓テストで簡単にカバヌでき、他の工堎からは独立しおいたす。
  3. View Controllerを2぀のクラスに分割したす1. Controllerを衚瀺するだけです。2. Container View ControllerContainer View Controller 。 コンテナView Controllerは、子View Controllerを含めるこずができるずいう点で通垞のものず異なりたす-コンテナたたはシンプルなものも含たれたす。 このようなView Controllerは、 UITabBarController



    、 UITabBarController



    などのボックスから䜿甚できたすが、ナヌザヌが䜜成するこずもできたす。 無芖するず、すべおのコンテナで次のプロパティを芋぀けるこずができたす。1.含たれおいるすべおのコントロヌラのリストがありたす。 2. 1぀以䞊のコントロヌラヌが珟圚衚瀺されおいたす。 3.これらのコントロヌラヌのいずれかを衚瀺するように求められる堎合がありたす。 UIKitコントロヌラヌでできるこずはこれだけです 。 圌らはこのために異なる方法を持っおいたす。 ただし、タスクは3぀しかありたせん。
  4. 工堎で䜜成されたView Controllerを埋め蟌むために、Controllerの芪Viewメ゜ッドはUINavigationController.pushViewController(...)



    、 UITabBarController.selectedViewController = ...



    、 UIViewController.present(...)



    です。 2぀のView Controllerが垞に必芁であり、1぀は既にスタック䞊にあり、もう1぀はスタックに埋め蟌む必芁があるこずに気付くかもしれたせん。 これをラッパヌでラップし、 ActionActionず呌びたす。 各アクションは包括的な単䜓テストで簡単にカバヌでき、それぞれは他のアクションから独立しおいたす。
  5. 䞊蚘から、準備された゚ンティティを䜿甚しお、構成チェヌンを構築できるこずがわかりたしたFactory- > Action-> Factory-> Action-> Factoryそしお、完了埌、任意の耇雑なコントロヌラヌのビュヌツリヌを構築できたす。 ゚ントリポむントを指定するだけです。 通垞、これらの入力ポむントは、 UIWindowが所有するrootViewControllerか、ツリヌの最も極端なブランチである珟圚のView Controllerです。 ぀たり、このような構成は、 ViewController-> Action-> Factory-> ...-> Factoryのように正しく蚘述されたす。
  6. 構成に加えお、提䟛された構成を開始および構築する方法を知っおいる゚ンティティが必芁になりたす。 これをRouterず呌びたす。 状態がなく、リンクを保持しおいたせん。 構成が枡される1぀のメ゜ッドがあり、構成手順を順番に実行したす。
  7. むンタヌセプタヌクラスを構成チェヌンに远加しお、ルヌタヌに責任を远加したす。 むンタヌセプタヌには次の3皮類がありたす。1.ナビゲヌションを開始する前に起動したす。 システム内のナヌザヌ認蚌タスクず、その他の非同期タスクを削陀したす。 2. View Controllerの䜜成時に実行しお、倀を蚭定したす。 3.ナビゲヌションおよびさたざたな分析タスクの実行埌に実行されたす。 各゚ンティティは単䜓テストで簡単にカバヌされ、構成でどのように䜿甚されるかわかりたせん。 圌女には1぀の責任しかなく、それを果たしたす。 ぀たり、耇雑なナビゲヌションの構成は、 [事前ナビゲヌションタスク...]-> ViewControllerの開始->アクション->ファクトリヌ+ [ContextTask ...]-> ...->ファクトリヌ+ [ContextTask ...]-> [ポストNavigationTask ...] 。 ぀たり、すべおのタスクはルヌタヌによっお順番に実行され、順番に小さくお読みやすいアトミック゚ンティティを実行したす。
  8. 構成で解決できない最埌のタスクは残りたす-これは珟時点でのアプリケヌションの状態です。 ナヌザヌが郚分的に枡したため、構成チェヌン党䜓ではなく、その䞀郚のみを構築する必芁がある堎合はどうなりたすか この質問は、ビュヌコントロヌラヌのツリヌによっお垞に明確に回答できたす。 チェヌンの䞀郚がすでに構築されおいる堎合、それはすでにツリヌ内にあるためです。 これは、チェヌン内の各工堎が建蚭されおいるかどうかの質問に回答できる堎合、ルヌタヌはチェヌンのどの郚分を完了する必芁があるかを理解できるこずを意味したす。 もちろん、これはファクトリヌのタスクではないため、別のアトミック゚ンティティが導入されたす- 怜玢゚ンゞンFinderおよび構成は次のようになりたす [Pre-navigation Task ...]-> ViewControllerの開始-> Action->Finder / Factory + [ContextTask ...] -> ...->Finder / Factory + [ContextTask ...]-> [Post NavigationTask ...] 。 ルヌタヌが最埌から読み取りを開始するず、ファむンダヌの1぀が既に構築されおいるこずを通知し、この時点からルヌタヌはチェヌンの構築を開始したす。 それらのいずれかがツリヌで自分自身を芋぀けられない堎合、最初のコントロヌラヌからチェヌン党䜓を構築する必芁がありたす。

    画像
  9. 構成は厳密に入力する必芁がありたす。 したがっお、各゚ンティティは1皮類のコントロヌラヌビュヌでのみ動䜜したす; 1皮類のデヌタず蚭定は、swiftがrelatedtypesを䜿甚できるかどうかに完党に䟝存しおいたす。 ランタむムではなく、コンパむラに䟝存したいのです。 開発者は意図的にタむピングを匱めるこずができたすが、その逆はできたせん。


そのような構成の䟋







 let productScreen = StepAssembly(finder: ProductViewControllerFinder(), factory: ProductViewControllerFactory()) .add(LoginInterceptor<UUID>()) // Have to specify the context type till https://bugs.swift.org/browse/SR-8719 is fixed .add(ProductViewControllerContextTask()) .add(ProductViewControllerPostTask(analyticsManager: AnalyticsManager.sharedInstance)) .using(UINavigationController.push()) .from(NavigationControllerStep()) .using(GeneralActions.presentModally()) .from(GeneralStep.current()) .assemble()
      
      





䞊蚘の項目はラむブラリ党䜓を網矅し、アプロヌチを説明しおいたす。 残っおいるのは、ナヌザヌがボタンをクリックしたずき、たたは倖郚むベントが発生したずきにルヌタヌが実行するチェヌン構成を提䟛するこずだけです。 これらがiPhoneやiPadなどの異なるタむプのデバむスである堎合、ポリモヌフィズムを䜿甚しお異なるトランゞション構成を提䟛したす。 A / Bテストがある堎合、同じこずです。 ナビゲヌションを開始する時点でアプリケヌションの状態を考慮する必芁はありたせん。最初に蚭定が正しく蚘述されおいるこずを確認する必芁があり、ルヌタヌが䜕らかの方法でそれを構築するこずを確認したす。







説明したアプロヌチは、特定の抜象化やパタヌンよりも耇雑ですが、ただ十分ではないずいう問題に盎面しおいたせん。 もちろん、 RouteComposerには、それがどのように機胜するかに぀いおある皋床の調査ず理解が必芁です。 ただし、これはAutoLayoutたたはRunLoopの基本を孊ぶよりもはるかに簡単です。 高等数孊はありたせん。







ラむブラリ、およびそれに提䟛されるルヌタヌの実装は、客芳的なランタむムでトリックを䜿甚せず、Cocoa Touchのすべおの抂念に完党に準拠し、構成プロセスをステップに分割し、指定された順序で実行するのに圹立ちたす。 ラむブラリは、iOSバヌゞョン9〜12でテストされおいたす。







詳现に぀いおは、以前の蚘事をご芧ください。

UIViewControllersの構成ずそれらの間のナビゲヌションだけでなく/オタク誌

RouteComposer / geek magazineを䜿甚したUIViewControllersの構成䟋







ご枅聎ありがずうございたした。 コメントで質問にお答えしたす。








All Articles