[ADD] Debug entries for refresh attempts, sending feedback, advanced settings, and resetting the pairing file

This commit is contained in:
Fabian Thies
2023-02-04 14:29:02 +01:00
parent fbc754d8b7
commit de7e909c01
5 changed files with 241 additions and 13 deletions

View File

@@ -63,6 +63,8 @@
1F943C702927F90400ABE095 /* BrowseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FAFC5BA2927E0F800B8D837 /* BrowseView.swift */; }; 1F943C702927F90400ABE095 /* BrowseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FAFC5BA2927E0F800B8D837 /* BrowseView.swift */; };
1F943C712927F90400ABE095 /* MyAppsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FAFC5BD2927E10D00B8D837 /* MyAppsView.swift */; }; 1F943C712927F90400ABE095 /* MyAppsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FAFC5BD2927E10D00B8D837 /* MyAppsView.swift */; };
1FA1C8CA294906890083119D /* MyAppsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FA1C8C9294906890083119D /* MyAppsViewModel.swift */; }; 1FA1C8CA294906890083119D /* MyAppsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FA1C8C9294906890083119D /* MyAppsViewModel.swift */; };
1FA5A6CA298E8B2F007BA946 /* RefreshAttemptsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FA5A6C9298E8B2F007BA946 /* RefreshAttemptsView.swift */; };
1FA5A6CC298E8FE4007BA946 /* MailComposeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FA5A6CB298E8FE4007BA946 /* MailComposeView.swift */; };
1FB84BA62928DE08006A5CF4 /* AppDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FB84BA52928DE08006A5CF4 /* AppDetailView.swift */; }; 1FB84BA62928DE08006A5CF4 /* AppDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FB84BA52928DE08006A5CF4 /* AppDetailView.swift */; };
1FB96FBE292A20E5007E68D1 /* ObservableScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FB96FBD292A20E5007E68D1 /* ObservableScrollView.swift */; }; 1FB96FBE292A20E5007E68D1 /* ObservableScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FB96FBD292A20E5007E68D1 /* ObservableScrollView.swift */; };
1FB96FC0292A63F2007E68D1 /* AppPillButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FB96FBF292A63F2007E68D1 /* AppPillButton.swift */; }; 1FB96FC0292A63F2007E68D1 /* AppPillButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FB96FBF292A63F2007E68D1 /* AppPillButton.swift */; };
@@ -604,6 +606,8 @@
1F943C652927F36600ABE095 /* NewsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsViewModel.swift; sourceTree = "<group>"; }; 1F943C652927F36600ABE095 /* NewsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsViewModel.swift; sourceTree = "<group>"; };
1F943C672927F39400ABE095 /* ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModel.swift; sourceTree = "<group>"; }; 1F943C672927F39400ABE095 /* ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModel.swift; sourceTree = "<group>"; };
1FA1C8C9294906890083119D /* MyAppsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyAppsViewModel.swift; sourceTree = "<group>"; }; 1FA1C8C9294906890083119D /* MyAppsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyAppsViewModel.swift; sourceTree = "<group>"; };
1FA5A6C9298E8B2F007BA946 /* RefreshAttemptsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RefreshAttemptsView.swift; sourceTree = "<group>"; };
1FA5A6CB298E8FE4007BA946 /* MailComposeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MailComposeView.swift; sourceTree = "<group>"; };
1FAFC5A42927E00000B8D837 /* SideStoreUIApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SideStoreUIApp.swift; sourceTree = "<group>"; }; 1FAFC5A42927E00000B8D837 /* SideStoreUIApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SideStoreUIApp.swift; sourceTree = "<group>"; };
1FAFC5B52927E06300B8D837 /* RootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootView.swift; sourceTree = "<group>"; }; 1FAFC5B52927E06300B8D837 /* RootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootView.swift; sourceTree = "<group>"; };
1FAFC5B82927E0EE00B8D837 /* NewsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsView.swift; sourceTree = "<group>"; }; 1FAFC5B82927E0EE00B8D837 /* NewsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsView.swift; sourceTree = "<group>"; };
@@ -1115,6 +1119,7 @@
1F66F5B92938CA5700A910CA /* VisualEffectView.swift */, 1F66F5B92938CA5700A910CA /* VisualEffectView.swift */,
1F6284D6295218980060AAD8 /* DocumentPicker.swift */, 1F6284D6295218980060AAD8 /* DocumentPicker.swift */,
1F545E84298D84CF00589F68 /* FilePreviewView.swift */, 1F545E84298D84CF00589F68 /* FilePreviewView.swift */,
1FA5A6CB298E8FE4007BA946 /* MailComposeView.swift */,
); );
path = "UIView Representables"; path = "UIView Representables";
sourceTree = "<group>"; sourceTree = "<group>";
@@ -1200,6 +1205,7 @@
1F0DD83E29367F6C007608A4 /* ConnectAppleIDView.swift */, 1F0DD83E29367F6C007608A4 /* ConnectAppleIDView.swift */,
1F2EF786297C4D40002FD839 /* LicensesView.swift */, 1F2EF786297C4D40002FD839 /* LicensesView.swift */,
1F545E82298D79E400589F68 /* ErrorLogView.swift */, 1F545E82298D79E400589F68 /* ErrorLogView.swift */,
1FA5A6C9298E8B2F007BA946 /* RefreshAttemptsView.swift */,
); );
path = Settings; path = Settings;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -2876,6 +2882,7 @@
BF6C8FAC242935ED00125131 /* NSAttributedString+Markdown.m in Sources */, BF6C8FAC242935ED00125131 /* NSAttributedString+Markdown.m in Sources */,
BFF00D322501BDA100746320 /* BackgroundRefreshAppsOperation.swift in Sources */, BFF00D322501BDA100746320 /* BackgroundRefreshAppsOperation.swift in Sources */,
1F66F5BC2938F03700A910CA /* Modifiers.swift in Sources */, 1F66F5BC2938F03700A910CA /* Modifiers.swift in Sources */,
1FA5A6CA298E8B2F007BA946 /* RefreshAttemptsView.swift in Sources */,
1F5DF9D82974426300DDAA47 /* AppScreenshot.swift in Sources */, 1F5DF9D82974426300DDAA47 /* AppScreenshot.swift in Sources */,
1F66F5BA2938CA5700A910CA /* VisualEffectView.swift in Sources */, 1F66F5BA2938CA5700A910CA /* VisualEffectView.swift in Sources */,
BF0C4EBD22A1BD8B009A2DD7 /* AppManager.swift in Sources */, BF0C4EBD22A1BD8B009A2DD7 /* AppManager.swift in Sources */,
@@ -2902,6 +2909,7 @@
1FB84BA62928DE08006A5CF4 /* AppDetailView.swift in Sources */, 1FB84BA62928DE08006A5CF4 /* AppDetailView.swift in Sources */,
D57DF63F271E51E400677701 /* ALTAppPatcher.m in Sources */, D57DF63F271E51E400677701 /* ALTAppPatcher.m in Sources */,
BFB6B220231870B00022A802 /* NewsCollectionViewCell.swift in Sources */, BFB6B220231870B00022A802 /* NewsCollectionViewCell.swift in Sources */,
1FA5A6CC298E8FE4007BA946 /* MailComposeView.swift in Sources */,
BF3BEFBF2408673400DE7D55 /* FetchProvisioningProfilesOperation.swift in Sources */, BF3BEFBF2408673400DE7D55 /* FetchProvisioningProfilesOperation.swift in Sources */,
BFF0B69023219C6D007A79E1 /* PatreonComponents.swift in Sources */, BFF0B69023219C6D007A79E1 /* PatreonComponents.swift in Sources */,
1FB96FC5292A7251007E68D1 /* BrowseAppPreviewView.swift in Sources */, 1FB96FC5292A7251007E68D1 /* BrowseAppPreviewView.swift in Sources */,

View File

@@ -65,7 +65,7 @@ class NotificationManager: ObservableObject {
self.showNotification(title: text, detailText: detailText) self.showNotification(title: text, detailText: detailText)
} }
func showNotification(title: String, detailText: String?) { func showNotification(title: String, detailText: String? = nil) {
let notificationId = UUID() let notificationId = UUID()
DispatchQueue.main.async { DispatchQueue.main.async {

View File

@@ -0,0 +1,71 @@
//
// MailComposeView.swift
// SideStore
//
// Created by Fabian Thies on 04.02.23.
// Copyright © 2023 SideStore. All rights reserved.
//
import SwiftUI
import MessageUI
struct MailComposeView: UIViewControllerRepresentable {
typealias ActionHandler = () -> Void
typealias ErrorHandler = (Error) -> Void
static var canSendMail: Bool {
MFMailComposeViewController.canSendMail()
}
let recipients: [String]
let subject: String
var body: String? = nil
var onMailSent: ActionHandler? = nil
var onError: ErrorHandler? = nil
func makeCoordinator() -> Coordinator {
Coordinator(mailSentHandler: self.onMailSent, errorHandler: self.onError)
}
func makeUIViewController(context: Context) -> some UIViewController {
let mailViewController = MFMailComposeViewController()
mailViewController.mailComposeDelegate = context.coordinator
mailViewController.setToRecipients(self.recipients)
mailViewController.setSubject(self.subject)
if let body {
mailViewController.setMessageBody(body, isHTML: false)
}
return mailViewController
}
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
}
}
extension MailComposeView {
class Coordinator: NSObject, MFMailComposeViewControllerDelegate {
let mailSentHandler: ActionHandler?
let errorHandler: ErrorHandler?
init(mailSentHandler: ActionHandler?, errorHandler: ErrorHandler?) {
self.mailSentHandler = mailSentHandler
self.errorHandler = errorHandler
super.init()
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
if result == .sent, let mailSentHandler {
mailSentHandler()
} else if result == .failed, let errorHandler, let error {
errorHandler(error)
}
controller.dismiss(animated: true)
}
}
}

View File

@@ -0,0 +1,90 @@
//
// RefreshAttemptsView.swift
// SideStore
//
// Created by Fabian Thies on 04.02.23.
// Copyright © 2023 SideStore. All rights reserved.
//
import SwiftUI
import AltStoreCore
struct RefreshAttemptsView: View {
@SwiftUI.FetchRequest(sortDescriptors: [
NSSortDescriptor(keyPath: \RefreshAttempt.date, ascending: false)
])
var refreshAttempts: FetchedResults<RefreshAttempt>
var groupedRefreshAttempts: [Date: [RefreshAttempt]] {
Dictionary(grouping: refreshAttempts, by: { Calendar.current.startOfDay(for: $0.date) })
}
var body: some View {
List {
ForEach(groupedRefreshAttempts.keys.sorted(by: { $0 > $1 }), id: \.self) { date in
Section {
let attempts = groupedRefreshAttempts[date] ?? []
ForEach(attempts, id: \.date) { attempt in
VStack(alignment: .leading, spacing: 8) {
HStack {
if attempt.isSuccess {
Text("Success")
.bold()
.foregroundColor(.green)
} else {
Text("Failure")
.bold()
.foregroundColor(.red)
}
Spacer()
Text(DateFormatterHelper.timeString(for: attempt.date))
.font(.caption)
.foregroundColor(.secondary)
}
if let description = attempt.errorDescription {
Text(description)
}
}
}
} header: {
Text(DateFormatterHelper.string(for: date))
}
}
}
.background(self.listBackground)
.navigationTitle("Refresh Attempts")
}
@ViewBuilder
var listBackground: some View {
if self.refreshAttempts.isEmpty {
VStack(spacing: 8) {
Spacer()
Text("No Refresh Attempts")
.font(.title)
Text("The more you use SideStore, the more often iOS will allow it to refresh apps in the background.")
Spacer()
}
.multilineTextAlignment(.center)
.foregroundColor(.secondary)
.padding()
} else {
Color.clear
}
}
}
struct RefreshAttemptsView_Previews: PreviewProvider {
static var previews: some View {
NavigationView {
RefreshAttemptsView()
}
}
}

View File

@@ -27,6 +27,8 @@ struct SettingsView: View {
@State var isShowingConnectAppleIDView = false @State var isShowingConnectAppleIDView = false
@State var isShowingAddShortcutView = false @State var isShowingAddShortcutView = false
@State var isShowingFeedbackMailView = false
@State var isShowingResetPairingFileConfirmation = false
@State var externalURLToShow: URL? @State var externalURLToShow: URL?
@@ -147,18 +149,45 @@ struct SettingsView: View {
} }
Section { Section {
SwiftUI.Button(action: switchToUIKit) { NavigationLink("Show Error Log") {
Text(L10n.SettingsView.switchToUIKit)
}
SwiftUI.Button(action: resetImageCache) {
Text(L10n.SettingsView.resetImageCache)
}
NavigationLink {
ErrorLogView() ErrorLogView()
} label: { }
Text("Show Error Log")
NavigationLink("Show Refresh Attempts") {
RefreshAttemptsView()
}
if MailComposeView.canSendMail {
SwiftUI.Button("Send Feedback") {
self.isShowingFeedbackMailView = true
}
.sheet(isPresented: self.$isShowingFeedbackMailView) {
MailComposeView(recipients: ["support@sidestore.io"],
subject: "SideStore Beta \(appVersion) Feedback") {
NotificationManager.shared.showNotification(title: "Thank you for your feedback!")
} onError: { error in
NotificationManager.shared.reportError(error: error)
}
.ignoresSafeArea()
}
}
SwiftUI.Button(L10n.SettingsView.switchToUIKit, action: self.switchToUIKit)
SwiftUI.Button("Advanced Settings", action: self.showAdvancedSettings)
SwiftUI.Button(L10n.SettingsView.resetImageCache, action: self.resetImageCache)
.foregroundColor(.red)
SwiftUI.Button("Reset Pairing File") {
self.isShowingResetPairingFileConfirmation = true
}
.foregroundColor(.red)
.actionSheet(isPresented: self.$isShowingResetPairingFileConfirmation) {
ActionSheet(title: Text("Are you sure to reset the pairing file?"), message: Text("You can reset the pairing file when you cannot sideload apps or enable JIT. SideStore will close when the file has been deleted."), buttons: [
.destructive(Text("Delete and Reset"), action: self.resetPairingFile),
.cancel()
])
} }
} header: { } header: {
Text(L10n.SettingsView.debug) Text(L10n.SettingsView.debug)
@@ -231,7 +260,6 @@ struct SettingsView: View {
} }
} }
func switchToUIKit() { func switchToUIKit() {
let storyboard = UIStoryboard(name: "Main", bundle: .main) let storyboard = UIStoryboard(name: "Main", bundle: .main)
let rootVC = storyboard.instantiateViewController(withIdentifier: "tabBarController") as! TabBarController let rootVC = storyboard.instantiateViewController(withIdentifier: "tabBarController") as! TabBarController
@@ -251,6 +279,37 @@ struct SettingsView: View {
fatalError("\(error)") fatalError("\(error)")
} }
} }
func resetPairingFile() {
let filename = "ALTPairingFile.mobiledevicepairing"
let fileURL = FileManager.default.documentsDirectory.appendingPathComponent(filename)
// Delete the pairing file if it exists
if FileManager.default.fileExists(atPath: fileURL.path) {
do {
try FileManager.default.removeItem(at: fileURL)
print("Pairing file deleted successfully.")
} catch {
print("Failed to delete pairing file:", error)
}
}
// Close and exit SideStore
UIApplication.shared.perform(#selector(URLSessionTask.suspend))
DispatchQueue.main.asyncAfter(deadline: .now().advanced(by: .milliseconds(500))) {
exit(0)
}
}
func showAdvancedSettings() {
// Create the URL that deep links to our app's custom settings.
guard let url = URL(string: UIApplication.openSettingsURLString) else {
return
}
// Ask the system to open that URL.
UIApplication.shared.open(url)
}
} }
struct SettingsView_Previews: PreviewProvider { struct SettingsView_Previews: PreviewProvider {