commit 0eb4e83758623590d615efd918102bf6eeb9e47b
parent 47f06da9152de36546f7516aff5ff4843f4f5f55
Author: therealFIGBERT <figbertwelner@gmail.com>
Date: Wed, 30 Oct 2019 18:37:40 -0700
Adding audio playback functionality
Diffstat:
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
-}