Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
A story about me trying to make a router that m...
Search
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
Elvis Shi
September 27, 2021
Programming
480
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
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
More Decks by Elvis Shi
See All by Elvis Shi
@Environment(\.keyPath)那么好我不允许你们不知道! / atEnvironment keyPath is so good and you should know it!
lovee
0
460
ゼロから始めるPreferenceの実装 / Let's implement Preferences from scratch
lovee
0
150
Kotlin エンジニアへ送る:Swift 案件に参加させられる日に備えて~似てるけど色々違う Swift の仕様 / from Kotlin to Swift
lovee
1
390
個人アプリを2年ぶりにアプデしたから褒めて / I just updated my personal app, praise me!
lovee
0
740
How did I build an Open-Source SwiftUI Toast Library
lovee
1
170
SwiftUIで使いやすいToastの作り方 / How to build a Toast system which is easy to use in SwiftUI
lovee
3
1.3k
SwiftUIで二重スクロール作ってみた / When I tried to make a dual-scroll-ish view in SwiftUI
lovee
1
380
Observation のあれこれ / A brief introduction about Observation
lovee
3
440
ChatGPT 時代の勉強 / Learning under ChatGPT era
lovee
27
9k
Other Decks in Programming
See All in Programming
Hunting Vulnerabilities in Symfony with LLMs
vinceamstoutz
0
560
Make SRE Operations Easier with Azure SRE Agent
kkamegawa
0
8.5k
Dataformのリポジトリを立ち上げるときにまずやること / dataform-day0-2026
snhryt
0
190
Observability in Practice:Grafana 與 Edge Device SRE 的那些事
blueswen
0
180
Signal Forms: Details & Live Coding @enterJS 2026 in Mannheim
manfredsteyer
PRO
0
200
Oxcを導入して開発体験が向上した話
yug1224
4
340
さぁV100、メモリをお食べ・・・
nilpe
0
160
Vue × Nuxt × Oxc どこまで使える?実運用の現在地
andpad
0
310
キャリア迷子上等 ─ "ない道"は自分で作ればいい
16bitidol
3
2.3k
PHPで使える日時の表現と、その知り方 #frontend_phpcon_do
o0h
PRO
0
270
ランチタイムLT会3周年!ランチタイムLT会を3年間続けられたお話
y0hgi
1
110
[2026年度第1回ORセミナー] 計画最適化ベンチャーと競技プログラミング人材
terryu16
0
280
Featured
See All Featured
Bioeconomy Workshop: Dr. Julius Ecuru, Opportunities for a Bioeconomy in West Africa
akademiya2063
PRO
1
160
Marketing Yourself as an Engineer | Alaka | Gurzu
gurzu
0
250
How to optimise 3,500 product descriptions for ecommerce in one day using ChatGPT
katarinadahlin
PRO
1
3.6k
技術選定の審美眼(2025年版) / Understanding the Spiral of Technologies 2025 edition
twada
PRO
118
120k
Ethics towards AI in product and experience design
skipperchong
2
320
Paper Plane (Part 1)
katiecoart
PRO
0
9.3k
Dominate Local Search Results - an insider guide to GBP, reviews, and Local SEO
greggifford
PRO
0
200
Fashionably flexible responsive web design (full day workshop)
malarkey
408
66k
How to Get Subject Matter Experts Bought In and Actively Contributing to SEO & PR Initiatives.
livdayseo
0
140
Primal Persuasion: How to Engage the Brain for Learning That Lasts
tmiket
0
380
GraphQLとの向き合い方2022年版
quramy
50
15k
Believing is Seeing
oripsolob
1
160
Transcript
4XJGU6*Ͱը໘ભҠͷঢ়ଶͱํ๏Λ ཧ͢ΔϧʔλΛ࡞ͬͨ f o r : 6 .
& . * T X J G U ʙ ཪ J 0 4 % $ ʙ ͓ͨͩ͠קΊ͠ͳ͍
} 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 {
Ұൠతͳ4XJGU6*ͷը໘ભҠ ʢ.PEBMભҠʣ struct ParentView: View { @State var showsChild =
false var body: some View { Text("Parent") .fullScreenCover(isPresented: $showsChild, content: { ChildView() }) } } ભҠ੍ޚ༻ .PEJ fi FS ભҠઌ ભҠϑϥά
Ұൠతͳ4XJGU6*ͷը໘ભҠ ʢ1VTIભҠʣ struct ParentView: View { @State var showsChild =
false var body: some View { NavigationLink(destination: { ChildView() }, label: { Text("Show Child") }) } } ભҠઌ ભҠϑϥάʜʁ
Ұൠతͳ4XJGU6*ͷը໘ભҠ ʢ1VTIભҠʣ struct ParentView: View { @State var showsChild =
false var body: some View { NavigationLink(isActive: $showsChild, destination: { ChildView() }, label: { EmptyView() }) } } ભҠઌ ભҠϑϥά ભҠ੍ޚ͕ .PEJ fi FSͰͳ͍
Ұൠతͳ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
4XJGU6*ͷը໘ભҠʹඞཁͳใ w ભҠ੍ޚ༻ͷ.PEJ fi FS w ભҠϑϥά w ભҠઌ ϧʔλ
ϧʔλ֎
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() } } ભҠઌ ભҠϑϥά
3PVUFSΛ͏ struct ParentView: View { @StateObject var router = Router()
var body: some View { Text("Parent") .background(NavigationLink( isActive: router.navigationBinding(), destination: { router.nextView() }, label: { EmptyView() } )) } }
͝੩ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠ ͳ༁͋Δ͔ʂ
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Λશʹ Ѳ͍ͯ͠ͳ͚ΕͳΒͳ͍
ݱঢ়ͷ w Ͱ͖Ε7JFXଆͰ3PVUFSΛҰΓͨ͘ͳ͍ w 3PVUFS͕ݺͼग़͠ݩͷ7JFXΛѲͰ͖ͳ͍ w .PEJ fi FSͰ͍3PVUFSద༻ίʔυॻ͘ͷ͕ͩΔ͍
ཧ 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ʹ͍ͤͨʂ
Ξϓϩʔν w 3PVUFSͷநԽΛׂ w 7JFX͔ΒΓ͍ͨભҠˡ7JFXʹґଘ w ભҠΛ࣮ݱ͢ΔͨΊͷΈˡڞ௨ϩδοΫ
7JFX͔ΒΓ͍ͨભҠ protocol ParentRouterDelegate: ObservableObject { func parentDidSubmit() } struct ParentView<R:
ParentRouterDelegate>: View { @ObservedObject var router: R // ... }
ભҠΛ࣮ݱ͢ΔͨΊͷΈ 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)) } } ͜ΕͰݺͼग़͠ݩ͕ Θ͔Δ
3PVUFSΛ࣮
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ʹࣗࣗΛೖ͢Ε͍͍
3PVUFSͷભҠ%FMFHBUFద߹ extension TestRouter: ParentRouterDelegate { func parentDidSubmit() { parentViewRoute =
.child } } ભҠ͕ඞཁͳͱ͖ͷಈ࡞͚ͩΛ ઐ೦Ͱ͖Δ
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Λฦ͢
͕ͨ͠ Φεεϝ͠ͳ͍ 😇
%&.0
w ֊Λލ͙1PQΞχϝʔγϣϯ͕͓͔͍͕࣌͋͠Δ w J04Ͱͦͦ֊Λލ͙1VTIભҠ͕Ͱ͖ͳ͍ w ଞʹ4XJGU6*ͷόά͕͋Δ͔͠Εͳ͍
ݱ࣮తʹ6*,JUܦ༝Ͱ 3PVUFSΛ࡞ͬͨํ͕͍͍͔
ࢀߟ IUUQTHJUIVCDPNFMIPTIJOP4XJGU6*3PVUFS%FNP