├── VoiceRecTest
├── Assets.xcassets
│ ├── Contents.json
│ ├── AppIcon.appiconset
│ │ ├── Voice-rec-app-icon-128.png
│ │ ├── Voice-rec-app-icon-16.png
│ │ ├── Voice-rec-app-icon-256.png
│ │ ├── Voice-rec-app-icon-32.png
│ │ ├── Voice-rec-app-icon-512.png
│ │ ├── Voice-rec-app-icon-64.png
│ │ ├── Voice-rec-app-icon-1024 1.png
│ │ ├── Voice-rec-app-icon-1024.png
│ │ ├── Voice-rec-app-icon-256 1.png
│ │ ├── Voice-rec-app-icon-32 1.png
│ │ ├── Voice-rec-app-icon-512 1.png
│ │ └── Contents.json
│ └── AccentColor.colorset
│ │ └── Contents.json
├── Preview Content
│ └── Preview Assets.xcassets
│ │ └── Contents.json
├── VoiceRecTest.xcdatamodeld
│ ├── .xccurrentversion
│ └── VoiceRecTest.xcdatamodel
│ │ └── contents
├── VoiceRecTest.entitlements
├── App
│ └── VoiceRecTestApp.swift
├── Support
│ └── Extensions.swift
├── Views
│ ├── ContentView.swift
│ ├── RecorderBar.swift
│ ├── RecordingsList.swift
│ └── PlayerBar.swift
├── CoreData
│ └── Persistence.swift
└── AudioManager
│ ├── AudioPlayer.swift
│ └── AudioRecorder.swift
├── VoiceRecTest.xcodeproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── xcuserdata
│ └── umayanga.xcuserdatad
│ │ └── xcschemes
│ │ └── xcschememanagement.plist
└── project.pbxproj
└── README.md
/VoiceRecTest/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/VoiceRecTest/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/VoiceRecTest/Assets.xcassets/AppIcon.appiconset/Voice-rec-app-icon-128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/USBA/VoiceRecorder/HEAD/VoiceRecTest/Assets.xcassets/AppIcon.appiconset/Voice-rec-app-icon-128.png
--------------------------------------------------------------------------------
/VoiceRecTest/Assets.xcassets/AppIcon.appiconset/Voice-rec-app-icon-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/USBA/VoiceRecorder/HEAD/VoiceRecTest/Assets.xcassets/AppIcon.appiconset/Voice-rec-app-icon-16.png
--------------------------------------------------------------------------------
/VoiceRecTest/Assets.xcassets/AppIcon.appiconset/Voice-rec-app-icon-256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/USBA/VoiceRecorder/HEAD/VoiceRecTest/Assets.xcassets/AppIcon.appiconset/Voice-rec-app-icon-256.png
--------------------------------------------------------------------------------
/VoiceRecTest/Assets.xcassets/AppIcon.appiconset/Voice-rec-app-icon-32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/USBA/VoiceRecorder/HEAD/VoiceRecTest/Assets.xcassets/AppIcon.appiconset/Voice-rec-app-icon-32.png
--------------------------------------------------------------------------------
/VoiceRecTest/Assets.xcassets/AppIcon.appiconset/Voice-rec-app-icon-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/USBA/VoiceRecorder/HEAD/VoiceRecTest/Assets.xcassets/AppIcon.appiconset/Voice-rec-app-icon-512.png
--------------------------------------------------------------------------------
/VoiceRecTest/Assets.xcassets/AppIcon.appiconset/Voice-rec-app-icon-64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/USBA/VoiceRecorder/HEAD/VoiceRecTest/Assets.xcassets/AppIcon.appiconset/Voice-rec-app-icon-64.png
--------------------------------------------------------------------------------
/VoiceRecTest/Assets.xcassets/AppIcon.appiconset/Voice-rec-app-icon-1024 1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/USBA/VoiceRecorder/HEAD/VoiceRecTest/Assets.xcassets/AppIcon.appiconset/Voice-rec-app-icon-1024 1.png
--------------------------------------------------------------------------------
/VoiceRecTest/Assets.xcassets/AppIcon.appiconset/Voice-rec-app-icon-1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/USBA/VoiceRecorder/HEAD/VoiceRecTest/Assets.xcassets/AppIcon.appiconset/Voice-rec-app-icon-1024.png
--------------------------------------------------------------------------------
/VoiceRecTest/Assets.xcassets/AppIcon.appiconset/Voice-rec-app-icon-256 1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/USBA/VoiceRecorder/HEAD/VoiceRecTest/Assets.xcassets/AppIcon.appiconset/Voice-rec-app-icon-256 1.png
--------------------------------------------------------------------------------
/VoiceRecTest/Assets.xcassets/AppIcon.appiconset/Voice-rec-app-icon-32 1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/USBA/VoiceRecorder/HEAD/VoiceRecTest/Assets.xcassets/AppIcon.appiconset/Voice-rec-app-icon-32 1.png
--------------------------------------------------------------------------------
/VoiceRecTest/Assets.xcassets/AppIcon.appiconset/Voice-rec-app-icon-512 1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/USBA/VoiceRecorder/HEAD/VoiceRecTest/Assets.xcassets/AppIcon.appiconset/Voice-rec-app-icon-512 1.png
--------------------------------------------------------------------------------
/VoiceRecTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/VoiceRecTest/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/VoiceRecTest.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/VoiceRecTest/VoiceRecTest.xcdatamodeld/.xccurrentversion:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | _XCCurrentVersionName
6 | VoiceRecTest.xcdatamodel
7 |
8 |
9 |
--------------------------------------------------------------------------------
/VoiceRecTest/VoiceRecTest.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.files.user-selected.read-only
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # VoiceRecorder
2 | A simple voice recorder app created to try out voice recording and audio playing on SwiftUI.
3 |
4 | **Requirements**
5 | * Xcode 13 or higher
6 | * iOS 15 or higher
7 |
8 | **Technologies**
9 | * SwiftUI
10 | * AVFoundation
11 | * CoreData
12 |
13 | 
14 |
--------------------------------------------------------------------------------
/VoiceRecTest.xcodeproj/xcuserdata/umayanga.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | VoiceRecTest.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/VoiceRecTest/App/VoiceRecTestApp.swift:
--------------------------------------------------------------------------------
1 | //
2 | // VoiceRecTestApp.swift
3 | // VoiceRecTest
4 | //
5 | // Created by Umayanga Alahakoon on 2022-07-21.
6 | //
7 |
8 | import SwiftUI
9 |
10 | @main
11 | struct VoiceRecTestApp: App {
12 | let persistenceController = PersistenceController.shared
13 |
14 | var body: some Scene {
15 | WindowGroup {
16 | ContentView()
17 | .environment(\.managedObjectContext, persistenceController.container.viewContext)
18 | // always use dark mode
19 | .preferredColorScheme(.dark)
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/VoiceRecTest/Support/Extensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Extensions.swift
3 | // VoiceRecTest
4 | //
5 | // Created by Umayanga Alahakoon on 2022-07-21.
6 | //
7 |
8 | import Foundation
9 |
10 | extension Date {
11 | func toString( dateFormat format : String ) -> String {
12 | let dateFormatter = DateFormatter()
13 | dateFormatter.dateFormat = format
14 | return dateFormatter.string(from: self)
15 | }
16 | }
17 |
18 | extension DateComponentsFormatter {
19 | static let positional: DateComponentsFormatter = {
20 | let formatter = DateComponentsFormatter()
21 | formatter.allowedUnits = [.minute, .second]
22 | formatter.unitsStyle = .positional
23 | formatter.zeroFormattingBehavior = .pad
24 | return formatter
25 | }()
26 | }
27 |
--------------------------------------------------------------------------------
/VoiceRecTest/VoiceRecTest.xcdatamodeld/VoiceRecTest.xcdatamodel/contents:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/VoiceRecTest/Views/ContentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ContentView.swift
3 | // VoiceRecTest
4 | //
5 | // Created by Umayanga Alahakoon on 2022-07-21.
6 | //
7 |
8 | import SwiftUI
9 | import CoreData
10 |
11 | struct ContentView: View {
12 | @Environment(\.managedObjectContext) private var moc
13 | @ObservedObject var audioPlayer = AudioPlayer()
14 |
15 | @ObservedObject var audioRecorder = AudioRecorder()
16 |
17 | @FetchRequest(
18 | sortDescriptors: [NSSortDescriptor(keyPath: \Item.timestamp, ascending: true)],
19 | animation: .default)
20 | private var items: FetchedResults-
21 |
22 |
23 | var body: some View {
24 | NavigationView {
25 | RecordingsList(audioPlayer: audioPlayer)
26 | .safeAreaInset(edge: .bottom) {
27 | bottomBar
28 | }
29 | .navigationTitle("Voice Recorder")
30 | }
31 | }
32 |
33 | var bottomBar: some View {
34 | VStack {
35 | PlayerBar(audioPlayer: audioPlayer)
36 | RecorderBar(audioPlayer: audioPlayer)
37 | }
38 | .background(.thinMaterial)
39 | }
40 |
41 | }
42 |
43 | struct ContentView_Previews: PreviewProvider {
44 | static var previews: some View {
45 | ContentView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/VoiceRecTest/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Voice-rec-app-icon-1024.png",
5 | "idiom" : "universal",
6 | "platform" : "ios",
7 | "size" : "1024x1024"
8 | },
9 | {
10 | "filename" : "Voice-rec-app-icon-16.png",
11 | "idiom" : "mac",
12 | "scale" : "1x",
13 | "size" : "16x16"
14 | },
15 | {
16 | "filename" : "Voice-rec-app-icon-32.png",
17 | "idiom" : "mac",
18 | "scale" : "2x",
19 | "size" : "16x16"
20 | },
21 | {
22 | "filename" : "Voice-rec-app-icon-32 1.png",
23 | "idiom" : "mac",
24 | "scale" : "1x",
25 | "size" : "32x32"
26 | },
27 | {
28 | "filename" : "Voice-rec-app-icon-64.png",
29 | "idiom" : "mac",
30 | "scale" : "2x",
31 | "size" : "32x32"
32 | },
33 | {
34 | "filename" : "Voice-rec-app-icon-128.png",
35 | "idiom" : "mac",
36 | "scale" : "1x",
37 | "size" : "128x128"
38 | },
39 | {
40 | "filename" : "Voice-rec-app-icon-256 1.png",
41 | "idiom" : "mac",
42 | "scale" : "2x",
43 | "size" : "128x128"
44 | },
45 | {
46 | "filename" : "Voice-rec-app-icon-256.png",
47 | "idiom" : "mac",
48 | "scale" : "1x",
49 | "size" : "256x256"
50 | },
51 | {
52 | "filename" : "Voice-rec-app-icon-512 1.png",
53 | "idiom" : "mac",
54 | "scale" : "2x",
55 | "size" : "256x256"
56 | },
57 | {
58 | "filename" : "Voice-rec-app-icon-512.png",
59 | "idiom" : "mac",
60 | "scale" : "1x",
61 | "size" : "512x512"
62 | },
63 | {
64 | "filename" : "Voice-rec-app-icon-1024 1.png",
65 | "idiom" : "mac",
66 | "scale" : "2x",
67 | "size" : "512x512"
68 | }
69 | ],
70 | "info" : {
71 | "author" : "xcode",
72 | "version" : 1
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/VoiceRecTest/CoreData/Persistence.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Persistence.swift
3 | // VoiceRecTest
4 | //
5 | // Created by Umayanga Alahakoon on 2022-07-21.
6 | //
7 |
8 | import CoreData
9 |
10 | struct PersistenceController {
11 | static let shared = PersistenceController()
12 |
13 | static var preview: PersistenceController = {
14 | let result = PersistenceController(inMemory: true)
15 | let viewContext = result.container.viewContext
16 | for _ in 0..<10 {
17 | let newItem = Item(context: viewContext)
18 | newItem.timestamp = Date()
19 | }
20 | do {
21 | try viewContext.save()
22 | } catch {
23 | // Replace this implementation with code to handle the error appropriately.
24 | // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
25 | let nsError = error as NSError
26 | fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
27 | }
28 | return result
29 | }()
30 |
31 | let container: NSPersistentContainer
32 |
33 | init(inMemory: Bool = false) {
34 | container = NSPersistentContainer(name: "VoiceRecTest")
35 | if inMemory {
36 | container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
37 | }
38 | container.loadPersistentStores(completionHandler: { (storeDescription, error) in
39 | if let error = error as NSError? {
40 | // Replace this implementation with code to handle the error appropriately.
41 | // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
42 |
43 | /*
44 | Typical reasons for an error here include:
45 | * The parent directory does not exist, cannot be created, or disallows writing.
46 | * The persistent store is not accessible, due to permissions or data protection when the device is locked.
47 | * The device is out of space.
48 | * The store could not be migrated to the current model version.
49 | Check the error message to determine what the actual problem was.
50 | */
51 | fatalError("Unresolved error \(error), \(error.userInfo)")
52 | }
53 | })
54 | container.viewContext.automaticallyMergesChangesFromParent = true
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/VoiceRecTest/Views/RecorderBar.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RecorderBar.swift
3 | // VoiceRecTest
4 | //
5 | // Created by Umayanga Alahakoon on 2022-07-22.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct RecorderBar: View {
11 | @ObservedObject var audioRecorder = AudioRecorder()
12 | @ObservedObject var audioPlayer: AudioPlayer
13 |
14 | @State var buttonSize: CGFloat = 1
15 |
16 | var repeatingAnimation: Animation {
17 | Animation.linear(duration: 0.5)
18 | .repeatForever()
19 | }
20 |
21 | var body: some View {
22 | VStack {
23 |
24 | if let audioRecorder = audioRecorder.audioRecorder, audioRecorder.isRecording {
25 | TimelineView(.periodic(from: .now, by: 1)) { _ in
26 | // recording duration
27 | Text(DateComponentsFormatter.positional.string(from: audioRecorder.currentTime) ?? "0:00")
28 | .font(.title3)
29 | .fontWeight(.semibold)
30 | }
31 | .transition(.scale)
32 | }
33 |
34 | recordButton
35 | }
36 | .padding()
37 | .frame(maxWidth: .infinity, alignment: .center)
38 | }
39 |
40 | var recordButton: some View {
41 | Button {
42 | if audioRecorder.isRecording {
43 | stopRecording()
44 | } else {
45 | startRecording()
46 | }
47 | } label: {
48 | Image(systemName: audioRecorder.isRecording ? "stop.circle.fill" : "mic.circle.fill")
49 | .resizable()
50 | .aspectRatio(contentMode: .fill)
51 | .frame(width: 65, height: 65)
52 | .clipped()
53 | .foregroundColor(.red)
54 | .scaleEffect(buttonSize)
55 | .onChange(of: audioRecorder.isRecording) { isRecording in
56 | if isRecording {
57 | withAnimation(repeatingAnimation) { buttonSize = 1.1 }
58 | } else {
59 | withAnimation { buttonSize = 1 }
60 | }
61 | }
62 | }
63 | }
64 |
65 | func startRecording() {
66 | if audioPlayer.audioPlayer?.isPlaying ?? false {
67 | // stop any playing recordings
68 | audioPlayer.stopPlayback()
69 | DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
70 | // Start Recording
71 | audioRecorder.startRecording()
72 | }
73 | } else {
74 | // Start Recording
75 | audioRecorder.startRecording()
76 | }
77 | }
78 |
79 | func stopRecording() {
80 | // Stop Recording
81 | audioRecorder.stopRecording()
82 | }
83 |
84 | }
85 |
86 | struct RecorderBar_Previews: PreviewProvider {
87 | static var previews: some View {
88 | RecorderBar(audioPlayer: AudioPlayer())
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/VoiceRecTest/AudioManager/AudioPlayer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AudioPlayer.swift
3 | // VoiceRecTest
4 | //
5 | // Created by Umayanga Alahakoon on 2022-07-21.
6 | //
7 |
8 | import Foundation
9 | import SwiftUI
10 | import Combine
11 | import AVFoundation
12 |
13 | class AudioPlayer: NSObject, ObservableObject, AVAudioPlayerDelegate {
14 |
15 | @Published var currentlyPlaying: Recording?
16 | @Published var isPlaying = false
17 |
18 | var audioPlayer: AVAudioPlayer?
19 |
20 | func startPlayback(recording: Recording) {
21 | if let recordingData = recording.recordingData {
22 | let playbackSession = AVAudioSession.sharedInstance()
23 |
24 |
25 | do {
26 | try playbackSession.setCategory(AVAudioSession.Category.playback, mode: AVAudioSession.Mode.spokenAudio)
27 | try playbackSession.setActive(true)
28 | print("Start Recording - Playback session setted")
29 | } catch {
30 | print("Play Recording - Failed to set up playback session")
31 | }
32 |
33 | do {
34 | audioPlayer = try AVAudioPlayer(data: recordingData)
35 | audioPlayer?.delegate = self
36 | audioPlayer?.play()
37 | isPlaying = true
38 | print("Play Recording - Playing")
39 | withAnimation(.spring()) {
40 | currentlyPlaying = recording
41 | }
42 | } catch {
43 | print("Play Recording - Playback failed: - \(error)")
44 | withAnimation {
45 | currentlyPlaying = nil
46 | }
47 | }
48 | } else {
49 | print("Play Recording - Could not get the recording data")
50 | withAnimation {
51 | currentlyPlaying = nil
52 | }
53 | }
54 | }
55 |
56 | func pausePlayback() {
57 | audioPlayer?.pause()
58 | isPlaying = false
59 | print("Play Recording - Paused")
60 | }
61 |
62 | func resumePlayback() {
63 | audioPlayer?.play()
64 | isPlaying = true
65 | print("Play Recording - Resumed")
66 | }
67 |
68 | func stopPlayback() {
69 | if audioPlayer != nil {
70 | audioPlayer?.stop()
71 | isPlaying = false
72 | print("Play Recording - Stopped")
73 | withAnimation(.spring()) {
74 | self.currentlyPlaying = nil
75 | }
76 | } else {
77 | print("Play Recording - Failed to Stop playing - Coz the recording is not playing")
78 | }
79 | }
80 |
81 | func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
82 | if flag {
83 | isPlaying = false
84 | print("Play Recording - Recoring finished playing")
85 | DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
86 | withAnimation(.spring()) {
87 | self.currentlyPlaying = nil
88 | }
89 | }
90 | }
91 | }
92 |
93 | }
94 |
--------------------------------------------------------------------------------
/VoiceRecTest/Views/RecordingsList.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RecordingsList.swift
3 | // VoiceRecTest
4 | //
5 | // Created by Umayanga Alahakoon on 2022-07-21.
6 | //
7 |
8 | import SwiftUI
9 | import CoreData
10 | import AVFoundation
11 |
12 | struct RecordingsList: View {
13 | @Environment(\.managedObjectContext) private var moc
14 | @ObservedObject var audioPlayer: AudioPlayer
15 |
16 | @FetchRequest(
17 | sortDescriptors: [NSSortDescriptor(keyPath: \Recording.createdAt, ascending: true)],
18 | animation: .default)
19 | private var recordings: FetchedResults
20 |
21 | var body: some View {
22 | List {
23 | ForEach(recordings, id: \.id) { recording in
24 | RecordingRow(audioPlayer: audioPlayer, recording: recording)
25 | }
26 | .onDelete(perform: delete)
27 | }
28 | }
29 |
30 | func delete(at offsets: IndexSet) {
31 | withAnimation {
32 | offsets.map { recordings[$0] }.forEach(moc.delete)
33 |
34 | do {
35 | try moc.save()
36 | } catch {
37 | let nsError = error as NSError
38 | fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
39 | }
40 | }
41 | }
42 | }
43 |
44 | struct RecordingRow: View {
45 | @ObservedObject var audioPlayer: AudioPlayer
46 | var recording: Recording
47 |
48 | var isPlayingThisRecording: Bool {
49 | audioPlayer.currentlyPlaying?.id == recording.id
50 | }
51 |
52 | var body: some View {
53 | HStack {
54 | VStack(alignment: .leading) {
55 | Text(recording.name ?? "Recording")
56 | .fontWeight(isPlayingThisRecording ? .bold : .regular)
57 | Group {
58 | if let recordingData = recording.recordingData, let duration = getDuration(of: recordingData) {
59 | Text(DateComponentsFormatter.positional.string(from: duration) ?? "0:00")
60 | .font(.caption2)
61 | .foregroundColor(.secondary)
62 | }
63 | }
64 | }
65 | Spacer()
66 | Button {
67 | audioPlayer.startPlayback(recording: recording)
68 | } label: {
69 | Image(systemName: "play.circle.fill")
70 | .imageScale(.large)
71 | .symbolRenderingMode(.palette)
72 | .foregroundStyle(.primary, .tertiary)
73 | }
74 | }
75 | .tint(isPlayingThisRecording ? .green : .blue)
76 | }
77 |
78 | func getDuration(of recordingData: Data) -> TimeInterval? {
79 | do {
80 | return try AVAudioPlayer(data: recordingData).duration
81 | } catch {
82 | print("Failed to get the duration for recording on the list: Recording Name - \(recording.name ?? "")")
83 | return nil
84 | }
85 | }
86 | }
87 |
88 | struct RecordingsList_Previews: PreviewProvider {
89 | static var previews: some View {
90 | RecordingsList(audioPlayer: AudioPlayer())
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/VoiceRecTest/Views/PlayerBar.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PlayerBar.swift
3 | // VoiceRecTest
4 | //
5 | // Created by Umayanga Alahakoon on 2022-07-21.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct PlayerBar: View {
11 | @ObservedObject var audioPlayer: AudioPlayer
12 | @State var sliderValue: Double = 0.0
13 | @State private var isDragging = false
14 |
15 | let timer = Timer
16 | .publish(every: 0.5, on: .main, in: .common)
17 | .autoconnect()
18 |
19 | var body: some View {
20 | if let player = audioPlayer.audioPlayer, let currentlyPlaying = audioPlayer.currentlyPlaying {
21 | VStack {
22 |
23 | // Slider
24 | Slider(value: $sliderValue, in: 0...player.duration) { dragging in
25 | print("Editing the slider: \(dragging)")
26 | isDragging = dragging
27 | if !dragging {
28 | player.currentTime = sliderValue
29 | }
30 | }
31 | .tint(.primary)
32 |
33 | // Time passed & Time remaining
34 | HStack {
35 | Text(DateComponentsFormatter.positional.string(from: player.currentTime) ?? "0:00")
36 | Spacer()
37 | Text("-\(DateComponentsFormatter.positional.string(from: (player.duration - player.currentTime) ) ?? "0:00")")
38 | }
39 | .font(.caption)
40 | .foregroundColor(.secondary)
41 |
42 | HStack(spacing: 15) {
43 | // Play/Pause Button
44 | Button {
45 | if audioPlayer.isPlaying {
46 | // Pause
47 | audioPlayer.pausePlayback()
48 | } else {
49 | // Play
50 | audioPlayer.resumePlayback()
51 | }
52 | } label: {
53 | Image(systemName: audioPlayer.isPlaying ? "pause.fill" : "play.fill")
54 | .font(.title2)
55 | .imageScale(.large)
56 | }
57 |
58 | // Recording name
59 | Text(currentlyPlaying.name ?? "Recording")
60 | .fontWeight(.semibold)
61 | .lineLimit(1)
62 |
63 | Spacer()
64 |
65 | // Stop button
66 | Button {
67 | audioPlayer.stopPlayback()
68 | } label: {
69 | Image(systemName: "xmark.circle.fill")
70 | .font(.title2)
71 | .imageScale(.large)
72 | .symbolRenderingMode(.hierarchical)
73 | }
74 |
75 | }
76 | .padding(.top, 10)
77 | }
78 | .padding()
79 | .foregroundColor(.primary)
80 | .onAppear {
81 | sliderValue = 0
82 | }
83 | .onReceive(timer) { _ in
84 | guard let player = audioPlayer.audioPlayer, !isDragging else { return }
85 | sliderValue = player.currentTime
86 | }
87 | .transition(.scale(scale: 0, anchor: .bottom))
88 |
89 | Divider()
90 | }
91 | }
92 | }
93 |
94 | struct PlayerBar_Previews: PreviewProvider {
95 | static var previews: some View {
96 | PlayerBar(audioPlayer: AudioPlayer())
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/VoiceRecTest/AudioManager/AudioRecorder.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AudioRecorder.swift
3 | // VoiceRecTest
4 | //
5 | // Created by Umayanga Alahakoon on 2022-07-21.
6 | //
7 |
8 | import Foundation
9 | import SwiftUI
10 | import AVFoundation
11 | import Combine
12 | import CoreData
13 |
14 | class AudioRecorder: NSObject,ObservableObject {
15 |
16 | let moc = PersistenceController.shared.container.viewContext
17 |
18 | var audioRecorder: AVAudioRecorder?
19 |
20 | @Published private var recordingName = "Recording1"
21 | @Published private var recordingDate = Date()
22 | @Published private var recordingURL: URL?
23 |
24 | @Published var isRecording = false
25 |
26 | // MARK: - Start Recording
27 |
28 | func startRecording() {
29 | let recordingSession = AVAudioSession.sharedInstance()
30 |
31 | do {
32 | try recordingSession.setCategory(.playAndRecord, mode: .default)
33 | try recordingSession.setActive(true)
34 | print("Start Recording - Recording session setted")
35 | } catch {
36 | print("Start Recording - Failed to set up recording session")
37 | }
38 |
39 | let currentDateTime = Date.now
40 |
41 | recordingDate = currentDateTime
42 | recordingName = "\(currentDateTime.toString(dateFormat: "dd-MM-YY_'at'_HH:mm:ss"))"
43 |
44 | // save the recording to the temporary directory
45 | let tempDirectory = FileManager.default.temporaryDirectory
46 | let recordingFileURL = tempDirectory.appendingPathComponent(recordingName).appendingPathExtension("m4a")
47 | recordingURL = recordingFileURL
48 |
49 | let settings = [
50 | AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
51 | AVSampleRateKey: 12000,
52 | AVNumberOfChannelsKey: 1,
53 | AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
54 | ]
55 |
56 | do {
57 | audioRecorder = try AVAudioRecorder(url: recordingFileURL, settings: settings)
58 | audioRecorder?.record()
59 |
60 | withAnimation {
61 | isRecording = true
62 | }
63 | print("Start Recording - Recording Started")
64 | } catch {
65 | print("Start Recording - Could not start recording")
66 | }
67 | }
68 |
69 | // MARK: - Stop Recording
70 |
71 | func stopRecording() {
72 | audioRecorder?.stop()
73 | withAnimation {
74 | isRecording = false
75 | }
76 |
77 | if let recordingURL {
78 | do {
79 | let recordingDate = try Data(contentsOf: recordingURL)
80 | print("Stop Recording - Saving to CoreData")
81 | // save the recording to CoreData
82 | saveRecordingOnCoreData(recordingData: recordingDate)
83 | } catch {
84 | print("Stop Recording - Could not save to CoreData - Cannot get the recording data from URL: \(error)")
85 | }
86 |
87 | } else {
88 | print("Stop Recording - Could not save to CoreData - Cannot find the recording URL")
89 | }
90 |
91 | }
92 |
93 | // MARK: - CoreData --------------------------------------
94 |
95 | func saveRecordingOnCoreData(recordingData: Data) {
96 | let newRecording = Recording(context: moc)
97 | newRecording.id = UUID()
98 | newRecording.name = recordingName
99 | newRecording.createdAt = recordingDate
100 | newRecording.recordingData = recordingData
101 |
102 | do {
103 | try moc.save()
104 | print("Stop Recording - Successfully saved to CoreData")
105 | // delete the recording stored in the temporary directory
106 | deleteRecordingFile()
107 | } catch {
108 | let nsError = error as NSError
109 | fatalError("Stop Recording - Failed to save to CoreData - Unresolved error \(nsError), \(nsError.userInfo)")
110 | }
111 | }
112 |
113 | func deleteRecordingFile() {
114 | if let recordingURL {
115 | do {
116 | try FileManager.default.removeItem(at: recordingURL)
117 | print("Stop Recording - Successfully deleted the recording file")
118 | } catch {
119 | print("Stop Recording - Could not delete the recording file - Cannot find the recording URL")
120 | }
121 | }
122 | }
123 |
124 | }
125 |
--------------------------------------------------------------------------------
/VoiceRecTest.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 56;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 0236343E288A96D3008A0944 /* RecorderBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0236343D288A96D3008A0944 /* RecorderBar.swift */; };
11 | 027811B92889753F00A4A61D /* VoiceRecTestApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 027811B82889753F00A4A61D /* VoiceRecTestApp.swift */; };
12 | 027811BB2889753F00A4A61D /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 027811BA2889753F00A4A61D /* Persistence.swift */; };
13 | 027811BE2889753F00A4A61D /* VoiceRecTest.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 027811BC2889753F00A4A61D /* VoiceRecTest.xcdatamodeld */; };
14 | 027811C02889753F00A4A61D /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 027811BF2889753F00A4A61D /* ContentView.swift */; };
15 | 027811C22889754300A4A61D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 027811C12889754300A4A61D /* Assets.xcassets */; };
16 | 027811C62889754300A4A61D /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 027811C52889754300A4A61D /* Preview Assets.xcassets */; };
17 | 027811D428897A5600A4A61D /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 027811D328897A5600A4A61D /* Extensions.swift */; };
18 | 027811D728897A8600A4A61D /* AudioRecorder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 027811D628897A8600A4A61D /* AudioRecorder.swift */; };
19 | 027811D928897A9900A4A61D /* AudioPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 027811D828897A9900A4A61D /* AudioPlayer.swift */; };
20 | 027811DB28897AD200A4A61D /* RecordingsList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 027811DA28897AD200A4A61D /* RecordingsList.swift */; };
21 | 027811DD2889932100A4A61D /* PlayerBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 027811DC2889932100A4A61D /* PlayerBar.swift */; };
22 | /* End PBXBuildFile section */
23 |
24 | /* Begin PBXFileReference section */
25 | 0236343D288A96D3008A0944 /* RecorderBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecorderBar.swift; sourceTree = ""; };
26 | 027811B52889753F00A4A61D /* VoiceRecTest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = VoiceRecTest.app; sourceTree = BUILT_PRODUCTS_DIR; };
27 | 027811B82889753F00A4A61D /* VoiceRecTestApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceRecTestApp.swift; sourceTree = ""; };
28 | 027811BA2889753F00A4A61D /* Persistence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Persistence.swift; sourceTree = ""; };
29 | 027811BD2889753F00A4A61D /* VoiceRecTest.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = VoiceRecTest.xcdatamodel; sourceTree = ""; };
30 | 027811BF2889753F00A4A61D /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; };
31 | 027811C12889754300A4A61D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
32 | 027811C32889754300A4A61D /* VoiceRecTest.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = VoiceRecTest.entitlements; sourceTree = ""; };
33 | 027811C52889754300A4A61D /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
34 | 027811D328897A5600A4A61D /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = ""; };
35 | 027811D628897A8600A4A61D /* AudioRecorder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioRecorder.swift; sourceTree = ""; };
36 | 027811D828897A9900A4A61D /* AudioPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioPlayer.swift; sourceTree = ""; };
37 | 027811DA28897AD200A4A61D /* RecordingsList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecordingsList.swift; sourceTree = ""; };
38 | 027811DC2889932100A4A61D /* PlayerBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerBar.swift; sourceTree = ""; };
39 | /* End PBXFileReference section */
40 |
41 | /* Begin PBXFrameworksBuildPhase section */
42 | 027811B22889753F00A4A61D /* Frameworks */ = {
43 | isa = PBXFrameworksBuildPhase;
44 | buildActionMask = 2147483647;
45 | files = (
46 | );
47 | runOnlyForDeploymentPostprocessing = 0;
48 | };
49 | /* End PBXFrameworksBuildPhase section */
50 |
51 | /* Begin PBXGroup section */
52 | 021DAD69288AC939005AE5D1 /* App */ = {
53 | isa = PBXGroup;
54 | children = (
55 | 027811B82889753F00A4A61D /* VoiceRecTestApp.swift */,
56 | );
57 | path = App;
58 | sourceTree = "";
59 | };
60 | 027811AC2889753F00A4A61D = {
61 | isa = PBXGroup;
62 | children = (
63 | 027811B72889753F00A4A61D /* VoiceRecTest */,
64 | 027811B62889753F00A4A61D /* Products */,
65 | );
66 | sourceTree = "";
67 | };
68 | 027811B62889753F00A4A61D /* Products */ = {
69 | isa = PBXGroup;
70 | children = (
71 | 027811B52889753F00A4A61D /* VoiceRecTest.app */,
72 | );
73 | name = Products;
74 | sourceTree = "";
75 | };
76 | 027811B72889753F00A4A61D /* VoiceRecTest */ = {
77 | isa = PBXGroup;
78 | children = (
79 | 021DAD69288AC939005AE5D1 /* App */,
80 | 027811D528897A6800A4A61D /* AudioManager */,
81 | 027811CC2889786700A4A61D /* Views */,
82 | 027811D028897A2E00A4A61D /* Support */,
83 | 027811DE28899C9100A4A61D /* CoreData */,
84 | 027811C12889754300A4A61D /* Assets.xcassets */,
85 | 027811C32889754300A4A61D /* VoiceRecTest.entitlements */,
86 | 027811BC2889753F00A4A61D /* VoiceRecTest.xcdatamodeld */,
87 | 027811C42889754300A4A61D /* Preview Content */,
88 | );
89 | path = VoiceRecTest;
90 | sourceTree = "";
91 | };
92 | 027811C42889754300A4A61D /* Preview Content */ = {
93 | isa = PBXGroup;
94 | children = (
95 | 027811C52889754300A4A61D /* Preview Assets.xcassets */,
96 | );
97 | path = "Preview Content";
98 | sourceTree = "";
99 | };
100 | 027811CC2889786700A4A61D /* Views */ = {
101 | isa = PBXGroup;
102 | children = (
103 | 027811BF2889753F00A4A61D /* ContentView.swift */,
104 | 027811DA28897AD200A4A61D /* RecordingsList.swift */,
105 | 027811DC2889932100A4A61D /* PlayerBar.swift */,
106 | 0236343D288A96D3008A0944 /* RecorderBar.swift */,
107 | );
108 | path = Views;
109 | sourceTree = "";
110 | };
111 | 027811D028897A2E00A4A61D /* Support */ = {
112 | isa = PBXGroup;
113 | children = (
114 | 027811D328897A5600A4A61D /* Extensions.swift */,
115 | );
116 | path = Support;
117 | sourceTree = "";
118 | };
119 | 027811D528897A6800A4A61D /* AudioManager */ = {
120 | isa = PBXGroup;
121 | children = (
122 | 027811D628897A8600A4A61D /* AudioRecorder.swift */,
123 | 027811D828897A9900A4A61D /* AudioPlayer.swift */,
124 | );
125 | path = AudioManager;
126 | sourceTree = "";
127 | };
128 | 027811DE28899C9100A4A61D /* CoreData */ = {
129 | isa = PBXGroup;
130 | children = (
131 | 027811BA2889753F00A4A61D /* Persistence.swift */,
132 | );
133 | path = CoreData;
134 | sourceTree = "";
135 | };
136 | /* End PBXGroup section */
137 |
138 | /* Begin PBXNativeTarget section */
139 | 027811B42889753F00A4A61D /* VoiceRecTest */ = {
140 | isa = PBXNativeTarget;
141 | buildConfigurationList = 027811C92889754300A4A61D /* Build configuration list for PBXNativeTarget "VoiceRecTest" */;
142 | buildPhases = (
143 | 027811B12889753F00A4A61D /* Sources */,
144 | 027811B22889753F00A4A61D /* Frameworks */,
145 | 027811B32889753F00A4A61D /* Resources */,
146 | );
147 | buildRules = (
148 | );
149 | dependencies = (
150 | );
151 | name = VoiceRecTest;
152 | productName = VoiceRecTest;
153 | productReference = 027811B52889753F00A4A61D /* VoiceRecTest.app */;
154 | productType = "com.apple.product-type.application";
155 | };
156 | /* End PBXNativeTarget section */
157 |
158 | /* Begin PBXProject section */
159 | 027811AD2889753F00A4A61D /* Project object */ = {
160 | isa = PBXProject;
161 | attributes = {
162 | BuildIndependentTargetsInParallel = 1;
163 | LastSwiftUpdateCheck = 1400;
164 | LastUpgradeCheck = 1400;
165 | TargetAttributes = {
166 | 027811B42889753F00A4A61D = {
167 | CreatedOnToolsVersion = 14.0;
168 | };
169 | };
170 | };
171 | buildConfigurationList = 027811B02889753F00A4A61D /* Build configuration list for PBXProject "VoiceRecTest" */;
172 | compatibilityVersion = "Xcode 14.0";
173 | developmentRegion = en;
174 | hasScannedForEncodings = 0;
175 | knownRegions = (
176 | en,
177 | Base,
178 | );
179 | mainGroup = 027811AC2889753F00A4A61D;
180 | productRefGroup = 027811B62889753F00A4A61D /* Products */;
181 | projectDirPath = "";
182 | projectRoot = "";
183 | targets = (
184 | 027811B42889753F00A4A61D /* VoiceRecTest */,
185 | );
186 | };
187 | /* End PBXProject section */
188 |
189 | /* Begin PBXResourcesBuildPhase section */
190 | 027811B32889753F00A4A61D /* Resources */ = {
191 | isa = PBXResourcesBuildPhase;
192 | buildActionMask = 2147483647;
193 | files = (
194 | 027811C62889754300A4A61D /* Preview Assets.xcassets in Resources */,
195 | 027811C22889754300A4A61D /* Assets.xcassets in Resources */,
196 | );
197 | runOnlyForDeploymentPostprocessing = 0;
198 | };
199 | /* End PBXResourcesBuildPhase section */
200 |
201 | /* Begin PBXSourcesBuildPhase section */
202 | 027811B12889753F00A4A61D /* Sources */ = {
203 | isa = PBXSourcesBuildPhase;
204 | buildActionMask = 2147483647;
205 | files = (
206 | 027811B92889753F00A4A61D /* VoiceRecTestApp.swift in Sources */,
207 | 027811BB2889753F00A4A61D /* Persistence.swift in Sources */,
208 | 027811D928897A9900A4A61D /* AudioPlayer.swift in Sources */,
209 | 0236343E288A96D3008A0944 /* RecorderBar.swift in Sources */,
210 | 027811C02889753F00A4A61D /* ContentView.swift in Sources */,
211 | 027811BE2889753F00A4A61D /* VoiceRecTest.xcdatamodeld in Sources */,
212 | 027811DD2889932100A4A61D /* PlayerBar.swift in Sources */,
213 | 027811DB28897AD200A4A61D /* RecordingsList.swift in Sources */,
214 | 027811D728897A8600A4A61D /* AudioRecorder.swift in Sources */,
215 | 027811D428897A5600A4A61D /* Extensions.swift in Sources */,
216 | );
217 | runOnlyForDeploymentPostprocessing = 0;
218 | };
219 | /* End PBXSourcesBuildPhase section */
220 |
221 | /* Begin XCBuildConfiguration section */
222 | 027811C72889754300A4A61D /* Debug */ = {
223 | isa = XCBuildConfiguration;
224 | buildSettings = {
225 | ALWAYS_SEARCH_USER_PATHS = NO;
226 | CLANG_ANALYZER_NONNULL = YES;
227 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
228 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
229 | CLANG_ENABLE_MODULES = YES;
230 | CLANG_ENABLE_OBJC_ARC = YES;
231 | CLANG_ENABLE_OBJC_WEAK = YES;
232 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
233 | CLANG_WARN_BOOL_CONVERSION = YES;
234 | CLANG_WARN_COMMA = YES;
235 | CLANG_WARN_CONSTANT_CONVERSION = YES;
236 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
237 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
238 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
239 | CLANG_WARN_EMPTY_BODY = YES;
240 | CLANG_WARN_ENUM_CONVERSION = YES;
241 | CLANG_WARN_INFINITE_RECURSION = YES;
242 | CLANG_WARN_INT_CONVERSION = YES;
243 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
244 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
245 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
246 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
247 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
248 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
249 | CLANG_WARN_STRICT_PROTOTYPES = YES;
250 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
251 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
252 | CLANG_WARN_UNREACHABLE_CODE = YES;
253 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
254 | COPY_PHASE_STRIP = NO;
255 | DEBUG_INFORMATION_FORMAT = dwarf;
256 | ENABLE_STRICT_OBJC_MSGSEND = YES;
257 | ENABLE_TESTABILITY = YES;
258 | GCC_C_LANGUAGE_STANDARD = gnu11;
259 | GCC_DYNAMIC_NO_PIC = NO;
260 | GCC_NO_COMMON_BLOCKS = YES;
261 | GCC_OPTIMIZATION_LEVEL = 0;
262 | GCC_PREPROCESSOR_DEFINITIONS = (
263 | "DEBUG=1",
264 | "$(inherited)",
265 | );
266 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
267 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
268 | GCC_WARN_UNDECLARED_SELECTOR = YES;
269 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
270 | GCC_WARN_UNUSED_FUNCTION = YES;
271 | GCC_WARN_UNUSED_VARIABLE = YES;
272 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
273 | MTL_FAST_MATH = YES;
274 | ONLY_ACTIVE_ARCH = YES;
275 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
276 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
277 | };
278 | name = Debug;
279 | };
280 | 027811C82889754300A4A61D /* Release */ = {
281 | isa = XCBuildConfiguration;
282 | buildSettings = {
283 | ALWAYS_SEARCH_USER_PATHS = NO;
284 | CLANG_ANALYZER_NONNULL = YES;
285 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
286 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
287 | CLANG_ENABLE_MODULES = YES;
288 | CLANG_ENABLE_OBJC_ARC = YES;
289 | CLANG_ENABLE_OBJC_WEAK = YES;
290 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
291 | CLANG_WARN_BOOL_CONVERSION = YES;
292 | CLANG_WARN_COMMA = YES;
293 | CLANG_WARN_CONSTANT_CONVERSION = YES;
294 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
295 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
296 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
297 | CLANG_WARN_EMPTY_BODY = YES;
298 | CLANG_WARN_ENUM_CONVERSION = YES;
299 | CLANG_WARN_INFINITE_RECURSION = YES;
300 | CLANG_WARN_INT_CONVERSION = YES;
301 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
302 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
303 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
304 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
305 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
306 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
307 | CLANG_WARN_STRICT_PROTOTYPES = YES;
308 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
309 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
310 | CLANG_WARN_UNREACHABLE_CODE = YES;
311 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
312 | COPY_PHASE_STRIP = NO;
313 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
314 | ENABLE_NS_ASSERTIONS = NO;
315 | ENABLE_STRICT_OBJC_MSGSEND = YES;
316 | GCC_C_LANGUAGE_STANDARD = gnu11;
317 | GCC_NO_COMMON_BLOCKS = YES;
318 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
319 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
320 | GCC_WARN_UNDECLARED_SELECTOR = YES;
321 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
322 | GCC_WARN_UNUSED_FUNCTION = YES;
323 | GCC_WARN_UNUSED_VARIABLE = YES;
324 | MTL_ENABLE_DEBUG_INFO = NO;
325 | MTL_FAST_MATH = YES;
326 | SWIFT_COMPILATION_MODE = wholemodule;
327 | SWIFT_OPTIMIZATION_LEVEL = "-O";
328 | };
329 | name = Release;
330 | };
331 | 027811CA2889754300A4A61D /* Debug */ = {
332 | isa = XCBuildConfiguration;
333 | buildSettings = {
334 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
335 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
336 | CODE_SIGN_ENTITLEMENTS = VoiceRecTest/VoiceRecTest.entitlements;
337 | CODE_SIGN_STYLE = Automatic;
338 | CURRENT_PROJECT_VERSION = 1;
339 | DEVELOPMENT_ASSET_PATHS = "\"VoiceRecTest/Preview Content\"";
340 | DEVELOPMENT_TEAM = BJY4B596D6;
341 | ENABLE_HARDENED_RUNTIME = YES;
342 | ENABLE_PREVIEWS = YES;
343 | GENERATE_INFOPLIST_FILE = YES;
344 | INFOPLIST_KEY_CFBundleDisplayName = "Voice Recorder";
345 | INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
346 | INFOPLIST_KEY_NSMicrophoneUsageDescription = "Need access to the microphone in order to record audio";
347 | "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES;
348 | "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES;
349 | "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES;
350 | "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES;
351 | "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES;
352 | "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES;
353 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
354 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
355 | IPHONEOS_DEPLOYMENT_TARGET = 15.0;
356 | LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
357 | "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
358 | MACOSX_DEPLOYMENT_TARGET = 12.0;
359 | MARKETING_VERSION = 1.0;
360 | PRODUCT_BUNDLE_IDENTIFIER = com.umayanga.VoiceRecorder;
361 | PRODUCT_NAME = "$(TARGET_NAME)";
362 | SDKROOT = auto;
363 | SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx";
364 | SWIFT_EMIT_LOC_STRINGS = YES;
365 | SWIFT_VERSION = 5.0;
366 | TARGETED_DEVICE_FAMILY = "1,2";
367 | };
368 | name = Debug;
369 | };
370 | 027811CB2889754300A4A61D /* Release */ = {
371 | isa = XCBuildConfiguration;
372 | buildSettings = {
373 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
374 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
375 | CODE_SIGN_ENTITLEMENTS = VoiceRecTest/VoiceRecTest.entitlements;
376 | CODE_SIGN_STYLE = Automatic;
377 | CURRENT_PROJECT_VERSION = 1;
378 | DEVELOPMENT_ASSET_PATHS = "\"VoiceRecTest/Preview Content\"";
379 | DEVELOPMENT_TEAM = BJY4B596D6;
380 | ENABLE_HARDENED_RUNTIME = YES;
381 | ENABLE_PREVIEWS = YES;
382 | GENERATE_INFOPLIST_FILE = YES;
383 | INFOPLIST_KEY_CFBundleDisplayName = "Voice Recorder";
384 | INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
385 | INFOPLIST_KEY_NSMicrophoneUsageDescription = "Need access to the microphone in order to record audio";
386 | "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES;
387 | "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES;
388 | "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES;
389 | "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES;
390 | "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES;
391 | "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES;
392 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
393 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
394 | IPHONEOS_DEPLOYMENT_TARGET = 15.0;
395 | LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
396 | "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
397 | MACOSX_DEPLOYMENT_TARGET = 12.0;
398 | MARKETING_VERSION = 1.0;
399 | PRODUCT_BUNDLE_IDENTIFIER = com.umayanga.VoiceRecorder;
400 | PRODUCT_NAME = "$(TARGET_NAME)";
401 | SDKROOT = auto;
402 | SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx";
403 | SWIFT_EMIT_LOC_STRINGS = YES;
404 | SWIFT_VERSION = 5.0;
405 | TARGETED_DEVICE_FAMILY = "1,2";
406 | };
407 | name = Release;
408 | };
409 | /* End XCBuildConfiguration section */
410 |
411 | /* Begin XCConfigurationList section */
412 | 027811B02889753F00A4A61D /* Build configuration list for PBXProject "VoiceRecTest" */ = {
413 | isa = XCConfigurationList;
414 | buildConfigurations = (
415 | 027811C72889754300A4A61D /* Debug */,
416 | 027811C82889754300A4A61D /* Release */,
417 | );
418 | defaultConfigurationIsVisible = 0;
419 | defaultConfigurationName = Release;
420 | };
421 | 027811C92889754300A4A61D /* Build configuration list for PBXNativeTarget "VoiceRecTest" */ = {
422 | isa = XCConfigurationList;
423 | buildConfigurations = (
424 | 027811CA2889754300A4A61D /* Debug */,
425 | 027811CB2889754300A4A61D /* Release */,
426 | );
427 | defaultConfigurationIsVisible = 0;
428 | defaultConfigurationName = Release;
429 | };
430 | /* End XCConfigurationList section */
431 |
432 | /* Begin XCVersionGroup section */
433 | 027811BC2889753F00A4A61D /* VoiceRecTest.xcdatamodeld */ = {
434 | isa = XCVersionGroup;
435 | children = (
436 | 027811BD2889753F00A4A61D /* VoiceRecTest.xcdatamodel */,
437 | );
438 | currentVersion = 027811BD2889753F00A4A61D /* VoiceRecTest.xcdatamodel */;
439 | path = VoiceRecTest.xcdatamodeld;
440 | sourceTree = "";
441 | versionGroupType = wrapper.xcdatamodel;
442 | };
443 | /* End XCVersionGroup section */
444 | };
445 | rootObject = 027811AD2889753F00A4A61D /* Project object */;
446 | }
447 |
--------------------------------------------------------------------------------