Added logs and activate audio session each time the app enters the foreground

main
Laurent 3 years ago
parent bb9641e8e8
commit b709b6da24
  1. 6
      LeCountdown.xcodeproj/project.pbxproj
  2. 7
      LeCountdown.xcodeproj/xcshareddata/xcschemes/LeCountdown.xcscheme
  3. 8
      LeCountdown/AppDelegate.swift
  4. 16
      LeCountdown/Conductor.swift
  5. 1
      LeCountdown/Model/Model+SharedExtensions.swift
  6. 3
      LeCountdown/Model/Persistence.swift
  7. 9
      LeCountdown/Sound/DelaySoundPlayer.swift
  8. 1
      LeCountdown/Utils/TextToSpeechRecorder.swift

@ -114,6 +114,9 @@
C4742B59298411E800D5D950 /* CountdownFormView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4742B58298411E800D5D950 /* CountdownFormView.swift */; }; C4742B59298411E800D5D950 /* CountdownFormView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4742B58298411E800D5D950 /* CountdownFormView.swift */; };
C4742B5B298414B000D5D950 /* ImageSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4742B5A298414B000D5D950 /* ImageSelectionView.swift */; }; C4742B5B298414B000D5D950 /* ImageSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4742B5A298414B000D5D950 /* ImageSelectionView.swift */; };
C4742B5F2984205000D5D950 /* ViewModifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4742B5E2984205000D5D950 /* ViewModifiers.swift */; }; C4742B5F2984205000D5D950 /* ViewModifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4742B5E2984205000D5D950 /* ViewModifiers.swift */; };
C47C933529F01B5E00C780E2 /* FileLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4556F6A29E40B7800DEB40B /* FileLogger.swift */; };
C47C933629F01B6600C780E2 /* FileUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4556F6E29E40BED00DEB40B /* FileUtils.swift */; };
C47C933729F01B7A00C780E2 /* Codable+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4556F7029E40DCF00DEB40B /* Codable+Extensions.swift */; };
C48ECC0829DAC45900DE5A66 /* AppGuard.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2B56299FFA4F00CB4FBA /* AppGuard.swift */; }; C48ECC0829DAC45900DE5A66 /* AppGuard.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2B56299FFA4F00CB4FBA /* AppGuard.swift */; };
C48ECC0929DAC47200DE5A66 /* AppGuard.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2B56299FFA4F00CB4FBA /* AppGuard.swift */; }; C48ECC0929DAC47200DE5A66 /* AppGuard.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2B56299FFA4F00CB4FBA /* AppGuard.swift */; };
C498E59F298D4DEA00E90DE0 /* LiveTimerListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C498E59E298D4DEA00E90DE0 /* LiveTimerListView.swift */; }; C498E59F298D4DEA00E90DE0 /* LiveTimerListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C498E59E298D4DEA00E90DE0 /* LiveTimerListView.swift */; };
@ -1349,6 +1352,7 @@
C498E5A6299152C600E90DE0 /* GreenCheckmarkView.swift in Sources */, C498E5A6299152C600E90DE0 /* GreenCheckmarkView.swift in Sources */,
C4BA2B3B299F838000CB4FBA /* Model+SharedExtensions.swift in Sources */, C4BA2B3B299F838000CB4FBA /* Model+SharedExtensions.swift in Sources */,
C438C7EB2981266F00BF3EF9 /* SingleTimerView.swift in Sources */, C438C7EB2981266F00BF3EF9 /* SingleTimerView.swift in Sources */,
C47C933729F01B7A00C780E2 /* Codable+Extensions.swift in Sources */,
C438C7D62981216200BF3EF9 /* LaunchWidgetBundle.swift in Sources */, C438C7D62981216200BF3EF9 /* LaunchWidgetBundle.swift in Sources */,
C4BA2B16299BE6A000CB4FBA /* IntervalGroup+CoreDataClass.swift in Sources */, C4BA2B16299BE6A000CB4FBA /* IntervalGroup+CoreDataClass.swift in Sources */,
C4F8B18B298AC288005C86A5 /* Record+CoreDataClass.swift in Sources */, C4F8B18B298AC288005C86A5 /* Record+CoreDataClass.swift in Sources */,
@ -1358,6 +1362,7 @@
C4BA2AF72996A4EF00CB4FBA /* CustomSound+CoreDataClass.swift in Sources */, C4BA2AF72996A4EF00CB4FBA /* CustomSound+CoreDataClass.swift in Sources */,
C4F8B1AD298AC451005C86A5 /* AbstractSoundTimer+CoreDataClass.swift in Sources */, C4F8B1AD298AC451005C86A5 /* AbstractSoundTimer+CoreDataClass.swift in Sources */,
C445FA87298448730054D761 /* CoolPic.swift in Sources */, C445FA87298448730054D761 /* CoolPic.swift in Sources */,
C47C933529F01B5E00C780E2 /* FileLogger.swift in Sources */,
C438C8162982BE1E00BF3EF9 /* LeCountdown.xcdatamodeld in Sources */, C438C8162982BE1E00BF3EF9 /* LeCountdown.xcdatamodeld in Sources */,
C4BA2B4A299FCE0C00CB4FBA /* IntervalGroup+CoreDataProperties.swift in Sources */, C4BA2B4A299FCE0C00CB4FBA /* IntervalGroup+CoreDataProperties.swift in Sources */,
C4BA2B4D299FCE0C00CB4FBA /* Stopwatch+CoreDataProperties.swift in Sources */, C4BA2B4D299FCE0C00CB4FBA /* Stopwatch+CoreDataProperties.swift in Sources */,
@ -1376,6 +1381,7 @@
C438C7DA2981216200BF3EF9 /* LaunchWidget.swift in Sources */, C438C7DA2981216200BF3EF9 /* LaunchWidget.swift in Sources */,
C4BA2AF62996A4EF00CB4FBA /* CustomSound+CoreDataProperties.swift in Sources */, C4BA2AF62996A4EF00CB4FBA /* CustomSound+CoreDataProperties.swift in Sources */,
C4F8B192298AC288005C86A5 /* Activity+CoreDataProperties.swift in Sources */, C4F8B192298AC288005C86A5 /* Activity+CoreDataProperties.swift in Sources */,
C47C933629F01B6600C780E2 /* FileUtils.swift in Sources */,
C4BA2B18299BE6A000CB4FBA /* Interval+CoreDataClass.swift in Sources */, C4BA2B18299BE6A000CB4FBA /* Interval+CoreDataClass.swift in Sources */,
C4F8B18E298AC288005C86A5 /* AbstractTimer+CoreDataProperties.swift in Sources */, C4F8B18E298AC288005C86A5 /* AbstractTimer+CoreDataProperties.swift in Sources */,
C4A16DC829D311C800143D5E /* Extensions.swift in Sources */, C4A16DC829D311C800143D5E /* Extensions.swift in Sources */,

@ -64,13 +64,6 @@
isEnabled = "YES"> isEnabled = "YES">
</CommandLineArgument> </CommandLineArgument>
</CommandLineArguments> </CommandLineArguments>
<AdditionalOptions>
<AdditionalOption
key = "NSZombieEnabled"
value = "YES"
isEnabled = "YES">
</AdditionalOption>
</AdditionalOptions>
</LaunchAction> </LaunchAction>
<ProfileAction <ProfileAction
buildConfiguration = "Release" buildConfiguration = "Release"

@ -27,7 +27,7 @@ class AppDelegate : NSObject, UIApplicationDelegate {
UNUserNotificationCenter.current().delegate = self UNUserNotificationCenter.current().delegate = self
self._initSchemaIfNeeded() self._initSchemaIfNeeded()
self._activateAudioSession() // self._activateAudioSession()
Sound.computeSoundDurationsIfNecessary() Sound.computeSoundDurationsIfNecessary()
@ -40,6 +40,10 @@ class AppDelegate : NSObject, UIApplicationDelegate {
return true return true
} }
func applicationWillEnterForeground(_ application: UIApplication) {
self._activateAudioSession()
}
func applicationWillTerminate(_ application: UIApplication) { func applicationWillTerminate(_ application: UIApplication) {
Logger.log("applicationWillTerminate") Logger.log("applicationWillTerminate")
FileLogger.log("applicationWillTerminate") FileLogger.log("applicationWillTerminate")
@ -63,6 +67,7 @@ class AppDelegate : NSObject, UIApplicationDelegate {
} }
fileprivate func _activateAudioSession() { fileprivate func _activateAudioSession() {
Logger.log("_activateAudioSession")
do { do {
let audioSession: AVAudioSession = AVAudioSession.sharedInstance() let audioSession: AVAudioSession = AVAudioSession.sharedInstance()
try audioSession.setCategory(.playback, options: .duckOthers) try audioSession.setCategory(.playback, options: .duckOthers)
@ -114,6 +119,7 @@ extension AppDelegate: UNUserNotificationCenterDelegate {
if components.count == 2 { if components.count == 2 {
return components[0] return components[0]
} else { } else {
FileLogger.log("app terminated by ourselves")
fatalError("bad notification format : \(notificationId)") fatalError("bad notification format : \(notificationId)")
} }
} }

@ -69,7 +69,7 @@ class Conductor: ObservableObject {
self.currentStopwatches.removeValue(forKey: id) self.currentStopwatches.removeValue(forKey: id)
self.cancelledCountdowns.removeAll(where: { $0 == id }) self.cancelledCountdowns.removeAll(where: { $0 == id })
if let soundPlayer = self._delayedSoundPlayers[id] { if let soundPlayer = self._delayedSoundPlayers[id] {
FileLogger.log("Stop sound player: \(id)") FileLogger.log("Stop sound player: \(self._timerName(id))")
soundPlayer.stop() soundPlayer.stop()
} }
} }
@ -80,8 +80,6 @@ class Conductor: ObservableObject {
fileprivate func _buildLiveTimers() { fileprivate func _buildLiveTimers() {
// Logger.log("_buildLiveTimers")
let liveCountdowns = self.currentCountdowns.map { let liveCountdowns = self.currentCountdowns.map {
return LiveTimer(id: $0, date: $1.end) return LiveTimer(id: $0, date: $1.end)
} }
@ -144,7 +142,7 @@ class Conductor: ObservableObject {
do { do {
let start = Date() let start = Date()
let end = start.addingTimeInterval(countdown.duration) let end = start.addingTimeInterval(countdown.duration)
FileLogger.log("schedule countdown \(countdownId) at \(end)") FileLogger.log("schedule countdown \(self._timerName(countdownId)) at \(end)")
let sound = countdown.someSound let sound = countdown.someSound
let soundPlayer = try DelaySoundPlayer(timerID: countdownId, sound: sound) let soundPlayer = try DelaySoundPlayer(timerID: countdownId, sound: sound)
@ -183,7 +181,7 @@ class Conductor: ObservableObject {
func cancelCountdown(id: TimerID) { func cancelCountdown(id: TimerID) {
FileLogger.log("Cancel \(id)") FileLogger.log("Cancel \(self._timerName(id))")
CountdownScheduler.master.cancelCurrentNotifications(countdownId: id) CountdownScheduler.master.cancelCurrentNotifications(countdownId: id)
self.cancelSoundPlayer(id: id) self.cancelSoundPlayer(id: id)
self.cancelledCountdowns.append(id) self.cancelledCountdowns.append(id)
@ -256,7 +254,7 @@ class Conductor: ObservableObject {
let sound: Sound = countdown.someSound let sound: Sound = countdown.someSound
let soundPlayer = try DelaySoundPlayer(timerID: countdownId, sound: sound) let soundPlayer = try DelaySoundPlayer(timerID: countdownId, sound: sound)
self._delayedSoundPlayers[countdown.stringId] = soundPlayer self._delayedSoundPlayers[countdown.stringId] = soundPlayer
FileLogger.log("Restored sound player for \(countdown.stringId)") FileLogger.log("Restored sound player for \(self._timerName(countdownId))")
try soundPlayer.restore(for: interval.end, repeatCount: Int(countdown.repeatCount)) try soundPlayer.restore(for: interval.end, repeatCount: Int(countdown.repeatCount))
} catch { } catch {
Logger.error(error) Logger.error(error)
@ -357,7 +355,7 @@ class Conductor: ObservableObject {
if let soundPlayer = self._delayedSoundPlayers[id] { if let soundPlayer = self._delayedSoundPlayers[id] {
soundPlayer.stop() soundPlayer.stop()
self._delayedSoundPlayers.removeValue(forKey: id) self._delayedSoundPlayers.removeValue(forKey: id)
FileLogger.log("cancelled sound player for \(id)") FileLogger.log("cancelled sound player for \(self._timerName(id))")
} }
self.deactivateAudioSessionIfPossible() self.deactivateAudioSessionIfPossible()
@ -457,4 +455,8 @@ class Conductor: ObservableObject {
} }
} }
fileprivate func _timerName(_ id: TimerID) -> String {
return IntentDataProvider.main.timer(id: id)?.name ?? id
}
} }

@ -27,6 +27,7 @@ extension AbstractTimer {
if let url = URL(string: self.stringId) { if let url = URL(string: self.stringId) {
return url return url
} else { } else {
// FileLogger.log("app terminated by ourselves")
fatalError("Can't produce url with \(self.stringId)") fatalError("Can't produce url with \(self.stringId)")
} }
} }

@ -53,6 +53,7 @@ struct PersistenceController {
// Replace this implementation with code to handle the error appropriately. // Replace this implementation with code to handle the error appropriately.
// 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. // 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.
let nsError = error as NSError let nsError = error as NSError
FileLogger.log("app terminated by ourselves")
fatalError("Unresolved error \(nsError), \(nsError.userInfo)") fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
} }
return result return result
@ -101,6 +102,7 @@ struct PersistenceController {
* The store could not be migrated to the current model version. * The store could not be migrated to the current model version.
Check the error message to determine what the actual problem was. Check the error message to determine what the actual problem was.
*/ */
FileLogger.log("app terminated by ourselves")
fatalError("Unresolved error \(error), \(error.userInfo)") fatalError("Unresolved error \(error), \(error.userInfo)")
} }
}) })
@ -114,6 +116,7 @@ fileprivate extension URL {
/// Returns a URL for the given app group and database pointing to the sqlite database. /// Returns a URL for the given app group and database pointing to the sqlite database.
static func storeURL(for appGroup: String, databaseName: String) -> URL { static func storeURL(for appGroup: String, databaseName: String) -> URL {
guard let fileContainer = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroup) else { guard let fileContainer = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroup) else {
FileLogger.log("app terminated by ourselves")
fatalError("Shared file container could not be created.") fatalError("Shared file container could not be created.")
} }

@ -46,8 +46,8 @@ import AVFoundation
Logger.log("self._player.deviceCurrentTime = \(self._player.deviceCurrentTime)") Logger.log("self._player.deviceCurrentTime = \(self._player.deviceCurrentTime)")
let time: TimeInterval = self._player.deviceCurrentTime + duration let time: TimeInterval = self._player.deviceCurrentTime + duration
self._player.play(atTime: time) let result = self._player.play(atTime: time)
FileLogger.log("self._player.play(atTime: \(time)") FileLogger.log("play \(String(describing: self._player.url)) >atTime: \(time), result = \(result), isMainThread = \(Thread.isMainThread)")
} }
func stop() { func stop() {
@ -58,10 +58,9 @@ import AVFoundation
// MARK: - Delegate // MARK: - Delegate
func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) { func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
FileLogger.log("audioPlayerDidFinishPlaying: successfully = \(flag)") FileLogger.log("audioPlayerDidFinishPlaying: successfully = \(flag), player volume = \(player.volume)")
Logger.log("audioPlayerDidFinishPlaying: successfully = \(flag)") Logger.log("audioPlayerDidFinishPlaying: successfully = \(flag)")
// Conductor.maestro.cancelSoundPlayer(id: self._timerID)
Conductor.maestro.cleanupLiveActivities() Conductor.maestro.cleanupLiveActivities()
self.stop() self.stop()

@ -27,6 +27,7 @@ class TextToSpeechRecorder {
synthesizer.write(utterance) { buffer in synthesizer.write(utterance) { buffer in
guard let pcmBuffer = buffer as? AVAudioPCMBuffer else { guard let pcmBuffer = buffer as? AVAudioPCMBuffer else {
FileLogger.log("app terminated by ourselves")
fatalError("unknown buffer type: \(buffer)") fatalError("unknown buffer type: \(buffer)")
} }
let fileName = "\(UUID().uuidString).caf" let fileName = "\(UUID().uuidString).caf"

Loading…
Cancel
Save