Fixes crashes on mac app

main
Laurent 3 years ago
parent 5f70c4da00
commit 205efa3704
  1. 12
      LeCountdown.xcodeproj/project.pbxproj
  2. 14
      LeCountdown.xcodeproj/xcshareddata/xcschemes/LeCountdown.xcscheme
  3. 29
      LeCountdown/AppDelegate.swift
  4. 8
      LeCountdown/Intent/TimerIdentifierAppEntity.swift
  5. 31
      LeCountdown/LeCountdownApp.swift
  6. 1
      LeCountdown/Model/Persistence.swift
  7. 22
      LeCountdown/TimerRouter.swift
  8. 4
      LeCountdown/Views/TimersView.swift
  9. 5
      LeCountdown/Widget/IntentDataProvider.swift

@ -1461,7 +1461,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 0.1.1; MARKETING_VERSION = 0.1.2;
PRODUCT_BUNDLE_IDENTIFIER = com.staxriver.LeCountdown; PRODUCT_BUNDLE_IDENTIFIER = com.staxriver.LeCountdown;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_EMIT_LOC_STRINGS = YES;
@ -1495,7 +1495,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 0.1.1; MARKETING_VERSION = 0.1.2;
PRODUCT_BUNDLE_IDENTIFIER = com.staxriver.LeCountdown; PRODUCT_BUNDLE_IDENTIFIER = com.staxriver.LeCountdown;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_EMIT_LOC_STRINGS = YES;
@ -1598,7 +1598,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 0.1.1; MARKETING_VERSION = 0.1.2;
PRODUCT_BUNDLE_IDENTIFIER = com.staxriver.LeCountdown.LaunchWidget; PRODUCT_BUNDLE_IDENTIFIER = com.staxriver.LeCountdown.LaunchWidget;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
@ -1626,7 +1626,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 0.1.1; MARKETING_VERSION = 0.1.2;
PRODUCT_BUNDLE_IDENTIFIER = com.staxriver.LeCountdown.LaunchWidget; PRODUCT_BUNDLE_IDENTIFIER = com.staxriver.LeCountdown.LaunchWidget;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
@ -1653,7 +1653,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 0.1.1; MARKETING_VERSION = 0.1.2;
PRODUCT_BUNDLE_IDENTIFIER = com.staxriver.LeCountdown.LaunchIntents; PRODUCT_BUNDLE_IDENTIFIER = com.staxriver.LeCountdown.LaunchIntents;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
@ -1680,7 +1680,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 0.1.1; MARKETING_VERSION = 0.1.2;
PRODUCT_BUNDLE_IDENTIFIER = com.staxriver.LeCountdown.LaunchIntents; PRODUCT_BUNDLE_IDENTIFIER = com.staxriver.LeCountdown.LaunchIntents;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;

@ -50,6 +50,20 @@
ReferencedContainer = "container:LeCountdown.xcodeproj"> ReferencedContainer = "container:LeCountdown.xcodeproj">
</BuildableReference> </BuildableReference>
</BuildableProductRunnable> </BuildableProductRunnable>
<CommandLineArguments>
<CommandLineArgument
argument = "-com.apple.CoreData.Logging.stderr 0"
isEnabled = "YES">
</CommandLineArgument>
<CommandLineArgument
argument = "-com.apple.CoreData.CloudKitDebug 0"
isEnabled = "YES">
</CommandLineArgument>
<CommandLineArgument
argument = " -com.apple.CoreData.ConcurrencyDebug 1"
isEnabled = "YES">
</CommandLineArgument>
</CommandLineArguments>
</LaunchAction> </LaunchAction>
<ProfileAction <ProfileAction
buildConfiguration = "Release" buildConfiguration = "Release"

@ -7,6 +7,7 @@
import Foundation import Foundation
import UIKit import UIKit
import AVFoundation
class AppDelegate : NSObject, UIApplicationDelegate { class AppDelegate : NSObject, UIApplicationDelegate {
@ -20,10 +21,38 @@ class AppDelegate : NSObject, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
UNUserNotificationCenter.current().delegate = self UNUserNotificationCenter.current().delegate = self
self._initSchemaIfNeeded()
self._activateAudioSession()
Sound.computeSoundDurationsIfNecessary()
Conductor.maestro.cleanup() Conductor.maestro.cleanup()
return true return true
} }
fileprivate func _initSchemaIfNeeded() {
if !Preferences.cloudKitSchemaInitialized {
do {
try PersistenceController.shared.container.initializeCloudKitSchema()
Preferences.cloudKitSchemaInitialized = true
} catch {
print("initializeCloudKitSchema error: \(error)")
}
}
}
fileprivate func _activateAudioSession() {
do {
let audioSession: AVAudioSession = AVAudioSession.sharedInstance()
try audioSession.setCategory(.playback)
try audioSession.setActive(true)
} catch {
Logger.error(error)
}
}
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool { func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
if userActivity.interaction == nil { if userActivity.interaction == nil {

@ -21,10 +21,14 @@ struct TimerIdentifierAppEntity: AppEntity {
} }
func suggestedEntities() async throws -> [TimerIdentifierAppEntity] { func suggestedEntities() async throws -> [TimerIdentifierAppEntity] {
try await PersistenceController.shared.container.performBackgroundTask { context in
let timers = try IntentDataProvider.main.timers(context: context)
return timers.map { TimerIdentifierAppEntity(id: $0.stringId, displayString: $0.displayName) }
}
// TODO: return likely TimerIdentifierAppEntity entities here. // TODO: return likely TimerIdentifierAppEntity entities here.
// This method is optional; the default implementation returns an empty array. // This method is optional; the default implementation returns an empty array.
let timers = try IntentDataProvider.main.timers()
return timers.map { TimerIdentifierAppEntity(id: $0.stringId, displayString: $0.displayName) }
} }
} }

@ -24,31 +24,15 @@ struct LeCountdownApp: App {
UIPageControl.appearance().currentPageIndicatorTintColor = .systemPink UIPageControl.appearance().currentPageIndicatorTintColor = .systemPink
UIPageControl.appearance().pageIndicatorTintColor = UIColor(white: 0.7, alpha: 1.0) UIPageControl.appearance().pageIndicatorTintColor = UIColor(white: 0.7, alpha: 1.0)
Logger.log("path = \(Bundle.main.bundlePath)")
self._registerBackgroundRefreshes() self._registerBackgroundRefreshes()
self._initSchemaIfNeeded()
self._activateAudioSession()
} // if !ProcessInfo.processInfo.isiOSAppOnMac {
// self._initSchemaIfNeeded()
fileprivate func _initSchemaIfNeeded() { // self._activateAudioSession()
if !Preferences.cloudKitSchemaInitialized { // }
do {
try persistenceController.container.initializeCloudKitSchema()
Preferences.cloudKitSchemaInitialized = true
} catch {
print("ERROR \(error)")
}
}
}
fileprivate func _activateAudioSession() {
do {
let audioSession: AVAudioSession = AVAudioSession.sharedInstance()
try audioSession.setCategory(.playback)
try audioSession.setActive(true)
} catch {
Logger.error(error)
}
} }
@Environment(\.scenePhase) var scenePhase @Environment(\.scenePhase) var scenePhase
@ -98,7 +82,6 @@ struct LeCountdownApp: App {
Logger.log("preferredLanguages = \(String(describing: Locale.preferredLanguages))") Logger.log("preferredLanguages = \(String(describing: Locale.preferredLanguages))")
Sound.computeSoundDurationsIfNecessary()
self._patch() self._patch()

@ -38,7 +38,6 @@ struct PersistenceController {
record.activity = activities.randomElement() record.activity = activities.randomElement()
} }
do { do {
try viewContext.save() try viewContext.save()
} catch { } catch {

@ -59,16 +59,18 @@ class TimerRouter {
fileprivate static func _launchCountdown(_ countdown: Countdown, handler: @escaping (Result<Void, Error>) -> Void) { fileprivate static func _launchCountdown(_ countdown: Countdown, handler: @escaping (Result<Void, Error>) -> Void) {
UNUserNotificationCenter.current().getNotificationSettings { settings in UNUserNotificationCenter.current().getNotificationSettings { settings in
switch settings.authorizationStatus { DispatchQueue.main.async {
case .notDetermined, .denied: switch settings.authorizationStatus {
handler(.failure(TimerRouterError.notificationAuthorizationMissing)) case .notDetermined, .denied:
default: handler(.failure(TimerRouterError.notificationAuthorizationMissing))
CountdownScheduler.master.scheduleIfPossible(countdown: countdown) { result in default:
switch result { CountdownScheduler.master.scheduleIfPossible(countdown: countdown) { result in
case .success: switch result {
handler(.success(Void())) case .success:
case .failure(let failure): handler(.success(Void()))
handler(.failure(failure)) case .failure(let failure):
handler(.failure(failure))
}
} }
} }
} }

@ -53,7 +53,8 @@ struct TimersView: View {
} }
} }
}.padding(.horizontal, itemSpacing) }
.padding(.horizontal, itemSpacing)
} else { } else {
Text("You'll find your timers here. Start by creating them on the left screen").multilineTextAlignment(.center) Text("You'll find your timers here. Start by creating them on the left screen").multilineTextAlignment(.center)
} }
@ -75,6 +76,7 @@ struct TimersView: View {
} }
fileprivate func _handleSiriTips(timer: AbstractTimer) { fileprivate func _handleSiriTips(timer: AbstractTimer) {
let timerId = timer.stringId let timerId = timer.stringId
if !Preferences.timerSiriTips.contains(timerId) { if !Preferences.timerSiriTips.contains(timerId) {
self.boringContext.siriTimer = timer self.boringContext.siriTimer = timer

@ -13,7 +13,10 @@ class IntentDataProvider {
static let main: IntentDataProvider = IntentDataProvider() static let main: IntentDataProvider = IntentDataProvider()
func timers() throws -> [AbstractTimer] { func timers() throws -> [AbstractTimer] {
let context = PersistenceController.shared.container.viewContext return try timers(context: PersistenceController.shared.container.viewContext)
}
func timers(context: NSManagedObjectContext) throws -> [AbstractTimer] {
let request: NSFetchRequest<AbstractTimer> = AbstractTimer.fetchRequest() let request: NSFetchRequest<AbstractTimer> = AbstractTimer.fetchRequest()
request.sortDescriptors = [NSSortDescriptor(keyPath: (\AbstractTimer.order), ascending: true)] request.sortDescriptors = [NSSortDescriptor(keyPath: (\AbstractTimer.order), ascending: true)]
return try context.fetch(request) return try context.fetch(request)

Loading…
Cancel
Save