[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
committed by Joe Mattiello
parent 07159b0ea6
commit 723c8e9539
5 changed files with 241 additions and 13 deletions

View File

@@ -65,7 +65,7 @@ class NotificationManager: ObservableObject {
self.showNotification(title: text, detailText: detailText)
}
func showNotification(title: String, detailText: String?) {
func showNotification(title: String, detailText: String? = nil) {
let notificationId = UUID()
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 isShowingAddShortcutView = false
@State var isShowingFeedbackMailView = false
@State var isShowingResetPairingFileConfirmation = false
@State var externalURLToShow: URL?
@@ -147,18 +149,45 @@ struct SettingsView: View {
}
Section {
SwiftUI.Button(action: switchToUIKit) {
Text(L10n.SettingsView.switchToUIKit)
}
SwiftUI.Button(action: resetImageCache) {
Text(L10n.SettingsView.resetImageCache)
}
NavigationLink {
NavigationLink("Show Error Log") {
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: {
Text(L10n.SettingsView.debug)
@@ -231,7 +260,6 @@ struct SettingsView: View {
}
}
func switchToUIKit() {
let storyboard = UIStoryboard(name: "Main", bundle: .main)
let rootVC = storyboard.instantiateViewController(withIdentifier: "tabBarController") as! TabBarController
@@ -251,6 +279,37 @@ struct SettingsView: View {
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 {