[diagnostics]: Added switches for OperationLogging to use them for debugging/diagnostics on device

This commit is contained in:
Magesh K
2025-01-14 07:23:23 +05:30
parent abd3735ae4
commit 0da743e9a6
7 changed files with 485 additions and 82 deletions

View File

@@ -65,6 +65,8 @@
A859ED5D2D1EE827003DCC58 /* OpenSSL.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = A859ED5B2D1EE80D003DCC58 /* OpenSSL.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
A868CFE42D31999A002F1201 /* SingletonGenericMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = A868CFE32D319988002F1201 /* SingletonGenericMap.swift */; };
A8696EE42D34512C00E96389 /* RemoveAppExtensionsOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8696EE32D34512C00E96389 /* RemoveAppExtensionsOperation.swift */; };
A88B8C492D35AD3200F53F9D /* OperationsLoggingContolView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A88B8C482D35AD3200F53F9D /* OperationsLoggingContolView.swift */; };
A88B8C552D35F1EC00F53F9D /* OperationsLoggingControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = A88B8C542D35F1EC00F53F9D /* OperationsLoggingControl.swift */; };
A8945AA62D059B6100D86CBE /* Roxas.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A8945AA52D059B6100D86CBE /* Roxas.framework */; };
A8A543302D04F14400D72399 /* libfragmentzip.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A8A5432F2D04F0C100D72399 /* libfragmentzip.a */; };
A8A853AF2D3065A300995795 /* ActiveAppsTimelineProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8A853AE2D3065A300995795 /* ActiveAppsTimelineProvider.swift */; };
@@ -647,6 +649,8 @@
A86202332D1F35640091187B /* AltStoreCore.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AltStoreCore.xcconfig; sourceTree = "<group>"; };
A868CFE32D319988002F1201 /* SingletonGenericMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingletonGenericMap.swift; sourceTree = "<group>"; };
A8696EE32D34512C00E96389 /* RemoveAppExtensionsOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoveAppExtensionsOperation.swift; sourceTree = "<group>"; };
A88B8C482D35AD3200F53F9D /* OperationsLoggingContolView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OperationsLoggingContolView.swift; sourceTree = "<group>"; };
A88B8C542D35F1EC00F53F9D /* OperationsLoggingControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OperationsLoggingControl.swift; sourceTree = "<group>"; };
A8945AA52D059B6100D86CBE /* Roxas.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Roxas.framework; sourceTree = BUILT_PRODUCTS_DIR; };
A8A853AE2D3065A300995795 /* ActiveAppsTimelineProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveAppsTimelineProvider.swift; sourceTree = "<group>"; };
A8AD35582D31BF29003A28B4 /* PageInfoManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageInfoManager.swift; sourceTree = "<group>"; };
@@ -1211,6 +1215,14 @@
path = xcconfigs;
sourceTree = "<group>";
};
A88B8C532D35F1E800F53F9D /* operations */ = {
isa = PBXGroup;
children = (
A88B8C542D35F1EC00F53F9D /* OperationsLoggingControl.swift */,
);
path = operations;
sourceTree = "<group>";
};
A8A543222D04F0C100D72399 /* Products */ = {
isa = PBXGroup;
children = (
@@ -1249,6 +1261,7 @@
A8B516DE2D2666900047047C /* dignostics */ = {
isa = PBXGroup;
children = (
A88B8C532D35F1E800F53F9D /* operations */,
A8B516DF2D2666A00047047C /* database */,
);
path = dignostics;
@@ -1995,6 +2008,7 @@
BFDB69FB22A9A7A6007EA6D6 /* Settings */ = {
isa = PBXGroup;
children = (
A88B8C482D35AD3200F53F9D /* OperationsLoggingContolView.swift */,
BFE60737231ADF49002B0E8E /* Settings.storyboard */,
BFE60739231ADF82002B0E8E /* SettingsViewController.swift */,
0EA426392C2230150026D7FB /* AnisetteServerList.swift */,
@@ -2018,30 +2032,30 @@
children = (
D513F6152A12CE210061EAA1 /* Errors */,
BFDB6A0A22AAEDB7007EA6D6 /* Operation.swift */,
BF770E5722BC3D0F002A40FE /* RefreshGroup.swift */,
BF770E5322BC044E002A40FE /* OperationContexts.swift */,
BF770E5722BC3D0F002A40FE /* RefreshGroup.swift */,
BFE6326B22A86FF300F30809 /* AuthenticationOperation.swift */,
D561AF812B21669400BF59C6 /* VerifyAppPledgeOperation.swift */,
BFC1F38C22AEE3A4003AC21A /* DownloadAppOperation.swift */,
BFDB6A0722AAED73007EA6D6 /* ResignAppOperation.swift */,
BFCCB519245E3401001853EA /* VerifyAppOperation.swift */,
A8696EE32D34512C00E96389 /* RemoveAppExtensionsOperation.swift */,
BFA8172A23C5633D001B5953 /* FetchAnisetteDataOperation.swift */,
BF3BEFBE2408673400DE7D55 /* FetchProvisioningProfilesOperation.swift */,
BF3BEFC024086A1E00DE7D55 /* RefreshAppOperation.swift */,
BFDB6A0722AAED73007EA6D6 /* ResignAppOperation.swift */,
BFDB6A0E22AB2776007EA6D6 /* SendAppOperation.swift */,
BF770E5022BB1CF6002A40FE /* InstallAppOperation.swift */,
BFE338DE22F0EADB002E24B9 /* FetchSourceOperation.swift */,
BFA8172A23C5633D001B5953 /* FetchAnisetteDataOperation.swift */,
BF3BEFC024086A1E00DE7D55 /* RefreshAppOperation.swift */,
BF56D2AB23DF8E170006506D /* FetchAppIDsOperation.swift */,
BFC57A642416C72400EB891E /* DeactivateAppOperation.swift */,
BFCCB519245E3401001853EA /* VerifyAppOperation.swift */,
BF44EEFB246B4550002A52F2 /* RemoveAppOperation.swift */,
BFE338DE22F0EADB002E24B9 /* FetchSourceOperation.swift */,
D5E1E7C028077DE90016FC96 /* UpdateKnownSourcesOperation.swift */,
BF3432FA246B894F0052F4A1 /* BackupAppOperation.swift */,
BFDBBD7F246CB84F004ED2F3 /* RemoveAppBackupOperation.swift */,
BF44EEFB246B4550002A52F2 /* RemoveAppOperation.swift */,
BFC57A642416C72400EB891E /* DeactivateAppOperation.swift */,
BFF00D312501BDA100746320 /* BackgroundRefreshAppsOperation.swift */,
D57F2C9026E0070200B9FA39 /* EnableJITOperation.swift */,
D5DAE0952804DF430034D8D4 /* UpdatePatronsOperation.swift */,
D5E1E7C028077DE90016FC96 /* UpdateKnownSourcesOperation.swift */,
D5ACE84428E3B8450021CAB9 /* ClearAppCacheOperation.swift */,
D561AF812B21669400BF59C6 /* VerifyAppPledgeOperation.swift */,
A8696EE32D34512C00E96389 /* RemoveAppExtensionsOperation.swift */,
BF7B44062725A4B8005288A4 /* Patch App */,
);
path = Operations;
@@ -3063,6 +3077,7 @@
BF770E5822BC3D0F002A40FE /* RefreshGroup.swift in Sources */,
19B9B7452845E6DF0076EF69 /* SelectTeamViewController.swift in Sources */,
0E13E5862CC8F55900E9C0DF /* ProcessInfo+SideStore.swift in Sources */,
A88B8C492D35AD3200F53F9D /* OperationsLoggingContolView.swift in Sources */,
D59162AB29BA60A9005CBF47 /* SourceHeaderView.swift in Sources */,
BF18B0F122E25DF9005C4CF5 /* ToastView.swift in Sources */,
BF3D649F22E7B24C00E9056B /* CollapsingTextView.swift in Sources */,
@@ -3072,6 +3087,7 @@
BFC84A4D2421A19100853474 /* SourcesViewController.swift in Sources */,
BFF0B696232242D3007A79E1 /* LicensesViewController.swift in Sources */,
D57FE84428C7DB7100216002 /* ErrorLogViewController.swift in Sources */,
A88B8C552D35F1EC00F53F9D /* OperationsLoggingControl.swift in Sources */,
D50107EC2ADF2E1A0069F2A1 /* AddSourceTextFieldCell.swift in Sources */,
D5151BD92A8FF64300C96F28 /* RefreshAllAppsIntent.swift in Sources */,
BFBE0007250AD0E70080826E /* ViewAppIntentHandler.swift in Sources */,

View File

@@ -14,6 +14,8 @@ import AltStoreCore
import AltSign
import Roxas
class ANISETTE_VERBOSITY: Operation {} // dummy tag iface
@objc(FetchAnisetteDataOperation)
final class FetchAnisetteDataOperation: ResultOperation<ALTAnisetteData>, WebSocketDelegate
{
@@ -58,7 +60,7 @@ final class FetchAnisetteDataOperation: ResultOperation<ALTAnisetteData>, WebSoc
UserDefaults.standard.menuAnisetteURL = urlString
let url = URL(string: urlString)
self.url = url
print("Anisette URL: \(self.url!.absoluteString)")
self.printOut("Anisette URL: \(self.url!.absoluteString)")
if let identifier = Keychain.shared.identifier,
let adiPb = Keychain.shared.adiPb {
@@ -107,7 +109,7 @@ final class FetchAnisetteDataOperation: ResultOperation<ALTAnisetteData>, WebSoc
guard let url = URL(string: currentServerUrlString) else {
// Invalid URL, skip to next
let errmsg = "Skipping invalid URL: \(currentServerUrlString)"
print(errmsg)
self.printOut(errmsg)
showToast(viewContext: viewContext, message: errmsg)
tryNextServer(from: serverUrls, viewContext, currentIndex: currentIndex + 1, completion: completion)
return
@@ -118,7 +120,7 @@ final class FetchAnisetteDataOperation: ResultOperation<ALTAnisetteData>, WebSoc
if success {
// If the server is reachable, return the URL
let okmsg = "Found working server: \(url.absoluteString)"
print(okmsg)
self.printOut(okmsg)
if(currentIndex > 0){
// notify user if available server is different the user-specified one
self.showToast(viewContext: viewContext, message: okmsg)
@@ -127,7 +129,7 @@ final class FetchAnisetteDataOperation: ResultOperation<ALTAnisetteData>, WebSoc
} else {
// If not, try the next URL
let errmsg = "Failed to reach server: \(url.absoluteString), trying next server."
print(errmsg)
self.printOut(errmsg)
self.showToast(viewContext: viewContext, message: errmsg)
self.tryNextServer(from: serverUrls, viewContext, currentIndex: currentIndex + 1, completion: completion)
}
@@ -170,10 +172,10 @@ final class FetchAnisetteDataOperation: ResultOperation<ALTAnisetteData>, WebSoc
if v3 {
if json["result"] == "GetHeadersError" {
let message = json["message"]
print("Error getting V3 headers: \(message ?? "no message")")
self.printOut("Error getting V3 headers: \(message ?? "no message")")
if let message = message,
message.contains("-45061") {
print("Error message contains -45061 (not provisioned), resetting adi.pb and retrying")
self.printOut("Error message contains -45061 (not provisioned), resetting adi.pb and retrying")
Keychain.shared.adiPb = nil
return provision()
} else { throw OperationError.anisetteV3Error(message: message ?? "Unknown error") }
@@ -214,16 +216,16 @@ final class FetchAnisetteDataOperation: ResultOperation<ALTAnisetteData>, WebSoc
if let response = response,
let version = response.value(forHTTPHeaderField: "Implementation-Version") {
print("Implementation-Version: \(version)")
} else { print("No Implementation-Version header") }
self.printOut("Implementation-Version: \(version)")
} else { self.printOut("No Implementation-Version header") }
print("Anisette used: \(formattedJSON)")
print("Original JSON: \(json)")
self.printOut("Anisette used: \(formattedJSON)")
self.printOut("Original JSON: \(json)")
if let anisette = ALTAnisetteData(json: formattedJSON) {
print("Anisette is valid!")
self.printOut("Anisette is valid!")
self.finish(.success(anisette))
} else {
print("Anisette is invalid!!!!")
self.printOut("Anisette is invalid!!!!")
if v3 {
throw OperationError.anisetteV3Error(message: "Invalid anisette (the returned data may not have all the required fields)")
} else {
@@ -242,22 +244,22 @@ final class FetchAnisetteDataOperation: ResultOperation<ALTAnisetteData>, WebSoc
// MARK: - V1
func handleV1() {
print("Server is V1")
self.printOut("Server is V1")
if UserDefaults.shared.trustedServerURL == AnisetteManager.currentURLString {
print("Server has already been trusted, fetching anisette")
self.printOut("Server has already been trusted, fetching anisette")
return self.fetchAnisetteV1()
}
print("Alerting user about outdated server")
self.printOut("Alerting user about outdated server")
let alert = UIAlertController(title: "WARNING: Outdated anisette server", message: "We've detected you are using an older anisette server. Using this server has a higher likelihood of locking your account and causing other issues. Are you sure you want to continue?", preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction(title: "Continue", style: UIAlertAction.Style.destructive, handler: { action in
print("Fetching anisette via V1")
self.printOut("Fetching anisette via V1")
UserDefaults.shared.trustedServerURL = AnisetteManager.currentURLString
self.fetchAnisetteV1()
}))
alert.addAction(UIAlertAction(title: "Cancel", style: UIAlertAction.Style.cancel, handler: { action in
print("Cancelled anisette operation")
self.printOut("Cancelled anisette operation")
self.finish(.failure(OperationError.cancelled))
}))
@@ -273,14 +275,14 @@ final class FetchAnisetteDataOperation: ResultOperation<ALTAnisetteData>, WebSoc
}
func fetchAnisetteV1() {
print("Fetching anisette V1")
self.printOut("Fetching anisette V1")
URLSession.shared.dataTask(with: self.url!) { data, response, error in
do {
guard let data = data, error == nil else { throw OperationError.anisetteV1Error(message: "Unable to fetch data\(error != nil ? " (\(error!.localizedDescription))" : "")") }
try self.extractAnisetteData(data, response as? HTTPURLResponse, v3: false)
} catch let error as NSError {
print("Failed to load: \(error.localizedDescription)")
self.printOut("Failed to load: \(error.localizedDescription)")
self.finish(.failure(error))
}
}.resume()
@@ -290,7 +292,7 @@ final class FetchAnisetteDataOperation: ResultOperation<ALTAnisetteData>, WebSoc
func provision() {
fetchClientInfo {
print("Getting provisioning URLs")
self.printOut("Getting provisioning URLs")
var request = self.buildAppleRequest(url: URL(string: "https://gsa.apple.com/grandslam/GsService2/lookup")!)
request.httpMethod = "GET"
URLSession.shared.dataTask(with: request) { data, response, error in
@@ -302,12 +304,12 @@ final class FetchAnisetteDataOperation: ResultOperation<ALTAnisetteData>, WebSoc
let endProvisioningURL = URL(string: endProvisioningString) {
self.startProvisioningURL = startProvisioningURL
self.endProvisioningURL = endProvisioningURL
print("startProvisioningURL: \(self.startProvisioningURL!.absoluteString)")
print("endProvisioningURL: \(self.endProvisioningURL!.absoluteString)")
print("Starting a provisioning session")
self.printOut("startProvisioningURL: \(self.startProvisioningURL!.absoluteString)")
self.printOut("endProvisioningURL: \(self.endProvisioningURL!.absoluteString)")
self.printOut("Starting a provisioning session")
self.startProvisioningSession()
} else {
print("Apple didn't give valid URLs! Got response: \(String(data: data ?? Data("nothing".utf8), encoding: .utf8) ?? "not utf8")")
self.printOut("Apple didn't give valid URLs! Got response: \(String(data: data ?? Data("nothing".utf8), encoding: .utf8) ?? "not utf8")")
self.finish(.failure(OperationError.provisioningError(result: "Apple didn't give valid URLs. Please try again later", message: nil)))
}
}.resume()
@@ -329,19 +331,19 @@ final class FetchAnisetteDataOperation: ResultOperation<ALTAnisetteData>, WebSoc
do {
if let json = try JSONSerialization.jsonObject(with: string.data(using: .utf8)!, options: []) as? [String: Any] {
guard let result = json["result"] as? String else {
print("The server didn't give us a result")
self.printOut("The server didn't give us a result")
client.disconnect(closeCode: 0)
self.finish(.failure(OperationError.provisioningError(result: "The server didn't give us a result", message: nil)))
return
}
print("Received result: \(result)")
self.printOut("Received result: \(result)")
switch result {
case "GiveIdentifier":
print("Giving identifier")
self.printOut("Giving identifier")
client.json(["identifier": Keychain.shared.identifier!])
case "GiveStartProvisioningData":
print("Getting start provisioning data")
self.printOut("Getting start provisioning data")
let body = [
"Header": [String: Any](),
"Request": [String: Any](),
@@ -353,19 +355,19 @@ final class FetchAnisetteDataOperation: ResultOperation<ALTAnisetteData>, WebSoc
if let data = data,
let plist = try? PropertyListSerialization.propertyList(from: data, format: nil) as? Dictionary<String, Dictionary<String, Any>>,
let spim = plist["Response"]?["spim"] as? String {
print("Giving start provisioning data")
self.printOut("Giving start provisioning data")
client.json(["spim": spim])
} else {
print("Apple didn't give valid start provisioning data! Got response: \(String(data: data ?? Data("nothing".utf8), encoding: .utf8) ?? "not utf8")")
self.printOut("Apple didn't give valid start provisioning data! Got response: \(String(data: data ?? Data("nothing".utf8), encoding: .utf8) ?? "not utf8")")
client.disconnect(closeCode: 0)
self.finish(.failure(OperationError.provisioningError(result: "Apple didn't give valid start provisioning data. Please try again later", message: nil)))
}
}.resume()
case "GiveEndProvisioningData":
print("Getting end provisioning data")
self.printOut("Getting end provisioning data")
guard let cpim = json["cpim"] as? String else {
print("The server didn't give us a cpim")
self.printOut("The server didn't give us a cpim")
client.disconnect(closeCode: 0)
self.finish(.failure(OperationError.provisioningError(result: "The server didn't give us a cpim", message: nil)))
return
@@ -384,20 +386,20 @@ final class FetchAnisetteDataOperation: ResultOperation<ALTAnisetteData>, WebSoc
let plist = try? PropertyListSerialization.propertyList(from: data, format: nil) as? Dictionary<String, Dictionary<String, Any>>,
let ptm = plist["Response"]?["ptm"] as? String,
let tk = plist["Response"]?["tk"] as? String {
print("Giving end provisioning data")
self.printOut("Giving end provisioning data")
client.json(["ptm": ptm, "tk": tk])
} else {
print("Apple didn't give valid end provisioning data! Got response: \(String(data: data ?? Data("nothing".utf8), encoding: .utf8) ?? "not utf8")")
self.printOut("Apple didn't give valid end provisioning data! Got response: \(String(data: data ?? Data("nothing".utf8), encoding: .utf8) ?? "not utf8")")
client.disconnect(closeCode: 0)
self.finish(.failure(OperationError.provisioningError(result: "Apple didn't give valid end provisioning data. Please try again later", message: nil)))
}
}.resume()
case "ProvisioningSuccess":
print("Provisioning succeeded!")
self.printOut("Provisioning succeeded!")
client.disconnect(closeCode: 0)
guard let adiPb = json["adi_pb"] as? String else {
print("The server didn't give us an adi.pb file")
self.printOut("The server didn't give us an adi.pb file")
self.finish(.failure(OperationError.provisioningError(result: "The server didn't give us an adi.pb file", message: nil)))
return
}
@@ -406,27 +408,27 @@ final class FetchAnisetteDataOperation: ResultOperation<ALTAnisetteData>, WebSoc
default:
if result.contains("Error") || result.contains("Invalid") || result == "ClosingPerRequest" || result == "Timeout" || result == "TextOnly" {
print("Failing because of \(result)")
self.printOut("Failing because of \(result)")
self.finish(.failure(OperationError.provisioningError(result: result, message: json["message"] as? String)))
}
}
}
} catch let error as NSError {
print("Failed to handle text: \(error.localizedDescription)")
self.printOut("Failed to handle text: \(error.localizedDescription)")
self.finish(.failure(OperationError.provisioningError(result: error.localizedDescription, message: nil)))
}
case .connected:
print("Connected")
self.printOut("Connected")
case .disconnected(let string, let code):
print("Disconnected: \(code); \(string)")
self.printOut("Disconnected: \(code); \(string)")
case .error(let error):
print("Got error: \(String(describing: error))")
self.printOut("Got error: \(String(describing: error))")
default:
print("Unknown event: \(event)")
self.printOut("Unknown event: \(event)")
}
}
@@ -460,10 +462,10 @@ final class FetchAnisetteDataOperation: ResultOperation<ALTAnisetteData>, WebSoc
self.mdLu != nil &&
self.deviceId != nil &&
Keychain.shared.identifier != nil {
print("Skipping client_info fetch since all the properties we need aren't nil")
self.printOut("Skipping client_info fetch since all the properties we need aren't nil")
return callback()
}
print("Trying to get client_info")
self.printOut("Trying to get client_info")
let clientInfoURL = self.url!.appendingPathComponent("v3").appendingPathComponent("client_info")
URLSession.shared.dataTask(with: clientInfoURL) { data, response, error in
do {
@@ -473,20 +475,20 @@ final class FetchAnisetteDataOperation: ResultOperation<ALTAnisetteData>, WebSoc
if let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: String] {
if let clientInfo = json["client_info"] {
print("Server is V3")
self.printOut("Server is V3")
self.clientInfo = clientInfo
self.userAgent = json["user_agent"]!
print("Client-Info: \(self.clientInfo!)")
print("User-Agent: \(self.userAgent!)")
self.printOut("Client-Info: \(self.clientInfo!)")
self.printOut("User-Agent: \(self.userAgent!)")
if Keychain.shared.identifier == nil {
print("Generating identifier")
self.printOut("Generating identifier")
var bytes = [Int8](repeating: 0, count: 16)
let status = SecRandomCopyBytes(kSecRandomDefault, bytes.count, &bytes)
if status != errSecSuccess {
print("ERROR GENERATING IDENTIFIER!!! \(status)")
self.printOut("ERROR GENERATING IDENTIFIER!!! \(status)")
return self.finish(.failure(OperationError.provisioningError(result: "Couldn't generate identifier", message: nil)))
}
@@ -495,16 +497,16 @@ final class FetchAnisetteDataOperation: ResultOperation<ALTAnisetteData>, WebSoc
let decoded = Data(base64Encoded: Keychain.shared.identifier!)!
self.mdLu = decoded.sha256().hexEncodedString()
print("X-Apple-I-MD-LU: \(self.mdLu!)")
self.printOut("X-Apple-I-MD-LU: \(self.mdLu!)")
let uuid: UUID = decoded.object()
self.deviceId = uuid.uuidString.uppercased()
print("X-Mme-Device-Id: \(self.deviceId!)")
self.printOut("X-Mme-Device-Id: \(self.deviceId!)")
callback()
} else { self.handleV1() }
} else { self.finish(.failure(OperationError.anisetteV3Error(message: "Couldn't fetch client info. The returned data may not be in JSON"))) }
} catch let error as NSError {
print("Failed to load: \(error.localizedDescription)")
self.printOut("Failed to load: \(error.localizedDescription)")
self.handleV1()
}
}.resume()
@@ -512,7 +514,7 @@ final class FetchAnisetteDataOperation: ResultOperation<ALTAnisetteData>, WebSoc
func fetchAnisetteV3(_ identifier: String, _ adiPb: String) {
fetchClientInfo {
print("Fetching anisette V3")
self.printOut("Fetching anisette V3")
let url = UserDefaults.standard.menuAnisetteURL
var request = URLRequest(url: self.url!.appendingPathComponent("v3").appendingPathComponent("get_headers"))
request.httpMethod = "POST"
@@ -527,12 +529,21 @@ final class FetchAnisetteDataOperation: ResultOperation<ALTAnisetteData>, WebSoc
try self.extractAnisetteData(data, response as? HTTPURLResponse, v3: true)
} catch let error as NSError {
print("Failed to load: \(error.localizedDescription)")
self.printOut("Failed to load: \(error.localizedDescription)")
self.finish(.failure(error))
}
}.resume()
}
}
private func printOut(_ text: String?){
let isInternalLoggingEnabled = OperationsLoggingControl.getFromDatabase(for: ANISETTE_VERBOSITY.self)
if(isInternalLoggingEnabled){
// logging enabled, so log it
text.map{ _ in print(text!) } ?? print()
}
}
}
extension WebSocketClient {

View File

@@ -39,7 +39,8 @@ class ResultOperation<ResultType>: Operation
}
// Diagnostics: perform verbose logging of the operations only if enabled (so as to not flood console logs)
if UserDefaults.standard.isVerboseOperationsLoggingEnabled {
let isLoggingEnabledForThisOperation = OperationsLoggingControl.getFromDatabase(for: type(of: self))
if UserDefaults.standard.isVerboseOperationsLoggingEnabled && isLoggingEnabledForThisOperation {
// diagnostics logging
let resultStatus = String(describing: result).prefix("success".count).uppercased()
print("\n ====> OPERATION: `\(type(of: self))` completed with: \(resultStatus) <====\n\n" +

View File

@@ -0,0 +1,277 @@
//
// SettingsView.swift
// AltStore
//
// Created by Magesh K on 14/01/25.
// Copyright © 2025 SideStore. All rights reserved.
//
import SwiftUI
import AltStoreCore
private final class DummyConformance: EnableJITContext
{
private init(){} // non instantiatable
var installedApp: AltStoreCore.InstalledApp?
var error: (any Error)?
}
struct OperationsLoggingControlView: View {
let TITLE = "Operations Logging"
let BACKGROUND_COLOR = Color(.settingsBackground)
var viewModel = OperationsLoggingControl()
var body: some View {
NavigationView {
ZStack {
// BACKGROUND_COLOR.ignoresSafeArea() // Force background to cover the entire screen
VStack{
Group{}.padding(12)
CustomList {
CustomSection(header: Text("Install Operations"))
{
CustomToggle("1. Authentication", isOn: Binding(
get: { self.viewModel.getFromDatabase(for: AuthenticationOperation.self) },
set: { value in
self.viewModel.updateDatabase(for: AuthenticationOperation.self, value: value)
}
))
CustomToggle("2. VerifyAppPledge", isOn: Binding(
get: { self.viewModel.getFromDatabase(for: VerifyAppPledgeOperation.self) },
set: { value in
self.viewModel.updateDatabase(for: VerifyAppPledgeOperation.self, value: value)
}
))
CustomToggle("3. DownloadApp", isOn: Binding(
get: { self.viewModel.getFromDatabase(for: DownloadAppOperation.self) },
set: { value in
self.viewModel.updateDatabase(for: DownloadAppOperation.self, value: value)
}
))
CustomToggle("4. VerifyApp", isOn: Binding(
get: { self.viewModel.getFromDatabase(for: VerifyAppOperation.self) },
set: { value in
self.viewModel.updateDatabase(for: VerifyAppOperation.self, value: value)
}
))
CustomToggle("5. RemoveAppExtensions", isOn: Binding(
get: { self.viewModel.getFromDatabase(for: RemoveAppExtensionsOperation.self) },
set: { value in
self.viewModel.updateDatabase(for: RemoveAppExtensionsOperation.self, value: value)
}
))
CustomToggle("6. FetchAnisetteData", isOn: Binding(
get: { self.viewModel.getFromDatabase(for: FetchAnisetteDataOperation.self) },
set: { value in
self.viewModel.updateDatabase(for: FetchAnisetteDataOperation.self, value: value)
}
))
CustomToggle("7. FetchProvisioningProfiles", isOn: Binding(
get: { self.viewModel.getFromDatabase(for: FetchProvisioningProfilesOperation.self) },
set: { value in
self.viewModel.updateDatabase(for: FetchProvisioningProfilesOperation.self, value: value)
}
))
CustomToggle("8. ResignApp", isOn: Binding(
get: { self.viewModel.getFromDatabase(for: ResignAppOperation.self) },
set: { value in
self.viewModel.updateDatabase(for: ResignAppOperation.self, value: value)
}
))
CustomToggle("9. SendApp", isOn: Binding(
get: { self.viewModel.getFromDatabase(for: SendAppOperation.self) },
set: { value in
self.viewModel.updateDatabase(for: SendAppOperation.self, value: value)
}
))
CustomToggle("10. InstallApp", isOn: Binding(
get: { self.viewModel.getFromDatabase(for: InstallAppOperation.self) },
set: { value in
self.viewModel.updateDatabase(for: InstallAppOperation.self, value: value)
}
))
}
CustomSection(header: Text("Refresh Operations"))
{
CustomToggle("1. RefreshApp", isOn: Binding(
get: { self.viewModel.getFromDatabase(for: RefreshAppOperation.self) },
set: { value in
self.viewModel.updateDatabase(for: RefreshAppOperation.self, value: value)
}
))
}
CustomSection(header: Text("AppIDs related Operations"))
{
CustomToggle("1. FetchAppIDs", isOn: Binding(
get: { self.viewModel.getFromDatabase(for: FetchAppIDsOperation.self) },
set: { value in
self.viewModel.updateDatabase(for: FetchAppIDsOperation.self, value: value)
}
))
}
CustomSection(header: Text("Sources related Operations"))
{
CustomToggle("1. FetchSource", isOn: Binding(
get: { self.viewModel.getFromDatabase(for: FetchSourceOperation.self) },
set: { value in
self.viewModel.updateDatabase(for: FetchSourceOperation.self, value: value)
}
))
CustomToggle("2. UpdateKnownSources", isOn: Binding(
get: { self.viewModel.getFromDatabase(for: UpdateKnownSourcesOperation.self) },
set: { value in
self.viewModel.updateDatabase(for: UpdateKnownSourcesOperation.self, value: value)
}
))
}
CustomSection(header: Text("Backup Operations"))
{
CustomToggle("1. BackupApp", isOn: Binding(
get: { self.viewModel.getFromDatabase(for: BackupAppOperation.self) },
set: { value in
self.viewModel.updateDatabase(for: BackupAppOperation.self, value: value)
}
))
CustomToggle("2. RemoveAppBackup", isOn: Binding(
get: { self.viewModel.getFromDatabase(for: RemoveAppBackupOperation.self) },
set: { value in
self.viewModel.updateDatabase(for: RemoveAppBackupOperation.self, value: value)
}
))
}
CustomSection(header: Text("Activate/Deactive Operations"))
{
CustomToggle("1. RemoveApp", isOn: Binding(
get: { self.viewModel.getFromDatabase(for: RemoveAppOperation.self) },
set: { value in
self.viewModel.updateDatabase(for: RemoveAppOperation.self, value: value)
}
))
CustomToggle("2. DeactivateApp", isOn: Binding(
get: { self.viewModel.getFromDatabase(for: DeactivateAppOperation.self) },
set: { value in
self.viewModel.updateDatabase(for: DeactivateAppOperation.self, value: value)
}
))
}
CustomSection(header: Text("Background refresh Operations"))
{
CustomToggle("1. BackgroundRefreshApps", isOn: Binding(
get: { self.viewModel.getFromDatabase(for: BackgroundRefreshAppsOperation.self) },
set: { value in
self.viewModel.updateDatabase(for: BackgroundRefreshAppsOperation.self, value: value)
}
))
}
CustomSection(header: Text("Enable JIT Operations"))
{
CustomToggle("1. EnableJIT", isOn: Binding(
get: { self.viewModel.getFromDatabase(for: EnableJITOperation<DummyConformance>.self) },
set: { value in
self.viewModel.updateDatabase(for: EnableJITOperation<DummyConformance>.self, value: value)
}
))
}
CustomSection(header: Text("Patrons Operations"))
{
CustomToggle("1. UpdatePatrons", isOn: Binding(
get: { self.viewModel.getFromDatabase(for: UpdatePatronsOperation.self) },
set: { value in
self.viewModel.updateDatabase(for: UpdatePatronsOperation.self, value: value)
}
))
}
CustomSection(header: Text("Cache Operations"))
{
CustomToggle("1. ClearAppCache", isOn: Binding(
get: { self.viewModel.getFromDatabase(for: ClearAppCacheOperation.self) },
set: { value in
self.viewModel.updateDatabase(for: ClearAppCacheOperation.self, value: value)
}
))
}
CustomSection(header: Text("Misc Logging"))
{
CustomToggle("1. Anisette Internal Logging", isOn: Binding(
// enable anisette internal logging by default since it was already printing before
get: { OperationsLoggingControl.getUpdatedFromDatabase(
for: ANISETTE_VERBOSITY.self, defaultVal: true
)},
set: { value in
self.viewModel.updateDatabase(for: ANISETTE_VERBOSITY.self, value: value)
}
))
}
}
}
}
.navigationTitle(TITLE)
}
.ignoresSafeArea(edges: .all)
}
private func CustomList<Content: View>(@ViewBuilder content: () -> Content) -> some View {
// ScrollView {
List {
content()
}
// .listStyle(.plain)
// .listStyle(InsetGroupedListStyle()) // Or PlainListStyle for iOS 15
// .background(Color.clear)
// .background(Color(.settingsBackground))
// .onAppear(perform: {
// // cache the current background color
// UITableView.appearance().backgroundColor = UIColor.red
// })
// .onDisappear(perform: {
// // reset the background color to the cached value
// UITableView.appearance().backgroundColor = UIColor.systemBackground
// })
}
private func CustomSection<Content: View>(header: Text, @ViewBuilder content: () -> Content) -> some View {
Section(header: header) {
content()
}
// .listRowBackground(Color.clear)
}
private func CustomToggle(_ title: String, isOn: Binding<Bool>) -> some View {
Toggle(title, isOn: isOn)
.padding(3)
// .foregroundColor(.white) // Ensures text color is always white
// .font(.headline)
}
}
struct SettingsView_Previews: PreviewProvider {
static var previews: some View {
OperationsLoggingControlView()
}
}

View File

@@ -22,7 +22,7 @@
<color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<color key="separatorColor" white="1" alpha="0.25" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<stackView key="tableFooterView" opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="equalCentering" alignment="center" spacing="15" id="48g-cT-stR">
<rect key="frame" x="0.0" y="1760.3333377838135" width="402" height="125"/>
<rect key="frame" x="0.0" y="1818.3333377838135" width="402" height="125"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="900" text="Follow SideStore for updates" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="XFa-MY-7cV">
@@ -530,7 +530,7 @@
</connections>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="hFh-X1-ZAi" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="856.33333587646484" width="402" height="51"/>
<rect key="frame" x="0.0" y="863.33333587646484" width="402" height="51"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="hFh-X1-ZAi" id="nCs-Ro-A6t">
<rect key="frame" x="0.0" y="0.0" width="402" height="51"/>
@@ -562,7 +562,7 @@
<tableViewSection headerTitle="" id="J90-vn-u2O">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="i4T-2q-jF3" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="947.66666984558105" width="402" height="51"/>
<rect key="frame" x="0.0" y="954.66666984558105" width="402" height="51"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="i4T-2q-jF3" id="VTQ-H4-aCM">
<rect key="frame" x="0.0" y="0.0" width="402" height="51"/>
@@ -606,7 +606,7 @@
</userDefinedRuntimeAttributes>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="oHX-oR-nwJ" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="998.66666984558105" width="402" height="51"/>
<rect key="frame" x="0.0" y="1005.6666698455811" width="402" height="51"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="oHX-oR-nwJ" id="hN4-i5-igu">
<rect key="frame" x="0.0" y="0.0" width="402" height="51"/>
@@ -650,7 +650,7 @@
</userDefinedRuntimeAttributes>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="0MT-ht-Sit" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="1049.6666698455811" width="402" height="51"/>
<rect key="frame" x="0.0" y="1056.6666698455811" width="402" height="51"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="0MT-ht-Sit" id="OZp-WM-5H7">
<rect key="frame" x="0.0" y="0.0" width="402" height="51"/>
@@ -694,7 +694,7 @@
</userDefinedRuntimeAttributes>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="O5R-Al-lGj" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="1100.6666698455811" width="402" height="51"/>
<rect key="frame" x="0.0" y="1071.6666698455811" width="402" height="51"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="O5R-Al-lGj" id="CrG-Mr-xQq">
<rect key="frame" x="0.0" y="0.0" width="402" height="51"/>
@@ -734,7 +734,7 @@
<tableViewSection headerTitle="" id="OMa-EK-hRI">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="FMZ-as-Ljo" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="1192.0000038146973" width="402" height="51"/>
<rect key="frame" x="0.0" y="1163.0000038146973" width="402" height="51"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="FMZ-as-Ljo" id="JzL-Of-A3T">
<rect key="frame" x="0.0" y="0.0" width="402" height="51"/>
@@ -767,7 +767,7 @@
</userDefinedRuntimeAttributes>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="Qca-pU-sJh" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="1243.0000038146973" width="402" height="51"/>
<rect key="frame" x="0.0" y="1214.0000038146973" width="402" height="51"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="Qca-pU-sJh" id="QtU-8J-VQN">
<rect key="frame" x="0.0" y="0.0" width="402" height="51"/>
@@ -803,7 +803,7 @@
</connections>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="VrV-qI-zXF" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="1294.0000038146973" width="402" height="51"/>
<rect key="frame" x="0.0" y="1265.0000038146973" width="402" height="51"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="VrV-qI-zXF" id="w1r-uY-4pD">
<rect key="frame" x="0.0" y="0.0" width="402" height="51"/>
@@ -836,7 +836,7 @@
</userDefinedRuntimeAttributes>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="VNn-u4-cN8" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="1345.0000038146973" width="402" height="51"/>
<rect key="frame" x="0.0" y="1316.0000038146973" width="402" height="51"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="VNn-u4-cN8" id="4bh-qe-l2N">
<rect key="frame" x="0.0" y="0.0" width="402" height="51"/>
@@ -869,7 +869,7 @@
</userDefinedRuntimeAttributes>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="e7s-hL-kv9" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="1396.0000038146973" width="402" height="51"/>
<rect key="frame" x="0.0" y="1367.0000038146973" width="402" height="51"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="e7s-hL-kv9" id="yjL-Mu-HTk">
<rect key="frame" x="0.0" y="0.0" width="402" height="51"/>
@@ -902,7 +902,7 @@
</userDefinedRuntimeAttributes>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="XW5-Zc-nXH" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="1447.0000038146973" width="402" height="51"/>
<rect key="frame" x="0.0" y="1418.0000038146973" width="402" height="51"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="XW5-Zc-nXH" id="AtM-bL-8pS">
<rect key="frame" x="0.0" y="0.0" width="402" height="51"/>
@@ -942,7 +942,7 @@
<tableViewSection headerTitle="" id="lLQ-K0-XSb">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="daQ-mk-yqC" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="1538.3333377838135" width="402" height="51"/>
<rect key="frame" x="0.0" y="1509.3333377838135" width="402" height="51"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="daQ-mk-yqC" id="ZkW-ZR-twy">
<rect key="frame" x="0.0" y="0.0" width="402" height="51"/>
@@ -978,7 +978,7 @@
</userDefinedRuntimeAttributes>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="hRP-jU-2hd" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="1589.3333377838135" width="402" height="51"/>
<rect key="frame" x="0.0" y="1560.3333377838135" width="402" height="51"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="hRP-jU-2hd" id="JhE-O4-pRg">
<rect key="frame" x="0.0" y="0.0" width="402" height="51"/>
@@ -1014,7 +1014,7 @@
</userDefinedRuntimeAttributes>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="JoN-Aj-XtZ" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="1640.3333377838135" width="402" height="51"/>
<rect key="frame" x="0.0" y="1611.3333377838135" width="402" height="51"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="JoN-Aj-XtZ" id="v8Q-VQ-Q1h">
<rect key="frame" x="0.0" y="0.0" width="402" height="51"/>
@@ -1050,7 +1050,7 @@
</userDefinedRuntimeAttributes>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="QOO-bO-4M5" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="1691.3333377838135" width="402" height="51"/>
<rect key="frame" x="0.0" y="1662.3333377838135" width="402" height="51"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="QOO-bO-4M5" id="VTT-z5-C89">
<rect key="frame" x="0.0" y="0.0" width="402" height="51"/>
@@ -1075,6 +1075,39 @@
</tableViewCellContentView>
<color key="backgroundColor" white="1" alpha="0.14999999999999999" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<edgeInsets key="layoutMargins" top="8" left="30" bottom="8" right="30"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="style">
<integer key="value" value="2"/>
</userDefinedRuntimeAttribute>
<userDefinedRuntimeAttribute type="boolean" keyPath="isSelectable" value="YES"/>
</userDefinedRuntimeAttributes>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="xtI-eU-LFb" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="1713.3333377838135" width="402" height="51"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="xtI-eU-LFb" id="bc9-41-6mE">
<rect key="frame" x="0.0" y="0.0" width="402" height="51"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="Operations Logging Control" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="LW3-gm-lj5">
<rect key="frame" x="30" y="15.333333333333334" width="137.66666666666666" height="20.333333333333329"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/>
</label>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" image="Next" translatesAutoresizingMaskIntoConstraints="NO" id="zl4-ti-HTW">
<rect key="frame" x="354" y="16.666666666666668" width="18" height="18.000000000000004"/>
</imageView>
</subviews>
<constraints>
<constraint firstAttribute="trailingMargin" secondItem="zl4-ti-HTW" secondAttribute="trailing" id="2MC-2C-NDd"/>
<constraint firstItem="LW3-gm-lj5" firstAttribute="centerY" secondItem="bc9-41-6mE" secondAttribute="centerY" id="4Ft-s9-hPY"/>
<constraint firstItem="zl4-ti-HTW" firstAttribute="centerY" secondItem="bc9-41-6mE" secondAttribute="centerY" id="8Gt-oe-nd0"/>
<constraint firstItem="LW3-gm-lj5" firstAttribute="leading" secondItem="bc9-41-6mE" secondAttribute="leadingMargin" id="Pg6-I4-3d4"/>
</constraints>
</tableViewCellContentView>
<color key="backgroundColor" white="1" alpha="0.14999999999999999" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<edgeInsets key="layoutMargins" top="8" left="30" bottom="8" right="30"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="style">
<integer key="value" value="3"/>

View File

@@ -80,6 +80,7 @@ extension SettingsViewController
case exportResignedApp
case verboseOperationsLogging
case exportSqliteDB
case operationsLoggingControl
}
}
@@ -1091,6 +1092,13 @@ extension SettingsViewController
}
}
case .operationsLoggingControl:
// Instantiate SwiftUI View inside UIHostingController
let operationsLoggingControlView = OperationsLoggingControlView()
let operationsLoggingController = UIHostingController(rootView: operationsLoggingControlView)
let segue = UIStoryboardSegue(identifier: "operationsLoggingControl", source: self, destination: operationsLoggingController)
self.present(segue.destination, animated: true, completion: nil)
case .responseCaching, .exportResignedApp, .verboseOperationsLogging : break
}

View File

@@ -0,0 +1,57 @@
//
// OperationsLoggingControl.swift
// AltStore
//
// Created by Magesh K on 14/01/25.
// Copyright © 2025 SideStore. All rights reserved.
//
import Foundation
class OperationsLoggingControl {
func updateDatabase(for operation: Operation.Type, value: Bool) {
Self.updateDatabase(for: operation, value: value)
}
private static func updateDatabase(for operation: Operation.Type, value: Bool) {
// This method should handle the database update logic based on the operation and value
let key = Self.getKey(operation)
print("Updating database for key: \(key), value: \(value)")
UserDefaults.standard.set(value, forKey: key)
}
private static func stripGenericTypeName(from string: String) -> String {
// ex: 1. "EnableJITOperation<DummyConformance>"
// ex: 1. "EnableJITOperation<DummyConformance<SomeMoreType>>"
// will become EnableJITOperation without the generics type info
if let range = string.range(of: "<") {
return String(string[..<range.lowerBound])
}
return string
}
private static func getKey(_ operation: Operation.Type) -> String {
let processedOperation = Self.stripGenericTypeName(from: "\(operation)")
return "\(processedOperation)LoggingEnabled"
}
func getFromDatabase(for operation: Operation.Type) -> Bool{
return Self.getFromDatabase(for: operation)
}
static func getUpdatedFromDatabase(for operation: Operation.Type, defaultVal: Bool) -> Bool{
let key = Self.getKey(operation)
let valueInDb = UserDefaults.standard.value(forKey: key) as? Bool
if valueInDb == nil {
// put the value if not already present
updateDatabase(for: operation, value: defaultVal)
}
return valueInDb ?? defaultVal
}
public static func getFromDatabase(for operation: Operation.Type) -> Bool {
let key = Self.getKey(operation)
return UserDefaults.standard.bool(forKey: key)
}
}