captains-log

[TO-FIX] audiojournaling app
git clone git://git.figbert.com/captains-log.git
Log | Files | Refs

commit 0eb4e83758623590d615efd918102bf6eeb9e47b
parent 47f06da9152de36546f7516aff5ff4843f4f5f55
Author: therealFIGBERT <figbertwelner@gmail.com>
Date:   Wed, 30 Oct 2019 18:37:40 -0700

Adding audio playback functionality

Diffstat:
MAudioRecorder.swift | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
McaptainsLog.xcodeproj/project.pbxproj | 12++++++------
AcaptainsLog/AudioPlayer.swift | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
McaptainsLog/ContentView.swift | 2+-
McaptainsLog/PreviousRecordings.swift | 32++++++++++++++------------------
DcaptainsLog/RecordingDataModel.swift | 14--------------
6 files changed, 143 insertions(+), 39 deletions(-)

diff --git a/AudioRecorder.swift b/AudioRecorder.swift @@ -96,4 +96,63 @@ class AudioRecorder: NSObject, ObservableObject { recordings.sort(by: { $0.createdAt.compare($1.createdAt) == .orderedAscending}) objectWillChange.send(self) } + + func deleteRecording(urlsToDelete: [URL]) { + for url in urlsToDelete { + do { + try FileManager.default.removeItem(at: url) + } catch { + print("File could not be deleted") + } + } + fetchRecordings() + } +} + +struct RecordingRow: View { + var audioURL: URL + @ObservedObject var audioPlayer = AudioPlayer() + var body: some View { + HStack { + Text("\(audioURL.lastPathComponent)") + Spacer() + if (!audioPlayer.isPlaying) { + Button(action: { + self.audioPlayer.startPlayback(audio: self.audioURL) + }) { + Image( + uiImage: UIImage( + systemName: "play.circle", + withConfiguration: UIImage.SymbolConfiguration( + pointSize: CGFloat.init(15), + weight: .regular, + scale: .large + ) + )! + ) + } + } else { + Button(action: { + self.audioPlayer.pausePlayback() + }) { + Image( + uiImage: UIImage ( + systemName: "pause.circle", + withConfiguration: UIImage.SymbolConfiguration( + pointSize: CGFloat.init(15), + weight: .regular, + scale: .large + ) + )! + ) + } + } + } + } +} + + +struct Recording { + let fileURL: URL + let createdAt: Date } diff --git a/captainsLog.xcodeproj/project.pbxproj b/captainsLog.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 3A10D510236A25DC00FD1C96 /* AudioPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A10D50F236A25DC00FD1C96 /* AudioPlayer.swift */; }; 3A2D60F6236801FD004FA1CD /* PreviousRecordings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A2D60F5236801FD004FA1CD /* PreviousRecordings.swift */; }; 3A35D6E023579F7B005B7610 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A35D6DF23579F7B005B7610 /* AppDelegate.swift */; }; 3A35D6E223579F7B005B7610 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A35D6E123579F7B005B7610 /* SceneDelegate.swift */; }; @@ -18,10 +19,10 @@ 3A64E4F52363E98A00B5389D /* GlobalVars.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A64E4F42363E98A00B5389D /* GlobalVars.swift */; }; 3A76A1E8235AD08C00964901 /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A76A1E7235AD08C00964901 /* Settings.swift */; }; 3A76A1EA235AE6D500964901 /* Elements.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A76A1E9235AE6D500964901 /* Elements.swift */; }; - 3AE17D01236954260098D4BD /* RecordingDataModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE17D00236954260098D4BD /* RecordingDataModel.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 3A10D50F236A25DC00FD1C96 /* AudioPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioPlayer.swift; sourceTree = "<group>"; }; 3A2D60F5236801FD004FA1CD /* PreviousRecordings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviousRecordings.swift; sourceTree = "<group>"; }; 3A35D6DC23579F7B005B7610 /* captainsLog.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = captainsLog.app; sourceTree = BUILT_PRODUCTS_DIR; }; 3A35D6DF23579F7B005B7610 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; }; @@ -35,7 +36,6 @@ 3A64E4F42363E98A00B5389D /* GlobalVars.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalVars.swift; sourceTree = SOURCE_ROOT; }; 3A76A1E7235AD08C00964901 /* Settings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Settings.swift; sourceTree = SOURCE_ROOT; }; 3A76A1E9235AE6D500964901 /* Elements.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Elements.swift; sourceTree = "<group>"; }; - 3AE17D00236954260098D4BD /* RecordingDataModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecordingDataModel.swift; sourceTree = "<group>"; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -71,10 +71,10 @@ 3A35D6DF23579F7B005B7610 /* AppDelegate.swift */, 3A35D6E123579F7B005B7610 /* SceneDelegate.swift */, 3A35D6E323579F7B005B7610 /* ContentView.swift */, - 3A5D1BF323691F6F00677C61 /* AudioRecorder.swift */, - 3AE17D00236954260098D4BD /* RecordingDataModel.swift */, - 3A76A1E7235AD08C00964901 /* Settings.swift */, 3A2D60F5236801FD004FA1CD /* PreviousRecordings.swift */, + 3A76A1E7235AD08C00964901 /* Settings.swift */, + 3A5D1BF323691F6F00677C61 /* AudioRecorder.swift */, + 3A10D50F236A25DC00FD1C96 /* AudioPlayer.swift */, 3A76A1E9235AE6D500964901 /* Elements.swift */, 3A64E4F42363E98A00B5389D /* GlobalVars.swift */, 3A35D6E523579F7C005B7610 /* Assets.xcassets */, @@ -166,8 +166,8 @@ files = ( 3A35D6E023579F7B005B7610 /* AppDelegate.swift in Sources */, 3A35D6E223579F7B005B7610 /* SceneDelegate.swift in Sources */, + 3A10D510236A25DC00FD1C96 /* AudioPlayer.swift in Sources */, 3A35D6E423579F7B005B7610 /* ContentView.swift in Sources */, - 3AE17D01236954260098D4BD /* RecordingDataModel.swift in Sources */, 3A76A1E8235AD08C00964901 /* Settings.swift in Sources */, 3A2D60F6236801FD004FA1CD /* PreviousRecordings.swift in Sources */, 3A64E4F52363E98A00B5389D /* GlobalVars.swift in Sources */, diff --git a/captainsLog/AudioPlayer.swift b/captainsLog/AudioPlayer.swift @@ -0,0 +1,63 @@ +// +// AudioPlayer.swift +// captainsLog +// +// Created by Benjamin Welner on 10/30/19. +// Copyright © 2019 FIGBERT Industries. All rights reserved. +// + +import Foundation +import SwiftUI +import Combine +import AVFoundation + +class AudioPlayer: NSObject, ObservableObject, AVAudioPlayerDelegate { + + let objectWillChange = PassthroughSubject<AudioPlayer, Never>() + + var audioPlayer: AVAudioPlayer! + var isPlaying = false { + didSet { + objectWillChange.send(self) + } + } + var hasPlayed = false { + didSet { + objectWillChange.send(self) + } + } + + func startPlayback(audio: URL) { + if (!hasPlayed) { + let playbackSession = AVAudioSession.sharedInstance() + do { + try playbackSession.overrideOutputAudioPort(AVAudioSession.PortOverride.speaker) + } catch { + print("Playing over the devices speakers failed") + } + do { + audioPlayer = try AVAudioPlayer(contentsOf: audio) + audioPlayer.delegate = self + audioPlayer.play(atTime: TimeInterval(0)) + isPlaying = true + hasPlayed = true + } catch { + print("Playback failed") + } + } else { + audioPlayer.play() + isPlaying = true + } + } + + func pausePlayback() { + audioPlayer.pause() + isPlaying = false + } + + func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) { + if flag { + isPlaying = false + } + } +} diff --git a/captainsLog/ContentView.swift b/captainsLog/ContentView.swift @@ -80,7 +80,7 @@ struct ContentView: View { }) { imgTextButtonStyle(sysImg: "waveform", imgSize: 15, buttonText: "Save") } - NavigationLink(destination: PreviousRecordings()) { + NavigationLink(destination: PreviousRecordings(audioRecorder: AudioRecorder())) { imgTextButtonStyle(sysImg: "tray.full", imgSize: 15, buttonText: "Previous") } NavigationLink(destination: Settings()) { diff --git a/captainsLog/PreviousRecordings.swift b/captainsLog/PreviousRecordings.swift @@ -10,36 +10,32 @@ import SwiftUI struct PreviousRecordings: View { @EnvironmentObject var globalVars: GlobalVars + @ObservedObject var audioRecorder: AudioRecorder + var body: some View { Form { - Section { - ForEach(0 ..< globalVars.previousRecordings.count) {pos in - HStack { - VStack(alignment: .leading) { - Text("\(self.globalVars.previousRecordings[pos]["title"] ?? "Title")") - .font(.headline) - Text("\(self.globalVars.previousRecordings[pos]["date"] ?? "Date")") - .font(.caption) - } - Spacer() - VStack(alignment: .trailing) { - Text("#\(self.globalVars.previousRecordings[pos]["number"]!)") - Text("\(self.globalVars.previousRecordings[pos]["length"]!)") - .font(.caption) - } - } - } + ForEach(audioRecorder.recordings, id: \.createdAt) { recording in + RecordingRow(audioURL: recording.fileURL) } + .onDelete(perform: delete) } .navigationBarTitle( Text("Recordings"), displayMode: .inline ) + .navigationBarItems(trailing: EditButton()) + } + func delete(at offsets: IndexSet) { + var urlsToDelete = [URL]() + for index in offsets { + urlsToDelete.append(audioRecorder.recordings[index].fileURL) + } + audioRecorder.deleteRecording(urlsToDelete: urlsToDelete) } } struct PreviousRecordings_Previews: PreviewProvider { static var previews: some View { - PreviousRecordings().environmentObject(GlobalVars()) + PreviousRecordings(audioRecorder: AudioRecorder()).environmentObject(GlobalVars()) } } diff --git a/captainsLog/RecordingDataModel.swift b/captainsLog/RecordingDataModel.swift @@ -1,14 +0,0 @@ -// -// RecordingDataModel.swift -// captainsLog -// -// Created by Benjamin Welner on 10/29/19. -// Copyright © 2019 FIGBERT Industries. All rights reserved. -// - -import Foundation - -struct Recording { - let fileURL: URL - let createdAt: Date -}