SwiftUI - วิธีการหลีกเลี่ยงการนำฮาร์ดโค้ดลงในมุมมอง


33

ฉันพยายามทำสถาปัตยกรรมสำหรับแอป SwiftUI ที่มีขนาดใหญ่ขึ้นและพร้อมใช้งาน ฉันใช้เวลาทั้งหมดในปัญหาเดียวกันซึ่งชี้ไปที่ข้อบกพร่องการออกแบบที่สำคัญใน SwiftUI

ยังไม่มีใครสามารถให้คำตอบที่พร้อมสำหรับการทำงานอย่างเต็มที่กับฉันได้

วิธีการใช้ซ้ำ Views ในSwiftUIที่มีการนำทาง?

เนื่องจากความSwiftUI NavigationLinkผูกพันกับมุมมองอย่างมากจึงไม่สามารถทำได้ในลักษณะที่ปรับขนาดในแอปที่ใหญ่กว่า NavigationLinkในแอปตัวอย่างขนาดเล็กเหล่านั้นทำงานได้ใช่ - แต่ไม่ใช่ทันทีที่คุณต้องการใช้ Views จำนวนมากในแอปเดียว และอาจนำมาใช้ซ้ำเกินขอบเขตของโมดูล (เช่น: ใช้การดูซ้ำใน iOS, WatchOS, ฯลฯ ... )

ปัญหาการออกแบบ: NavigationLinks ถูกฮาร์ดโค้ดลงในมุมมอง

NavigationLink(destination: MyCustomView(item: item))

แต่ถ้ามุมมองที่มีสิ่งนี้NavigationLinkควรจะนำมาใช้ซ้ำฉันไม่สามารถ hardcodeปลายทางได้ จะต้องมีกลไกที่ให้ปลายทาง ฉันถามที่นี่และได้คำตอบที่ดี แต่ก็ยังไม่ได้คำตอบทั้งหมด:

SwiftUI MVVM ผู้ประสานงาน / เราเตอร์ / NavigationLink

แนวคิดคือการฉีดลิงค์ปลายทางลงในมุมมองที่นำมาใช้ซ้ำได้ โดยทั่วไปแล้วความคิดนี้ใช้งานได้ แต่น่าเสียดายที่นี่ไม่ได้ปรับใช้กับแอปการผลิตจริง ทันทีที่ฉันมีหน้าจอที่ใช้ซ้ำได้หลายหน้าจอฉันพบปัญหาตรรกะที่มุมมองที่นำมาใช้ซ้ำได้หนึ่งรายการ ( ViewA) ต้องมีมุมมองปลายทางที่กำหนดไว้ล่วงหน้า ( ViewB) แต่ถ้าViewBต้องการปลายทางการดูที่กำหนดไว้ล่วงหน้าด้วยViewCล่ะ ฉันจะต้องสร้างViewBแล้วในลักษณะที่ผู้ViewCถูกฉีดแล้วในViewBก่อนที่ผมจะฉีดเข้าไปในViewB ViewAและอื่น ๆ .... แต่เนื่องจากข้อมูลที่ในเวลานั้นไม่สามารถใช้ได้การสร้างทั้งหมดล้มเหลว

อีกความคิดหนึ่งที่ฉันได้ถูกใช้เป็นกลไกในการพึ่งพาการฉีดไปยังจุดหมายปลายทางสำหรับฉีดEnvironment NavigationLinkแต่ฉันคิดว่านี่น่าจะถือว่าเป็นแฮ็กมากกว่าหรือน้อยกว่าและไม่ใช่โซลูชันที่ปรับขนาดได้สำหรับแอพขนาดใหญ่ เราจะใช้สิ่งแวดล้อมเป็นพื้นฐานสำหรับทุกสิ่ง แต่เนื่องจากสภาพแวดล้อมสามารถใช้งานได้เฉพาะภายใน View (ไม่ใช่แยกจากผู้ประสานงานหรือ ViewModels) สิ่งนี้จะสร้างสิ่งแปลกใหม่ในความคิดของฉัน

เช่นตรรกะทางธุรกิจ (เช่นดูรหัสรุ่น) และมุมมองต้องแยกออกจากกันการนำทางและมุมมองต้องแยกออกจากกัน (เช่นรูปแบบผู้ประสานงาน) UIKitเป็นไปได้เพราะเราเข้าถึงUIViewControllerและUINavigationControllerอยู่ข้างหลังมุมมอง UIKit'sMVC มีปัญหาแล้วว่ามันบดบังแนวคิดมากมายจนกลายเป็นชื่อ "Massive-View-Controller" ที่สนุกสนานแทนที่จะเป็น "Model-View-Controller" ตอนนี้ปัญหาที่คล้ายกันยังคงดำเนินต่อไปSwiftUIแต่ยิ่งแย่ลงในความคิดของฉัน การนำทางและมุมมองนั้นเชื่อมโยงกันอย่างมากและไม่สามารถแยกได้ ดังนั้นจึงเป็นไปไม่ได้ที่จะใช้มุมมองที่นำมาใช้ซ้ำได้หากมีการนำทาง เป็นไปได้ที่จะแก้ปัญหานี้UIKitแต่ตอนนี้ฉันไม่เห็นวิธีแก้ปัญหาที่มีสติSwiftUI. น่าเสียดายที่ Apple ไม่ได้ให้คำอธิบายวิธีแก้ปัญหาด้านสถาปัตยกรรมให้เรา เรามีแอพตัวอย่างเล็ก ๆ น้อย ๆ

ฉันชอบที่จะพิสูจน์ว่าผิด โปรดแสดงรูปแบบการออกแบบแอพที่สะอาดซึ่งช่วยแก้ปัญหานี้สำหรับแอพที่พร้อมใช้งานขนาดใหญ่

ขอบคุณล่วงหน้า.


อัปเดต: เงินรางวัลนี้จะจบลงในอีกไม่กี่นาทีและน่าเสียดายที่ไม่มีใครสามารถยกตัวอย่างการทำงานได้ แต่ฉันจะเริ่มต้นเงินรางวัลใหม่เพื่อแก้ปัญหานี้หากฉันไม่พบโซลูชันอื่นและเชื่อมโยงที่นี่ ขอบคุณทุกคนสำหรับผลงานยอดเยี่ยมของพวกเขา!


1
ตกลงกัน! ฉันสร้างคำขอสำหรับสิ่งนี้ใน” ผู้ช่วยข้อเสนอแนะ” เมื่อหลายเดือนที่แล้วยังไม่มีการตอบกลับ: gist.github.com/Sajjon/b7edb4cc11bcb6462f4e28dc170be245
Sajjon

@Sajjon ขอบคุณ! ฉันตั้งใจจะเขียน Apple ด้วยลองดูว่าฉันจะได้รับคำตอบไหม
Darko

1
เขียนจดหมายถึง Apple เกี่ยวกับเรื่องนี้ มาดูกันว่าเราได้รับ repsonse ไหม
Darko

1
ดี! มันจะเป็นของขวัญที่ดีที่สุดในช่วง WWDC!
Sajjon

คำตอบ:


10

การปิดเป็นสิ่งที่คุณต้องการ!

struct ItemsView<Destination: View>: View {
    let items: [Item]
    let buildDestination: (Item) -> Destination

    var body: some View {
        NavigationView {
            List(items) { item in
                NavigationLink(destination: self.buildDestination(item)) {
                    Text(item.id.uuidString)
                }
            }
        }
    }
}

ฉันเขียนโพสต์เกี่ยวกับการแทนที่รูปแบบผู้แทนใน SwiftUI ด้วยการปิด https://swiftwithmajid.com/2019/11/06/the-power-of-closures-in-swiftui/


การปิดเป็นความคิดที่ดีขอบคุณ! แต่จะเป็นอย่างไรในมุมมองที่ลึกล้ำ ลองนึกภาพฉันมี NavigationView ซึ่งจะลึกลงไป 10 ระดับ, รายละเอียด, รายละเอียด, รายละเอียด, ฯลฯ ...
Darko

ฉันขอเชิญคุณแสดงตัวอย่างรหัสง่ายๆที่มีความลึกเพียงสามระดับเท่านั้น
Darko

7

ความคิดของฉันน่าจะเป็นส่วนผสมCoordinatorและDelegateลวดลาย ขั้นแรกสร้างCoordinatorคลาส:


struct Coordinator {
    let window: UIWindow

      func start() {
        var view = ContentView()
        window.rootViewController = UIHostingController(rootView: view)
        window.makeKeyAndVisible()
    }
}

ปรับตัวSceneDelegateเพื่อใช้Coordinator:

  func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        if let windowScene = scene as? UIWindowScene {
            let window = UIWindow(windowScene: windowScene)
            let coordinator = Coordinator(window: window)
            coordinator.start()
        }
    }

ภายในContentViewมีสิ่งนี้:


struct ContentView: View {
    var delegate: ContentViewDelegate?

    var body: some View {
        NavigationView {
            List {
                NavigationLink(destination: delegate!.didSelect(Item())) {
                    Text("Destination1")
                }
            }
        }
    }
}

เราสามารถกำหนดContenViewDelegateโปรโตคอลดังนี้:

protocol ContentViewDelegate {
    func didSelect(_ item: Item) -> AnyView
}

ที่ไหนItemเป็นเพียงโครงสร้างที่สามารถระบุตัวตนได้อาจเป็นอย่างอื่น (เช่น id ขององค์ประกอบบางอย่างเช่นTableViewใน UIKit)

ขั้นตอนต่อไปคือนำโปรโตคอลนี้ไปใช้Coordinatorและเพียงผ่านมุมมองที่คุณต้องการนำเสนอ:

extension Coordinator: ContentViewDelegate {
    func didSelect(_ item: Item) -> AnyView {
        AnyView(Text("Returned Destination1"))
    }
}

สิ่งนี้ทำงานได้ดีมากในแอพของฉัน ฉันหวังว่ามันจะช่วย


ขอบคุณสำหรับตัวอย่างโค้ด ผมอยากจะเชิญชวนให้คุณเปลี่ยนเพื่อสิ่งที่ต้องการText("Returned Destination1") MyCustomView(item: ItemType, destinationView: View)เพื่อที่จะMyCustomViewต้องการข้อมูลและฉีดปลายทาง คุณจะแก้ปัญหาอย่างไร
Darko

คุณพบปัญหารังที่ฉันอธิบายในโพสต์ของฉัน โปรดแก้ไขฉันหากฉันผิด โดยทั่วไปวิธีการนี้จะใช้งานได้หากคุณมีมุมมองที่นำมาใช้ซ้ำได้และมุมมองที่นำมาใช้ซ้ำนั้นไม่ได้มีมุมมองที่นำมาใช้ใหม่ได้อีกด้วย NavigationLink ซึ่งค่อนข้างใช้งานได้ง่าย แต่ไม่ปรับขนาดเป็นแอพขนาดใหญ่ (ที่เกือบทุกมุมมองสามารถนำมาใช้ซ้ำได้)
Darko

สิ่งนี้ขึ้นอยู่กับว่าคุณจะจัดการแอพพลิเคชั่นของคุณอย่างไร หากคุณมีการพึ่งพาในที่เดียวคุณควร IMO (หรือที่รู้จักกันในชื่อ root ของการประพันธ์) คุณไม่ควรพบปัญหานี้
Nikola Matijevic

สิ่งที่ได้ผลสำหรับฉันคือการกำหนดการอ้างอิงทั้งหมดของคุณสำหรับมุมมองเป็นโปรโตคอล เพิ่มความสอดคล้องกับโปรโตคอลในรูทองค์ประกอบ ผ่านการอ้างอิงถึงผู้ประสานงาน ฉีดพวกเขาจากผู้ประสานงาน ในทางทฤษฎีแล้วคุณควรจะจบลงที่มีมากกว่าสามพารามิเตอร์ถ้าทำอย่างถูกต้องไม่เกินและdependencies destination
Nikola Matijevic

1
ฉันชอบที่จะเห็นตัวอย่างที่เป็นรูปธรรม อย่างที่ฉันพูดไปแล้วมาเริ่มText("Returned Destination1")กันเลย MyCustomView(item: ItemType, destinationView: View)เกิดอะไรขึ้นถ้าความต้องการนี้จะเป็น คุณจะฉีดอะไรที่นั่น ฉันเข้าใจการฉีดพึ่งพาการเชื่อมต่อแบบหลวมผ่านโปรโตคอลและการพึ่งพาที่ใช้ร่วมกันกับผู้ประสานงาน ทั้งหมดนี้ไม่ใช่ปัญหา แต่เป็นรังที่ต้องการ ขอบคุณ
Darko

2

สิ่งที่เกิดขึ้นกับฉันคือเมื่อคุณพูดว่า:

แต่ถ้าหาก ViewB ยังต้องการ ViewC ปลายทางปลายทางที่กำหนดไว้ล่วงหน้าด้วยล่ะ ฉันจะต้องสร้าง ViewB ในลักษณะที่ ViewC ถูกฉีดเข้าไปใน ViewB แล้วก่อนที่จะฉีด ViewB เข้าไปใน ViewA และอื่น ๆ .... แต่เนื่องจากข้อมูลที่ในเวลานั้นไม่สามารถใช้ได้การสร้างทั้งหมดล้มเหลว

มันไม่เป็นความจริงเลย แทนที่จะจัดหามุมมองคุณสามารถออกแบบส่วนประกอบที่สามารถใช้งานได้อีกครั้งเพื่อให้คุณจัดหาการปิดซึ่งจัดหามุมมองตามความต้องการ

ด้วยวิธีการปิดซึ่งสร้าง ViewB ตามต้องการสามารถจัดหาได้ด้วยการปิดซึ่งสร้าง ViewC ตามต้องการ แต่การสร้างมุมมองที่แท้จริงสามารถเกิดขึ้นได้ตลอดเวลาเมื่อมีข้อมูลเชิงบริบทที่คุณต้องการ


แต่การสร้าง "ต้นไม้ปิด" นั้นแตกต่างจากมุมมองจริงอย่างไร รายการที่ให้ปัญหาจะได้รับการแก้ไข แต่ไม่ใช่การซ้อนที่จำเป็น ฉันสร้างการปิดที่สร้างมุมมอง - ตกลง แต่ในการปิดนั้นฉันจะต้องสร้างการปิดครั้งต่อไป และในที่สุดคนต่อไป ฯลฯ ... แต่บางทีฉันอาจเข้าใจผิด ตัวอย่างโค้ดจะช่วยได้ ขอบคุณ
Darko

2

นี่คือตัวอย่างที่สนุกสนานของการเจาะลึกและการเปลี่ยนแปลงข้อมูลของคุณสำหรับมุมมองรายละเอียดถัดไปโดยทางโปรแกรม

import SwiftUI

struct ContentView: View {
    @EnvironmentObject var navigationManager: NavigationManager

    var body: some View {
        NavigationView {
            DynamicView(viewModel: ViewModel(message: "Get Information", type: .information))
        }
    }
}

struct DynamicView: View {
    @EnvironmentObject var navigationManager: NavigationManager

    let viewModel: ViewModel

    var body: some View {
        VStack {
            if viewModel.type == .information {
                InformationView(viewModel: viewModel)
            }
            if viewModel.type == .person {
                PersonView(viewModel: viewModel)
            }
            if viewModel.type == .productDisplay {
                ProductView(viewModel: viewModel)
            }
            if viewModel.type == .chart {
                ChartView(viewModel: viewModel)
            }
            // If you want the DynamicView to be able to be other views, add to the type enum and then add a new if statement!
            // Your Dynamic view can become "any view" based on the viewModel
            // If you want to be able to navigate to a new chart UI component, make the chart view
        }
    }
}

struct InformationView: View {
    @EnvironmentObject var navigationManager: NavigationManager
    let viewModel: ViewModel

    // Customize your  view based on more properties you add to the viewModel
    var body: some View {
        VStack {
            VStack {
                Text(viewModel.message)
                .foregroundColor(.white)
            }
            .frame(width: 300, height: 300)
            .background(Color.blue)


            NavigationLink(destination: navigationManager.destination(forModel: viewModel)) {
                Text("Navigate")
            }
        }
    }
}

struct PersonView: View {
    @EnvironmentObject var navigationManager: NavigationManager
    let viewModel: ViewModel

    // Customize your  view based on more properties you add to the viewModel
    var body: some View {
        VStack {
            VStack {
                Text(viewModel.message)
                .foregroundColor(.white)
            }
            .frame(width: 300, height: 300)
            .background(Color.red)
            NavigationLink(destination: navigationManager.destination(forModel: viewModel)) {
                Text("Navigate")
            }
        }
    }
}

struct ProductView: View {
    @EnvironmentObject var navigationManager: NavigationManager
    let viewModel: ViewModel

    // Customize your  view based on more properties you add to the viewModel
    var body: some View {
        VStack {
            VStack {
                Text(viewModel.message)
                    .foregroundColor(.white)
            }
            .frame(width: 300, height: 300)
            .background(Color.green)
            NavigationLink(destination: navigationManager.destination(forModel: viewModel)) {
                Text("Navigate")
            }
        }
    }
}

struct ChartView: View {
    @EnvironmentObject var navigationManager: NavigationManager
    let viewModel: ViewModel

    var body: some View {
        VStack {
            VStack {
                Text(viewModel.message)
                    .foregroundColor(.white)
            }
            .frame(width: 300, height: 300)
            .background(Color.green)
            NavigationLink(destination: navigationManager.destination(forModel: viewModel)) {
                Text("Navigate")
            }
        }
    }
}

struct ViewModel {
    let message: String
    let type: DetailScreenType
}

enum DetailScreenType: String {
    case information
    case productDisplay
    case person
    case chart
}

class NavigationManager: ObservableObject {
    func destination(forModel viewModel: ViewModel) -> DynamicView {
        DynamicView(viewModel: generateViewModel(context: viewModel))
    }

    // This is where you generate your next viewModel dynamically.
    // replace the switch statement logic inside with whatever logic you need.
    // DYNAMICALLY MAKE THE VIEWMODEL AND YOU DYNAMICALLY MAKE THE VIEW
    // You could even lead to a view with no navigation link in it, so that would be a dead end, if you wanted it.
    // In my case my "context" is the previous viewMode, by you could make it something else.
    func generateViewModel(context: ViewModel) -> ViewModel {
        switch context.type {
        case .information:
            return ViewModel(message: "Serial Number 123", type: .productDisplay)
        case .productDisplay:
            return ViewModel(message: "Susan", type: .person)
        case .person:
            return ViewModel(message: "Get Information", type: .chart)
        case .chart:
            return ViewModel(message: "Chart goes here. If you don't want the navigation link on this page, you can remove it! Or do whatever you want! It's all dynamic. The point is, the DynamicView can be as dynamic as your model makes it.", type: .information)
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
        .environmentObject(NavigationManager())
    }
}

-> บางมุมมองบังคับให้คุณส่งคืนมุมมองประเภทเดียวเสมอ
Darko

การฉีดพึ่งพากับ EnvironmentObject แก้ปัญหาส่วนหนึ่ง แต่: สิ่งที่สำคัญและสำคัญในกรอบ UI ควรซับซ้อนหรือไม่ ... ?
Darko

ฉันหมายถึง - ถ้าการฉีดพึ่งพาเป็นทางออกเดียวสำหรับเรื่องนี้ฉันก็จะยอมรับอย่างไม่เต็มใจ แต่นี่จะได้กลิ่นจริงๆ ...
ดาร์ก

1
ฉันไม่เห็นว่าทำไมคุณไม่สามารถใช้สิ่งนี้กับตัวอย่างกรอบงานของคุณ หากคุณกำลังพูดถึงเฟรมเวิร์กที่มีมุมมองที่ไม่รู้จักฉันจะจินตนาการว่ามันสามารถคืนมุมมองบางอย่างได้ ฉันจะไม่แปลกใจถ้า AnyView ภายใน NavigationLink นั้นไม่ใช่เรื่องใหญ่ที่น่าสนใจเพราะมุมมองพาเรนต์ถูกแยกออกจากเลย์เอาท์ที่แท้จริงของเด็ก ฉันไม่มีผู้เชี่ยวชาญ แต่ต้องผ่านการทดสอบ แทนที่จะขอรหัสตัวอย่างจากทุกคนที่พวกเขาไม่สามารถเข้าใจข้อกำหนดของคุณได้อย่างสมบูรณ์ทำไมคุณถึงไม่เขียนตัวอย่าง UIKit และขอคำแปล?
jasongregori

1
การออกแบบนี้เป็นพื้นฐานของแอพ (UIKit) ที่ฉันใช้ในงาน โมเดลถูกสร้างขึ้นซึ่งลิงก์ไปยังโมเดลอื่น ระบบกลางจะกำหนดว่า vc ควรโหลดอะไรสำหรับโมเดลนั้นจากนั้น parent vc จะพุชลงบนสแต็ก
jasongregori

2

ฉันกำลังเขียนชุดบทความบล็อกในการสร้างวิธีการประสานงาน MVP + ใน SwiftUI ซึ่งอาจมีประโยชน์:

https://lascorbe.com/posts/2020-04-27-MVPCoordinators-SwiftUI-part1/

โครงการเต็มรูปแบบสามารถใช้ได้ใน Github: https://github.com/Lascorbe/SwiftUI-MVP-Coordinator

ฉันพยายามทำราวกับว่ามันเป็นแอพตัวใหญ่ในแง่ของความยืดหยุ่น ฉันคิดว่าฉันได้แยกแยะปัญหาเกี่ยวกับการนำทางแล้ว แต่ฉันยังต้องดูวิธีการเชื่อมโยงอย่างลึกซึ้งซึ่งเป็นสิ่งที่ฉันกำลังทำงานอยู่ ฉันหวังว่ามันจะช่วย


ว้าวเยี่ยมมากขอบคุณ! คุณทำได้ค่อนข้างดีในการใช้งานผู้ประสานงานใน SwiftUI แนวคิดในการสร้างNavigationViewรูทวิวนั้นยอดเยี่ยม นี่คือการใช้งานผู้ประสานงาน SwiftUI ที่ทันสมัยที่สุดเท่าที่ฉันเคยเห็นมา
Darko

ฉันต้องการมอบรางวัลให้คุณเพียงเพราะโซลูชั่นผู้ประสานงานของคุณยอดเยี่ยมมาก ปัญหาเดียวที่ฉันมี - มันไม่ได้แก้ปัญหาที่ฉันอธิบาย มันแยกได้NavigationLinkแต่มันทำได้โดยการแนะนำการพึ่งพาคู่ใหม่ ในตัวอย่างของคุณไม่ขึ้นอยู่กับMasterView NavigationButtonลองนึกภาพการวางMasterViewใน Swift Package - มันจะไม่รวบรวมอีกต่อไปเพราะNavigationButtonไม่ทราบชนิด นอกจากนี้ฉันไม่เห็นว่าปัญหาของการใช้ซ้ำซ้อนซ้อนViewsจะแก้ไขได้อย่างไร
Darko

ฉันยินดีที่จะผิดและหากฉันโปรดอธิบายให้ฉัน แม้ว่าความโปรดปรานจะหมดลงในไม่กี่นาทีฉันหวังว่าฉันจะให้รางวัลกับคุณในบางประเด็น (ไม่เคยได้รับรางวัลมาก่อน แต่ฉันคิดว่าฉันสามารถสร้างคำถามติดตามด้วยคำถามใหม่ได้หรือไม่)
Darko

1

นี่เป็นคำตอบที่ไม่สุดยอดเลยดังนั้นอาจเป็นเรื่องไร้สาระ แต่ฉันถูกล่อลวงให้ใช้วิธีไฮบริด

ใช้สภาพแวดล้อมในการผ่านวัตถุผู้ประสานงานเดียว - ให้เรียกมันว่า NavigationCoordinator

ให้มุมมองที่ใช้งานได้ของคุณอีกครั้งซึ่งเป็นตัวระบุที่ตั้งค่าแบบไดนามิก ตัวระบุนี้ให้ข้อมูลความหมายที่สอดคล้องกับกรณีการใช้งานจริงและลำดับชั้นการนำทางของไคลเอ็นต์

ให้มุมมองที่ใช้งานซ้ำได้สอบถาม NavigationCoordinator สำหรับมุมมองปลายทางผ่านตัวระบุและตัวระบุประเภทมุมมองที่พวกเขากำลังนำทางไป

ซึ่งจะทำให้ NavigationCoordinator เป็นจุดฉีดเดียวและเป็นวัตถุที่ไม่ใช่มุมมองที่สามารถเข้าถึงได้นอกลำดับชั้นมุมมอง

ในระหว่างการตั้งค่าคุณสามารถลงทะเบียนคลาสมุมมองที่ถูกต้องเพื่อให้มันกลับมาโดยใช้การจับคู่บางอย่างกับตัวระบุที่ถูกส่งไปที่รันไทม์ บางสิ่งที่ง่ายพอ ๆ กับการจับคู่กับตัวระบุปลายทางอาจใช้ได้ในบางกรณี หรือจับคู่กับโฮสต์และตัวระบุปลายทาง

ในกรณีที่ซับซ้อนมากขึ้นคุณสามารถเขียนคอนโทรลเลอร์แบบกำหนดเองซึ่งคำนึงถึงข้อมูลเฉพาะแอปอื่น ๆ

เนื่องจากมันถูกฉีดผ่านสภาพแวดล้อมมุมมองใด ๆ สามารถแทนที่ NavigationCoordinator ที่เป็นค่าเริ่มต้น ณ จุดใดก็ได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.