Slide 1

Slide 1 text

TCAͰiOS։ൃΛ ֶͿ3ͭͷར఺ entaku potatotips #85@Voicy

Slide 2

Slide 2 text

entaku • name: entaku • job: iOSΤϯδχΞͳͲ • SIer໿6೥ • εϙʔπϚονϯάΞϓϦ1೥ • ෺ྲྀITαʔϏε ໿2೥ • Voicy • Twitter • @entaku_0818

Slide 3

Slide 3 text

γϯϓϧ࿥Իͱ͍͏ΞϓϦͰ TCAΛ0.39.1->1.2.0΁όʔδϣϯΞοϓ https://github.com/entaku0818/VoiceMemo https://apps.apple.com/jp/app/id6443528409

Slide 4

Slide 4 text

TCA࢖ͬͯ·͔͢ʁʁ https://github.com/pointfreeco/swift-composable-architecture

Slide 5

Slide 5 text

TCAͬͯͲ͏Αʁ ·ͨ5$"࿩͔ʙ ·ͩ5$"ͱ͔࢖Θͳ͍͠ɺαϯϓϧͱ ͔Έͯ΋ͪΐͬͱΘ͔Βͳ͍ͳ Ͳ͏ͤ·ͩۀ຿Ͱ࢖͑Δ಺༰ͳΜͯҰ ͭ΋ͳ͍ΜͰ͠ΐ͏ʁʁʁ

Slide 6

Slide 6 text

TCAͬͯͲ͏Αʁ ·ͨ5$"࿩͔ʙ ·ͩ5$"ͱ͔࢖Θͳ͍͠ɺαϯϓϧͱ ͔Έͯ΋ͪΐͬͱΘ͔Βͳ͍ͳ Ͳ͏ͤ·ͩۀ຿Ͱ࢖͑Δ಺༰ͳΜͯҰ ͭ΋ͳ͍ΜͰ͠ΐ͏ʁʁʁ

Slide 7

Slide 7 text

͋ͳ͕ͨTCAΛ࢖͏΂͖ཧ༝ • TCAͰ͸SwiftͷSwift concurrencyͷར༻ྫ͕ݟ ΕΔʂ • TCAͰ͸TCAͷػೳͷ಺ɺҰ෦Λ֎෦ϥΠϒϥ Ϧͱͯ͠ར༻͢Δ͜ͱ͕Ͱ͖Δʂ • TCAͰ͸Swift concurrencyͷ࠷৽ΛΩϟον ΞοϓͰ͖Δʂ

Slide 8

Slide 8 text

TCAͰ͸SwiftͷSwift concurrencyͷར༻ྫ͕ݟΕΔʂ https://developer.apple.com/documentation/swift/ concurrency

Slide 9

Slide 9 text

͜Ε·Ͱͷ࿥Իॲཧ 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)") } } }

Slide 10

Slide 10 text

࿥ԻॲཧͰ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 { 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Ͱ࿥ԻͷඇಉظॲཧΛ࣮ݱ

Slide 11

Slide 11 text

࿥ԻॲཧͰ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 • ੒ޭ࣌ͷॲཧͷΈΞϓϦଆʹॻ͚ΔΑ͏ʹͳΔ

Slide 12

Slide 12 text

TCAͰ͸TCAͷػೳͷ಺ɺҰ෦Λ֎෦ϥ ΠϒϥϦͱͯ͠ར༻͢Δ͜ͱ͕Ͱ͖Δʂ https://developer.apple.com/documentation/swift/ concurrency

Slide 13

Slide 13 text

TCAҎ֎΋ϥΠϒϥϦͱͯ͠ఏڙ https://developer.apple.com/documentation/swift/ concurrency https://github.com/pointfreeco/swift-dependencies

Slide 14

Slide 14 text

TCAҎ֎΋ϥΠϒϥϦͱͯ͠ఏڙ https://developer.apple.com/documentation/swift/ concurrency https://github.com/pointfreeco/swift-dependencies

Slide 15

Slide 15 text

TCAͰґଘੑͷ࢓૊Έ https://developer.apple.com/documentation/swift/ concurrency https://github.com/pointfreeco/swift-dependencies • live • ओʹ࣮ΞϓϦͰಈ࡞͢Δ • test • ςετͰಈ࡞͢Δ Dependency • preview • Xcode Previews Ͱಈ࡞͢Δ

Slide 16

Slide 16 text

ґଘੑͷ࢓૊Έ 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

Slide 17

Slide 17 text

ґଘੑͷ࢓૊Έ 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") ) }

Slide 18

Slide 18 text

TCAͰ͸Swift concurrencyͷ ࠷৽ΛΩϟονΞοϓͰ͖Δʂ https://developer.apple.com/documentation/swift/ concurrency

Slide 19

Slide 19 text

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 } • ࿥Ի࣌ͷλΠϚʔॲཧ

Slide 20

Slide 20 text

clockͷར༻ https://developer.apple.com/documentation/swift/ concurrency @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) public protocol Clock : 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ͱ͍͏࠷৽ͷػೳΛར༻

Slide 21

Slide 21 text

αϯϓϧ https://github.com/pointfreeco/swift-composable-architecture/tree/main/Examples

Slide 22

Slide 22 text

·ͱΊ • TCAͷαϯϓϧͰԼهͷ͜ͱ͕Ͱ͖Δʂ • TCAͰ͸SwiftͷओʹSwift concurrencyͷར༻ ྫ͕ݟΕΔʂ • TCAͰ͸TCAͷػೳͷ಺ɺҰ෦Λ֎෦ϥΠϒϥ Ϧͱͯ͠ར༻͢Δ͜ͱ͕Ͱ͖Δʂ • TCAͰ͸Swift concurrencyͷ࠷৽ΛΩϟον ΞοϓͰ͖Δʂ

Slide 23

Slide 23 text

ࠓճௐࠪʹ͝ڠྗ͍͍ͨͩͨΞϓϦ https://github.com/entaku0818/VoiceMemo https://apps.apple.com/jp/app/id6443528409 ⭐ελʔ͍ͩ͘͞ʂʂʂ⭐