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
WebMCP Challenge に星空観察アプリで参加した話
okajun35
0
120
AIは賢い。でも実行環境は? CLIおじさんがAI時代に伝えたいこと ~ CLIおじさんがAI時代に伝えたいこと ~
curekoshimizu
1
210
From 6 People Classroom Meetup to 100 People Regional Conference / FOSS4G Hiroshima 2026
furukawayasuto
0
120
AI駆動開発にグラフDBを重ねてみた
satoshi256kbyte
2
760
「AI時代、配布するPythonコードをどう守るか: 難読化の実験と判断軸」 #PyconJP2026
pkshadeck
PRO
2
180
LoopHub - ローカルで動く GitHub で、AI と共同開発
jugyo
1
470
XP祭りでしか伝わらないフリップネタ #xpjug
murabayashi
0
110
AIを上手に使っていこうとしたら越境せざるを得なくなった話 〜実践1年で見えた境界を越えなければならない理由と進め方〜 / Crossing borders with AI
tomoyakitaura
4
1.1k
AgentCore CLI で進化した AWS での AI エージェントの作り方 : 必要な機能を必要な時に
icoxfog417
PRO
2
220
Omarchy Tokyo やると聞いて UMPC 買ってセットアップしてきた
mtsmfm
0
110
変化を抱擁するドキュメントの作り方 - ビジネスルール駆動開発がもたらす、コードとの新しい関係
ioki
2
130
Go × SIMDで高速化するベクトル検索 ~ルーフラインモデルでSIMDが効く境界を探れ! ~
po3rin
1
170
Featured
See All Featured
Redefining SEO in the New Era of Traffic Generation
szymonslowik
1
410
Statistics for Hackers
jakevdp
799
230k
Git: the NoSQL Database
bkeepers
PRO
432
67k
The Anti-SEO Checklist Checklist. Pubcon Cyber Week
ryanjones
0
230
The innovator’s Mindset - Leading Through an Era of Exponential Change - McGill University 2025
jdejongh
PRO
1
320
From π to Pie charts
rasagy
0
360
Measuring & Analyzing Core Web Vitals
bluesmoon
9
990
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
128
56k
Learning to Love Humans: Emotional Interface Design
aarron
275
41k
Building a Scalable Design System with Sketch
lauravandoore
463
34k
技術選定の審美眼(2025年版) / Understanding the Spiral of Technologies 2025 edition
twada
PRO
120
120k
No one is an island. Learnings from fostering a developers community.
thoeni
21
3.8k
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%$ʹظʜʜ🙏
͋Γ͕ͱ͏͍͟͝·ͨ͠🙇