0

I have developed a WatchOS app that records the voice while user is sleeping. I have noticed that this app is getting closed after several hours. How can I prevent this?

PS. The app can be running on the user's iPhone at the same time as user Apple watc, in case app running on iPhone could somehow prevent this.

Full code for audio recorder part:

import SwiftUI
import AVFoundation
import WatchKit
import CryptoKit
class AudioRecorder: NSObject, ObservableObject, AVAudioRecorderDelegate {
 @Published var isRecording = false
 @Published var recordings: [Recording] = []
 private var audioRecorder: AVAudioRecorder?
 private var currentRecordingURL: URL?
 private var encryptionKey : SymmetricKey = SymmetricKey(size: .bits256)
 private var audioBuffer: Data = Data()
 private let bufferSizeThreshold = 4096
 private var recordingTimer: Timer?
 private let recordingChunkDuration: TimeInterval = 10
 private let pauseDuration: TimeInterval = 5
 
 private var audioSessionInitialized = false // Flag to track session setup
 override init(){
 super.init()
 recordings = loadRecordings()
 encryptionKey = getEncryptionKey()
 }
 
 func startRecording() {
 setupAudioSession() // Ensure session is set up before recording
 let fileManager = FileManager.default
 let documentsDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0]
 let dateFormatter = DateFormatter()
 dateFormatter.dateFormat = "yyyyMMdd_HHmmss"
 let recordingName = dateFormatter.string(from: Date()) + ".m4a"
 currentRecordingURL = documentsDirectory.appendingPathComponent(recordingName)
 let settings: [String: Any] = [
 AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
 AVSampleRateKey: 22050.0,
 AVNumberOfChannelsKey: 1,
 AVEncoderAudioQualityKey: AVAudioQuality.min.rawValue
 ]
 do {
 audioRecorder = try AVAudioRecorder(url: currentRecordingURL!, settings: settings)
 audioRecorder?.delegate = self
 // Remove intermittent recording for now to simplify debugging
 audioRecorder?.record() // Record continuously instead of in chunks
 isRecording = true
 print("Started recording to:", currentRecordingURL!) // Debug print
 } catch {
 print("Error starting recording:", error)
 isRecording = false
 }
 }
 private func setupAudioSession() {
 do {
 let audioSession = AVAudioSession.sharedInstance()
 // Specify .record for recording-only
 //https://stackoverflow.com/questions/51597590/avaudiosession-configuration-to-record-and-play-with-others
 // [.mixWithOthers, .defaultToSpeaker] .defaultToSpeaker is unavailablke in WatchOS:
 try audioSession.setCategory(.playAndRecord, mode: .default, options: [.mixWithOthers]) //
 try audioSession.setActive(true)
 
 print("Audio session activated.")
 } catch {
 print("Error setting up audio session:", error)
 }
 }
 func stopRecording() {
 audioRecorder?.stop()
 isRecording = false
 currentRecordingURL = nil
 audioBuffer = Data()
 }
 func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder, successfully flag: Bool) {
 guard flag else { return }
 
 let tempURL = recorder.url
 let encryptionEnabled = UserDefaults.standard.bool(forKey: "encryptionEnabled")
 
 do {
 if encryptionEnabled {
 let data = try Data(contentsOf: tempURL)
 let encryptedData = try ChaChaPoly.seal(data, using: encryptionKey).combined
 
 let encryptedFileName = "encrypted_\(tempURL.lastPathComponent)"
 let encryptedURL = tempURL.deletingLastPathComponent().appendingPathComponent(encryptedFileName)
 
 try encryptedData.write(to: encryptedURL)
 
 DispatchQueue.main.async {
 self.recordings.insert(Recording(url: encryptedURL, createdAt: Date()), at: 0)
 }
 
 try FileManager.default.removeItem(at: tempURL)
 } else {
 DispatchQueue.main.async {
 self.recordings.insert(Recording(url: tempURL, createdAt: Date()), at: 0)
 }
 }
 } catch {
 print("Failed to process recording:", error)
 }
 }
 func getDecryptedData(from url: URL) throws -> Data? {
 let fileData = try Data(contentsOf: url)
 if url.lastPathComponent.hasPrefix("encrypted_") {
 let sealedBox = try ChaChaPoly.SealedBox(combined: fileData)
 let decryptedData = try ChaChaPoly.open(sealedBox, using: encryptionKey)
 return decryptedData
 } else {
 return fileData
 }
 }
 
 func loadRecordings() -> [Recording] {
 let fileManager = FileManager.default
 let documentsDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0]
 do {
 let files = try fileManager.contentsOfDirectory(at: documentsDirectory, includingPropertiesForKeys: nil)
 let recordingFiles = files.filter { 0ドル.pathExtension == "m4a" }
 return recordingFiles.map { fileURL in
 let attributes = try? fileManager.attributesOfItem(atPath: fileURL.path)
 let createdAt = attributes?[.creationDate] as? Date ?? Date()
 return Recording(url: fileURL, createdAt: createdAt)
 }.sorted(by: { 0ドル.createdAt > 1ドル.createdAt })
 } catch {
 print("Error loading recordings: \(error)")
 return []
 }
 }
 
 func getEncryptionKey() -> SymmetricKey {
 let keychainKey = "ziziapps.guardianangel.encryptionKey"
 let keychainHelper = KeychainHelper()
 if let keyData = keychainHelper.read(key: keychainKey) {
 return SymmetricKey(data: keyData)
 } else {
 // Generate a new key if one doesn't exist, then store it
 let newKey = SymmetricKey(size: .bits256)
 let keyData = newKey.withUnsafeBytes { Data(0ドル) }
 keychainHelper.save(keyData, key: keychainKey)
 return newKey
 }
 }
}
asked Feb 26, 2025 at 4:27

0

Know someone who can answer? Share a link to this question via email, Twitter, or Facebook.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.