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
20260914 AIエージェント時代のPlatform Engineering LLM基盤とプロダクトの責務境界線
kanfab1
7
2.3k
Unity6 URPによる海底をテーマにしたVJシステムの解説 | Unity Shader 完全に理解した 勉強会
gam0022
0
250
AI が書く Go コードの品質を劇的に向上させる Linter: “declscope”
mpyw
0
460
ゲームコントローラやキーボードのファームウェアをSwiftで書く
kishikawakatsumi
1
270
技術的負債を組織課題として解く-増えすぎたマイクロサービスとの戦い-
reimaru
1
2.6k
[ハンズオン]AIへの指示だけで「五目並べ」を作ってみよう
satoshi256kbyte
1
320
GitHubハンズオン講座 — 実務レベルのチーム開発のフローを身につけよう
junhat6
0
110
動作中のプログラムの中身をリアルタイムに覗く / Realtime Debugger for CSharp with Roslyn
prota
1
1.7k
テストを司るデーモンに会いに行く 〜隔離した仮想マシンでテストを通すまで〜
h1d3mun3
1
600
マイコン向けの軽量Ruby「PicoRuby」で各種デバイスを制御するネイティブアプリの実現手法
bash0c7
0
470
スマートフォンでモールス信号を送受信する 〜スマートフォンのLEDとカメラで作る光通信の設計と実装〜
atsuki_seo
0
220
Apple Intelligence を用いた個人情報誤送信防止、及びユーザーリクエスト体験の改善について
yukiny
0
270
Featured
See All Featured
Java REST API Framework Comparison - PWX 2021
mraible
34
9.7k
The SEO Collaboration Effect
kristinabergwall1
1
580
The untapped power of vector embeddings
frankvandijk
2
1.9k
How to Build an AI Search Optimization Roadmap - Criteria and Steps to Take #SEOIRL
aleyda
1
2.2k
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
16
2.2k
Navigating Team Friction
lara
192
16k
Dominate Local Search Results - an insider guide to GBP, reviews, and Local SEO
greggifford
PRO
0
350
Abbi's Birthday
coloredviolet
4
10k
The innovator’s Mindset - Leading Through an Era of Exponential Change - McGill University 2025
jdejongh
PRO
1
350
Navigating the moral maze — ethical principles for Al-driven product design
skipperchong
2
590
Become a Pro
speakerdeck
PRO
31
6.3k
Crafting Experiences
bethany
1
360
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%$ʹظʜʜ🙏
͋Γ͕ͱ͏͍͟͝·ͨ͠🙇