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
sakaue
May 19, 2021
Programming
570
2
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
SwiftUIを新規プロダクトで用いた話
sakaue
May 19, 2021
More Decks by sakaue
See All by sakaue
先端事例「Amazon Chime SDK」を用いた1on1ビデオ通話の構築
tosaka07
0
1.8k
UICollectionView 基礎編
tosaka07
0
150
Other Decks in Programming
See All in Programming
Next.js: The final boss of Web Performance
guaca
0
390
graphql-ruby を Spinel で速くしようとした話
mtsmfm
0
140
私なりのハーネスエンジニアリング ~独自の言語処理系の開発を通して~
seosoft
0
200
flutter_test のその先へ — Flutter アプリの LLM 機能に「評価」という品質保証を
kosukesaigusa
0
200
小さなアプリを作って公開するまで
onozaty
0
150
[JA] Domain Modeling with Vanilla Rails: 「操作」から再考するRailsの設計論
yasaichi
4
1.8k
AI時代のTerminal環境を UNIX哲学で設計・運用する
yasunori0418
1
350
Web Framework Benchmark Battle 2026, Any%
a_matsuda
3
3.2k
DroidKnights 2026 Android A2UI Jetpack Compose 기반의 안전한 AI UI 렌더링 - 박제창
itsmedreamwalker
1
210
Railsバージョンを4倍にした話
optim
0
970
Vapor Mode でアクセシビリティは 壊れないか検証した話
fuqda
1
330
作れるエンジニアから、価値を届けるエンジニアへ
ryunakayama
1
250
Featured
See All Featured
How to make the Groovebox
asonas
2
2.5k
Max Prin - Stacking Signals: How International SEO Comes Together (And Falls Apart)
techseoconnect
PRO
0
530
My Coaching Mixtape
mlcsv
0
380
Speed Design
sergeychernyshev
33
2.2k
Color Theory Basics | Prateek | Gurzu
gurzu
1
510
Navigating the moral maze — ethical principles for Al-driven product design
skipperchong
2
680
Tips & Tricks on How to Get Your First Job In Tech
honzajavorek
1
830
Abbi's Birthday
coloredviolet
4
11k
svc-hook: hooking system calls on ARM64 by binary rewriting
retrage
2
670
Agile that works and the tools we love
rasmusluckow
331
22k
Product Roadmaps are Hard
iamctodd
55
13k
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
47
8.4k
Transcript
4XJGU6*Λ ৽نϓϩμΫτͰ༻͍ͨ $"TXJGUʙ͍·ߟ͑Δ࣍ੈͷઃܭʙ
ࣗݾհͱϓϩμΫτ 4XJGU6*ͷྑ͞ͱ࠾༻ܦҢ Ͳ͏࡞͍͔ͬͯͬͨ w ڞଘΞʔΩςΫνϟ෦ ·ͱΊ
࣍
ࣗݾհͱϓϩμΫτ 4XJGU6*ͷྑ͞ͱ࠾༻ܦҢ Ͳ͏࡞͍͔ͬͯͬͨ w ڞଘΞʔΩςΫνϟ෦ ·ͱΊ
࣍
J04%FWFMPQFS w גࣜձࣾ$". w ಈը৴ΞϓϦ w ήολʔζ൧ాͷ͍ w 'FOTJ w
ౡݝग़ ࡔ্ᠳޛ4BLBVF4IPHP UPTBLB@UFDI UPTBLB
None
None
ֹ݄՝ۚ 'FOTJɺ ͋ΒΏΔදݱ׆ಈΛ͢ΔํΛ શྗͰԠԉ͢ΔαʔϏεͰ͢ɻ ΦϑΟγϟϧαΠτ ݶఆίϛϡχςΟ άοζൢച POτʔΫ
FUD
͜Μͳײ͡ͷ ΞϓϦͰ͢
ࣗݾհͱϓϩμΫτ 4XJGU6*ͷྑ͞ͱ࠾༻ܦҢ Ͳ͏࡞͍͔ͬͯͬͨ w ڞଘΞʔΩςΫνϟ෦ ·ͱΊ
࣍
4XJGU6*ͷྑ͞ w એݴత w ࠶ར༻Λҙࣝͨ͠࡞Γ w 1SFWJFX w "VUP-BZPVU4UPSZCPBSE9JC͔Βͷղ์ w
ίʔυϨϏϡʔ͍͢͠
4XJGU6*Λ࠾༻ͨ͠ܦҢ w 8FCϢʔβʔJ04Ҏ্͕΄ͱΜͲͩͬͨ w 4XJGU6*͕ൃද͞ΕͯҰ͘Β͍ܦ͍ͬͯͯɺ ίϛϡχςΟͷݟ͋Δఔஷ·͍ͬͯͨ w ৽͍ٕ͠ज़ʹ৮ΕΔػձΛ࡞Γ͔ͨͬͨ w
"VUP-BZPVU͔Βͷղ์
։ൃώετϦʔ ։ൃελʔτ ΞϓϦϦϦʔε POϦϦʔε ݱࡏ ։ൃਓ
ਓ ਓ ਓ 4XJGU6*
ࣗݾհͱϓϩμΫτ 4XJGU6*ͷྑ͞ͱ࠾༻ܦҢ Ͳ͏࡞͍͔ͬͯͬͨ w ڞଘΞʔΩςΫνϟ෦ ·ͱΊ
࣍
Ͳ͏࡞͍͔ͬͯͬͨ ·ͣ J04ͷ4XJGU6*ͱدΓఴ͏🤝
دΓఴͬͨ݁Ռ👻 w 4XJGU6*ʹରԠͯ͠ͳ͍෦͕ଟ͍ w J04Ͱ͢Βόʔδϣϯ͝ͱʹڍಈ͕ҧ͏ w ͱ͜ΖͲ͜ΖόάͬΆ͍ڍಈ
دΓఴͬͨ݁Ռ👻 6*,JU🤝 4XJGU6*🥲
دΓఴͬͨ݁Ռ👻 6*,JU🤝 4XJGU6*🥲 6*,JUΛجຊͱ͠ɺ4XJGU6*Λ෦తʹͬͨ΄͏͕ ϦεΫ͕͍ɻ
6*,JUͱ4XJGU6*ͷڞଘ w 6*7JFX$POUSPMMFSʹ 4XJGU6*7JFXΛͷ͚ͬΔ w ભҠ6*,JUΛ͏ w 7JFX.PEFM͔Β
ભҠΠϕϯτΛड͚औΔͷ 6*7JFX$POUSPMMFS 6*7JFX$POUSPMMFS 4XJGU6* 7JFX 7JFX.PEFM
4XJGU6*7JFXΛ7$ʹͷ͚ͬΔ class SampleViewModel: ObservableObject { @Published var title: String =
"" func fetch() { self.title = "Hello, World!" } } struct SampleView: View { @ObservedObject var viewModel: SampleViewModel var body: some View { Text(viewModel.title) } }
4XJGU6*7JFXΛ7$ʹͷ͚ͬΔ class SampleViewController: UIViewController { let viewModel = SampleViewModel() override
func viewDidLoad() { super.viewDidLoad() let vc = UIHostingController( rootView: SampleView(viewModel: viewModel) ) addChild(vc) view.addSubview(vc.view) NSLayoutConstraint.activate([/* ... */]) vc.didMove(toParent: self) viewModel.fetch() } }
ભҠ class SampleViewModel: ObservableObject { @Published var presentDetail: String? func
presentDetail(id: String) { presentDetail = id } } struct SampleView: View { @ObservedObject var viewModel: SampleViewModel var body: some View { Button("present to Detail") { viewModel.presentDetail(id: "1") } } }
ભҠ class SampleViewController: UIViewController { var cancellables = Set<AnyCancellable>() let
viewModel = SampleViewModel() override func viewDidLoad() { super.viewDidLoad() viewModel.$presentDetail .compactMap { $0 } .sink { [weak self] id in self?.present( DetailViewController(id: id), animated: true, completion: nil ) } .store(in: &cancellables) } }
ڞଘͷϝϦοτ w ͍͔Α͏ʹͭ͘ΕΔ w τϥϯδγϣϯΞχϝʔγϣϯ w ϧʔςΟϯά w ճస੍ޚ w
Ұը໘͝ͱʹཧͰ͖Δ
ΞʔΩςΫνϟ
ΞʔΩςΫνϟ w ϓϩμΫτνʔϜʹ߹ΘͤΔ w 6OJEJSFDUJPOBM w 4PVSDFPGUSVUI w 'FOTJͰ$PNCJOFʹஔ͖ ͑ͨ3FBDUPS,JUΛࣗ࡞
IUUQTEFWFMPQFSBQQMFDPNEPDVNFOUBUJPOTXJGUVJTUBUFBOEEBUB fl PX
3FBDUPS,JU w νʔϜͰͷܦݧ͕͋ͬͨ w 6OJEJSFDUJPOBM͕4XJGU6* ʹ߹ͬͯͨ w طଘͷઃܭ͔Β4XJGU6*ʹ ߹ΘͤΔͷଟগͷख͕ؒ͋ Δ
IUUQTHJUIVCDPN3FBDUPS,JU3FBDUPS,JU
3FBDUPS,JU public protocol ReactorCore: class { associatedtype Action associatedtype Mutation
= Action associatedtype State func transform(action: AnyPublisher<Action, Never>) -> AnyPublisher<Action, Never> func mutate(state: State, action: Action) -> AnyPublisher<Mutation, Never> func transform(mutation: AnyPublisher<Mutation, Never>) -> AnyPublisher<Mutation, Never> func reduce(state: State, mutation: Mutation) -> State func transform(state: AnyPublisher<State, Never>) -> AnyPublisher<State, Never> } class Reactor<Core: ReactorCore> { var state: CurrentValueSubject<Core.State, Never> init(initialState: Core.State, core: Core) func send(_ action: Core.Action) }
3FBDUPS,JU class SampleCore: ReactorCore { enum Action { case increment,
decrement } enum Mutation { case setNumber(Int) } struct State { var number: Int } func mutate(state: State, action: Action) -> AnyPublisher<Mutation, Never> { switch action { case .increment: return Just(Mutation.setNumber(state.number + 1)).eraseToAnyPublisher() case .decrement: return Just(Mutation.setNumber(state.number - 1)).eraseToAnyPublisher() } } func reduce(state: State, mutation: Mutation) -> State { var state = state switch mutation { case .setNumber(let number): state.number = number } return state } }
3FBDUPS,JU class SampleViewController: UIViewController { var cancellables = Set<AnyCancellable>() let
reactor: Reactor<SampleCore> override func viewDidLoad() { super.viewDidLoad() bind(reactor: reactor) } func bind(reactor: Reactor<SampleCore>) { let store = ViewStore(reactor) store.publisher.number .sink { (number) in // UIKit ʹόΠϯυ } .store(in: &cancellables) let view = UIHostingController( rootView: SampleView(viewStore: store) ) // addSubView... } } struct SampleView: View { @ObservedObject var viewStore: ViewStore< SampleCore.State, SampleCore.Action > var body: some View { HStack { Button("-") { viewStore.send(.decrement) } Text("\(viewStore.number)") Button("-") { viewStore.send(.increment) } } } }
7JFX4UPSF @dynamicMemberLookup public class ViewStore<State, Action>: ObservableObject { public let
objectWillChange = ObservableObjectPublisher() public private(set) var state: State { willSet { objectWillChange.send() } } public let publisher: StatePublisher<State> private var viewCancellable: AnyCancellable? private let _send: (Action) -> Void init<Core: ReactorCore>( _ reactor: Reactor<Core> ) where Core.State == State, Core.Action == Action { publisher = StatePublisher(reactor.$state) state = reactor.state _send = { [weak reactor] in reactor?.send($0) } viewCancellable = publisher .receive(on: DispatchQueue.main) .sink(receiveValue: { [weak self] in self?.state = $0 }) } public subscript<LocalState>(dynamicMember keyPath: KeyPath<State, LocalState>) -> LocalState { state[keyPath: keyPath] } public func send(_ action: Action) { DispatchQueue.main.async { [weak self] in self?._send(action) } } w 5$"ͰΘΕ͍ͯΔ w EZOBNJD.FNCFS-PPLVQ w ॏෳͷআ w #JOEJOH IUUQTHJUIVCDPNQPJOUGSFFDPTXJGUDPNQPTBCMFBSDIJUFDUVSF
෦
࡞ͬͨ w 5FYU w #VUUPO w 5FYU'JFME w *NBHF w
ͱ͔ˠҎ֎ͷ෦ 4XJGU6*Ͱ࡞ͬͨ෦ ࡞Βͳ͔ͬͨ w 5BC7JFX w 8FC7JFX w 5FYU7JFX w $PMMFDUJPO7JFX w $VTUPN7JFXT w 6*,JU-JCSBSZ -PUUJF
ૉʹ 6*7JFX3FQSFTFOUBCMF 6*7JFX$POUSPMMFS3FQSFTFOUBCMF Λ͍·͠ΐ͏ ඇରԠͰ4XJGU6*ʹΈࠐΈ͍ͨ struct WebView: UIViewRepresentable
{ var url: URL func makeUIView(context: Context) -> WKWebView { return WKWebView(frame: .zero) } func updateUIView(_ uiView: WKWebView, context: Context) { uiView.load(URLRequest(url: url)) } }
$PMMFDUJPO7JFXʹٻΊΔͷ w 6*$PMMFDUJPO7JFXY4XJGU6*🤔 w 6*$PMMFDUJPO7JFX$PNQPTJUJPOBM-BZPVU͍͍ͨ w 6*$PMMFDUJPO7JFX%J ff BCMF%BUB4PVSDF͍͍ͨ
$PMMFDUJPO7JFXY4XJGU6* 6*$PMMFDUJPO7JFX$FMM 6*$PMMFDUJPO7JFX 4XJGU6*7JFX 4FDUJPO*% JE4FDUJPO*% EBUB-JTUσʔλྻ
EBUB*EσʔλͷҰҙੑΛද͢,FZ1BUI DPOUFOUσʔλΛݩʹੜ͢Δ7JFX 4UBUF 4FDUJPO $FMM
3FOEFSFS public protocol CollectionRenderer { associatedtype ID: Hashable associatedtype Action
associatedtype State func render(state: State, store: ViewStore<State, Action>) func createSections(state: State, store: ViewStore<State, Action>) -> [Section<ID>] func createLayoutSection( id: ID, environment: NSCollectionLayoutEnvironment ) -> NSCollectionLayoutSection }
3FOEFSFS class TodoListCollectionRenderer: CollectionRenderer { typealias State = ToDoListReactor.State typealias
Store = ViewStore<State, ToDoListReactor.Action> enum ID: Int { case todo case empty } @SectionBuilder func createSections(state: State, store: Store) -> [Section<ID>] { if state.todoList.isEmpty { empty() } else { todo(state: state, store: store) } } func createLayoutSection(id: ID, env: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { switch id { case .list: return .list(itemWidth: .fractionalWidth(1.0), itemHeight: .estimated(.s222)) case .empty: return .list(itemWidth: .fractionalWidth(1.0), itemHeight: .fractionalHeight(1.0)) } } } func todo(state: State, store: Store) -> Section<ID> { Section( id: ID.todo, data: state.todoList, dataID: \.self ) { todo in TodoItemView( Title: todo.content, done: todo.done ) .onTapGesture { store.send(.showDetail(todo)) } } }
ࣗݾհͱϓϩμΫτ 4XJGU6*ͷྑ͞ͱ࠾༻ܦҢ Ͳ͏࡞͍͔ͬͯͬͨ w ڞଘΞʔΩςΫνϟ෦ ·ͱΊ
࣍
·ͱΊ w J04αϙʔτͭΒ͍ ˠJ04ʹͰ͖ΔͳΒ͠·͠ΐ͏ w શʹ4XJGU6*ʹدͤΔ͜ͱϦεΫ͕Ͱ͔͍ ˠ6*,JUͷಀ͛ΒΕΔઃܭΛ͠·͠ΐ͏ w
88%$ʹظʜʜ🙏
͋Γ͕ͱ͏͍͟͝·ͨ͠🙇