Add developer mode

This commit is contained in:
naturecodevoid
2023-02-19 08:06:33 -08:00
parent 49b9be160f
commit 81409227d6
15 changed files with 822 additions and 8 deletions

View File

@@ -83,7 +83,12 @@
4879A95F2861046500FC1BBD /* AltSign in Frameworks */ = {isa = PBXBuildFile; productRef = 4879A95E2861046500FC1BBD /* AltSign */; }; 4879A95F2861046500FC1BBD /* AltSign in Frameworks */ = {isa = PBXBuildFile; productRef = 4879A95E2861046500FC1BBD /* AltSign */; };
4879A9622861049C00FC1BBD /* OpenSSL in Frameworks */ = {isa = PBXBuildFile; productRef = 4879A9612861049C00FC1BBD /* OpenSSL */; }; 4879A9622861049C00FC1BBD /* OpenSSL in Frameworks */ = {isa = PBXBuildFile; productRef = 4879A9612861049C00FC1BBD /* OpenSSL */; };
99C4EF4D2979132100CB538D /* SemanticVersion in Frameworks */ = {isa = PBXBuildFile; productRef = 99C4EF4C2979132100CB538D /* SemanticVersion */; }; 99C4EF4D2979132100CB538D /* SemanticVersion in Frameworks */ = {isa = PBXBuildFile; productRef = 99C4EF4C2979132100CB538D /* SemanticVersion */; };
99D87A60299F1B1100ED09A9 /* DevModeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99D87A5F299F1B1100ED09A9 /* DevModeView.swift */; };
99D87A62299F3EC300ED09A9 /* FileExplorer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99D87A61299F3EC300ED09A9 /* FileExplorer.swift */; };
99D87A6529A04D5E00ED09A9 /* Inject in Frameworks */ = {isa = PBXBuildFile; productRef = 99D87A6429A04D5E00ED09A9 /* Inject */; }; 99D87A6529A04D5E00ED09A9 /* Inject in Frameworks */ = {isa = PBXBuildFile; productRef = 99D87A6429A04D5E00ED09A9 /* Inject */; };
99DE640129A1271100B920BF /* AsyncFallibleButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99DE640029A1271100B920BF /* AsyncFallibleButton.swift */; };
99DE640329A1624500B920BF /* View+SideStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99DE640229A1624500B920BF /* View+SideStore.swift */; };
99DE640629A1753800B920BF /* ZIPFoundation in Frameworks */ = {isa = PBXBuildFile; productRef = 99DE640529A1753800B920BF /* ZIPFoundation */; };
99E59E1D299BFE5D00FAF33D /* AppIconsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99E59E1C299BFE5D00FAF33D /* AppIconsView.swift */; }; 99E59E1D299BFE5D00FAF33D /* AppIconsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99E59E1C299BFE5D00FAF33D /* AppIconsView.swift */; };
B3146ED2284F581E00BBC3FD /* Roxas.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B3146ECD284F580500BBC3FD /* Roxas.framework */; }; B3146ED2284F581E00BBC3FD /* Roxas.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B3146ECD284F580500BBC3FD /* Roxas.framework */; };
B3146ED3284F581E00BBC3FD /* Roxas.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = B3146ECD284F580500BBC3FD /* Roxas.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; B3146ED3284F581E00BBC3FD /* Roxas.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = B3146ECD284F580500BBC3FD /* Roxas.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
@@ -633,6 +638,10 @@
1FB96FF2292D0539007E68D1 /* PillButtonProgressViewStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PillButtonProgressViewStyle.swift; sourceTree = "<group>"; }; 1FB96FF2292D0539007E68D1 /* PillButtonProgressViewStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PillButtonProgressViewStyle.swift; sourceTree = "<group>"; };
1FFA56C1299994390011B6F5 /* OutputCapturer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OutputCapturer.swift; sourceTree = "<group>"; }; 1FFA56C1299994390011B6F5 /* OutputCapturer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OutputCapturer.swift; sourceTree = "<group>"; };
1FFEF103298552DB0098374C /* AppVersionHistoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppVersionHistoryView.swift; sourceTree = "<group>"; }; 1FFEF103298552DB0098374C /* AppVersionHistoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppVersionHistoryView.swift; sourceTree = "<group>"; };
99D87A5F299F1B1100ED09A9 /* DevModeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DevModeView.swift; sourceTree = "<group>"; };
99D87A61299F3EC300ED09A9 /* FileExplorer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileExplorer.swift; sourceTree = "<group>"; };
99DE640029A1271100B920BF /* AsyncFallibleButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncFallibleButton.swift; sourceTree = "<group>"; };
99DE640229A1624500B920BF /* View+SideStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+SideStore.swift"; sourceTree = "<group>"; };
99E59E1C299BFE5D00FAF33D /* AppIconsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppIconsView.swift; sourceTree = "<group>"; }; 99E59E1C299BFE5D00FAF33D /* AppIconsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppIconsView.swift; sourceTree = "<group>"; };
B3146EC6284F580500BBC3FD /* Roxas.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Roxas.xcodeproj; path = Dependencies/Roxas/Roxas.xcodeproj; sourceTree = "<group>"; }; B3146EC6284F580500BBC3FD /* Roxas.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Roxas.xcodeproj; path = Dependencies/Roxas/Roxas.xcodeproj; sourceTree = "<group>"; };
B33FFBA9295F8F78002259E6 /* preboard.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = preboard.c; path = src/preboard.c; sourceTree = "<group>"; }; B33FFBA9295F8F78002259E6 /* preboard.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = preboard.c; path = src/preboard.c; sourceTree = "<group>"; };
@@ -1056,6 +1065,7 @@
1F07F5672955D16A00F7BE95 /* SFSafeSymbols in Frameworks */, 1F07F5672955D16A00F7BE95 /* SFSafeSymbols in Frameworks */,
1FFA56C52999978C0011B6F5 /* LocalConsole in Frameworks */, 1FFA56C52999978C0011B6F5 /* LocalConsole in Frameworks */,
BF66EE852501AE50007EE018 /* AltStoreCore.framework in Frameworks */, BF66EE852501AE50007EE018 /* AltStoreCore.framework in Frameworks */,
99DE640629A1753800B920BF /* ZIPFoundation in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -1215,6 +1225,7 @@
1F545E82298D79E400589F68 /* ErrorLogView.swift */, 1F545E82298D79E400589F68 /* ErrorLogView.swift */,
1FA5A6C9298E8B2F007BA946 /* RefreshAttemptsView.swift */, 1FA5A6C9298E8B2F007BA946 /* RefreshAttemptsView.swift */,
99E59E1C299BFE5D00FAF33D /* AppIconsView.swift */, 99E59E1C299BFE5D00FAF33D /* AppIconsView.swift */,
99D87A5F299F1B1100ED09A9 /* DevModeView.swift */,
); );
path = Settings; path = Settings;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -1241,6 +1252,8 @@
1F6E08E529280F4B005059C0 /* RatingStars.swift */, 1F6E08E529280F4B005059C0 /* RatingStars.swift */,
1F0DD8422936B0F9007608A4 /* RoundedTextField.swift */, 1F0DD8422936B0F9007608A4 /* RoundedTextField.swift */,
1F545E86298D86D800589F68 /* ModalNavigationLink.swift */, 1F545E86298D86D800589F68 /* ModalNavigationLink.swift */,
99D87A61299F3EC300ED09A9 /* FileExplorer.swift */,
99DE640029A1271100B920BF /* AsyncFallibleButton.swift */,
); );
path = "View Components"; path = "View Components";
sourceTree = "<group>"; sourceTree = "<group>";
@@ -1951,6 +1964,7 @@
1F66F5BD2938F06100A910CA /* StoreApp+Filterable.swift */, 1F66F5BD2938F06100A910CA /* StoreApp+Filterable.swift */,
1F180F91298E7A1B00D1C98B /* StoreApp+Trusted.swift */, 1F180F91298E7A1B00D1C98B /* StoreApp+Trusted.swift */,
1F180F93298E7A2500D1C98B /* Source+Trusted.swift */, 1F180F93298E7A2500D1C98B /* Source+Trusted.swift */,
99DE640229A1624500B920BF /* View+SideStore.swift */,
); );
path = Extensions; path = Extensions;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -2312,6 +2326,7 @@
1F1295802989B51F0048FCB9 /* ExpandableText */, 1F1295802989B51F0048FCB9 /* ExpandableText */,
1FFA56C42999978C0011B6F5 /* LocalConsole */, 1FFA56C42999978C0011B6F5 /* LocalConsole */,
99D87A6429A04D5E00ED09A9 /* Inject */, 99D87A6429A04D5E00ED09A9 /* Inject */,
99DE640529A1753800B920BF /* ZIPFoundation */,
); );
productName = AltStore; productName = AltStore;
productReference = BFD2476A2284B9A500981D42 /* SideStore.app */; productReference = BFD2476A2284B9A500981D42 /* SideStore.app */;
@@ -2389,6 +2404,7 @@
1F12957F2989B51F0048FCB9 /* XCRemoteSwiftPackageReference "ExpandableText" */, 1F12957F2989B51F0048FCB9 /* XCRemoteSwiftPackageReference "ExpandableText" */,
1FFA56C32999978C0011B6F5 /* XCRemoteSwiftPackageReference "LocalConsole" */, 1FFA56C32999978C0011B6F5 /* XCRemoteSwiftPackageReference "LocalConsole" */,
99D87A6329A04D5E00ED09A9 /* XCRemoteSwiftPackageReference "Inject" */, 99D87A6329A04D5E00ED09A9 /* XCRemoteSwiftPackageReference "Inject" */,
99DE640429A1753800B920BF /* XCRemoteSwiftPackageReference "ZIPFoundation" */,
); );
productRefGroup = BFD2476B2284B9A500981D42 /* Products */; productRefGroup = BFD2476B2284B9A500981D42 /* Products */;
projectDirPath = ""; projectDirPath = "";
@@ -2758,12 +2774,14 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
BFDB6A0F22AB2776007EA6D6 /* SendAppOperation.swift in Sources */, BFDB6A0F22AB2776007EA6D6 /* SendAppOperation.swift in Sources */,
99DE640129A1271100B920BF /* AsyncFallibleButton.swift in Sources */,
1F6E08E029280B12005059C0 /* SafariView.swift in Sources */, 1F6E08E029280B12005059C0 /* SafariView.swift in Sources */,
1F943C6C2927F90400ABE095 /* SettingsView.swift in Sources */, 1F943C6C2927F90400ABE095 /* SettingsView.swift in Sources */,
BFDB6A0D22AAFC1A007EA6D6 /* OperationError.swift in Sources */, BFDB6A0D22AAFC1A007EA6D6 /* OperationError.swift in Sources */,
BF74989B23621C0700CED65F /* ForwardingNavigationController.swift in Sources */, BF74989B23621C0700CED65F /* ForwardingNavigationController.swift in Sources */,
1F66F5C02938F07C00A910CA /* Filterable.swift in Sources */, 1F66F5C02938F07C00A910CA /* Filterable.swift in Sources */,
BF3D649D22E7AC1B00E9056B /* PermissionPopoverViewController.swift in Sources */, BF3D649D22E7AC1B00E9056B /* PermissionPopoverViewController.swift in Sources */,
99D87A60299F1B1100ED09A9 /* DevModeView.swift in Sources */,
D57F2C9426E01BC700B9FA39 /* UIDevice+Vibration.swift in Sources */, D57F2C9426E01BC700B9FA39 /* UIDevice+Vibration.swift in Sources */,
1F07F556295458D800F7BE95 /* Assets.swift in Sources */, 1F07F556295458D800F7BE95 /* Assets.swift in Sources */,
BFD2478F2284C8F900981D42 /* Button.swift in Sources */, BFD2478F2284C8F900981D42 /* Button.swift in Sources */,
@@ -2835,6 +2853,7 @@
BFE338E822F10E56002E24B9 /* LaunchViewController.swift in Sources */, BFE338E822F10E56002E24B9 /* LaunchViewController.swift in Sources */,
BFA8172B23C5633D001B5953 /* FetchAnisetteDataOperation.swift in Sources */, BFA8172B23C5633D001B5953 /* FetchAnisetteDataOperation.swift in Sources */,
BF9ABA4722DD0638008935CF /* BrowseCollectionViewCell.swift in Sources */, BF9ABA4722DD0638008935CF /* BrowseCollectionViewCell.swift in Sources */,
99D87A62299F3EC300ED09A9 /* FileExplorer.swift in Sources */,
BFD6B03322DFF20800B86064 /* MyAppsComponents.swift in Sources */, BFD6B03322DFF20800B86064 /* MyAppsComponents.swift in Sources */,
BF41B808233433C100C593A3 /* LoadingState.swift in Sources */, BF41B808233433C100C593A3 /* LoadingState.swift in Sources */,
BFF0B69A2322D7D0007A79E1 /* UIScreen+CompactHeight.swift in Sources */, BFF0B69A2322D7D0007A79E1 /* UIScreen+CompactHeight.swift in Sources */,
@@ -2871,6 +2890,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 */,
99DE640329A1624500B920BF /* View+SideStore.swift in Sources */,
1FA5A6CA298E8B2F007BA946 /* RefreshAttemptsView.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 */,
@@ -3809,6 +3829,14 @@
minimumVersion = 1.0.0; minimumVersion = 1.0.0;
}; };
}; };
99DE640429A1753800B920BF /* XCRemoteSwiftPackageReference "ZIPFoundation" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/weichsel/ZIPFoundation.git";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 0.9.9;
};
};
B3C395EF284F2DE700DA9E2F /* XCRemoteSwiftPackageReference "KeychainAccess" */ = { B3C395EF284F2DE700DA9E2F /* XCRemoteSwiftPackageReference "KeychainAccess" */ = {
isa = XCRemoteSwiftPackageReference; isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/kishikawakatsumi/KeychainAccess.git"; repositoryURL = "https://github.com/kishikawakatsumi/KeychainAccess.git";
@@ -3920,6 +3948,11 @@
package = 99D87A6329A04D5E00ED09A9 /* XCRemoteSwiftPackageReference "Inject" */; package = 99D87A6329A04D5E00ED09A9 /* XCRemoteSwiftPackageReference "Inject" */;
productName = Inject; productName = Inject;
}; };
99DE640529A1753800B920BF /* ZIPFoundation */ = {
isa = XCSwiftPackageProductDependency;
package = 99DE640429A1753800B920BF /* XCRemoteSwiftPackageReference "ZIPFoundation" */;
productName = ZIPFoundation;
};
B3C395F0284F2DE700DA9E2F /* KeychainAccess */ = { B3C395F0284F2DE700DA9E2F /* KeychainAccess */ = {
isa = XCSwiftPackageProductDependency; isa = XCSwiftPackageProductDependency;
package = B3C395EF284F2DE700DA9E2F /* XCRemoteSwiftPackageReference "KeychainAccess" */; package = B3C395EF284F2DE700DA9E2F /* XCRemoteSwiftPackageReference "KeychainAccess" */;

View File

@@ -134,6 +134,15 @@
"branch" : "master", "branch" : "master",
"revision" : "10a9150ef32d444af326beba76356ae9af95a3e7" "revision" : "10a9150ef32d444af326beba76356ae9af95a3e7"
} }
},
{
"identity" : "zipfoundation",
"kind" : "remoteSourceControl",
"location" : "https://github.com/weichsel/ZIPFoundation.git",
"state" : {
"revision" : "43ec568034b3731101dbf7670765d671c30f54f3",
"version" : "0.9.16"
}
} }
], ],
"version" : 2 "version" : 2

View File

@@ -10,6 +10,7 @@ import UIKit
import UserNotifications import UserNotifications
import AVFoundation import AVFoundation
import Intents import Intents
import LocalConsole
import AltStoreCore import AltStoreCore
import AltSign import AltSign
@@ -64,6 +65,8 @@ final class AppDelegate: UIResponder, UIApplicationDelegate {
// Register default settings before doing anything else. // Register default settings before doing anything else.
UserDefaults.registerDefaults() UserDefaults.registerDefaults()
LCManager.shared.isVisible = UserDefaults.standard.isConsoleEnabled
DatabaseManager.shared.start { (error) in DatabaseManager.shared.start { (error) in
if let error = error if let error = error
{ {

View File

@@ -0,0 +1,22 @@
//
// View+SideStore.swift
// SideStore
//
// Created by naturecodevoid on 2/18/23.
// Copyright © 2023 SideStore. All rights reserved.
//
import SwiftUI
// https://stackoverflow.com/a/59228385 (modified)
extension View {
@ViewBuilder func isHidden(_ hidden: Binding<Bool>, remove: Bool = false) -> some View {
if hidden.wrappedValue {
if !remove {
self.hidden()
}
} else {
self
}
}
}

View File

@@ -31,7 +31,14 @@ internal enum Asset {
internal static let blurTint = ColorAsset(name: "BlurTint") internal static let blurTint = ColorAsset(name: "BlurTint")
internal static let settingsBackground = ColorAsset(name: "SettingsBackground") internal static let settingsBackground = ColorAsset(name: "SettingsBackground")
internal static let settingsHighlighted = ColorAsset(name: "SettingsHighlighted") internal static let settingsHighlighted = ColorAsset(name: "SettingsHighlighted")
internal static let honeydewImage = ImageAsset(name: "Honeydew-image")
internal static let midnightImage = ImageAsset(name: "Midnight-image")
internal static let neonImage = ImageAsset(name: "Neon-image")
internal static let next = ImageAsset(name: "Next") internal static let next = ImageAsset(name: "Next")
internal static let skyImage = ImageAsset(name: "Sky-image")
internal static let starburstImage = ImageAsset(name: "Starburst-image")
internal static let steelImage = ImageAsset(name: "Steel-image")
internal static let stormImage = ImageAsset(name: "Storm-image")
internal static let browse = ImageAsset(name: "Browse") internal static let browse = ImageAsset(name: "Browse")
internal static let myApps = ImageAsset(name: "MyApps") internal static let myApps = ImageAsset(name: "MyApps")
internal static let news = ImageAsset(name: "News") internal static let news = ImageAsset(name: "News")

View File

@@ -15,6 +15,8 @@ internal enum L10n {
internal static let close = L10n.tr("Localizable", "Action.close", fallback: "Close") internal static let close = L10n.tr("Localizable", "Action.close", fallback: "Close")
/// General Actions /// General Actions
internal static let done = L10n.tr("Localizable", "Action.done", fallback: "Done") internal static let done = L10n.tr("Localizable", "Action.done", fallback: "Done")
/// Enable
internal static let enable = L10n.tr("Localizable", "Action.enable", fallback: "Enable")
} }
internal enum AddSourceView { internal enum AddSourceView {
/// Continue /// Continue
@@ -150,6 +152,10 @@ internal enum L10n {
/// AppRowView /// AppRowView
internal static let sideloaded = L10n.tr("Localizable", "AppRowView.sideloaded", fallback: "Sideloaded") internal static let sideloaded = L10n.tr("Localizable", "AppRowView.sideloaded", fallback: "Sideloaded")
} }
internal enum AsyncFallibleButton {
/// AsyncFallibleButton
internal static let error = L10n.tr("Localizable", "AsyncFallibleButton.error", fallback: "An error occurred")
}
internal enum BrowseView { internal enum BrowseView {
/// Search /// Search
internal static let search = L10n.tr("Localizable", "BrowseView.search", fallback: "Search") internal static let search = L10n.tr("Localizable", "BrowseView.search", fallback: "Search")
@@ -223,6 +229,41 @@ internal enum L10n {
/// Why do we need this? /// Why do we need this?
internal static let whyDoWeNeedThis = L10n.tr("Localizable", "ConnectAppleIDView.whyDoWeNeedThis", fallback: "Why do we need this?") internal static let whyDoWeNeedThis = L10n.tr("Localizable", "ConnectAppleIDView.whyDoWeNeedThis", fallback: "Why do we need this?")
} }
internal enum DevModeView {
/// Console
internal static let console = L10n.tr("Localizable", "DevModeView.console", fallback: "Console")
/// Data File Explorer
internal static let dataExplorer = L10n.tr("Localizable", "DevModeView.dataExplorer", fallback: "Data File Explorer")
/// minimuxer debug actions
internal static let minimuxer = L10n.tr("Localizable", "DevModeView.minimuxer", fallback: "minimuxer debug actions")
/// SideStore's Developer Mode gives access to a menu with some debugging actions commonly used by developers. **However, some of them can break SideStore if used in the wrong way.**
///
/// You should only enable Developer Mode if you meet one of the following requirements:
/// - You are a SideStore developer or contributor
/// - When getting support, you were asked to do this by a helper
/// - You were asked to do this when you reported a bug or helped a developer test a change
///
/// **_We will not provide support if you break SideStore with Developer Mode._**
internal static let prompt = L10n.tr("Localizable", "DevModeView.prompt", fallback: "SideStore's Developer Mode gives access to a menu with some debugging actions commonly used by developers. **However, some of them can break SideStore if used in the wrong way.**\n\nYou should only enable Developer Mode if you meet one of the following requirements:\n- You are a SideStore developer or contributor\n- When getting support, you were asked to do this by a helper\n- You were asked to do this when you reported a bug or helped a developer test a change\n\n**_We will not provide support if you break SideStore with Developer Mode._**")
/// Read the text!
internal static let read = L10n.tr("Localizable", "DevModeView.read", fallback: "Read the text!")
/// Skip Resign
internal static let skipResign = L10n.tr("Localizable", "DevModeView.skipResign", fallback: "Skip Resign")
/// Skip Resign should only be used when you have an IPA that you have self signed. Otherwise, it will break things, and might make SideStore crash (there is absolutely no error handling and everything is expected to work). Useful for debugging ApplicationVerificationError
internal static let skipResignInfo = L10n.tr("Localizable", "DevModeView.skipResignInfo", fallback: "Skip Resign should only be used when you have an IPA that you have self signed. Otherwise, it will break things, and might make SideStore crash (there is absolutely no error handling and everything is expected to work). Useful for debugging ApplicationVerificationError")
/// DevModeView
internal static let title = L10n.tr("Localizable", "DevModeView.title", fallback: "Developer Mode")
/// Temporary File Explorer
internal static let tmpExplorer = L10n.tr("Localizable", "DevModeView.tmpExplorer", fallback: "Temporary File Explorer")
internal enum Minimuxer {
/// Dump provisioning profiles to Documents directory
internal static let dumpProfiles = L10n.tr("Localizable", "DevModeView.Minimuxer.dumpProfiles", fallback: "Dump provisioning profiles to Documents directory")
/// PublicStaging File Explorer
internal static let stagingExplorer = L10n.tr("Localizable", "DevModeView.Minimuxer.stagingExplorer", fallback: "PublicStaging File Explorer")
/// View provisioning profiles
internal static let viewProfiles = L10n.tr("Localizable", "DevModeView.Minimuxer.viewProfiles", fallback: "View provisioning profiles")
}
}
internal enum MyAppsView { internal enum MyAppsView {
/// MyAppsView /// MyAppsView
internal static let active = L10n.tr("Localizable", "MyAppsView.active", fallback: "Active") internal static let active = L10n.tr("Localizable", "MyAppsView.active", fallback: "Active")

View File

@@ -204,6 +204,8 @@
</dict> </dict>
</dict> </dict>
</array> </array>
<key>UISupportsDocumentBrowser</key>
<true/>
<key>UIFileSharingEnabled</key> <key>UIFileSharingEnabled</key>
<true/> <true/>
</dict> </dict>

View File

@@ -8,6 +8,8 @@
import Foundation import Foundation
import Roxas import Roxas
import SwiftUI
import ZIPFoundation
import AltStoreCore import AltStoreCore
import AltSign import AltSign
@@ -15,6 +17,9 @@ import AltSign
@objc(ResignAppOperation) @objc(ResignAppOperation)
final class ResignAppOperation: ResultOperation<ALTApplication> final class ResignAppOperation: ResultOperation<ALTApplication>
{ {
static var skipResign: Bool = false
static var skipResignBinding: Binding<Bool> { Binding<Bool>(get: { skipResign }, set: { skipResign = $0 }) }
let context: InstallAppOperationContext let context: InstallAppOperationContext
init(context: InstallAppOperationContext) init(context: InstallAppOperationContext)
@@ -50,6 +55,23 @@ final class ResignAppOperation: ResultOperation<ALTApplication>
let prepareAppBundleProgress = self.prepareAppBundle(for: app, profiles: profiles) { (result) in let prepareAppBundleProgress = self.prepareAppBundle(for: app, profiles: profiles) { (result) in
guard let appBundleURL = self.process(result) else { return } guard let appBundleURL = self.process(result) else { return }
if ResignAppOperation.skipResign {
print("⚠️ WARNING: Skipping resign. Unless you correctly resigned the IPA before installing it, things will not work! Also, this might crash SideStore. You have been warned!")
let ipaFile = self.context.temporaryDirectory.appendingPathComponent("App.ipa")
let archive = Archive(url: ipaFile, accessMode: .create)!
for case let fileURL as URL in FileManager.default.enumerator(at: appBundleURL, includingPropertiesForKeys: [])! {
let relative = fileURL.description.replacingOccurrences(of: appBundleURL.description, with: "").removingPercentEncoding!
try! archive.addEntry(with: "Payload/App.app\(relative)", fileURL: fileURL)
}
let destinationURL = InstalledApp.refreshedIPAURL(for: app)
try! FileManager.default.copyItem(at: ipaFile, to: destinationURL, shouldReplace: true)
// Use appBundleURL since we need an app bundle, not .ipa.
let resignedApplication = ALTApplication(fileURL: appBundleURL)!
self.finish(.success(resignedApplication))
return
}
print("Resigning App:", self.context.bundleIdentifier) print("Resigning App:", self.context.bundleIdentifier)
// Resign app bundle // Resign app bundle

View File

@@ -10,13 +10,12 @@
/* General Actions */ /* General Actions */
"Action.done" = "Done"; "Action.done" = "Done";
"Action.close" = "Close"; "Action.close" = "Close";
"Action.enable" = "Enable";
/* NewsView */ /* NewsView */
"NewsView.title" = "News"; "NewsView.title" = "News";
"NewsView.Section.FromSources.title" = "From your Sources"; "NewsView.Section.FromSources.title" = "From your Sources";
/* BrowseView */ /* BrowseView */
"BrowseView.title" = "Browse"; "BrowseView.title" = "Browse";
"BrowseView.search" = "Search"; "BrowseView.search" = "Search";
@@ -166,3 +165,27 @@
/* AppIconsView */ /* AppIconsView */
"AppIconsView.title" = "App Icon"; "AppIconsView.title" = "App Icon";
/* DevModeView */
"DevModeView.title" = "Developer Mode";
"DevModeView.prompt" = "SideStore's Developer Mode gives access to a menu with some debugging actions commonly used by developers. **However, some of them can break SideStore if used in the wrong way.**
You should only enable Developer Mode if you meet one of the following requirements:
- You are a SideStore developer or contributor
- When getting support, you were asked to do this by a helper
- You were asked to do this when you reported a bug or helped a developer test a change
**_We will not provide support if you break SideStore with Developer Mode._**";
"DevModeView.read" = "Read the text!";
"DevModeView.console" = "Console";
"DevModeView.dataExplorer" = "Data File Explorer";
"DevModeView.tmpExplorer" = "Temporary File Explorer";
"DevModeView.skipResign" = "Skip Resign";
"DevModeView.skipResignInfo" = "Skip Resign should only be used when you have an IPA that you have self signed. Otherwise, it will break things, and might make SideStore crash (there is absolutely no error handling and everything is expected to work). Useful for debugging ApplicationVerificationError";
"DevModeView.minimuxer" = "minimuxer debug actions";
"DevModeView.Minimuxer.stagingExplorer" = "PublicStaging File Explorer";
"DevModeView.Minimuxer.viewProfiles" = "View provisioning profiles";
"DevModeView.Minimuxer.dumpProfiles" = "Dump provisioning profiles to Documents directory";
/* AsyncFallibleButton */
"AsyncFallibleButton.error" = "An error occurred";

View File

@@ -0,0 +1,118 @@
//
// AsyncFallibleButton.swift
// SideStore
//
// Created by naturecodevoid on 2/18/23.
// Copyright © 2023 SideStore. All rights reserved.
//
import SwiftUI
private enum AsyncFallibleButtonState {
case none
case loading
case success
case error
}
struct AsyncFallibleButton<Label: View>: View {
@ObservedObject private var iO = Inject.observer
let action: () throws -> Void
let label: (_ execute: @escaping () -> Void) -> Label
var afterFinish: (_ success: Bool) -> Void = { success in } // runs after the checkmark/X has disappeared
var wrapInButton = true
var secondsToDisplayResultIcon: Double = 3
@State private var state: AsyncFallibleButtonState = .none
@State private var showErrorAlert = false
@State private var errorAlertMessage = ""
private var inside: some View {
HStack {
label(execute)
if state != .none {
if wrapInButton {
Spacer()
}
switch (state) {
case .loading:
ProgressView()
case .success:
Image(systemSymbol: .checkmark)
.foregroundColor(Color.green)
case .error:
Image(systemSymbol: .xmark)
.foregroundColor(Color.red)
default:
Image(systemSymbol: .questionmark)
.foregroundColor(Color.yellow)
}
}
}
}
private var wrapped: some View {
if wrapInButton {
return AnyView(SwiftUI.Button(action: {
execute()
}) {
inside
})
} else {
return AnyView(inside)
}
}
var body: some View {
wrapped
.alert(isPresented: $showErrorAlert) {
Alert(
title: Text(L10n.AsyncFallibleButton.error),
message: Text(errorAlertMessage)
)
}
.disabled(state != .none)
.animation(.default, value: state)
.enableInjection()
}
func execute() {
if state != .none { return }
state = .loading
DispatchQueue.global().async {
do {
try action()
DispatchQueue.main.async { state = .success }
} catch {
DispatchQueue.main.async {
state = .error
errorAlertMessage = error.localizedDescription
showErrorAlert = true
}
}
DispatchQueue.main.asyncAfter(deadline: .now() + secondsToDisplayResultIcon) {
let lastState = state
state = .none
afterFinish(lastState == .success)
}
}
}
}
struct AsyncFallibleButton_Previews: PreviewProvider {
static var previews: some View {
AsyncFallibleButton(action: {
print("Start")
for index in 0...5000000 {
_ = index + index
}
throw NSError(domain: "TestError", code: -1)
//print("Finish")
}) { execute in
Text("Hello World")
}
}
}

View File

@@ -0,0 +1,366 @@
//
// FileExplorer.swift
// SideStore
//
// Created by naturecodevoid on 2/16/23.
// Copyright © 2023 SideStore. All rights reserved.
//
import SwiftUI
import ZIPFoundation
import UniformTypeIdentifiers
// https://stackoverflow.com/a/72165424
func allUTITypes() -> [UTType] {
let types: [UTType] =
[.item,
.content,
.compositeContent,
.diskImage,
.data,
.directory,
.resolvable,
.symbolicLink,
.executable,
.mountPoint,
.aliasFile,
.urlBookmarkData,
.url,
.fileURL,
.text,
.plainText,
.utf8PlainText,
.utf16ExternalPlainText,
.utf16PlainText,
.delimitedText,
.commaSeparatedText,
.tabSeparatedText,
.utf8TabSeparatedText,
.rtf,
.html,
.xml,
.yaml,
.sourceCode,
.assemblyLanguageSource,
.cSource,
.objectiveCSource,
.swiftSource,
.cPlusPlusSource,
.objectiveCPlusPlusSource,
.cHeader,
.cPlusPlusHeader]
let types_1: [UTType] =
[.script,
.appleScript,
.osaScript,
.osaScriptBundle,
.javaScript,
.shellScript,
.perlScript,
.pythonScript,
.rubyScript,
.phpScript,
.json,
.propertyList,
.xmlPropertyList,
.binaryPropertyList,
.pdf,
.rtfd,
.flatRTFD,
.webArchive,
.image,
.jpeg,
.tiff,
.gif,
.png,
.icns,
.bmp,
.ico,
.rawImage,
.svg,
.livePhoto,
.heif,
.heic,
.webP,
.threeDContent,
.usd,
.usdz,
.realityFile,
.sceneKitScene,
.arReferenceObject,
.audiovisualContent]
let types_2: [UTType] =
[.movie,
.video,
.audio,
.quickTimeMovie,
UTType("com.apple.quicktime-image"),
.mpeg,
.mpeg2Video,
.mpeg2TransportStream,
.mp3,
.mpeg4Movie,
.mpeg4Audio,
.appleProtectedMPEG4Audio,
.appleProtectedMPEG4Video,
.avi,
.aiff,
.wav,
.midi,
.playlist,
.m3uPlaylist,
.folder,
.volume,
.package,
.bundle,
.pluginBundle,
.spotlightImporter,
.quickLookGenerator,
.xpcService,
.framework,
.application,
.applicationBundle,
.applicationExtension,
.unixExecutable,
.exe,
.systemPreferencesPane,
.archive,
.gzip,
.bz2,
.zip,
.appleArchive,
.spreadsheet,
.presentation,
.database,
.message,
.contact,
.vCard,
.toDoItem,
.calendarEvent,
.emailMessage,
.internetLocation,
.internetShortcut,
.font,
.bookmark,
.pkcs12,
.x509Certificate,
.epub,
.log]
.compactMap({ $0 })
return types + types_1 + types_2
}
extension Binding<URL?>: Equatable {
public static func == (lhs: Binding<URL?>, rhs: Binding<URL?>) -> Bool {
return lhs.wrappedValue == rhs.wrappedValue
}
}
private struct DirectoryEntry: Identifiable {
var id = UUID()
var path: URL
var parent: URL
var isFile = false
var childFiles = [URL]()
var childDirectories: [DirectoryEntry]?
var filesAndDirectories: [DirectoryEntry]? {
if childFiles.count <= 0 { return childDirectories }
var filesAndDirectories = childDirectories ?? []
for file in childFiles {
filesAndDirectories.insert(DirectoryEntry(path: file, parent: path, isFile: true), at: 0)
}
return filesAndDirectories.sorted(by: { $0.asString < $1.asString })
}
var asString: String {
let str = path.description.replacingOccurrences(of: parent.description, with: "").removingPercentEncoding!
if str.count <= 0 {
return "/"
}
return str
}
}
private enum FileExplorerAction {
case delete
case zip
case insert
}
private struct File: View {
@ObservedObject private var iO = Inject.observer
var item: DirectoryEntry
@Binding var explorerHidden: Bool
@State var quickLookURL: URL?
@State var fileExplorerAction: FileExplorerAction?
@State var hidden = false
@State var isShowingFilePicker = false
@State var selectedFile: URL?
var body: some View {
AsyncFallibleButton(action: {
switch (fileExplorerAction) {
case .delete:
print("deleting \(item.path.description)")
try FileManager.default.removeItem(at: item.path)
case .zip:
print("zipping \(item.path.description)")
let dest = FileManager.default.documentsDirectory.appendingPathComponent(item.path.pathComponents.last! + ".zip")
do {
try FileManager.default.removeItem(at: dest)
} catch {}
try FileManager.default.zipItem(at: item.path, to: dest)
case .insert:
print("inserting \(selectedFile!.description) to \(item.path.description)")
try FileManager.default.copyItem(at: selectedFile!, to: item.path.appendingPathComponent(selectedFile!.pathComponents.last!))
explorerHidden = true
explorerHidden = false
default:
print("unknown action for \(item.path.description): \(String(describing: fileExplorerAction))")
}
}, label: { execute in
HStack {
Text(item.asString)
if item.isFile {
Text(getFileSize(file: item.path)).foregroundColor(.secondary)
}
Spacer()
Menu {
if item.isFile {
SwiftUI.Button(action: { quickLookURL = item.path }) {
Label("View/Share", systemSymbol: .eye)
}
} else {
SwiftUI.Button(action: {
fileExplorerAction = .zip
execute()
}) {
Label("Save to ZIP file", systemSymbol: .squareAndArrowDown)
}
SwiftUI.Button {
isShowingFilePicker = true
} label: {
Label("Insert file", systemSymbol: .plus)
}
}
if item.asString != "/" {
SwiftUI.Button(action: {
fileExplorerAction = .delete
execute()
}) {
Label("Delete", systemSymbol: .trash)
}
}
} label: {
Image(systemSymbol: .ellipsis)
.frame(width: 20, height: 20) // Make it easier to tap
}
}
.onChange(of: $selectedFile) { file in
guard file.wrappedValue != nil else { return }
fileExplorerAction = .insert
execute()
}
}, afterFinish: { success in
switch (fileExplorerAction) {
case .delete:
if success { hidden = true }
case .zip:
UIApplication.shared.open(URL(string: "shareddocuments://" + FileManager.default.documentsDirectory.description.replacingOccurrences(of: "file://", with: ""))!, options: [:], completionHandler: nil)
default: break
}
}, wrapInButton: false)
.quickLookPreview($quickLookURL)
.sheet(isPresented: $isShowingFilePicker) {
DocumentPicker(selectedUrl: $selectedFile, supportedTypes: allUTITypes().map({ $0.identifier }))
.ignoresSafeArea()
}
.isHidden($hidden)
.enableInjection()
}
func getFileSize(file: URL) -> String {
guard let attributes = try? FileManager.default.attributesOfItem(atPath: file.description.replacingOccurrences(of: "file://", with: "")) else { return "Unknown file size" }
var bytes = attributes[FileAttributeKey.size] as! Double
// https://stackoverflow.com/a/14919494 (ported to swift)
let thresh = 1024.0;
if (bytes < thresh) {
return String(describing: bytes) + " B";
}
let units = ["kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
var u = -1;
while (bytes >= thresh && u < units.count - 1) {
bytes /= thresh;
u += 1;
}
return String(format: "%.2f", bytes) + " " + units[u];
}
}
struct FileExplorer: View {
@ObservedObject private var iO = Inject.observer
var url: URL?
@State var hidden = false
var body: some View {
List([iterateOverDirectory(directory: url!, parent: url!)], children: \.filesAndDirectories) { item in
File(item: item, explorerHidden: $hidden)
}
.toolbar {
ToolbarItem {
SwiftUI.Button {
hidden = true
hidden = false
} label: {
Image(systemSymbol: .arrowClockwise)
}
}
}
.isHidden($hidden)
.enableInjection()
}
private func iterateOverDirectory(directory: URL, parent: URL) -> DirectoryEntry {
var directoryEntry = DirectoryEntry(path: directory, parent: parent)
if let contents = try? FileManager.default.contentsOfDirectory(at: directory, includingPropertiesForKeys: nil, options: []) {
for entry in contents {
if entry.hasDirectoryPath {
if directoryEntry.childDirectories == nil { directoryEntry.childDirectories = [] }
directoryEntry.childDirectories!.append(iterateOverDirectory(directory: entry, parent: directory))
} else {
directoryEntry.childFiles.append(entry)
}
}
}
return directoryEntry
}
}
struct FileExplorer_Previews: PreviewProvider {
static var previews: some View {
FileExplorer(url: FileManager.default.altstoreSharedDirectory)
}
}

View File

@@ -9,13 +9,13 @@
import SwiftUI import SwiftUI
import SFSafeSymbols import SFSafeSymbols
struct Icon: Identifiable { private struct Icon: Identifiable {
var id: String { assetName } var id: String { assetName }
var displayName: String var displayName: String
let assetName: String let assetName: String
} }
struct SpecialIcon { private struct SpecialIcon {
let assetName: String let assetName: String
let suffix: String? let suffix: String?
let forceIndex: Int? let forceIndex: Int?

View File

@@ -0,0 +1,147 @@
//
// DevModeView.swift
// SideStore
//
// Created by naturecodevoid on 2/16/23.
// Copyright © 2023 SideStore. All rights reserved.
//
import SwiftUI
import LocalConsole
struct DevModePrompt: View {
@Binding var isShowingDevModePrompt: Bool
@Binding var isShowingDevModeMenu: Bool
@State var countdown = 0
var button: some View {
SwiftUI.Button(action: {
UserDefaults.standard.isDevModeEnabled = true
isShowingDevModePrompt = false
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
isShowingDevModeMenu = true
}
}) {
Text(countdown <= 0 ? L10n.Action.enable + " " + L10n.DevModeView.title : L10n.DevModeView.read + " (\(countdown))")
.foregroundColor(.red)
}
.disabled(countdown > 0)
}
var text: some View {
if #available(iOS 15.0, *) {
do {
return Text(try AttributedString(markdown: L10n.DevModeView.prompt, options: AttributedString.MarkdownParsingOptions(interpretedSyntax: .inlineOnlyPreservingWhitespace)))
} catch {
return Text(L10n.DevModeView.prompt)
}
} else {
return Text(L10n.DevModeView.prompt)
}
}
var body: some View {
NavigationView {
ScrollView {
VStack {
text
.foregroundColor(.primary)
.padding(.bottom)
if #available(iOS 15.0, *) {
button.buttonStyle(.bordered)
} else {
button
}
}
.padding(.horizontal)
}
.frame(maxWidth: .infinity)
.navigationTitle(L10n.DevModeView.title)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
SwiftUI.Button(action: { isShowingDevModePrompt = false }) {
Text(L10n.Action.close)
}
}
}
.onAppear {
countdown = 20
tickCountdown()
}
}
}
func tickCountdown() {
if countdown <= 0 { return }
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
countdown -= 1
tickCountdown()
}
}
}
struct DevModeMenu: View {
@ObservedObject private var iO = Inject.observer
@AppStorage("isConsoleEnabled")
var isConsoleEnabled: Bool = false
var body: some View {
List {
Section {
Toggle(L10n.DevModeView.console, isOn: self.$isConsoleEnabled)
.onChange(of: self.isConsoleEnabled) { value in
LCManager.shared.isVisible = value
}
NavigationLink(L10n.DevModeView.dataExplorer) {
FileExplorer(url: FileManager.default.altstoreSharedDirectory)
.navigationTitle(L10n.DevModeView.dataExplorer)
}.foregroundColor(.red)
NavigationLink(L10n.DevModeView.tmpExplorer) {
FileExplorer(url: FileManager.default.temporaryDirectory)
.navigationTitle(L10n.DevModeView.tmpExplorer)
}.foregroundColor(.red)
Toggle(L10n.DevModeView.skipResign, isOn: ResignAppOperation.skipResignBinding)
.foregroundColor(.red)
} footer: {
Text(L10n.DevModeView.skipResignInfo)
}
Section {
NavigationLink(L10n.DevModeView.Minimuxer.stagingExplorer + " (Coming soon, needs minimuxer additions)") {
FileExplorer(url: FileManager.default.altstoreSharedDirectory)
.navigationTitle(L10n.DevModeView.Minimuxer.stagingExplorer)
}.foregroundColor(.red).disabled(true)
NavigationLink(L10n.DevModeView.Minimuxer.viewProfiles + " (Coming soon, needs minimuxer additions)") {
}.disabled(true)
SwiftUI.Button(L10n.DevModeView.Minimuxer.dumpProfiles + " (Coming soon, needs minimuxer additions)", action: {
// TODO: dump profiles to Documents/ProfileDump/[current time]
}).disabled(true)
} header: {
Text(L10n.DevModeView.minimuxer)
}
}
.navigationTitle(L10n.DevModeView.title)
.enableInjection()
}
}
struct DevModeView_Previews: PreviewProvider {
static var previews: some View {
NavigationView {
List {
NavigationLink("DevModeMenu") {
DevModeMenu()
}
}
}
}
}

View File

@@ -26,10 +26,15 @@ struct SettingsView: View {
@AppStorage("isBackgroundRefreshEnabled") @AppStorage("isBackgroundRefreshEnabled")
var isBackgroundRefreshEnabled: Bool = true var isBackgroundRefreshEnabled: Bool = true
@AppStorage("isDevModeEnabled")
var isDevModeEnabled: Bool = false
@State var isShowingConnectAppleIDView = false @State var isShowingConnectAppleIDView = false
@State var isShowingAddShortcutView = false @State var isShowingAddShortcutView = false
@State var isShowingFeedbackMailView = false @State var isShowingFeedbackMailView = false
@State var isShowingResetPairingFileConfirmation = false @State var isShowingResetPairingFileConfirmation = false
@State var isShowingDevModePrompt = false
@State var isShowingDevModeMenu = false
@State var externalURLToShow: URL? @State var externalURLToShow: URL?
@@ -163,10 +168,6 @@ struct SettingsView: View {
RefreshAttemptsView() RefreshAttemptsView()
} }
SwiftUI.Button("Toggle Console") {
LCManager.shared.isVisible.toggle()
}
if MailComposeView.canSendMail { if MailComposeView.canSendMail {
SwiftUI.Button("Send Feedback") { SwiftUI.Button("Send Feedback") {
self.isShowingFeedbackMailView = true self.isShowingFeedbackMailView = true
@@ -199,6 +200,21 @@ struct SettingsView: View {
.cancel() .cancel()
]) ])
} }
if isDevModeEnabled {
NavigationLink(L10n.DevModeView.title, isActive: self.$isShowingDevModeMenu) {
DevModeMenu()
}
.foregroundColor(.red)
} else {
SwiftUI.Button(L10n.DevModeView.title) {
self.isShowingDevModePrompt = true
}
.foregroundColor(.red)
.sheet(isPresented: self.$isShowingDevModePrompt) {
DevModePrompt(isShowingDevModePrompt: self.$isShowingDevModePrompt, isShowingDevModeMenu: self.$isShowingDevModeMenu)
}
}
} header: { } header: {
Text(L10n.SettingsView.debug) Text(L10n.SettingsView.debug)
} }

View File

@@ -26,6 +26,9 @@ public extension UserDefaults
@NSManaged var customAnisetteURL: String? @NSManaged var customAnisetteURL: String?
@NSManaged var preferredServerID: String? @NSManaged var preferredServerID: String?
@NSManaged var isDevModeEnabled: Bool
@NSManaged var isConsoleEnabled: Bool
@NSManaged var isBackgroundRefreshEnabled: Bool @NSManaged var isBackgroundRefreshEnabled: Bool
@NSManaged var isDebugModeEnabled: Bool @NSManaged var isDebugModeEnabled: Bool
@NSManaged var presentedLaunchReminderNotification: Bool @NSManaged var presentedLaunchReminderNotification: Bool
@@ -70,6 +73,8 @@ public extension UserDefaults
let localServerSupportsRefreshing = !ProcessInfo.processInfo.isOperatingSystemAtLeast(ios14) let localServerSupportsRefreshing = !ProcessInfo.processInfo.isOperatingSystemAtLeast(ios14)
let defaults = [ let defaults = [
#keyPath(UserDefaults.isDevModeEnabled): false,
#keyPath(UserDefaults.isConsoleEnabled): false,
#keyPath(UserDefaults.isBackgroundRefreshEnabled): true, #keyPath(UserDefaults.isBackgroundRefreshEnabled): true,
#keyPath(UserDefaults.isLegacyDeactivationSupported): isLegacyDeactivationSupported, #keyPath(UserDefaults.isLegacyDeactivationSupported): isLegacyDeactivationSupported,
#keyPath(UserDefaults.activeAppLimitIncludesExtensions): activeAppLimitIncludesExtensions, #keyPath(UserDefaults.activeAppLimitIncludesExtensions): activeAppLimitIncludesExtensions,