Upgrade to Pro — share decks privately, control downloads, hide ads and more …

A story about me trying to make a router that m...

Elvis Shi
September 27, 2021

A story about me trying to make a router that manages when, how and which view to transit in a SwiftUI app

Elvis Shi

September 27, 2021
Tweet

More Decks by Elvis Shi

Other Decks in Programming

Transcript

  1. 4XJGU6*Ͱը໘ભҠͷঢ়ଶͱํ๏Λ 
 ؅ཧ͢ΔϧʔλΛ࡞ͬͨ࿩ f o r  : 6 .

    & . *  T X J G U      ʙ ཪ J 0 4 % $ ʙ ͓ͨͩ͠קΊ͸͠ͳ͍
  2. } var employedBy = "YUMEMI Inc." var job = "iOS

    Tech Lead" var favoriteLanguage = "Swift" var twitter = "@lovee" var qiita = "lovee" var github = "el-hoshino" var additionalInfo = """ ϒϩάॻ͘·Ͱ͕ iOSDC ͩΑʁ """ final class Me: Developable, Talkable {
  3. Ұൠతͳ4XJGU6*ͷը໘ભҠ ʢ.PEBMભҠʣ struct ParentView: View { @State var showsChild =

    false var body: some View { Text("Parent") .fullScreenCover(isPresented: $showsChild, content: { ChildView() }) } } ભҠ੍ޚ༻ 
 .PEJ fi FS ભҠઌ ભҠϑϥά
  4. Ұൠతͳ4XJGU6*ͷը໘ભҠ ʢ1VTIભҠʣ struct ParentView: View { @State var showsChild =

    false var body: some View { NavigationLink(destination: { ChildView() }, label: { Text("Show Child") }) } } ભҠઌ ભҠϑϥάʜʁ
  5. Ұൠతͳ4XJGU6*ͷը໘ભҠ ʢ1VTIભҠʣ struct ParentView: View { @State var showsChild =

    false var body: some View { NavigationLink(isActive: $showsChild, destination: { ChildView() }, label: { EmptyView() }) } } ભҠઌ ભҠϑϥά ભҠ੍ޚ͕ 
 .PEJ fi FSͰ͸ͳ͍
  6. Ұൠతͳ4XJGU6*ͷը໘ભҠ ʢ1VTIભҠʣ struct ParentView: View { @State var showsChild =

    false var body: some View { Text("Parent") .background(NavigationLink( isActive: $showsChild, destination: { ChildView() }, label: { EmptyView() } )) } } ભҠઌ ભҠϑϥά ભҠ੍ޚ༻ 
 .PEJ fi FS
  7. 3PVUFSΛ࡞Δ final class Router: ObservableObject { @Published var showsChild =

    false func navigationBinding() -> Binding<Bool> { return .init(get: { [unowned self] in return self.showsChild }, set: { [unowned self] in self.showsChild = $0 }) } @ViewBuilder func nextView() -> some View { ChildView() } } ભҠઌ ભҠϑϥά
  8. 3PVUFSΛ࢖͏ struct ParentView: View { @StateObject var router = Router()

    var body: some View { Text("Parent") .background(NavigationLink( isActive: router.navigationBinding(), destination: { router.nextView() }, label: { EmptyView() } )) } }
  9. 3PVUFSΛ࢖͏ struct ParentView: View { @StateObject var router = Router()

    var body: some View { Text("Parent") .background(NavigationLink( isActive: router.navigationBinding(), destination: { router.nextView() }, label: { EmptyView() } )) } } 7JFX͕3PVUFSΛ׬શʹ 
 ೺Ѳ͍ͯ͠ͳ͚Ε͹ͳΒͳ͍
  10. ཧ૝ protocol ParentRouterDelegate: ObservableObject { func parentDidSubmit() } struct ParentView<R:

    ParentRouterDelegate>: View { @ObservedObject var router: R var body: some View { Button { router.parentDidSubmit() } label: { Text("Submit") } } } ໘౗ͳ࡞ۀ͸্ҐϨΠϠʔͷ͸ͣͷ 
 3PVUFSʹ೚͍ͤͨʂ
  11. ભҠΛ࣮ݱ͢ΔͨΊͷ࢓૊Έ enum ViewID { //... } protocol RouterObject: ObservableObject {

    associatedtype NextView: View func pushFlag(for view: ViewID) -> Binding<Bool> func modalFlag(for view: ViewID) -> Binding<Bool> func nextView(after view: ViewID) -> NextView } struct RoutingModifier<R: RouterObject>: ViewModifier { @ObservedObject var router: R var viewID: ViewID func body(content: Content) -> some View { content .background(//... .fullScreenCover(//... } } extension View { func injectRouter<R: RouterObject>(_ router: R, as viewID: ViewID) -> some View { modifier(RoutingModifier(router: router, viewID: viewID)) } } ͜ΕͰݺͼग़͠ݩ͕ 
 Θ͔Δ
  12. 3PVUFSͷॳظ࣮૷ final class Router: ObservableObject { @Published var parentViewRoute: ViewID?

    // ... func makeParentView() -> some View { ParentView(router: self) .injectRouter(self, as: .parent) } func makeChildView() -> some View { ChildView() } } 3PVUFS͕ඞཁʹԠͯ͡ 
 7JFXʹࣗ෼ࣗ਎Λ஫ೖ͢Ε͹͍͍
  13. 3PVUFSͷભҠ࢓૊Έͷద߹ extension TestRouter: TestRouterObject { func pushFlag(for view: ViewID) ->

    Binding<Bool> { switch view { case .parent: return .init(get: { [unowned self] in self.parentViewRoute != nil }, set: { [unowned self] in assert($0 == false); self.parentViewRoute = nil }) case .child: return .constant(false) } } func modalFlag(for view: ViewID) -> Binding<Bool> { return .constant(false) } @ViewBuilder func nextView(after view: ViewID) -> some View { switch view { case .parent: if let route = parentViewRoute, route == .child { makeChildView() } case .child: EmptyView() } } } 4XJGU6*ͷ࢓্༷ɺ 
 جຊ͜͜ݺ͹ΕΔͷ͸ 
 ໭ΔભҠ࣌ͷΈͳͷͰ ͳ͍͸ͣͷભҠ͸ 
 DPOTUBOU GBMTF Ͱ 
 ฦͤ͹͍͍ ঢ়گʹԠͯ͡ 
 7JFXΛฦ͢