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
280
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でもいろんな文字_の読み上げをしたい__.pdf
entaku
0
150
AVAudioSessionの全体像を掴む.pdf
entaku
0
200
Core_Audio徹底解剖.pdf
entaku
4
470
わたしと今年のSwift.pdf
entaku
1
220
個人開発をTCAで運用していくということ.pdf
entaku
0
85
パッケージ管理でモバイル開発を安全に進める.pdf
entaku
0
1.3k
個人開発をTCAで運用していくということ.pdf
entaku
0
1.5k
技術コミュニティで技術書典出してみた.pdf
entaku
0
140
What_s_new_in_voice_processing他What_s_new_in_Audio.pdf
entaku
1
340
Featured
See All Featured
Digital Projects Gone Horribly Wrong (And the UX Pros Who Still Save the Day) - Dean Schuster
uxyall
1
3.3k
Building Experiences: Design Systems, User Experience, and Full Site Editing
marktimemedia
1
620
Understanding Cognitive Biases in Performance Measurement
bluesmoon
32
3k
エンジニアに許された特別な時間の終わり
watany
109
250k
Fashionably flexible responsive web design (full day workshop)
malarkey
409
67k
Scaling GitHub
holman
464
140k
VelocityConf: Rendering Performance Case Studies
addyosmani
331
25k
Skip the Path - Find Your Career Trail
mkilby
1
250
The Invisible Side of Design
smashingmag
301
52k
Facilitating Awesome Meetings
lara
57
7.2k
The Cult of Friendly URLs
andyhume
79
7k
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
659
62k
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 ⭐ελʔ͍ͩ͘͞ʂʂʂ⭐