Avoid systematic crashes when things go wrong + better email management

main
Laurent 2 years ago
parent 6b4fcaac4c
commit ea6293799c
  1. 4
      LeCountdown.xcodeproj/project.pbxproj
  2. 11
      LeCountdown/AppDelegate.swift
  3. 3
      LeCountdown/Model/Model+SharedExtensions.swift
  4. 12
      LeCountdown/Utils/URLs.swift
  5. 3
      LeCountdown/Views/Reusable/MailView.swift
  6. 25
      LeCountdown/Views/SettingsView.swift

@ -126,6 +126,7 @@
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 */; };
C47A9AF32AD1B32C00618A50 /* URLs.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47A9AF22AD1B32C00618A50 /* URLs.swift */; };
C47C933529F01B5E00C780E2 /* FileLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4556F6A29E40B7800DEB40B /* FileLogger.swift */; }; C47C933529F01B5E00C780E2 /* FileLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4556F6A29E40B7800DEB40B /* FileLogger.swift */; };
C47C933629F01B6600C780E2 /* FileUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4556F6E29E40BED00DEB40B /* FileUtils.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 */; }; C47C933729F01B7A00C780E2 /* Codable+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4556F7029E40DCF00DEB40B /* Codable+Extensions.swift */; };
@ -434,6 +435,7 @@
C4742B58298411E800D5D950 /* CountdownFormView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CountdownFormView.swift; sourceTree = "<group>"; }; C4742B58298411E800D5D950 /* CountdownFormView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CountdownFormView.swift; sourceTree = "<group>"; };
C4742B5A298414B000D5D950 /* ImageSelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageSelectionView.swift; sourceTree = "<group>"; }; C4742B5A298414B000D5D950 /* ImageSelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageSelectionView.swift; sourceTree = "<group>"; };
C4742B5E2984205000D5D950 /* ViewModifiers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModifiers.swift; sourceTree = "<group>"; }; C4742B5E2984205000D5D950 /* ViewModifiers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModifiers.swift; sourceTree = "<group>"; };
C47A9AF22AD1B32C00618A50 /* URLs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLs.swift; sourceTree = "<group>"; };
C47C933829F13BD100C780E2 /* AppleMusicPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppleMusicPickerView.swift; sourceTree = "<group>"; }; C47C933829F13BD100C780E2 /* AppleMusicPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppleMusicPickerView.swift; sourceTree = "<group>"; };
C47C933C29F13DBD00C780E2 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; }; C47C933C29F13DBD00C780E2 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
C48940DD2AC307860086F4FA /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; }; C48940DD2AC307860086F4FA /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
@ -779,6 +781,7 @@
C4060DF4297AE9A7003FAB80 /* TimeInterval+Extensions.swift */, C4060DF4297AE9A7003FAB80 /* TimeInterval+Extensions.swift */,
C473C33B29ACEC4F0056B38A /* Tip.swift */, C473C33B29ACEC4F0056B38A /* Tip.swift */,
C4BA2B7829A65C1400CB4FBA /* UIDevice+Extensions.swift */, C4BA2B7829A65C1400CB4FBA /* UIDevice+Extensions.swift */,
C47A9AF22AD1B32C00618A50 /* URLs.swift */,
); );
path = Utils; path = Utils;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1349,6 +1352,7 @@
C4F8B17A298AC234005C86A5 /* Countdown+CoreDataClass.swift in Sources */, C4F8B17A298AC234005C86A5 /* Countdown+CoreDataClass.swift in Sources */,
C42E970229E6B32B005B1B8C /* CalendarView.swift in Sources */, C42E970229E6B32B005B1B8C /* CalendarView.swift in Sources */,
C4BA2B0F299BE61E00CB4FBA /* Interval+CoreDataClass.swift in Sources */, C4BA2B0F299BE61E00CB4FBA /* Interval+CoreDataClass.swift in Sources */,
C47A9AF32AD1B32C00618A50 /* URLs.swift in Sources */,
C4F8B164298A9A92005C86A5 /* AlarmFormView.swift in Sources */, C4F8B164298A9A92005C86A5 /* AlarmFormView.swift in Sources */,
C4286EB02A1B75AB0070D075 /* BoringContext.swift in Sources */, C4286EB02A1B75AB0070D075 /* BoringContext.swift in Sources */,
C40FDB622992985C0042A390 /* TextToSpeechRecorder.swift in Sources */, C40FDB622992985C0042A390 /* TextToSpeechRecorder.swift in Sources */,

@ -90,8 +90,9 @@ extension AppDelegate: UNUserNotificationCenterDelegate {
print("didReceive notification") print("didReceive notification")
FileLogger.log("userNotificationCenter didReceive > cancelling sound player") FileLogger.log("userNotificationCenter didReceive > cancelling sound player")
let timerId = self._timerId(notificationId: response.notification.request.identifier) if let timerId = self._timerId(notificationId: response.notification.request.identifier) {
Conductor.maestro.cancelSoundPlayer(id: timerId) Conductor.maestro.cancelSoundPlayer(id: timerId)
}
} }
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
@ -102,13 +103,13 @@ extension AppDelegate: UNUserNotificationCenterDelegate {
// Conductor.maestro.notifyUser(countdownId: timerId) // Conductor.maestro.notifyUser(countdownId: timerId)
} }
fileprivate func _timerId(notificationId: String) -> TimerID { fileprivate func _timerId(notificationId: String) -> TimerID? {
let components = notificationId.components(separatedBy: CountdownScheduler.notificationIdSeparator) let components = notificationId.components(separatedBy: CountdownScheduler.notificationIdSeparator)
if components.count == 2 { if components.count == 2 {
return components[0] return components[0]
} else { } else {
FileLogger.log("app terminated by ourselves") FileLogger.log("Couldn't parse notification Id: \(notificationId)")
fatalError("bad notification format : \(notificationId)") return nil
} }
} }

@ -27,8 +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") return URL(filePath: "") // dummy URL to avoid the pain of dealing with optional/error
fatalError("Can't produce url with \(self.stringId)")
} }
} }

@ -0,0 +1,12 @@
//
// URLs.swift
// LeCountdown
//
// Created by Laurent Morvillier on 07/10/2023.
//
import Foundation
enum URLs: String {
case mail = "hello@getenchanted.app"
}

@ -33,10 +33,11 @@ struct MailView: UIViewControllerRepresentable {
} }
func makeUIViewController(context: UIViewControllerRepresentableContext<MailView>) -> MFMailComposeViewController { func makeUIViewController(context: UIViewControllerRepresentableContext<MailView>) -> MFMailComposeViewController {
let vc = MFMailComposeViewController() let vc = MFMailComposeViewController()
vc.mailComposeDelegate = context.coordinator vc.mailComposeDelegate = context.coordinator
vc.setSubject(Bundle.main.applicationName) vc.setSubject(Bundle.main.applicationName)
vc.setToRecipients(["hello@getenchanted.app"]) vc.setToRecipients([URLs.mail.rawValue])
let lastLogs = FileLogger.main.logs.suffix(40).reversed() let lastLogs = FileLogger.main.logs.suffix(40).reversed()
let logs = lastLogs.map { $0.date.formattedDateTime + "\n" + $0.content } let logs = lastLogs.map { $0.date.formattedDateTime + "\n" + $0.content }

@ -6,6 +6,7 @@
// //
import SwiftUI import SwiftUI
import MessageUI
struct SettingsView: View { struct SettingsView: View {
@ -15,6 +16,7 @@ struct SettingsView: View {
@State var showMailView: Bool = false @State var showMailView: Bool = false
@State var showLogsSheet: Bool = false @State var showLogsSheet: Bool = false
@State var emailCopied: Bool = false
var body: some View { var body: some View {
@ -36,11 +38,26 @@ struct SettingsView: View {
}) })
.frame(width: 120.0) .frame(width: 120.0)
} }
Button {
self.showMailView = true if MFMailComposeViewController.canSendMail() {
} label: { Button {
Text("Contact us") self.showMailView = true
} label: {
Text("Contact us")
}
} else {
if self.emailCopied {
Label("Copied", systemImage: "checkmark")
} else {
Button {
UIPasteboard.general.string = URLs.mail.rawValue
self.emailCopied = true
} label: {
LabeledContent("Contact us", value: "tap to copy email")
}
}
} }
Button { Button {
withAnimation { withAnimation {
self.showLogsSheet.toggle() self.showLogsSheet.toggle()

Loading…
Cancel
Save