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
TCAでiOS開発を_学ぶ3つの利点.pdf
Search
entaku
November 14, 2023
290
4
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
TCAでiOS開発を_学ぶ3つの利点.pdf
entaku
November 14, 2023
More Decks by entaku
See All by entaku
swift-de-tv-wo-ugokasu.pdf
entaku
0
50
Swiftでもいろんな文字_の読み上げをしたい__.pdf
entaku
0
150
AVAudioSessionの全体像を掴む.pdf
entaku
0
210
Core_Audio徹底解剖.pdf
entaku
4
480
わたしと今年のSwift.pdf
entaku
1
230
個人開発をTCAで運用していくということ.pdf
entaku
0
87
パッケージ管理でモバイル開発を安全に進める.pdf
entaku
0
1.4k
個人開発をTCAで運用していくということ.pdf
entaku
0
1.6k
技術コミュニティで技術書典出してみた.pdf
entaku
0
140
Featured
See All Featured
Chrome DevTools: State of the Union 2024 - Debugging React & Beyond
addyosmani
10
1.4k
WCS-LA-2024
lcolladotor
0
960
More Than Pixels: Becoming A User Experience Designer
marktimemedia
3
610
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
12
1.9k
Building Better People: How to give real-time feedback that sticks.
wjessup
371
20k
<Decoding/> the Language of Devs - We Love SEO 2024
nikkihalliwell
1
380
Jess Joyce - The Pitfalls of Following Frameworks
techseoconnect
PRO
1
470
DBのスキルで生き残る技術 - AI時代におけるテーブル設計の勘所
soudai
PRO
68
59k
How to Grow Your eCommerce with AI & Automation
katarinadahlin
PRO
2
340
Statistics for Hackers
jakevdp
799
230k
YesSQL, Process and Tooling at Scale
rocio
174
15k
Understanding Cognitive Biases in Performance Measurement
bluesmoon
32
3.1k
Transcript
TCAͰiOS։ൃΛ ֶͿ3ͭͷར entaku potatotips #85@Voicy
entaku • name: entaku • job: iOSΤϯδχΞͳͲ • SIer6 •
εϙʔπϚονϯάΞϓϦ1 • ྲྀITαʔϏε 2 • Voicy • Twitter • @entaku_0818
γϯϓϧԻͱ͍͏ΞϓϦͰ TCAΛ0.39.1->1.2.0όʔδϣϯΞοϓ https://github.com/entaku0818/VoiceMemo https://apps.apple.com/jp/app/id6443528409
TCAͬͯ·͔͢ʁʁ https://github.com/pointfreeco/swift-composable-architecture
TCAͬͯͲ͏Αʁ ·ͨ5$"͔ʙ ·ͩ5$"ͱ͔Θͳ͍͠ɺαϯϓϧͱ ͔ΈͯͪΐͬͱΘ͔Βͳ͍ͳ Ͳ͏ͤ·ͩۀͰ͑Δ༰ͳΜͯҰ ͭͳ͍ΜͰ͠ΐ͏ʁʁʁ
TCAͬͯͲ͏Αʁ ·ͨ5$"͔ʙ ·ͩ5$"ͱ͔Θͳ͍͠ɺαϯϓϧͱ ͔ΈͯͪΐͬͱΘ͔Βͳ͍ͳ Ͳ͏ͤ·ͩۀͰ͑Δ༰ͳΜͯҰ ͭͳ͍ΜͰ͠ΐ͏ʁʁʁ
͋ͳ͕ͨTCAΛ͏͖ཧ༝ • TCAͰSwiftͷSwift concurrencyͷར༻ྫ͕ݟ ΕΔʂ • TCAͰTCAͷػೳͷɺҰ෦Λ֎෦ϥΠϒϥ Ϧͱͯ͠ར༻͢Δ͜ͱ͕Ͱ͖Δʂ • TCAͰSwift
concurrencyͷ࠷৽ΛΩϟον ΞοϓͰ͖Δʂ
TCAͰSwiftͷSwift concurrencyͷར༻ྫ͕ݟΕΔʂ https://developer.apple.com/documentation/swift/ concurrency
͜Ε·ͰͷԻॲཧ https://developer.apple.com/documentation/swift/ concurrency class ViewController: UIViewController, AVAudioRecorderDelegate { var audioRecorder:
AVAudioRecorder? func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder, successfully flag: Bool) { if flag { print("Ի͕ਖ਼ৗʹऴྃ͠·ͨ͠ɻ") // Ի͕ޭͨ͠߹ͷॲཧΛ͜͜ʹهड़ } else { print("Ի͕ޭ͠·ͤΜͰͨ͠ɻ") } } func audioRecorderEncodeErrorDidOccur(_ recorder: AVAudioRecorder, error: Error?) { if let error = error { print("ΤϯίʔυதʹΤϥʔ͕ൃੜ͠·ͨ͠: \(error.localizedDescription)") } } }
ԻॲཧͰSwift concurrencyΛར༻ https://developer.apple.com/documentation/swift/ concurrency func start(url: URL) async throws ->
Bool { // ॲཧ͕͍ͷͰ͍ͨ͠෦Ҏ֎লུ self.delegate = Delegate( didFinishRecording: { flag in continuation.yield(flag) continuation.finish() try? AVAudioSession.sharedInstance().setActive(false) }, encodeErrorDidOccur: { error in continuation.finish(throwing: error) try? AVAudioSession.sharedInstance().setActive(false) } ) let stream = AsyncThrowingStream<Bool, Error> { continuation in do { self.recorder = recorder recorder.delegate = self.delegate continuation.onTermination = { [recorder = UncheckedSendable(recorder)] _ in recorder.wrappedValue.stop() } self.recorder?.record() } catch { continuation.finish(throwing: error) } } } } • async/awaitͰԻͷඇಉظॲཧΛ࣮ݱ
ԻॲཧͰSwift concurrencyΛར༻ https://developer.apple.com/documentation/swift/ concurrency case let .recordingMemo(.presented(.delegate(.didFinish(.success(recordingMemo))))): state.recordingMemo = nil
state.voiceMemos.insert( VoiceMemo.State( date: recordingMemo.date, duration: recordingMemo.duration, url: recordingMemo.url ), at: 0 ) return .none • ޭ࣌ͷॲཧͷΈΞϓϦଆʹॻ͚ΔΑ͏ʹͳΔ
TCAͰTCAͷػೳͷɺҰ෦Λ֎෦ϥ ΠϒϥϦͱͯ͠ར༻͢Δ͜ͱ͕Ͱ͖Δʂ https://developer.apple.com/documentation/swift/ concurrency
TCAҎ֎ϥΠϒϥϦͱͯ͠ఏڙ https://developer.apple.com/documentation/swift/ concurrency https://github.com/pointfreeco/swift-dependencies
TCAҎ֎ϥΠϒϥϦͱͯ͠ఏڙ https://developer.apple.com/documentation/swift/ concurrency https://github.com/pointfreeco/swift-dependencies
TCAͰґଘੑͷΈ https://developer.apple.com/documentation/swift/ concurrency https://github.com/pointfreeco/swift-dependencies • live • ओʹ࣮ΞϓϦͰಈ࡞͢Δ • test
• ςετͰಈ࡞͢Δ Dependency • preview • Xcode Previews Ͱಈ࡞͢Δ
ґଘੑͷΈ https://developer.apple.com/documentation/swift/ concurrency extension AudioRecorderClient: DependencyKey { static var liveValue:
Self { let audioRecorder = AudioRecorder() return Self( currentTime: { await audioRecorder.currentTime }, requestRecordPermission: { await AudioRecorder.requestPermission() }, startRecording: { url in try await audioRecorder.start(url: url) }, stopRecording: { await audioRecorder.stop() } ) } } https://github.com/pointfreeco/swift-dependencies
ґଘੑͷΈ https://developer.apple.com/documentation/swift/ concurrency https://github.com/pointfreeco/swift-dependencies extension AudioRecorderClient: TestDependencyKey { static var
previewValue: Self { let isRecording = ActorIsolated(false) let currentTime = ActorIsolated(0.0) return Self( currentTime: { await currentTime.value }, requestRecordPermission: { true }, startRecording: { _ in await isRecording.setValue(true) while await isRecording.value { try await Task.sleep(for: .seconds(1)) await currentTime.withValue { $0 += 1 } } return true }, stopRecording: { await isRecording.setValue(false) await currentTime.setValue(0) } ) } static let testValue = Self( currentTime: unimplemented("\(Self.self).currentTime", placeholder: nil), requestRecordPermission: unimplemented( "\(Self.self).requestRecordPermission", placeholder: false ), startRecording: unimplemented("\(Self.self).startRecording", placeholder: false), stopRecording: unimplemented("\(Self.self).stopRecording") ) }
TCAͰSwift concurrencyͷ ࠷৽ΛΩϟονΞοϓͰ͖Δʂ https://developer.apple.com/documentation/swift/ concurrency
clockͷར༻ https://developer.apple.com/documentation/swift/ concurrency @Dependency(\.continuousClock) var clock case .onTask: return .run
{ [url = state.url] send in async let startRecording: Void = send( .audioRecorderDidFinish( TaskResult { try await self.audioRecorder.startRecording(url) } ) ) for await _ in self.clock.timer(interval: .seconds(1)) { await send(.timerUpdated) } await startRecording } • Ի࣌ͷλΠϚʔॲཧ
clockͷར༻ https://developer.apple.com/documentation/swift/ concurrency @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS
16.0, *) public protocol Clock<Duration> : Sendable { associatedtype Duration where Self.Duration == Self.Instant.Duration associatedtype Instant : InstantProtocol var now: Self.Instant { get } var minimumResolution: Self.Duration { get } func sleep(until deadline: Self.Instant, tolerance: Self.Instant.Duration?) async throws } • Clockͱ͍͏࠷৽ͷػೳΛར༻
αϯϓϧ https://github.com/pointfreeco/swift-composable-architecture/tree/main/Examples
·ͱΊ • TCAͷαϯϓϧͰԼهͷ͜ͱ͕Ͱ͖Δʂ • TCAͰSwiftͷओʹSwift concurrencyͷར༻ ྫ͕ݟΕΔʂ • TCAͰTCAͷػೳͷɺҰ෦Λ֎෦ϥΠϒϥ Ϧͱͯ͠ར༻͢Δ͜ͱ͕Ͱ͖Δʂ
• TCAͰSwift concurrencyͷ࠷৽ΛΩϟον ΞοϓͰ͖Δʂ
ࠓճௐࠪʹ͝ڠྗ͍͍ͨͩͨΞϓϦ https://github.com/entaku0818/VoiceMemo https://apps.apple.com/jp/app/id6443528409 ⭐ελʔ͍ͩ͘͞ʂʂʂ⭐