Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
SwiftUIに最適のアーキテクチャ
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
Mike Apurin
March 29, 2024
Programming
980
2
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
SwiftUIに最適のアーキテクチャ
Mike Apurin
March 29, 2024
More Decks by Mike Apurin
See All by Mike Apurin
iOSDC 2024
auramagi
3
1.6k
SwiftUIとUIKitを仲良くさせる
auramagi
3
6.3k
SwiftUI Layout
auramagi
1
900
SwiftUIでUIViewを使うときのレイアウト処理 / Layout process when using UIKit view in SwiftUI
auramagi
0
780
Other Decks in Programming
See All in Programming
スマート反転とウェブアクセシビリティ
camiha
0
220
カツオ、ご期待ください
suneo3476
0
110
AGENTS.md Is Not Enough:Build Skills, Don't Download Them
lx_t
0
130
thread_parallel_with_free-threaded_Python_and_NumPy.pdf
riku_sakamoto
0
360
The Rails Doctrine Decade
koic
2
410
市販E-Readerを乗っ取れ 〜Embedded Swiftで電子ペーパーガジェットを制御する〜
trickart
0
220
Augmenting AI with the Power of Jakarta EE
ivargrimstad
0
410
iOS 27でニュースアプリはどう変わる!? 〜日経電子版の新機能対応と、開発事例から〜
lynnswap
7
13k
Agents on Rails - Rails at Scale 2026
irinanazarova
0
290
WebRTC映像をAirPlayに対応させる挑戦.pdf
monolithic_adam
0
320
wkhtmltopdfの次どうするか問題2026
willnet
2
1.8k
標準パッケージに uuid が追加された 背景から見る Go らしい意思決定 / go_127_uuid_decision
convto
5
9k
Featured
See All Featured
[SF Ruby Conf 2025] Rails X
palkan
3
1.4k
A Guide to Academic Writing Using Generative AI - A Workshop
ks91
PRO
1
500
HU Berlin: Industrial-Strength Natural Language Processing with spaCy and Prodigy
inesmontani
PRO
0
720
Designing Powerful Visuals for Engaging Learning
tmiket
1
580
Stewardship and Sustainability of Urban and Community Forests
pwiseman
0
530
Collaborative Software Design: How to facilitate domain modelling decisions
baasie
1
330
B2B Lead Gen: Tactics, Traps & Triumph
marketingsoph
0
250
Have SEOs Ruined the Internet? - User Awareness of SEO in 2025
akashhashmi
0
510
Self-Hosted WebAssembly Runtime for Runtime-Neutral Checkpoint/Restore in Edge–Cloud Continuum
chikuwait
0
840
Gemini Prompt Engineering: Practical Techniques for Tangible AI Outcomes
mfonobong
2
560
The agentic SEO stack - context over prompts
schlessera
0
940
How to Think Like a Performance Engineer
csswizardry
28
2.8k
Transcript
SwiftUIʹ࠷దͷ ΞʔΩςΫνϟ Mike Apurin
None
SwiftUIʹ࠷దͷ ΞʔΩςΫνϟͱ Կ͔ʁ
MVVM? – MV? – TCA? – MVC? – MVP? –
VIPER? –
ͦͷલʹ
ۜͷؙͳ͍
ΞʔΩςΫνϟҊ ݅ͷχʔζΛߟྀ͢ ͖
SwiftUIҎ֎ʹUIKitΘΕ͍ͯΔ͔ʁ – ΞʔΩςΫνϟ͕ղܾ͍ͯ͠Δ՝χʔζ ʹϚον͍ͯ͠Δ͔ – νʔϜϝϯόʔ͜ͷΞʔΩςΫνϟʹ׳Ε ͍ͯΔ͔ – etc –
ཧͱݱ࣮ဃ͠ ͍ͯΔ
UIKitͱSwiftUIͷޓ ੑ
UIKitͱSwiftUIͷޓ ੑ UINavigationController ͰUIHostingControllerΛ push͢ΔΞχϝʔγϣϯ͕όά Δ let vc = UIHostingController(rootView:
SwiftUIScreen()) navigationController.show(vc, sender: nil)
SwiftUIͷதͰNavigationLinkΛ౿Ήͱൃੜ ͠ͳ͍ – Ҋ݅ͷ߹্ɺUINavigationControllerΛ ͏͔͠ͳ͍ – ͨͩͦ͏͢Δͱɺ৽ΊͷNavigationStack UINavigationControllerͱޓੑ͕ͳ͍͔ Β NavigationLink(value:)
͑ͳ͍ –
ࠓߋݹ͍ NavigationLink(destination:) ͍ ͨ͘ͳ͍ͷͰࣗલͰ NavigationLink(value:) Λ NavigationStack͕ͳͯ͑͘ΔΑ͏ʹόοΫ ϙʔτ͍ͯ͠·͢ github.com/auramagi/Destinations
SwiftPMͷػೳ
None
Development AssetsΛѻ͏ػೳ͕ͳ͍ DebugϏϧυϓϨϏϡʔʹ͏ը૾ ϑΝΠϧ ReleaseϏϧυ͔Βࣗಈతʹഉআ͞ΕΔ
͍͔ͭ͘ϫʔΫΞϥϯυΛࢼ͍ͯ͠Δ Package.swiftͰΓସ͑͢Δʢඇਪʣ – XcodeͷLaunch argumentͰPackage.swiftΛΓସ͑Δ – ϏϧυϓϥάΠϯ – github.com/auramagi/ManageDevelopmentAssetsPlugin –
खಈͰϑΥϧμΛೖΕସ͑Δ – github.com/auramagi/swiftui-first-sample –
ࢲ͕ٻΊ͍ͯΔ SwiftUIʹ࠷దͷ ΞʔΩςΫνϟ
ཁ݅ SwiftUI First – Xcode Previews͕ਏ͘ͳ͍ – ϚϧνΠϯυ + Ϛϧνεςʔτ͕Մೳ
– UIKit, App Intentsߟ͑ΒΕ͍ͯΔ – ֎෦ϥΠϒϥϦͷґଘ͕ೱ͘ͳ͍ –
SwiftUI First SwiftUIͷঢ়ଶཧ͕͑Δ – SwiftUIͷϥΠϑαΠΫϧͰΓཱͬͯ ͍Δ – SwiftUIͰѻ͍͍͢ –
Previews͕ਏ͘ͳ͍ DI͕ͪΌΜͱ͍ͯ͠Δ – UIϨΠϠʔʹॏ͍ґଘΛஔ͔ͳ͍ – ϞοΫ͍͢͠ –
ϚϧνΠϯυՄೳ ෳΠϯυͰͦΕͧΕϩάΠϯঢ়ଶ ͕ҧ͏ͷઃܭՄೳʁ –
UIKit, App Intents DIೖͷํͲ͏ͳΔʁ – ઃܭͷॊೈੑ͕ඞཁ –
֎෦ϥΠϒϥϦͷґଘ ֎෦ϥΠϒϥϦΛத৺ʹΞϓϦΛઃܭͨ͠Β͍͍͔͕ ৻ॏʹݕ౼͠·͠ΐ͏ – ։ൃࢭ·ͬͯόά͕ग़ͨΒͲ͏͢Δʁ – ണ͕͢ͱ͖ʹͲΕ͚ͩେมʁ – Apple͕৽͍͠APIΛग़ͨ͠Β࠾༻ʹ͠ͳ͍͔ʁ –
github.com/auramagi/ swiftui-first-sample
Naive MV struct ContentView: View { let api: APIClient @State
var products: [Product] = [] var body: some View { ForEach(products) { product in Button("Buy \(product.name)") { Task { await api.execute( BuyProductsRequest(product) ) } } } .task { products = await api.execute( GetProductsRequest() ) } } }
Naive MV ີ݁߹ – ϓϨϏϡʔແཧ – ϏϡʔϩδοΫ࠶༻ੑ͕͍ – ґଘೖ͕ߟ͑ΒΕ͍ͯͳ͍ –
None
None
DIೖ struct AppActions { struct Products { var buy: (Product)
async -> Void var refresh: () async -> Void } } ActionsΛΓ͚Δ –
DIೖ struct AppState { final class Products: ObservableObject { @Published
var products: [Product] = [] } } StateΛΓ͚Δ –
struct ContentView: View { @ObservedObject var state: AppState.Products let actions:
AppActions.Products var body: some View { ForEach(state.products) { product in Button("Buy \(product.name)") { Task { await actions.buy(product) } } } .task { await actions.refresh() } } }
ΠχγϟϥΠβͰActionsͱStateೖՄೳʹͨ͠ ͕ɺதͷ࣮มΘΔͷ͕كͳͷͰຖճຖճࢦఆ͢Δ ͷ໘ EnvironmentͰґଘΛୡͤ͞Δ – State @EnvironmentObject Ͱ – Actions
@Environment Ͱ –
StateͱActionsΛଋͶͯ Dependency ͱͯ͠ఆٛ struct AppState { final class Products: ObservableObject
{ ... } var products = Products() } struct AppActions { struct Products { ... } var products: Products } extension EnvrionmentValues { var appActions: AppActions { ... } } struct AppDependency { var state: AppState var actions: AppActions }
Dependency Λอ࣋͢ΔίϯςφΛ ఆٛ protocol AppContainer { var app: AppDependency {
get set } } MockAppContainer ͱ LiveAppContainer Λ ४උ
struct ContentView: View { @EnvironmentObject var state: AppState.Products @Environment(\.appActions.products) let
actions var body: some View { ForEach(state.products) { product in Button("Buy \(product.name)") { Task { await actions.buy(product) } } } .task { await actions.refresh() } } }
ೖΛ͘͢͢͠Δ public protocol ViewInjectable { typealias Content = ViewDependencyModifier<Self>.Content associatedtype
InjectedBody: View func inject(content: Content) -> InjectedBody } public struct ViewDependencyModifier<D: ViewInjectable>: ViewModifier { let dependency: D public func body(content: Content) -> some View { dependency.inject(content: content) } } extension View { public func dependency(_ dependency: some ViewInjectable) -> some View { modifier(ViewDependencyModifier(dependency: dependency)) } }
Service Λ࣮ final class ProductsService { let api: APIClient let
state: AppState.Products func buy(product: Product) async { await api.execute( BuyProductsRequest(product) ) } func refresh() async { products = await api.execute( GetProductsRequest() ) } }
LiveAppContainer Ͱଓ final class LiveAppContainer: AppContainer { struct Configuration {
... } var app: AppDependency private let api: APIClient private let productsService: ProductsService init(configuration: Configuration) { self.app = .init() self.api = ... self.productsService = .init( api: api, state: app.state.products ) app.actions.products.buy = productsService.buy(product:) app.actions.products.refresh = productsService.refresh } }
Previews #Preview { ContentView() .mockContainer(.app) } #Preview { WithMockContainer(.app) {
container in MainFlow(container: container) } } ( .mockContainer ͱ WithMockContainer ͷׂ࣮Ѫ)
ίϯςφFlowͰ͢ struct MainFlow<Container: AppContainer>: View { let container: Container var
body: some View { ContentView() .dependency(container) } }
View – ͨͩͷUI෦ – Screen – ը໘શମΛදࣔ͢ΔUI෦ – Screen ಉ࢜Λ࡞͠ͳ͍
– Flow – DependencyContainer Λอ࣋ͯ͠ೖ͢Δ – Screen Λ࡞͢Δ – ભҠ –
AppͰίϯςφΛੜ @main struct SampleApp: App { @State var container =
LiveAppContainer( configuration: ... ) var body: some Scene { WindowGroup { MainFlow(container: container) .dependency(container) } } }
None
͝ਗ਼ௌ͋Γ͕ͱ͏ ͍͟͝·ͨ͠