Skip to content

Commit 985e8b2

Browse files
authored
Default debug logging on and prompt for description when sharing logs (#627)
Flip the debugLogLevel default to true and add a migration step so existing users with it stored as false also get it enabled, ensuring shared logs contain useful detail when reporting problems. When the user taps Share Logs, present a sheet asking for a short description of the issue. The description is written to a ShareNotice_<timestamp>.txt file (date, app version, branch+sha, user description) and included alongside the log files in the iOS share sheet.
1 parent 350e3af commit 985e8b2

6 files changed

Lines changed: 111 additions & 3 deletions

File tree

LoopFollow.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
6589CC6D2E9E7D1600BB18FE /* CalendarSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6589CC582E9E7D1600BB18FE /* CalendarSettingsView.swift */; };
6262
6589CC6E2E9E7D1600BB18FE /* SettingsMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6589CC5F2E9E7D1600BB18FE /* SettingsMenuView.swift */; };
6363
6589CC6F2E9E7D1600BB18FE /* AdvancedSettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6589CC572E9E7D1600BB18FE /* AdvancedSettingsViewModel.swift */; };
64+
6589CC712E9E7D1600BB18FE /* ShareLogNoticeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6589CC702E9E7D1600BB18FE /* ShareLogNoticeView.swift */; };
6465
6589CC712E9E814F00BB18FE /* AlarmSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6589CC702E9E814F00BB18FE /* AlarmSelectionView.swift */; };
6566
6589CC752E9EAFB700BB18FE /* SettingsMigrationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6589CC742E9EAFB700BB18FE /* SettingsMigrationManager.swift */; };
6667
65A100012F5AA00000AA1001 /* UnitsSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65A100002F5AA00000AA1001 /* UnitsSettingsView.swift */; };
@@ -527,6 +528,7 @@
527528
6589CC5E2E9E7D1600BB18FE /* GraphSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GraphSettingsView.swift; sourceTree = "<group>"; };
528529
6589CC5F2E9E7D1600BB18FE /* SettingsMenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsMenuView.swift; sourceTree = "<group>"; };
529530
6589CC602E9E7D1600BB18FE /* TabCustomizationModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabCustomizationModal.swift; sourceTree = "<group>"; };
531+
6589CC702E9E7D1600BB18FE /* ShareLogNoticeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareLogNoticeView.swift; sourceTree = "<group>"; };
530532
6589CC702E9E814F00BB18FE /* AlarmSelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlarmSelectionView.swift; sourceTree = "<group>"; };
531533
6589CC742E9EAFB700BB18FE /* SettingsMigrationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsMigrationManager.swift; sourceTree = "<group>"; };
532534
65A100002F5AA00000AA1001 /* UnitsSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnitsSettingsView.swift; sourceTree = "<group>"; };
@@ -1002,6 +1004,7 @@
10021004
6589CC5E2E9E7D1600BB18FE /* GraphSettingsView.swift */,
10031005
657F98172F043D8100F732BD /* HomeContentView.swift */,
10041006
6589CC5F2E9E7D1600BB18FE /* SettingsMenuView.swift */,
1007+
6589CC702E9E7D1600BB18FE /* ShareLogNoticeView.swift */,
10051008
65A100002F5AA00000AA1001 /* UnitsSettingsView.swift */,
10061009
65A100022F5AA00000AA1002 /* UnitsConfigurationView.swift */,
10071010
6589CC602E9E7D1600BB18FE /* TabCustomizationModal.swift */,
@@ -2329,6 +2332,7 @@
23292332
65A100032F5AA00000AA1002 /* UnitsConfigurationView.swift in Sources */,
23302333
657F98182F043D8100F732BD /* HomeContentView.swift in Sources */,
23312334
6589CC6F2E9E7D1600BB18FE /* AdvancedSettingsViewModel.swift in Sources */,
2335+
6589CC712E9E7D1600BB18FE /* ShareLogNoticeView.swift in Sources */,
23322336
DD493ADF2ACF22BB009A6922 /* SAge.swift in Sources */,
23332337
DDC6CA3F2DD7C6340060EE25 /* TemporaryAlarmEditor.swift in Sources */,
23342338
DDF699992C5AA3060058A8D9 /* TempTargetPresetManager.swift in Sources */,
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// LoopFollow
2+
// ShareLogNoticeView.swift
3+
4+
import SwiftUI
5+
6+
struct ShareLogNoticeView: View {
7+
@State private var noticeText: String = ""
8+
let onCancel: () -> Void
9+
let onShare: (String) -> Void
10+
11+
var body: some View {
12+
NavigationView {
13+
Form {
14+
Section {
15+
Text("Thanks for sharing these logs to help us find the problem. Please describe it in as much detail as possible — what time did it happen, what did you do, and what did you expect to happen that didn't?")
16+
.font(.callout)
17+
.foregroundColor(.secondary)
18+
}
19+
20+
Section(header: Text("Description")) {
21+
TextEditor(text: $noticeText)
22+
.frame(minHeight: 180)
23+
}
24+
}
25+
.preferredColorScheme(Storage.shared.appearanceMode.value.colorScheme)
26+
.navigationBarTitle("Share Logs", displayMode: .inline)
27+
.toolbar {
28+
ToolbarItem(placement: .cancellationAction) {
29+
Button("Cancel", action: onCancel)
30+
}
31+
ToolbarItem(placement: .confirmationAction) {
32+
Button("Share") { onShare(noticeText) }
33+
}
34+
}
35+
}
36+
}
37+
}

LoopFollow/Storage/Storage+Migrate.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,13 @@ extension Storage {
6060
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: legacyNotificationIDs)
6161
}
6262

63+
func migrateStep9() {
64+
// Default for debugLogLevel changed from false to true so users ship useful
65+
// logs when they report a problem. Force-enable for existing users.
66+
LogManager.shared.log(category: .general, message: "Running migrateStep9 — enabling debug log level")
67+
debugLogLevel.value = true
68+
}
69+
6370
func migrateStep6() {
6471
// APNs credential separation
6572
LogManager.shared.log(category: .general, message: "Running migrateStep6 — APNs credential separation")

LoopFollow/Storage/Storage.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class Storage {
3939

4040
var selectedBLEDevice = StorageValue<BLEDevice?>(key: "selectedBLEDevice", defaultValue: nil)
4141

42-
var debugLogLevel = StorageValue<Bool>(key: "debugLogLevel", defaultValue: false)
42+
var debugLogLevel = StorageValue<Bool>(key: "debugLogLevel", defaultValue: true)
4343

4444
var contactTrend = StorageValue<ContactIncludeOption>(key: "contactTrend", defaultValue: .off)
4545
var contactDelta = StorageValue<ContactIncludeOption>(key: "contactDelta", defaultValue: .off)
@@ -214,7 +214,7 @@ class Storage {
214214
// When adding a new migration step in `runMigrationsIfNeeded()`, bump this default
215215
// to the new latest step number so fresh installs skip all migrations. Other defaults
216216
// in this file must reflect the post-migration final state for a fresh install.
217-
var migrationStep = StorageValue<Int>(key: "migrationStep", defaultValue: 7)
217+
var migrationStep = StorageValue<Int>(key: "migrationStep", defaultValue: 9)
218218

219219
var persistentNotification = StorageValue<Bool>(key: "persistentNotification", defaultValue: false)
220220
var persistentNotificationLastBGTime = StorageValue<Date>(key: "persistentNotificationLastBGTime", defaultValue: .distantPast)

LoopFollow/ViewControllers/MainViewController.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -692,6 +692,11 @@ class MainViewController: UIViewController, ChartViewDelegate, UNUserNotificatio
692692
Storage.shared.migrateStep8()
693693
Storage.shared.migrationStep.value = 8
694694
}
695+
696+
if Storage.shared.migrationStep.value < 9 {
697+
Storage.shared.migrateStep9()
698+
Storage.shared.migrationStep.value = 9
699+
}
695700
}
696701

697702
@objc func appDidBecomeActive() {

LoopFollow/ViewControllers/MoreMenuView.swift

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,10 +140,65 @@ struct MoreMenuView: View {
140140
showAlert = true
141141
return
142142
}
143-
let avc = UIActivityViewController(activityItems: files, applicationActivities: nil)
143+
144+
let noticeView = ShareLogNoticeView(
145+
onCancel: {
146+
UIApplication.shared.topMost?.dismiss(animated: true)
147+
},
148+
onShare: { noticeText in
149+
let presenter = UIApplication.shared.topMost
150+
presenter?.dismiss(animated: true) {
151+
presentLogShareSheet(noticeText: noticeText, logFiles: files)
152+
}
153+
}
154+
)
155+
let host = UIHostingController(rootView: noticeView)
156+
host.overrideUserInterfaceStyle = Storage.shared.appearanceMode.value.userInterfaceStyle
157+
host.modalPresentationStyle = .formSheet
158+
UIApplication.shared.topMost?.present(host, animated: true)
159+
}
160+
161+
private func presentLogShareSheet(noticeText: String, logFiles: [URL]) {
162+
var items: [Any] = logFiles
163+
if let noticeURL = writeShareNoticeFile(text: noticeText) {
164+
items.insert(noticeURL, at: 0)
165+
}
166+
let avc = UIActivityViewController(activityItems: items, applicationActivities: nil)
144167
UIApplication.shared.topMost?.present(avc, animated: true)
145168
}
146169

170+
private func writeShareNoticeFile(text: String) -> URL? {
171+
let formatter = DateFormatter()
172+
formatter.dateFormat = "yyyy-MM-dd_HHmm"
173+
let timestamp = formatter.string(from: Date())
174+
175+
let version = AppVersionManager().version()
176+
let branchAndSha = BuildDetails.default.branchAndSha
177+
178+
let body = text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
179+
? "(no description provided)"
180+
: text
181+
182+
let contents = """
183+
LoopFollow Log Share Notice
184+
Date: \(ISO8601DateFormatter().string(from: Date()))
185+
App version: \(version) (\(branchAndSha))
186+
187+
User description:
188+
\(body)
189+
"""
190+
191+
let url = FileManager.default.temporaryDirectory
192+
.appendingPathComponent("ShareNotice_\(timestamp).txt")
193+
do {
194+
try contents.write(to: url, atomically: true, encoding: .utf8)
195+
return url
196+
} catch {
197+
LogManager.shared.log(category: .general, message: "Failed to write share notice file: \(error)")
198+
return nil
199+
}
200+
}
201+
147202
private func fetchVersionInfo() async {
148203
let mgr = AppVersionManager()
149204
let (latest, newer, blacklisted) = await mgr.checkForNewVersionAsync()

0 commit comments

Comments
 (0)