Slide 1

Slide 1 text

ݸਓ։ൃΛTCAͰӡ༻͍ͯ͘͠ ͱ͍͏͜ͱ entaku

Slide 2

Slide 2 text

ݸਓ։ൃͰγϯϓϧ࿥Իͱ͍͏ΞϓϦ Λग़ͯ͠·͢ γϯϓϧ࿥Ի - AppStore

Slide 3

Slide 3 text

ϦϦʔε౰ॳ2022೥10݄͔ΒTCA(0.39.1) Λಋೖ

Slide 4

Slide 4 text

TCAͰVoiceMemoͷαϯϓϧΞϓϦ͕͋ͬ ͨͷͰ͜ΕΛ͖͔͚ͬʹ৮Γ࢝Ί·ͨ͠ TCAެࣜͷVoiceMemoαϯϓϧ

Slide 5

Slide 5 text

TCA0.39.1->1.2.0ͷܦݧ͕͋Γ·͕͢ɺ ͨͩ͠ΜͲ͍͚ͩͩͬͨͷͰऔΓ্͛·ͤΜ ڵຯ͋Δਓ͸->౰࣌ͷGitHubͷPullRequest

Slide 6

Slide 6 text

TCAΛར༻ͨ͠ػೳ։ൃΛ ͲͷΑ͏ʹਐΊ͍ͯΔ͔ʁ

Slide 7

Slide 7 text

ࠓճ͸Ի੠ͷৄࡉը໘ͰͲͷΑ͏ʹTCAΛ ར༻ͯ͠࡞੒͍ͯ͠Δ͔ݟͯΈ·͠ΐ͏🔍

Slide 8

Slide 8 text

ػೳચ͍ग़͠ͱ ઃܭͷߟ͑ํ

Slide 9

Slide 9 text

͜ͷը໘ͷػೳ਺͸େ͖͘6ͭ • Ի੠λΠτϧมߋ • ࠶ੜػೳ • ϧʔϓػೳ • 10ඵεΩοϓ/10ඵר͖໭͠ • ഒ଎ػೳ • Ի੠ϑΝΠϧγΣΞ

Slide 10

Slide 10 text

ػೳʹ߹ΘͤͯTCAͷΞΫγϣϯΛ ઃܭ enum Action: Equatable { case audioPlayerClient(TaskResult, PlaybackMode) case delegate(Delegate) case playButtonTapped case timerUpdated(TimeInterval) case titleTextFieldChanged(String) case onTapPlaySpeed case skipBy(TimeInterval) case toggleLoop } • Ի੠λΠτϧมߋ • ࠶ੜػೳ • ϧʔϓػೳ • 10ඵεΩοϓ/10ඵר͖໭͠ • ഒ଎ػೳ • Ի੠ϑΝΠϧγΣΞ ͜Ε͸ແ͍ͷͰޙͰઆ໌͠·͢

Slide 11

Slide 11 text

Ұ෦ͷॲཧ͸Action΍ଞ͔Βݺͼ ग़͞ΕΔઃఆʹͳ͍ͬͯ·͢ enum Action: Equatable { case audioPlayerClient(TaskResult, PlaybackMode) case delegate(Delegate) case playButtonTapped case timerUpdated(TimeInterval) case titleTextFieldChanged(String) case onTapPlaySpeed case skipBy(TimeInterval) case toggleLoop } • AudioPlayer͔ΒͷDelegateॲཧ • Ұཡը໘΁ͷDelegateॲཧ • ࠶ੜதͷ࣌ؒߋ৽ॲཧ

Slide 12

Slide 12 text

1ྫͰԻ੠࠶ੜػೳͷ ྲྀΕΛݟ͍͖ͯ·͢

Slide 13

Slide 13 text

case .playButtonTapped: switch state.mode { case .notPlaying: state.mode = .playing(progress: state.time / state.duration) return .run { [ url = state.url, time = state.time, playSpeed = state.playSpeed, isLoop = state.isLooping ] send in await send(.delegate(.playbackStarted)) async let playAudio: Void = send( .audioPlayerClient(TaskResult { try await self.audioPlayer.play(url, time, playSpeed, isLoop) }, .aut ) for await _ in self.clock.timer(interval: .milliseconds(500)) { let time = try await self.audioPlayer.getCurrentTime() await send(.timerUpdated(time)) } await playAudio }.cancellable(id: CancelID.play, cancelInFlight: true) Ի੠࠶ੜػೳͷྲྀΕ 1.࠶ੜঢ়ଶͷมߋ

Slide 14

Slide 14 text

Ի੠࠶ੜػೳͷྲྀΕ case .playButtonTapped: switch state.mode { case .notPlaying: state.mode = .playing(progress: state.time / state.duration) return .run { [ url = state.url, time = state.time, playSpeed = state.playSpeed, isLoop = state.isLooping ] send in await send(.delegate(.playbackStarted)) async let playAudio: Void = send( .audioPlayerClient(TaskResult { try await self.audioPlayer.play(url, time, playSpeed, isLoop) }, .autom ) for await _ in self.clock.timer(interval: .milliseconds(500)) { let time = try await self.audioPlayer.getCurrentTime() await send(.timerUpdated(time)) } await playAudio }.cancellable(id: CancelID.play, cancelInFlight: true) 1.࠶ੜঢ়ଶͷมߋ 2.ඇಉظͰAudioPlayerΛ࣮ߦ

Slide 15

Slide 15 text

Ի੠࠶ੜػೳͷྲྀΕ case .playButtonTapped: switch state.mode { case .notPlaying: state.mode = .playing(progress: state.time / state.duration) return .run { [ url = state.url, time = state.time, playSpeed = state.playSpeed, isLoop = state.isLooping ] send in await send(.delegate(.playbackStarted)) async let playAudio: Void = send( .audioPlayerClient(TaskResult { try await self.audioPlayer.play(url, time, playSpeed, isLoop) }, .autom ) for await _ in self.clock.timer(interval: .milliseconds(500)) { let time = try await self.audioPlayer.getCurrentTime() await send(.timerUpdated(time)) } await playAudio }.cancellable(id: CancelID.play, cancelInFlight: true) 1.࠶ੜঢ়ଶͷมߋ 2.ඇಉظͰAudioPlayerΛ࣮ߦ 3.Ұཡը໘΁Ի੠͕ 
 ։࢝ͨ͜͠ͱΛ௨஌

Slide 16

Slide 16 text

Ի੠࠶ੜػೳͷྲྀΕ 1.࠶ੜঢ়ଶͷมߋ 2.ඇಉظͰAudioPlayerΛ࣮ߦ 3.Ұཡը໘΁Ի੠͕ 
 ։࢝ͨ͜͠ͱΛ௨஌ case .playButtonTapped: switch state.mode { case .notPlaying: state.mode = .playing(progress: state.time / state.duration) return .run { [ url = state.url, time = state.time, playSpeed = state.playSpeed, isLoop = state.isLooping ] send in await send(.delegate(.playbackStarted)) async let playAudio: Void = send( .audioPlayerClient(TaskResult { try await self.audioPlayer.play(url, time, playSpeed, isLoop) }, .autom ) for await _ in self.clock.timer(interval: .milliseconds(500)) { let time = try await self.audioPlayer.getCurrentTime() await send(.timerUpdated(time)) } await playAudio }.cancellable(id: CancelID.play, cancelInFlight: true) 4.AudioPlayerΛ࣮ߦ͠ 
 ݁ՌΛaudioPlayerClientʹ౉͢

Slide 17

Slide 17 text

Ի੠࠶ੜػೳͷྲྀΕ 1.࠶ੜঢ়ଶͷมߋ 2.ඇಉظͰAudioPlayerΛ࣮ߦ 3.Ұཡը໘΁Ի੠͕ 
 ։࢝ͨ͜͠ͱΛ௨஌ case .playButtonTapped: switch state.mode { case .notPlaying: state.mode = .playing(progress: state.time / state.duration) return .run { [ url = state.url, time = state.time, playSpeed = state.playSpeed, isLoop = state.isLooping ] send in await send(.delegate(.playbackStarted)) async let playAudio: Void = send( .audioPlayerClient(TaskResult { try await self.audioPlayer.play(url, time, playSpeed, isLoop) }, .autom ) for await _ in self.clock.timer(interval: .milliseconds(500)) { let time = try await self.audioPlayer.getCurrentTime() await send(.timerUpdated(time)) } await playAudio }.cancellable(id: CancelID.play, cancelInFlight: true) 4.AudioPlayerΛ࣮ߦ͠ 
 ݁ՌΛaudioPlayerClientʹ౉͢ 5.playAudio͕ऴΘΔ·Ͱtimer 
 Λߋ৽

Slide 18

Slide 18 text

͜ͷΑ͏ʹը໘ͷ֤ػೳΛTCAͰఆٛͯ͠·͢ • Ի੠λΠτϧมߋ • ࠶ੜػೳ • ϧʔϓػೳ • 10ඵεΩοϓ/10ඵר͖໭͠ • ഒ଎ػೳ • Ի੠ϑΝΠϧγΣΞ

Slide 19

Slide 19 text

TCAΛӡ༻͍ͯ͠ΔதͰ ΋΍ͬͱ͍ͯ͠Δ͜ͱ🤗

Slide 20

Slide 20 text

TCAͷதͰTimerͷॲཧΛࢸΔॴͰݺͼग़͠ ͍ͯΔ🤗 for await _ in self.clock.timer(interval: .milliseconds(500)) { let time = try await self.audioPlayer.getCurrentTime() await send(.timerUpdated(time)) } • Ի੠λΠτϧมߋ • ࠶ੜػೳ • ϧʔϓػೳ • 10ඵεΩοϓ/10ඵר͖໭͠ • ഒ଎ػೳ • Ի੠ϑΝΠϧγΣΞ

Slide 21

Slide 21 text

ʮԻ੠ϑΝΠϧγΣΞʯ͸TCA࢖Θͣʹଥڠͯ͠ ·͢🤗 .navigationBarItems(trailing: Button(action: { // γΣΞ͢ΔίϯςϯπΛઃఆ let textToShare = viewStore.title var itemsToShare: [Any] = [textToShare] let inputDocumentsPath = NSHomeDirectory() + "/Documents/" + viewStore.url.lastPathComponent let audioFileURL = NSURL(fileURLWithPath: inputDocumentsPath) itemsToShare.append(audioFileURL) // UIActivityViewControllerΛදࣔ let activityViewController = UIActivityViewController(activityItems: itemsToShare, applicationActivities: nil) UIApplication.shared.windows.first?.rootViewController?.present(activityViewController, animated: true, completion: nil) }) { Image(systemName: "square.and.arrow.up") } )

Slide 22

Slide 22 text

·ͱΊ • ը໘ͷ֤ػೳΛTCAͷActionʹ౰ͯ͸ΊΔܗͰઃܭ ͢Δͱྑ͍⭕ • ϞϠͬͱ͢Δ͜ͱ΋͋Δ͕ɺɺɺTCAಋೖͯ͠΋ඞ ͣ࢖Θͳ͖Ό͍͚ͳ͍ͬͯ͜ͱ͸ͳ͍͔ΒڪΕͣಋ ೖͰ͖Δʂ • version up͕௥͍͔ͭͳ͍ɻ΋͏TCA1.9…

Slide 23

Slide 23 text

ࢀߟ • γϯϓϧ࿥Ի • https://github.com/entaku0818/VoiceMemo • TCA • https://github.com/pointfreeco/swift- composable-architecture

Slide 24

Slide 24 text

ࣗݾ঺հ • Name: entaku • Job: iOSΤϯδχΞ / AndroidΤϯδχΞ • Hobby: • ԻָϑΣε؍ઓ(ࠓ೥͸JapanJam2ճߦͥ͘ʂ) • ݸਓ։ൃ • Career: • SIer໿6೥ • εϙʔπϚονϯάΞϓϦ1೥ • ෺ྲྀITαʔϏε ໿2೥ • Voicy 2೥ X

Slide 25

Slide 25 text

Voicy - μ΢ϯϩʔυͯ͠Ͷ🤗