diff --git a/Shared/Data/UserDefaults/Preferences.swift b/Shared/Data/UserDefaults/Preferences.swift index b7135d6..65d9626 100644 --- a/Shared/Data/UserDefaults/Preferences.swift +++ b/Shared/Data/UserDefaults/Preferences.swift @@ -23,9 +23,6 @@ enum Preferences { // Default repo is from the repository static var defaultRepos: Bool - @Storage(key: "Feather.automaticInstall", defaultValue: true) - static var automaticInstall: Bool - @Storage(key: "Feather.userIntefacerStyle", defaultValue: UIUserInterfaceStyle.unspecified.rawValue) static var preferredInterfaceStyle: Int @@ -42,19 +39,9 @@ enum Preferences { // random string static var pPQCheckString: String - @Storage(key: "Feather.fuckOffPpqcheckDetection", defaultValue: true) - static var isFuckingPPqcheckDetectionOff: Bool - - @Storage(key: "Feather.autoInstallAfterSign", defaultValue: false) - static var autoInstallAfterSign: Bool - @Storage(key: "Feather.CertificateTitleAppIDtoTeamID", defaultValue: false) static var certificateTitleAppIDtoTeamID: Bool - @Storage(key: "Feather.idWhitelist", defaultValue: ["kh.crysalis.feather", "kh.crysalis.feather2"]) - // Unused - static var idWhitelist: [String] - @Storage(key: "Feather.AppDescriptionAppearence", defaultValue: 0) // 0 == Default appearence // 1 == Replace subtitle with localizedDescription @@ -65,12 +52,17 @@ enum Preferences { /// Preferred language static var preferredLanguageCode: String? - @Storage(key: "Feather.Beta", defaultValue: false) // static var beta: Bool + @CodableStorage(key: "SortOption", defaultValue: SortOption.default) + static var currentSortOption: SortOption + + @Storage(key: "SortOptionAscending", defaultValue: true) + static var currentSortOptionAscending: Bool } + // MARK: - Callbacks fileprivate extension Preferences { static func preferredLangChangedCallback(newValue: String?) { diff --git a/Shared/Design/SectionIcons.swift b/Shared/Design/SectionIcons.swift index 585bef5..0685387 100644 --- a/Shared/Design/SectionIcons.swift +++ b/Shared/Design/SectionIcons.swift @@ -12,29 +12,18 @@ import Nuke class SectionIcons { @available(iOS 13.0, *) - static public func sectionIcon(to cell: UITableViewCell, with symbolName: String, gradientColors: [UIColor]) { - let symbolConfig = UIImage.SymbolConfiguration(pointSize: 14, weight: .medium) + static public func sectionIcon(to cell: UITableViewCell, with symbolName: String, backgroundColor: UIColor) { + let symbolConfig = UIImage.SymbolConfiguration(pointSize: 16, weight: .medium) guard let symbolImage = UIImage(systemName: symbolName, withConfiguration: symbolConfig)?.withTintColor(.white, renderingMode: .alwaysOriginal) else { return } - let imageSize = CGSize(width: 42, height: 42) - - let insetAmount: CGFloat = 5 + let imageSize = CGSize(width: 52, height: 52) + let insetAmount: CGFloat = 7 let scaledSymbolSize = symbolImage.size.aspectFit(in: imageSize, insetBy: insetAmount) let coloredBackgroundImage = UIGraphicsImageRenderer(size: imageSize).image { context in - let gradientLayer = CAGradientLayer() - gradientLayer.frame = CGRect(origin: .zero, size: imageSize) - gradientLayer.colors = gradientColors.map { $0.cgColor } - gradientLayer.startPoint = CGPoint(x: 0, y: 0) - gradientLayer.endPoint = CGPoint(x: 1, y: 1) - - gradientLayer.render(in: context.cgContext) - - // Optionally add rounded corners to the gradient background - let path = UIBezierPath(roundedRect: CGRect(origin: .zero, size: imageSize), cornerRadius: 10) - context.cgContext.addPath(path.cgPath) - context.cgContext.clip() + backgroundColor.setFill() + UIBezierPath(roundedRect: CGRect(origin: .zero, size: imageSize), cornerRadius: 7).fill() } let mergedImage = UIGraphicsImageRenderer(size: imageSize).image { context in @@ -48,9 +37,9 @@ class SectionIcons { } cell.imageView?.image = mergedImage - cell.imageView?.layer.cornerRadius = 10 - cell.imageView?.layer.cornerCurve = .continuous + cell.imageView?.layer.cornerRadius = 12 cell.imageView?.clipsToBounds = true + cell.imageView?.layer.borderWidth = 1 cell.imageView?.layer.borderColor = UIColor.lightGray.withAlphaComponent(0.3).cgColor } diff --git a/Shared/Signing/AppSigner.swift b/Shared/Signing/AppSigner.swift index dae0a04..e7ee328 100644 --- a/Shared/Signing/AppSigner.swift +++ b/Shared/Signing/AppSigner.swift @@ -11,35 +11,7 @@ import UIKit import AlertKit import CoreData -struct AppSigningOptions { - var name: String? - var version: String? - var bundleId: String? - var iconURL: UIImage? - - var uuid: String - var toInject: [URL]? - var removeInjectPaths: [String]? - - var removePlugins: Bool? - var forceFileSharing: Bool? - var removeSupportedDevices: Bool? - var removeURLScheme: Bool? - var forceProMotion: Bool? - - var forceForceFullScreen: Bool? - var forceiTunesFileSharing: Bool? - var forceMinimumVersion: String? - var forceLightDarkAppearence: String? - var forceTryToLocalize: Bool? - - var removeProvisioningFile: Bool? - var removeWatchPlaceHolder: Bool? - - var certificate: Certificate? -} - -func signInitialApp(options: AppSigningOptions, appPath: URL, completion: @escaping (Result<(URL, NSManagedObject), Error>) -> Void) { +func signInitialApp(bundle: BundleOptions, mainOptions: SigningMainDataWrapper, signingOptions: SigningDataWrapper, appPath: URL, completion: @escaping (Result<(URL, NSManagedObject), Error>) -> Void) { UIApplication.shared.isIdleTimerDisabled = true DispatchQueue(label: "Signing").async { let fileManager = FileManager.default @@ -50,13 +22,15 @@ func signInitialApp(options: AppSigningOptions, appPath: URL, completion: @escap do { Debug.shared.log(message: "============================================") - Debug.shared.log(message: "\(options)") + Debug.shared.log(message: "\(mainOptions.mainOptions)") + Debug.shared.log(message: "============================================") + Debug.shared.log(message: "\(signingOptions.signingOptions)") Debug.shared.log(message: "============================================") try fileManager.createDirectory(at: tmpDir, withIntermediateDirectories: true) try fileManager.copyItem(at: appPath, to: tmpDirApp) if let info = NSDictionary(contentsOf: tmpDirApp.appendingPathComponent("Info.plist"))!.mutableCopy() as? NSMutableDictionary { - try updateInfoPlist(infoDict: info, options: options, icon: options.iconURL, app: tmpDirApp) + try updateInfoPlist(infoDict: info, main: mainOptions, options: signingOptions, icon: mainOptions.mainOptions.iconURL, app: tmpDirApp) if let iconsDict = info["CFBundleIcons"] as? [String: Any], let primaryIconsDict = iconsDict["CFBundlePrimaryIcon"] as? [String: Any], @@ -66,26 +40,26 @@ func signInitialApp(options: AppSigningOptions, appPath: URL, completion: @escap } } - let handler = TweakHandler(urls: options.toInject ?? [], app: tmpDirApp) + let handler = TweakHandler(urls: signingOptions.signingOptions.toInject, app: tmpDirApp) try handler.getInputFiles() - if let removeInjectPaths = options.removeInjectPaths, !removeInjectPaths.isEmpty { - if let appexe = try TweakHandler.findExecutable(at: tmpDirApp) { - _ = uninstallDylibs(filePath: appexe.path, dylibPaths: removeInjectPaths) + if !mainOptions.mainOptions.removeInjectPaths.isEmpty { + if let appexe = try? TweakHandler.findExecutable(at: tmpDirApp) { + _ = uninstallDylibs(filePath: appexe.path, dylibPaths: mainOptions.mainOptions.removeInjectPaths) } } - try updatePlugIns(options: options, app: tmpDirApp) - try removeDumbAssPlaceHolderExtension(options: options, app: tmpDirApp) + try updatePlugIns(options: signingOptions, app: tmpDirApp) + try removeDumbAssPlaceHolderExtension(options: signingOptions, app: tmpDirApp) try updateMobileProvision(app: tmpDirApp) - let certPath = try CoreDataManager.shared.getCertifcatePath(source: options.certificate) - let provisionPath = certPath.appendingPathComponent("\(options.certificate?.provisionPath ?? "")").path - let p12Path = certPath.appendingPathComponent("\(options.certificate?.p12Path ?? "")").path + let certPath = try CoreDataManager.shared.getCertifcatePath(source: mainOptions.mainOptions.certificate) + let provisionPath = certPath.appendingPathComponent("\(mainOptions.mainOptions.certificate?.provisionPath ?? "")").path + let p12Path = certPath.appendingPathComponent("\(mainOptions.mainOptions.certificate?.p12Path ?? "")").path Debug.shared.log(message: "🦋 Start Signing 🦋") - try signAppWithZSign(tmpDirApp: tmpDirApp, certPaths: (provisionPath, p12Path), password: options.certificate?.password ?? "", options: options) + try signAppWithZSign(tmpDirApp: tmpDirApp, certPaths: (provisionPath, p12Path), password: mainOptions.mainOptions.certificate?.password ?? "", main: mainOptions, options: signingOptions) Debug.shared.log(message: "🦋 End Signing 🦋") @@ -98,14 +72,14 @@ func signInitialApp(options: AppSigningOptions, appPath: URL, completion: @escap var signedAppObject: NSManagedObject? = nil CoreDataManager.shared.addToSignedApps( - version: options.version!, - name: options.name!, - bundleidentifier: options.bundleId!, + version: (mainOptions.mainOptions.version ?? bundle.version)!, + name: (mainOptions.mainOptions.name ?? bundle.name)!, + bundleidentifier: (mainOptions.mainOptions.bundleId ?? bundle.bundleId)!, iconURL: iconURL, uuid: signedUUID, appPath: appPath.lastPathComponent, - timeToLive: options.certificate?.certData?.expirationDate ?? Date(), - teamName: options.certificate?.certData?.name ?? "" + timeToLive: mainOptions.mainOptions.certificate?.certData?.expirationDate ?? Date(), + teamName: mainOptions.mainOptions.certificate?.certData?.name ?? "" ) { result in @@ -118,7 +92,7 @@ func signInitialApp(options: AppSigningOptions, appPath: URL, completion: @escap } } - Debug.shared.log(message: String.localized("SUCCESS_SIGNED", arguments: "\(options.name ?? String.localized("UNKNOWN"))"), type: .success) + Debug.shared.log(message: String.localized("SUCCESS_SIGNED", arguments: "\((mainOptions.mainOptions.name ?? bundle.name) ?? String.localized("UNKNOWN"))"), type: .success) Debug.shared.log(message: "============================================") UIApplication.shared.isIdleTimerDisabled = false @@ -146,7 +120,7 @@ func resignApp(certificate: Certificate, appPath: URL, completion: @escaping (Bo Debug.shared.log(message: "============================================") Debug.shared.log(message: "🦋 Start Resigning 🦋") - try signAppWithZSign(tmpDirApp: appPath, certPaths: (provisionPath, p12Path), password: certificate.password ?? "", options: nil) + try signAppWithZSign(tmpDirApp: appPath, certPaths: (provisionPath, p12Path), password: certificate.password ?? "") Debug.shared.log(message: "🦋 End Resigning 🦋") DispatchQueue.main.async { @@ -162,15 +136,15 @@ func resignApp(certificate: Certificate, appPath: URL, completion: @escaping (Bo } } -private func signAppWithZSign(tmpDirApp: URL, certPaths: (provisionPath: String, p12Path: String), password: String, options: AppSigningOptions?) throws { +private func signAppWithZSign(tmpDirApp: URL, certPaths: (provisionPath: String, p12Path: String), password: String, main: SigningMainDataWrapper? = nil, options: SigningDataWrapper? = nil) throws { if zsign(tmpDirApp.path, certPaths.provisionPath, certPaths.p12Path, password, - options?.bundleId ?? "", - options?.name ?? "", - options?.version ?? "", - options?.removeProvisioningFile ?? false + main?.mainOptions.bundleId ?? "", + main?.mainOptions.name ?? "", + main?.mainOptions.version ?? "", + options?.signingOptions.removeProvisioningFile ?? false ) != 0 { throw NSError(domain: "AppSigningErrorDomain", code: 1, userInfo: [NSLocalizedDescriptionKey: String.localized("ERROR_ZSIGN_FAILED")]) } @@ -220,8 +194,8 @@ func uninstallDylibs(filePath: String, dylibPaths: [String]) -> Bool { } -func updatePlugIns(options: AppSigningOptions, app: URL) throws { - if options.removePlugins! { +func updatePlugIns(options: SigningDataWrapper, app: URL) throws { + if options.signingOptions.removePlugins { let filemanager = FileManager.default let path = app.appendingPathComponent("PlugIns") if filemanager.fileExists(atPath: path.path) { @@ -237,8 +211,8 @@ func updatePlugIns(options: AppSigningOptions, app: URL) throws { } } -func removeDumbAssPlaceHolderExtension(options: AppSigningOptions, app: URL) throws { - if options.removeWatchPlaceHolder! { +func removeDumbAssPlaceHolderExtension(options: SigningDataWrapper, app: URL) throws { + if options.signingOptions.removeWatchPlaceHolder { let filemanager = FileManager.default let path = app.appendingPathComponent("com.apple.WatchPlaceholder") if filemanager.fileExists(atPath: path.path) { @@ -254,8 +228,8 @@ func removeDumbAssPlaceHolderExtension(options: AppSigningOptions, app: URL) thr } } -func updateInfoPlist(infoDict: NSMutableDictionary, options: AppSigningOptions, icon: UIImage?, app: URL) throws { - if (options.iconURL != nil) { +func updateInfoPlist(infoDict: NSMutableDictionary, main: SigningMainDataWrapper, options: SigningDataWrapper, icon: UIImage?, app: URL) throws { + if (main.mainOptions.iconURL != nil) { let imageSizes = [ (width: 120, height: 120, name: "FRIcon60x60@2x.png"), @@ -263,7 +237,7 @@ func updateInfoPlist(infoDict: NSMutableDictionary, options: AppSigningOptions, ] for imageSize in imageSizes { - let resizedImage = options.iconURL!.resize(imageSize.width, imageSize.height) + let resizedImage = main.mainOptions.iconURL!.resize(imageSize.width, imageSize.height) let imageData = resizedImage.pngData() let fileURL = app.appendingPathComponent(imageSize.name) @@ -297,24 +271,24 @@ func updateInfoPlist(infoDict: NSMutableDictionary, options: AppSigningOptions, Debug.shared.log(message: "updateInfoPlist.updateicon: Does not include an icon, skipping!") } - if options.forceTryToLocalize! { + if options.signingOptions.forceTryToLocalize && (main.mainOptions.name != nil) { if let displayName = infoDict.value(forKey: "CFBundleDisplayName") as? String { - if displayName != options.name { - updateLocalizedInfoPlist(in: app, newDisplayName: options.name!) + if displayName != main.mainOptions.name { + updateLocalizedInfoPlist(in: app, newDisplayName: main.mainOptions.name!) } } else { Debug.shared.log(message: "updateInfoPlist.displayName: CFBundleDisplayName not found, skipping!") } } - if options.forceFileSharing! { infoDict.setObject(true, forKey: "UISupportsDocumentBrowser" as NSCopying) } - if options.forceiTunesFileSharing! { infoDict.setObject(true, forKey: "UIFileSharingEnabled" as NSCopying) } - if options.removeSupportedDevices! { infoDict.removeObject(forKey: "UISupportedDevices") } - if options.removeURLScheme! { infoDict.removeObject(forKey: "CFBundleURLTypes") } - if options.forceProMotion! { infoDict.setObject(true, forKey: "CADisableMinimumFrameDurationOnPhone" as NSCopying)} - if options.forceForceFullScreen! { infoDict.setObject(true, forKey: "UIRequiresFullScreen" as NSCopying) } - if options.forceMinimumVersion! != "Automatic" { infoDict.setObject(options.forceMinimumVersion!, forKey: "MinimumOSVersion" as NSCopying) } - if options.forceLightDarkAppearence! != "Automatic" { infoDict.setObject(options.forceLightDarkAppearence!, forKey: "UIUserInterfaceStyle" as NSCopying)} + if options.signingOptions.forceFileSharing { infoDict.setObject(true, forKey: "UISupportsDocumentBrowser" as NSCopying) } + if options.signingOptions.forceiTunesFileSharing { infoDict.setObject(true, forKey: "UIFileSharingEnabled" as NSCopying) } + if options.signingOptions.removeSupportedDevices { infoDict.removeObject(forKey: "UISupportedDevices") } + if options.signingOptions.removeURLScheme { infoDict.removeObject(forKey: "CFBundleURLTypes") } + if options.signingOptions.forceProMotion { infoDict.setObject(true, forKey: "CADisableMinimumFrameDurationOnPhone" as NSCopying)} + if options.signingOptions.forceForceFullScreen { infoDict.setObject(true, forKey: "UIRequiresFullScreen" as NSCopying) } + if options.signingOptions.forceMinimumVersion != "Automatic" { infoDict.setObject(options.signingOptions.forceMinimumVersion, forKey: "MinimumOSVersion" as NSCopying) } + if options.signingOptions.forceLightDarkAppearence != "Automatic" { infoDict.setObject(options.signingOptions.forceLightDarkAppearence, forKey: "UIUserInterfaceStyle" as NSCopying)} try infoDict.write(to: app.appendingPathComponent("Info.plist")) } diff --git a/Shared/Signing/TweakHandler.swift b/Shared/Signing/TweakHandler.swift index f05a1ae..a18b1ea 100644 --- a/Shared/Signing/TweakHandler.swift +++ b/Shared/Signing/TweakHandler.swift @@ -19,12 +19,12 @@ class TweakHandler { let fileManager = FileManager.default - private var urls: [URL] + private var urls: [String] private let app: URL private var urlsToInject: [URL] = [] private var directoriesToCheck: [URL] = [] - init(urls: [URL], app: URL) { + init(urls: [String], app: URL) { self.urls = urls self.app = app } @@ -38,7 +38,7 @@ class TweakHandler { let frameworksPath = app.appendingPathComponent("Frameworks").appendingPathComponent("CydiaSubstrate.framework") if !fileManager.fileExists(atPath: frameworksPath.path) { if let ellekitURL = Bundle.main.url(forResource: "ellekit", withExtension: "deb") { - self.urls.insert(ellekitURL, at: 0) + self.urls.insert(ellekitURL.absoluteString, at: 0) } else { Debug.shared.log(message: "Error: ellekit.deb not found in the app bundle ⁉️", type: .error) return @@ -55,13 +55,14 @@ class TweakHandler { // it will extract then add a url, if theres no url, i.e. // you haven't added a deb, it will skip for url in urls { - switch url.pathExtension.lowercased() { + let urlf = URL(string: url) + switch urlf!.pathExtension.lowercased() { case "dylib": - try handleDylib(at: url) + try handleDylib(at: urlf!) case "deb": - try handleDeb(at: url, baseTmpDir: baseTmpDir) + try handleDeb(at: urlf!, baseTmpDir: baseTmpDir) default: - Debug.shared.log(message: "Unsupported file type: \(url.lastPathComponent), skipping.") + Debug.shared.log(message: "Unsupported file type: \(urlf!.lastPathComponent), skipping.") } } diff --git a/feather.xcodeproj/project.pbxproj b/feather.xcodeproj/project.pbxproj index 114dd97..efdbda5 100644 --- a/feather.xcodeproj/project.pbxproj +++ b/feather.xcodeproj/project.pbxproj @@ -53,16 +53,19 @@ 339CE28A2C58539300B58502 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 339CE2892C58539300B58502 /* Logger.swift */; }; 339CE28C2C5870AC00B58502 /* CoreDataManager+Sources.swift in Sources */ = {isa = PBXBuildFile; fileRef = 339CE28B2C5870AC00B58502 /* CoreDataManager+Sources.swift */; }; 339CE29F2C58816500B58502 /* SourcesModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 339CE29E2C58816500B58502 /* SourcesModel.swift */; }; + 339FE3362CCDE06100C297BA /* SigningsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 339FE3352CCDE05A00C297BA /* SigningsViewController.swift */; }; + 339FE3392CCDF1A800C297BA /* SigningsOptionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 339FE3382CCDF19800C297BA /* SigningsOptionViewController.swift */; }; + 339FE33B2CCE01F000C297BA /* IdentifiersViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 339FE33A2CCE01DE00C297BA /* IdentifiersViewController.swift */; }; + 339FE33E2CCE06A100C297BA /* AddIdentifierViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 339FE33D2CCE06A000C297BA /* AddIdentifierViewController.swift */; }; + 339FE3422CCE29B400C297BA /* SigningsAdvancedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 339FE3412CCE29A700C297BA /* SigningsAdvancedViewController.swift */; }; + 339FE3442CCE343C00C297BA /* SwitchViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 339FE3432CCE343C00C297BA /* SwitchViewCell.swift */; }; + 339FE3462CCE3CBD00C297BA /* SigningsViewController+Import-AppIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 339FE3452CCE3CB100C297BA /* SigningsViewController+Import-AppIcon.swift */; }; 33A305D52C3A858100D650BA /* UIButton+longpress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33A305D42C3A858100D650BA /* UIButton+longpress.swift */; }; 33A305D72C3A85F300D650BA /* SourceDownload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33A305D62C3A85F300D650BA /* SourceDownload.swift */; }; 33AC87F12C3B7CBE003D1175 /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33AC87F02C3B7CBE003D1175 /* SettingsViewController.swift */; }; 33AC87F32C3B7D24003D1175 /* CertificatesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33AC87F22C3B7D24003D1175 /* CertificatesViewController.swift */; }; - 33B7D0682CCB860600BD9199 /* SigningView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33B7D0672CCB860600BD9199 /* SigningView.swift */; }; 33B7D06A2CCB8E5900BD9199 /* SigningOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33B7D0692CCB8E5900BD9199 /* SigningOptions.swift */; }; 33B7D06E2CCC890400BD9199 /* SigningDataWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33B7D06D2CCC890400BD9199 /* SigningDataWrapper.swift */; }; - 33B7D0742CCCAA7500BD9199 /* IdentifiersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33B7D0732CCCAA7500BD9199 /* IdentifiersView.swift */; }; - 33B7D0762CCCAACB00BD9199 /* AddTweaksView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33B7D0752CCCAACB00BD9199 /* AddTweaksView.swift */; }; - 33B7D0782CCCB14100BD9199 /* SigningViewHostingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33B7D0772CCCB14100BD9199 /* SigningViewHostingController.swift */; }; 33BA378F2BF8159F00FF530A /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33BA378E2BF8159F00FF530A /* AppDelegate.swift */; }; 33BA37932BF8159F00FF530A /* SourcesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33BA37922BF8159F00FF530A /* SourcesViewController.swift */; }; 33BA37982BF815A100FF530A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33BA37972BF815A100FF530A /* Assets.xcassets */; }; @@ -87,9 +90,8 @@ 33BE875D2C6ABF2C0044D245 /* LibraryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33BE875C2C6ABF2C0044D245 /* LibraryViewController.swift */; }; 33BE875F2C6AF91B0044D245 /* IconImageViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33BE875E2C6AF91B0044D245 /* IconImageViewCell.swift */; }; 33BE87612C6C37CE0044D245 /* UIImage+resize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33BE87602C6C37CE0044D245 /* UIImage+resize.swift */; }; - 33BE87632C6F17870044D245 /* AppSigningInputViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33BE87622C6F17870044D245 /* AppSigningInputViewController.swift */; }; - 33BE87652C6F17980044D245 /* AppSigningAdvancedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33BE87642C6F17980044D245 /* AppSigningAdvancedViewController.swift */; }; - 33BE87672C6F17DF0044D245 /* AppSigningTweakViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33BE87662C6F17DF0044D245 /* AppSigningTweakViewController.swift */; }; + 33BE87632C6F17870044D245 /* SigningsInputViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33BE87622C6F17870044D245 /* SigningsInputViewController.swift */; }; + 33BE87672C6F17DF0044D245 /* SigningsTweakViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33BE87662C6F17DF0044D245 /* SigningsTweakViewController.swift */; }; 33BE877B2C7178180044D245 /* TweakHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33BE877A2C7178180044D245 /* TweakHandler.swift */; }; 33BE877D2C729A7D0044D245 /* DownloadCertificate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33BE877C2C729A7D0044D245 /* DownloadCertificate.swift */; }; 33BE87802C72D2840044D245 /* AR.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33BE877F2C72D2840044D245 /* AR.swift */; }; @@ -104,7 +106,7 @@ 33DF8D7F2C0828FB00D6C05F /* Nuke.md in Resources */ = {isa = PBXBuildFile; fileRef = 33DF8D7E2C0828FB00D6C05F /* Nuke.md */; }; 33DF8D812C08290F00D6C05F /* HttpSwift_RequestSwift_SocketSwift.md in Resources */ = {isa = PBXBuildFile; fileRef = 33DF8D802C08290F00D6C05F /* HttpSwift_RequestSwift_SocketSwift.md */; }; 33E3B6702C7FE06C0075E3A2 /* RepoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33E3B66F2C7FE06C0075E3A2 /* RepoViewController.swift */; }; - 33E3B6722C8151ED0075E3A2 /* AppSigningDylibViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33E3B6712C8151ED0075E3A2 /* AppSigningDylibViewController.swift */; }; + 33E3B6722C8151ED0075E3A2 /* SigningsDylibViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33E3B6712C8151ED0075E3A2 /* SigningsDylibViewController.swift */; }; 33E4D8322C64CB39006A1C26 /* SearchResultsTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33E4D8312C64CB39006A1C26 /* SearchResultsTableViewController.swift */; }; 33E4D8342C6593D2006A1C26 /* UIView+parentcontroller.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33E4D8332C6593D2006A1C26 /* UIView+parentcontroller.swift */; }; 33E4D8362C6593F4006A1C26 /* CGSize+aspectFit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33E4D8352C6593F4006A1C26 /* CGSize+aspectFit.swift */; }; @@ -125,8 +127,6 @@ AA280D952C76AB7400CAC838 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AA280D942C76AB7400CAC838 /* MobileCoreServices.framework */; }; AA280D972C76ABEF00CAC838 /* UIApplication+returnToHomeScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA280D962C76ABEF00CAC838 /* UIApplication+returnToHomeScreen.swift */; }; AF4D46EF2C432CCA003FA335 /* TweakLibraryViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF4D46EE2C432CCA003FA335 /* TweakLibraryViewCell.swift */; }; - AF4D46F12C4332D7003FA335 /* SwitchViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF4D46F02C4332D7003FA335 /* SwitchViewCell.swift */; }; - AF84EEFD2C4080610050CBE4 /* AppSigningViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF84EEFC2C4080610050CBE4 /* AppSigningViewController.swift */; }; AF9B6B442C6346FF00735748 /* p12_password_check.mm in Sources */ = {isa = PBXBuildFile; fileRef = AF9B6B432C6346E900735748 /* p12_password_check.mm */; }; AFAC50922C48DB1300EDEAB6 /* ActivityIndicatorViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFAC50912C48DB1200EDEAB6 /* ActivityIndicatorViewCell.swift */; }; AFAC50952C48DF9300EDEAB6 /* AppSigner.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFAC50942C48DF9300EDEAB6 /* AppSigner.swift */; }; @@ -190,16 +190,19 @@ 339CE2892C58539300B58502 /* Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = ""; }; 339CE28B2C5870AC00B58502 /* CoreDataManager+Sources.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CoreDataManager+Sources.swift"; sourceTree = ""; }; 339CE29E2C58816500B58502 /* SourcesModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourcesModel.swift; sourceTree = ""; }; + 339FE3352CCDE05A00C297BA /* SigningsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SigningsViewController.swift; sourceTree = ""; }; + 339FE3382CCDF19800C297BA /* SigningsOptionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SigningsOptionViewController.swift; sourceTree = ""; }; + 339FE33A2CCE01DE00C297BA /* IdentifiersViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdentifiersViewController.swift; sourceTree = ""; }; + 339FE33D2CCE06A000C297BA /* AddIdentifierViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddIdentifierViewController.swift; sourceTree = ""; }; + 339FE3412CCE29A700C297BA /* SigningsAdvancedViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SigningsAdvancedViewController.swift; sourceTree = ""; }; + 339FE3432CCE343C00C297BA /* SwitchViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwitchViewCell.swift; sourceTree = ""; }; + 339FE3452CCE3CB100C297BA /* SigningsViewController+Import-AppIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SigningsViewController+Import-AppIcon.swift"; sourceTree = ""; }; 33A305D42C3A858100D650BA /* UIButton+longpress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIButton+longpress.swift"; sourceTree = ""; }; 33A305D62C3A85F300D650BA /* SourceDownload.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourceDownload.swift; sourceTree = ""; }; 33AC87F02C3B7CBE003D1175 /* SettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = ""; }; 33AC87F22C3B7D24003D1175 /* CertificatesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CertificatesViewController.swift; sourceTree = ""; }; - 33B7D0672CCB860600BD9199 /* SigningView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SigningView.swift; sourceTree = ""; }; 33B7D0692CCB8E5900BD9199 /* SigningOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SigningOptions.swift; sourceTree = ""; }; 33B7D06D2CCC890400BD9199 /* SigningDataWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SigningDataWrapper.swift; sourceTree = ""; }; - 33B7D0732CCCAA7500BD9199 /* IdentifiersView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdentifiersView.swift; sourceTree = ""; }; - 33B7D0752CCCAACB00BD9199 /* AddTweaksView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddTweaksView.swift; sourceTree = ""; }; - 33B7D0772CCCB14100BD9199 /* SigningViewHostingController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SigningViewHostingController.swift; sourceTree = ""; }; 33BA378B2BF8159F00FF530A /* feather.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = feather.app; sourceTree = BUILT_PRODUCTS_DIR; }; 33BA378E2BF8159F00FF530A /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 33BA37922BF8159F00FF530A /* SourcesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourcesViewController.swift; sourceTree = ""; }; @@ -224,9 +227,8 @@ 33BE875C2C6ABF2C0044D245 /* LibraryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryViewController.swift; sourceTree = ""; }; 33BE875E2C6AF91B0044D245 /* IconImageViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconImageViewCell.swift; sourceTree = ""; }; 33BE87602C6C37CE0044D245 /* UIImage+resize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+resize.swift"; sourceTree = ""; }; - 33BE87622C6F17870044D245 /* AppSigningInputViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSigningInputViewController.swift; sourceTree = ""; }; - 33BE87642C6F17980044D245 /* AppSigningAdvancedViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSigningAdvancedViewController.swift; sourceTree = ""; }; - 33BE87662C6F17DF0044D245 /* AppSigningTweakViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSigningTweakViewController.swift; sourceTree = ""; }; + 33BE87622C6F17870044D245 /* SigningsInputViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SigningsInputViewController.swift; sourceTree = ""; }; + 33BE87662C6F17DF0044D245 /* SigningsTweakViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SigningsTweakViewController.swift; sourceTree = ""; }; 33BE877A2C7178180044D245 /* TweakHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TweakHandler.swift; sourceTree = ""; }; 33BE877C2C729A7D0044D245 /* DownloadCertificate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadCertificate.swift; sourceTree = ""; }; 33BE877F2C72D2840044D245 /* AR.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AR.swift; sourceTree = ""; }; @@ -244,7 +246,7 @@ 33DF8D7E2C0828FB00D6C05F /* Nuke.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = Nuke.md; sourceTree = ""; }; 33DF8D802C08290F00D6C05F /* HttpSwift_RequestSwift_SocketSwift.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = HttpSwift_RequestSwift_SocketSwift.md; sourceTree = ""; }; 33E3B66F2C7FE06C0075E3A2 /* RepoViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RepoViewController.swift; sourceTree = ""; }; - 33E3B6712C8151ED0075E3A2 /* AppSigningDylibViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSigningDylibViewController.swift; sourceTree = ""; }; + 33E3B6712C8151ED0075E3A2 /* SigningsDylibViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SigningsDylibViewController.swift; sourceTree = ""; }; 33E4D8312C64CB39006A1C26 /* SearchResultsTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultsTableViewController.swift; sourceTree = ""; }; 33E4D8332C6593D2006A1C26 /* UIView+parentcontroller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+parentcontroller.swift"; sourceTree = ""; }; 33E4D8352C6593F4006A1C26 /* CGSize+aspectFit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CGSize+aspectFit.swift"; sourceTree = ""; }; @@ -268,8 +270,6 @@ AA280D942C76AB7400CAC838 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; }; AA280D962C76ABEF00CAC838 /* UIApplication+returnToHomeScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIApplication+returnToHomeScreen.swift"; sourceTree = ""; }; AF4D46EE2C432CCA003FA335 /* TweakLibraryViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TweakLibraryViewCell.swift; sourceTree = ""; }; - AF4D46F02C4332D7003FA335 /* SwitchViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwitchViewCell.swift; sourceTree = ""; }; - AF84EEFC2C4080610050CBE4 /* AppSigningViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSigningViewController.swift; sourceTree = ""; }; AF9B6B432C6346E900735748 /* p12_password_check.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = p12_password_check.mm; sourceTree = ""; }; AF9B6B452C634B0300735748 /* p12_password_check.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = p12_password_check.hpp; sourceTree = ""; }; AFAC50912C48DB1200EDEAB6 /* ActivityIndicatorViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityIndicatorViewCell.swift; sourceTree = ""; }; @@ -364,7 +364,6 @@ 338FFCDA2C6870E2006C3BE0 /* LibraryViewController+Import.swift */, 33570F602C32B80E008CB560 /* AppsTableViewCell.swift */, 33EEDAA82C5DA1C9009089DC /* Information */, - 33EEDAA52C5DA19A009089DC /* Signing */, ); path = Apps; sourceTree = ""; @@ -524,6 +523,49 @@ path = Download; sourceTree = ""; }; + 339FE3342CCDE02600C297BA /* SigningViewController */ = { + isa = PBXGroup; + children = ( + 339FE3352CCDE05A00C297BA /* SigningsViewController.swift */, + 339FE3452CCE3CB100C297BA /* SigningsViewController+Import-AppIcon.swift */, + 33BE87622C6F17870044D245 /* SigningsInputViewController.swift */, + 33E3B6712C8151ED0075E3A2 /* SigningsDylibViewController.swift */, + 33BE87662C6F17DF0044D245 /* SigningsTweakViewController.swift */, + 339FE3412CCE29A700C297BA /* SigningsAdvancedViewController.swift */, + 339FE33F2CCE1A7600C297BA /* Components */, + ); + path = SigningViewController; + sourceTree = ""; + }; + 339FE3372CCDF17E00C297BA /* SigningOptionsViewController */ = { + isa = PBXGroup; + children = ( + 339FE3382CCDF19800C297BA /* SigningsOptionViewController.swift */, + 339FE33C2CCE068600C297BA /* Identifier */, + ); + path = SigningOptionsViewController; + sourceTree = ""; + }; + 339FE33C2CCE068600C297BA /* Identifier */ = { + isa = PBXGroup; + children = ( + 339FE33A2CCE01DE00C297BA /* IdentifiersViewController.swift */, + 339FE33D2CCE06A000C297BA /* AddIdentifierViewController.swift */, + ); + path = Identifier; + sourceTree = ""; + }; + 339FE33F2CCE1A7600C297BA /* Components */ = { + isa = PBXGroup; + children = ( + 339FE3432CCE343C00C297BA /* SwitchViewCell.swift */, + AFAC50912C48DB1200EDEAB6 /* ActivityIndicatorViewCell.swift */, + 33BE875E2C6AF91B0044D245 /* IconImageViewCell.swift */, + AF4D46EE2C432CCA003FA335 /* TweakLibraryViewCell.swift */, + ); + path = Components; + sourceTree = ""; + }; 33AC87EE2C3B7C3D003D1175 /* Certificates */ = { isa = PBXGroup; children = ( @@ -556,7 +598,8 @@ 33B7D0642CCB613700BD9199 /* Signing */ = { isa = PBXGroup; children = ( - 33B7D0712CCC895F00BD9199 /* SigningView */, + 339FE3342CCDE02600C297BA /* SigningViewController */, + 339FE3372CCDF17E00C297BA /* SigningOptionsViewController */, 33B7D06C2CCC88F700BD9199 /* SigningData */, ); path = Signing; @@ -571,25 +614,6 @@ path = SigningData; sourceTree = ""; }; - 33B7D0712CCC895F00BD9199 /* SigningView */ = { - isa = PBXGroup; - children = ( - 33B7D0772CCCB14100BD9199 /* SigningViewHostingController.swift */, - 33B7D0672CCB860600BD9199 /* SigningView.swift */, - 33B7D0792CCCB21000BD9199 /* Configuration */, - 33B7D0752CCCAACB00BD9199 /* AddTweaksView.swift */, - ); - path = SigningView; - sourceTree = ""; - }; - 33B7D0792CCCB21000BD9199 /* Configuration */ = { - isa = PBXGroup; - children = ( - 33B7D0732CCCAA7500BD9199 /* IdentifiersView.swift */, - ); - path = Configuration; - sourceTree = ""; - }; 33BA37822BF8159F00FF530A = { isa = PBXGroup; children = ( @@ -748,22 +772,6 @@ path = "Signing Options"; sourceTree = ""; }; - 33EEDAA52C5DA19A009089DC /* Signing */ = { - isa = PBXGroup; - children = ( - AF84EEFC2C4080610050CBE4 /* AppSigningViewController.swift */, - 33BE87622C6F17870044D245 /* AppSigningInputViewController.swift */, - 33BE87642C6F17980044D245 /* AppSigningAdvancedViewController.swift */, - 33BE87662C6F17DF0044D245 /* AppSigningTweakViewController.swift */, - 33E3B6712C8151ED0075E3A2 /* AppSigningDylibViewController.swift */, - AF4D46EE2C432CCA003FA335 /* TweakLibraryViewCell.swift */, - AF4D46F02C4332D7003FA335 /* SwitchViewCell.swift */, - AFAC50912C48DB1200EDEAB6 /* ActivityIndicatorViewCell.swift */, - 33BE875E2C6AF91B0044D245 /* IconImageViewCell.swift */, - ); - path = Signing; - sourceTree = ""; - }; 33EEDAA82C5DA1C9009089DC /* Information */ = { isa = PBXGroup; children = ( @@ -947,7 +955,6 @@ buildActionMask = 2147483647; files = ( 33BF31672C7C05840087F3D2 /* Foundation.swift in Sources */, - 33B7D0742CCCAA7500BD9199 /* IdentifiersView.swift in Sources */, D0651C372BFEEA4B00D40829 /* openssl.cpp in Sources */, D0651C1A2BFEE97F00D40829 /* base64.cpp in Sources */, 33BE875F2C6AF91B0044D245 /* IconImageViewCell.swift in Sources */, @@ -963,7 +970,6 @@ 33BA37932BF8159F00FF530A /* SourcesViewController.swift in Sources */, 332476E62C3E8E25008C8EF0 /* SettingsCreditsTableCell.swift in Sources */, 33A305D52C3A858100D650BA /* UIButton+longpress.swift in Sources */, - 33B7D0682CCB860600BD9199 /* SigningView.swift in Sources */, 33B7D06E2CCC890400BD9199 /* SigningDataWrapper.swift in Sources */, D0651C3A2BFEEA4B00D40829 /* archo.cpp in Sources */, 33163FF02C3D224C0038E1EC /* DownloadTaskManager.swift in Sources */, @@ -971,7 +977,7 @@ 33E5A59B2CC858FE00532930 /* Language.swift in Sources */, D0651C1B2BFEE97F00D40829 /* common.cpp in Sources */, 33BE875D2C6ABF2C0044D245 /* LibraryViewController.swift in Sources */, - 33BE87672C6F17DF0044D245 /* AppSigningTweakViewController.swift in Sources */, + 33BE87672C6F17DF0044D245 /* SigningsTweakViewController.swift in Sources */, 339CE29F2C58816500B58502 /* SourcesModel.swift in Sources */, 332476EE2C3E92BC008C8EF0 /* DisplayViewController.swift in Sources */, AFAC50922C48DB1300EDEAB6 /* ActivityIndicatorViewCell.swift in Sources */, @@ -981,18 +987,17 @@ 332476ED2C3E92BC008C8EF0 /* DisplayCollectionTableViewCell.swift in Sources */, 339BDAE02BF953EE007009D9 /* BasicLayoutAnchorsHolding.swift in Sources */, 338FFCE42C695118006C3BE0 /* IconsListViewController.swift in Sources */, + 339FE33B2CCE01F000C297BA /* IdentifiersViewController.swift in Sources */, D0651C112BFEE93C00D40829 /* zsign.mm in Sources */, 332476DA2C3E4D35008C8EF0 /* SourceAppDownload.swift in Sources */, - 33BE87632C6F17870044D245 /* AppSigningInputViewController.swift in Sources */, + 33BE87632C6F17870044D245 /* SigningsInputViewController.swift in Sources */, 3322FF492BFEC97F001768D8 /* SourceAppViewController.swift in Sources */, 33E4D8342C6593D2006A1C26 /* UIView+parentcontroller.swift in Sources */, 33BE87612C6C37CE0044D245 /* UIImage+resize.swift in Sources */, 33E5A5A72CC86F7300532930 /* VariableBlurView.swift in Sources */, 33570F692C34E5B2008CB560 /* SectionHeaders.swift in Sources */, 335F0C2E2C5E0FFF00A4F0AE /* CoreDataManager+Certificates.swift in Sources */, - AF4D46F12C4332D7003FA335 /* SwitchViewCell.swift in Sources */, D0651C332BFEEA4B00D40829 /* macho.cpp in Sources */, - 33BE87652C6F17980044D245 /* AppSigningAdvancedViewController.swift in Sources */, 33A305D72C3A85F300D650BA /* SourceDownload.swift in Sources */, 33B7D06A2CCB8E5900BD9199 /* SigningOptions.swift in Sources */, 33570F672C34CCD5008CB560 /* AppsInformationViewController.swift in Sources */, @@ -1002,6 +1007,7 @@ AF9B6B442C6346FF00735748 /* p12_password_check.mm in Sources */, 33BA37A82BF8168900FF530A /* TabbarController.swift in Sources */, AFAC50952C48DF9300EDEAB6 /* AppSigner.swift in Sources */, + 339FE33E2CCE06A100C297BA /* AddIdentifierViewController.swift in Sources */, 33BA37AB2BF8196000FF530A /* Preferences.swift in Sources */, D0651C392BFEEA4B00D40829 /* signing.cpp in Sources */, 33C0409B2C30B4A100243D90 /* AppDownload.swift in Sources */, @@ -1015,6 +1021,7 @@ 33E4D8412C67E5C3006A1C26 /* DonationTableViewCell.swift in Sources */, 33E5A5992CC8586E00532930 /* LanguageViewController.swift in Sources */, 33EEDAA42C5D9358009089DC /* CoreDataManager+DownloadedApps.swift in Sources */, + 339FE3442CCE343C00C297BA /* SwitchViewCell.swift in Sources */, 33BE877D2C729A7D0044D245 /* DownloadCertificate.swift in Sources */, 33BDD0B02C7855A300AF6169 /* Server.swift in Sources */, D0651C3B2BFEEA4B00D40829 /* bundle.cpp in Sources */, @@ -1024,23 +1031,24 @@ 333912A02BF9816B00E72360 /* CertificateModel.swift in Sources */, 33BE87802C72D2840044D245 /* AR.swift in Sources */, 339CE28C2C5870AC00B58502 /* CoreDataManager+Sources.swift in Sources */, - AF84EEFD2C4080610050CBE4 /* AppSigningViewController.swift in Sources */, 33E5A5A52CC86C9400532930 /* SigningOptionsViewController.swift in Sources */, 33BDD0B62C785F5F00AF6169 /* Server+TLS.swift in Sources */, + 339FE3422CCE29B400C297BA /* SigningsAdvancedViewController.swift in Sources */, 33BDD0B82C785F9500AF6169 /* Server+Compute.swift in Sources */, - 33E3B6722C8151ED0075E3A2 /* AppSigningDylibViewController.swift in Sources */, + 33E3B6722C8151ED0075E3A2 /* SigningsDylibViewController.swift in Sources */, 33E5A5AE2CC8A41E00532930 /* LogsViewController.swift in Sources */, 339BDAE32BF95DC0007009D9 /* SectionIcons.swift in Sources */, 33E5A5AC2CC88BF500532930 /* ResetAlertOptions.swift in Sources */, 331616912C786B9B00894427 /* TransferPreview.swift in Sources */, + 339FE3392CCDF1A800C297BA /* SigningsOptionViewController.swift in Sources */, 33E5A5A22CC85CAA00532930 /* ServerOptionsViewController.swift in Sources */, 33AC87F32C3B7D24003D1175 /* CertificatesViewController.swift in Sources */, 338FFCF42C6971B2006C3BE0 /* IconsListTableViewCell.swift in Sources */, 33BA37AD2BF8197200FF530A /* Storage.swift in Sources */, 33CE81A92C4295F300C05327 /* CertImportingViewController.swift in Sources */, - 33B7D0762CCCAACB00BD9199 /* AddTweaksView.swift in Sources */, - 33B7D0782CCCB14100BD9199 /* SigningViewHostingController.swift in Sources */, 33570F612C32B80E008CB560 /* AppsTableViewCell.swift in Sources */, + 339FE3362CCDE06100C297BA /* SigningsViewController.swift in Sources */, + 339FE3462CCE3CBD00C297BA /* SigningsViewController+Import-AppIcon.swift in Sources */, 33E4D8432C6802BB006A1C26 /* SettingsHeaderTableViewCell.swift in Sources */, 33AC87F12C3B7CBE003D1175 /* SettingsViewController.swift in Sources */, 338FFCD92C682469006C3BE0 /* PopupViewController.swift in Sources */, diff --git a/iOS/Views/Apps/LibraryViewController.swift b/iOS/Views/Apps/LibraryViewController.swift index 5581c8b..16b7832 100644 --- a/iOS/Views/Apps/LibraryViewController.swift +++ b/iOS/Views/Apps/LibraryViewController.swift @@ -194,8 +194,9 @@ extension LibraryViewController { popupVC = PopupViewController() popupVC.modalPresentationStyle = .pageSheet + let singingData = SigningDataWrapper(signingOptions: UserDefaults.standard.signingOptions) let button1 = PopupViewControllerButton( - title: Preferences.autoInstallAfterSign + title: singingData.signingOptions.installAfterSigned ? "Sign & Install \((source!.value(forKey: "name") as? String ?? ""))" : "Sign \((source!.value(forKey: "name") as? String ?? ""))", color: .tintColor.withAlphaComponent(0.9)) @@ -254,7 +255,8 @@ extension LibraryViewController { @objc func startSigning(meow: NSManagedObject) { if FileManager.default.fileExists(atPath: CoreDataManager.shared.getFilesForDownloadedApps(for:(meow as! DownloadedApps)).path) { - let ap = AppSigningViewController(app: meow, appsViewController: self) + let signingDataWrapper = SigningDataWrapper(signingOptions: UserDefaults.standard.signingOptions) + let ap = SigningsViewController(signingDataWrapper: signingDataWrapper, application: meow, appsViewController: self) let navigationController = UINavigationController(rootViewController: ap) if UIDevice.current.userInterfaceIdiom == .pad { navigationController.modalPresentationStyle = .formSheet diff --git a/iOS/Views/Apps/Signing/AppSigningAdvancedViewController.swift b/iOS/Views/Apps/Signing/AppSigningAdvancedViewController.swift deleted file mode 100644 index 3fc4b7b..0000000 --- a/iOS/Views/Apps/Signing/AppSigningAdvancedViewController.swift +++ /dev/null @@ -1,197 +0,0 @@ -// -// AppSigningAdvancedViewController.swift -// feather -// -// Created by samara on 8/15/24. -// Copyright (c) 2024 Samara M (khcrysalis) -// - -import Foundation -import UIKit -// MARK: - AppSigningAdvancedViewController -class AppSigningAdvancedViewController: UITableViewController { - var cellsForSection0 = [UITableViewCell]() - var cellsForSection1 = [UITableViewCell]() - var cellsForSection2 = [UITableViewCell]() - var appSigningViewController: AppSigningViewController - - init(appSigningViewController: AppSigningViewController) { - self.appSigningViewController = appSigningViewController - super.init(style: .insetGrouped) - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func viewDidLoad() { - title = String.localized("APP_SIGNING_VIEW_CONTROLLER_CELL_ADVANCED") - navigationItem.largeTitleDisplayMode = .never - - let forceLightDarkAppearence = TweakLibraryViewCell() - forceLightDarkAppearence.selectionStyle = .none - forceLightDarkAppearence.configureSegmentedControl( - with: appSigningViewController.forceLightDarkAppearenceString, - selectedIndex: appSigningViewController.forceLightDarkAppearence - ) - forceLightDarkAppearence.segmentedControl.addTarget(self, action: #selector(forceLightDarkAppearenceDidChange(_:)), for: .valueChanged) - cellsForSection0.append(forceLightDarkAppearence) - - let forceMinimumVersion = TweakLibraryViewCell() - forceMinimumVersion.selectionStyle = .none - forceMinimumVersion.configureSegmentedControl( - with: appSigningViewController.forceMinimumVersionString, - selectedIndex: appSigningViewController.forceMinimumVersion - ) - forceMinimumVersion.segmentedControl.addTarget(self, action: #selector(forceMinimumVersionDidChange(_:)), for: .valueChanged) - cellsForSection1.append(forceMinimumVersion) - - let removePluginsCell = SwitchViewCell() - removePluginsCell.textLabel?.text = String.localized("APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_PLUGINS") - removePluginsCell.switchControl.addTarget(self, action: #selector(removePluginsToggled(_:)), for: .valueChanged) - removePluginsCell.switchControl.isOn = appSigningViewController.removePlugins - removePluginsCell.selectionStyle = .none - cellsForSection2.append(removePluginsCell) - - let removeSupportedDevicesCell = SwitchViewCell() - removeSupportedDevicesCell.textLabel?.text = String.localized("APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_UISUPPORTEDDEVICES") - removeSupportedDevicesCell.switchControl.addTarget(self, action: #selector(removeSupportedDevicesToggled(_:)), for: .valueChanged) - removeSupportedDevicesCell.switchControl.isOn = appSigningViewController.removeSupportedDevices - removeSupportedDevicesCell.selectionStyle = .none - cellsForSection2.append(removeSupportedDevicesCell) - - let removeURLSchemeCell = SwitchViewCell() - removeURLSchemeCell.textLabel?.text = String.localized("APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_URLSCHEME") - removeURLSchemeCell.switchControl.addTarget(self, action: #selector(removeURLSchemeToggled(_:)), for: .valueChanged) - removeURLSchemeCell.switchControl.isOn = appSigningViewController.removeURLScheme - removeURLSchemeCell.selectionStyle = .none - cellsForSection2.append(removeURLSchemeCell) - - let forceFileSharingCell = SwitchViewCell() - forceFileSharingCell.textLabel?.text = String.localized("APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_ALLOW_BROWSING_DOCUMENTS") - forceFileSharingCell.switchControl.addTarget(self, action: #selector(forceFileSharingToggled(_:)), for: .valueChanged) - forceFileSharingCell.switchControl.isOn = appSigningViewController.forceFileSharing - forceFileSharingCell.selectionStyle = .none - cellsForSection2.append(forceFileSharingCell) - - let forceiTunesFileSharing = SwitchViewCell() - forceiTunesFileSharing.textLabel?.text = String.localized("APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_ALLOW_ITUNES_SHARING") - forceiTunesFileSharing.switchControl.addTarget(self, action: #selector(forceiTunesFileSharingToggled(_:)), for: .valueChanged) - forceiTunesFileSharing.switchControl.isOn = appSigningViewController.forceiTunesFileSharing - forceiTunesFileSharing.selectionStyle = .none - cellsForSection2.append(forceiTunesFileSharing) - - let forceProMotion = SwitchViewCell() - forceProMotion.textLabel?.text = String.localized("APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_FORCE_PRO_MOTION") - forceProMotion.switchControl.addTarget(self, action: #selector(forceFileSharingToggled(_:)), for: .valueChanged) - forceProMotion.switchControl.isOn = appSigningViewController.forceProMotion - forceProMotion.selectionStyle = .none - cellsForSection2.append(forceProMotion) - - let forceForceFullScreen = SwitchViewCell() - forceForceFullScreen.textLabel?.text = String.localized("APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_FORCE_FULLSCREEN") - forceForceFullScreen.switchControl.addTarget(self, action: #selector(forceForceFullScreenToggled(_:)), for: .valueChanged) - forceForceFullScreen.switchControl.isOn = appSigningViewController.forceForceFullScreen - forceForceFullScreen.selectionStyle = .none - cellsForSection2.append(forceForceFullScreen) - - let forceLocalize = SwitchViewCell() - forceLocalize.textLabel?.text = "Force Localize" - forceLocalize.switchControl.addTarget(self, action: #selector(forceLocalized(_:)), for: .valueChanged) - forceLocalize.switchControl.isOn = appSigningViewController.forceTryToLocalize - forceLocalize.selectionStyle = .none - cellsForSection2.append(forceLocalize) - - let removeWatchPlaceHolder = SwitchViewCell() - removeWatchPlaceHolder.textLabel?.text = String.localized("APP_SIGNING_INPUT_VIEW_CONTROLLER_REMOVE_DELETE_PLACEHOLDER_WATCH_APP") - removeWatchPlaceHolder.switchControl.addTarget(self, action: #selector(removePlaceHolderWatchExtension(_:)), for: .valueChanged) - removeWatchPlaceHolder.switchControl.isOn = appSigningViewController.removeWatchPlaceHolder - removeWatchPlaceHolder.selectionStyle = .none - cellsForSection2.append(removeWatchPlaceHolder) - - let removeProvisioningFile = SwitchViewCell() - removeProvisioningFile.textLabel?.text = "Include Provisioning File" - removeProvisioningFile.switchControl.addTarget(self, action: #selector(removeProvisioningFile(_:)), for: .valueChanged) - removeProvisioningFile.switchControl.isOn = appSigningViewController.removeProvisioningFile - removeProvisioningFile.selectionStyle = .none - cellsForSection2.append(removeProvisioningFile) - } - - override func numberOfSections(in tableView: UITableView) -> Int { - return 3 - } - - override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { - - let titles = [ - String.localized("APP_SIGNING_INPUT_VIEW_CONTROLLER_SECTION_TITLE_APPEARENCE"), - String.localized("APP_SIGNING_INPUT_VIEW_CONTROLLER_SECTION_TITLE_MINIMUM_APP_VERSION"), - String.localized("APP_SIGNING_INPUT_VIEW_CONTROLLER_SECTION_TITLE_PROPERTIES") - ][section] - - let headerView = InsetGroupedSectionHeader(title: titles) - return headerView - } - - override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { - return [ - 40, - 40, - 40 - ][section] - } - - override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - switch section { - case 0: return cellsForSection0.count - case 1: return cellsForSection1.count - case 2: return cellsForSection2.count - default: return 0 - } - } - override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - switch indexPath.section { - case 0: return cellsForSection0[indexPath.item] - case 1: return cellsForSection1[indexPath.item] - case 2: return cellsForSection2[indexPath.item] - default: return UITableViewCell() - } - } - - @objc private func removePluginsToggled(_ sender: UISwitch) { - appSigningViewController.removePlugins = sender.isOn - } - @objc private func forceFileSharingToggled(_ sender: UISwitch) { - appSigningViewController.forceFileSharing = sender.isOn - } - @objc private func removeSupportedDevicesToggled(_ sender: UISwitch) { - appSigningViewController.removeSupportedDevices = sender.isOn - } - @objc private func removeURLSchemeToggled(_ sender: UISwitch) { - appSigningViewController.removeURLScheme = sender.isOn - } - @objc private func forceProMotionToggled(_ sender: UISwitch) { - appSigningViewController.forceProMotion = sender.isOn - } - @objc private func forceForceFullScreenToggled(_ sender: UISwitch) { - appSigningViewController.forceForceFullScreen = sender.isOn - } - @objc private func forceiTunesFileSharingToggled(_ sender: UISwitch) { - appSigningViewController.forceiTunesFileSharing = sender.isOn - } - @objc private func forceLightDarkAppearenceDidChange(_ sender: UISegmentedControl) { - appSigningViewController.forceLightDarkAppearence = sender.selectedSegmentIndex - } - @objc private func forceMinimumVersionDidChange(_ sender: UISegmentedControl) { - appSigningViewController.forceMinimumVersion = sender.selectedSegmentIndex - } - @objc private func removePlaceHolderWatchExtension(_ sender: UISwitch) { - appSigningViewController.removeWatchPlaceHolder = sender.isOn - } - @objc private func removeProvisioningFile(_ sender: UISwitch) { - appSigningViewController.removeProvisioningFile = sender.isOn - } - @objc private func forceLocalized(_ sender: UISwitch) { - appSigningViewController.forceTryToLocalize = sender.isOn - } -} diff --git a/iOS/Views/Apps/Signing/AppSigningInputViewController.swift b/iOS/Views/Apps/Signing/AppSigningInputViewController.swift deleted file mode 100644 index c2f7100..0000000 --- a/iOS/Views/Apps/Signing/AppSigningInputViewController.swift +++ /dev/null @@ -1,108 +0,0 @@ -// -// AppSigningInputViewController.swift -// feather -// -// Created by samara on 8/15/24. -// Copyright (c) 2024 Samara M (khcrysalis) -// - -import Foundation -import UIKit -// MARK: - AppSigningInputViewController -class AppSigningInputViewController: UITableViewController { - var appSigningViewController: AppSigningViewController - var initialValue: String! - var valueToSaveTo: String! - var indexPath: IndexPath! - private var changedValue: String? - - private lazy var textField: UITextField = { - let textField = UITextField(frame: .zero) - textField.translatesAutoresizingMaskIntoConstraints = false - textField.addTarget(self, action: #selector(textDidChange), for: .editingChanged) - return textField - }() - - init(appSigningViewController: AppSigningViewController, initialValue: String, valueToSaveTo: String, indexPath: IndexPath) { - self.appSigningViewController = appSigningViewController - self.initialValue = initialValue - self.valueToSaveTo = valueToSaveTo - self.indexPath = indexPath - super.init(style: .insetGrouped) - } - - required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - - override func viewDidLoad() { - super.viewDidLoad() - navigationItem.largeTitleDisplayMode = .never - self.title = valueToSaveTo.capitalized - - let saveButton = UIBarButtonItem(title: String.localized("SAVE"), style: .done, target: self, action: #selector(saveButton)) - saveButton.isEnabled = false - navigationItem.rightBarButtonItem = saveButton - - tableView.register(UITableViewCell.self, forCellReuseIdentifier: "InputCell") - } - - @objc func saveButton() { - appSigningViewController.updateValue(propertyName: valueToSaveTo, value: changedValue, indexPath: indexPath) - self.navigationController?.popViewController(animated: true) - } - - @objc private func textDidChange() { - navigationItem.rightBarButtonItem?.isEnabled = !(textField.text?.isEmpty ?? true) - changedValue = textField.text - } - - override func numberOfSections(in tableView: UITableView) -> Int { - if ((appSigningViewController.certs?.certData?.pPQCheck) != nil) && valueToSaveTo == "bundleId"{ - return 2 - } else { - return 1 - } - } - override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 1 } - - override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - var cell = tableView.dequeueReusableCell(withIdentifier: "InputCell", for: indexPath) - switch indexPath.section { - case 0: - textField.text = initialValue - textField.placeholder = initialValue - - if textField.superview == nil { - cell.contentView.addSubview(textField) - NSLayoutConstraint.activate([ - textField.centerYAnchor.constraint(equalTo: cell.contentView.centerYAnchor), - textField.leadingAnchor.constraint(equalTo: cell.contentView.layoutMarginsGuide.leadingAnchor), - textField.trailingAnchor.constraint(equalTo: cell.contentView.layoutMarginsGuide.trailingAnchor) - ]) - } - - cell.selectionStyle = .none - case 1: - cell = UITableViewCell(style: .subtitle, reuseIdentifier: "Cell") - - cell.textLabel?.text = "PPQCheck is Enabled" - cell.textLabel?.textColor = .systemRed - - cell.detailTextLabel?.text = "PPQCheck is a way for Apple to check if the app you're opening matches another bundle identifier found on the App Store, the check happens on the first time you open the signed installed application. We have an option for you to avoid this, however you will no longer receive the benefits of notifications and such relating to the default identifier." - cell.detailTextLabel?.textColor = .label - cell.textLabel?.numberOfLines = 0 - cell.detailTextLabel?.numberOfLines = 0 -// case 2: -// cell = UITableViewCell(style: .subtitle, reuseIdentifier: "Cell") -// -// cell.textLabel?.text = "Information" -// cell.textLabel?.textColor = .label -// -// cell.detailTextLabel?.text = "PPQCheck is a way for Apple to check if the app you're opening matches another bundle identifier found on the App Store, the check happens every time you open the signed installed application. By default we prepended the random string to save you from a headache of getting the Apple ID associated with the certificate locked." -// cell.detailTextLabel?.textColor = .secondaryLabel -// cell.textLabel?.numberOfLines = 0 -// cell.detailTextLabel?.numberOfLines = 0 - default: break - } - return cell - } -} diff --git a/iOS/Views/Apps/Signing/AppSigningViewController.swift b/iOS/Views/Apps/Signing/AppSigningViewController.swift deleted file mode 100644 index b6d8cd5..0000000 --- a/iOS/Views/Apps/Signing/AppSigningViewController.swift +++ /dev/null @@ -1,454 +0,0 @@ -// -// AppSigningViewController.swift -// feather -// -// Created by HAHALOSAH on 7/11/24. -// Copyright (c) 2024 Samara M (khcrysalis) -// - -import Foundation -import UIKit -import CoreData -import UniformTypeIdentifiers - -class AppSigningViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { - var tableView: UITableView! - - var appsViewController: LibraryViewController - var largeButton = ActivityIndicatorButton() - var iconCell = IconImageViewCell() - - private var variableBlurView: UIVariableBlurView? - - var toInject: [URL] = [] - var removeInjectPaths: [String] = [] - - var app: NSManagedObject! - var name: String = "Unknown" - var bundleId: String = "unknown" - var version: String = "unknown" - var signing = false - var icon: UIImage? - - var uuid = "unknown" - - var forceMinimumVersion = 0 - var forceMinimumVersionString = ["Automatic", "15.0", "14.0", "13.0"] - - var forceLightDarkAppearence = 0 - var forceLightDarkAppearenceString = ["Automatic", "Light", "Dark"] - - var removePlugins = false - var forceFileSharing = true - var removeSupportedDevices = true - var removeURLScheme = false - var forceProMotion = false - var forceForceFullScreen = false - var forceiTunesFileSharing = true - - var removeWatchPlaceHolder = true - var removeProvisioningFile = true - var forceTryToLocalize = false - - var certs: Certificate? - - init(app: NSManagedObject, appsViewController: LibraryViewController) { - self.appsViewController = appsViewController - self.app = app - super.init(nibName: nil, bundle: nil) - - if let hasGotCert = CoreDataManager.shared.getCurrentCertificate() { - self.certs = hasGotCert - } - - if let name = app.value(forKey: "name") as? String { - self.name = name - } - - if let bundleId = app.value(forKey: "bundleidentifier") as? String { - if self.certs?.certData?.pPQCheck == true && Preferences.isFuckingPPqcheckDetectionOff == true { - self.bundleId = bundleId+"."+Preferences.pPQCheckString - } else { - self.bundleId = bundleId - } - } - - if let version = app.value(forKey: "version") as? String { - self.version = version - } - - if let uuid = app.value(forKey: "uuid") as? String { - self.uuid = uuid - } - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func viewDidLoad() { - setupViews() - setupNavigation() - setupToolbar() - - if (certs == nil) { - #if !targetEnvironment(simulator) - DispatchQueue.main.async { - let alert = UIAlertController( - title: String.localized("APP_SIGNING_VIEW_CONTROLLER_NO_CERTS_ALERT_TITLE"), - message: String.localized("APP_SIGNING_VIEW_CONTROLLER_NO_CERTS_ALERT_DESCRIPTION"), - preferredStyle: .alert - ) - alert.addAction(UIAlertAction(title: String.localized("LAME"), style: .default) { _ in - self.dismiss(animated: true) - } - ) - self.present(alert, animated: true, completion: nil) - } - #endif - } - } - - fileprivate func setupViews() { - self.tableView = UITableView(frame: .zero, style: .insetGrouped) - self.tableView.translatesAutoresizingMaskIntoConstraints = false - self.tableView.dataSource = self - self.tableView.delegate = self - self.tableView.showsHorizontalScrollIndicator = false - self.tableView.showsVerticalScrollIndicator = false - self.tableView.contentInset.bottom = 40 - - self.tableView.register(TweakLibraryViewCell.self, forCellReuseIdentifier: "TweakLibraryViewCell") - self.tableView.register(SwitchViewCell.self, forCellReuseIdentifier: "SwitchViewCell") - self.tableView.register(ActivityIndicatorViewCell.self, forCellReuseIdentifier: "ActivityIndicatorViewCell") - - self.view.addSubview(tableView) - self.tableView.constraintCompletely(to: view) - } - - fileprivate func setupNavigation() { - let logoImageView = UIImageView(image: UIImage(named: "feather_glyph")) - logoImageView.contentMode = .scaleAspectFit - navigationItem.titleView = logoImageView - self.navigationController?.navigationBar.prefersLargeTitles = false - - self.isModalInPresentation = true - self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: String.localized("DISMISS"), style: .done, target: self, action: #selector(closeSheet)) - } - - fileprivate func setupToolbar() { - largeButton.translatesAutoresizingMaskIntoConstraints = false - largeButton.addTarget(self, action: #selector(startSign), for: .touchUpInside) - - let gradientMask = VariableBlurViewConstants.defaultGradientMask - variableBlurView = UIVariableBlurView(frame: .zero) - variableBlurView?.gradientMask = gradientMask - variableBlurView?.transform = CGAffineTransform(rotationAngle: CGFloat.pi) - variableBlurView?.translatesAutoresizingMaskIntoConstraints = false - - view.addSubview(variableBlurView!) - view.addSubview(largeButton) - - - var height = 90.0 - - if UIDevice.current.userInterfaceIdiom == .pad { - height = 50.0 - } - - NSLayoutConstraint.activate([ - variableBlurView!.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor), - variableBlurView!.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor), - variableBlurView!.bottomAnchor.constraint(equalTo: view.bottomAnchor), - variableBlurView!.heightAnchor.constraint(equalToConstant: height), - - largeButton.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 17), - largeButton.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -17), - largeButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -17), - largeButton.heightAnchor.constraint(equalToConstant: 50) - ]) - - variableBlurView?.layer.zPosition = 3 - largeButton.layer.zPosition = 4 - } - - @objc func startSign() { - self.navigationItem.leftBarButtonItem = nil - signing = true - largeButton.showLoadingIndicator() - signInitialApp(options: AppSigningOptions( - name: name, - version: version, - bundleId: bundleId, - iconURL: icon ?? nil, - uuid: uuid, - toInject: toInject, - removeInjectPaths: removeInjectPaths, - removePlugins: removePlugins, - forceFileSharing: forceFileSharing, - removeSupportedDevices: removeSupportedDevices, - removeURLScheme: removeURLScheme, - forceProMotion: forceProMotion, - forceForceFullScreen: forceForceFullScreen, - forceiTunesFileSharing: forceiTunesFileSharing, - forceMinimumVersion: forceMinimumVersionString[forceMinimumVersion], - forceLightDarkAppearence: forceLightDarkAppearenceString[forceLightDarkAppearence], - forceTryToLocalize: forceTryToLocalize, - removeProvisioningFile: removeProvisioningFile, - removeWatchPlaceHolder: removeWatchPlaceHolder, - certificate: certs - ), - appPath:getFilesForDownloadedApps(app: app as! DownloadedApps, getuuidonly: false) - ) { result in - switch result { - case .success(let (signedPath, signedApp)): - self.appsViewController.fetchSources() - self.appsViewController.tableView.reloadData() - Debug.shared.log(message: signedPath.path) - if Preferences.autoInstallAfterSign { - self.appsViewController.startInstallProcess(meow: signedApp, filePath: signedPath.path) - } - - case .failure(let error): - Debug.shared.log(message: "Signing failed: \(error.localizedDescription)", type: .error) - } - - self.dismiss(animated: true) - } - } - - @objc func closeSheet() { - dismiss(animated: true, completion: nil) - } - - func numberOfSections(in tableView: UITableView) -> Int { - return 5; - } - - func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - switch(section) { - case 0: - return 4; - case 1: - return 1; - case 2: - return 2; - case 3: - return 1; - default: - return 0; - } - } - - func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell = UITableViewCell(style: .value1, reuseIdentifier: "Cell") - cell.accessoryType = .none - cell.textLabel?.textColor = .label - cell.selectionStyle = .gray - - switch (indexPath.section, indexPath.item) { - case (0, 0): - let cell = iconCell - - if (icon != nil) { - cell.configure(with: icon) - } else { - cell.configure(with: CoreDataManager.shared.loadImage(from: getIconURL(for: app as! DownloadedApps))) - } - - cell.accessoryType = .disclosureIndicator - return cell - case (0, 1): - cell.textLabel?.text = String.localized("APPS_INFORMATION_TITLE_NAME") - cell.detailTextLabel?.text = name - cell.accessoryType = .disclosureIndicator - case (0, 2): - cell.textLabel?.text = String.localized("APPS_INFORMATION_TITLE_IDENTIFIER") - cell.detailTextLabel?.text = bundleId - cell.accessoryType = .disclosureIndicator - case (0, 3): - cell.textLabel?.text = String.localized("APPS_INFORMATION_TITLE_VERSION") - cell.detailTextLabel?.text = version - cell.accessoryType = .disclosureIndicator - case (1, 0): - if let hasGotCert = certs { - let cell = CertificateViewTableViewCell() - cell.configure(with: hasGotCert, isSelected: false) - cell.selectionStyle = .none - return cell - } else { - cell.textLabel?.text = String.localized("SETTINGS_VIEW_CONTROLLER_CELL_CURRENT_CERTIFICATE_NOSELECTED") - cell.textLabel?.textColor = .secondaryLabel - cell.selectionStyle = .none - } - break - case (2, 0): - cell.textLabel?.text = String.localized("APP_SIGNING_VIEW_CONTROLLER_CELL_ADD_TWEAKS") - let badgeView = BadgeView(frame: CGRect(x: 0, y: 0, width: 60, height: 20)) - cell.accessoryView = badgeView - break - case (2, 1): - cell.textLabel?.text = "Remove dylibs" - let badgeView = BadgeView(frame: CGRect(x: 0, y: 0, width: 60, height: 20)) - cell.accessoryView = badgeView - break - case (3, 0): - cell.textLabel?.text = String.localized("APP_SIGNING_VIEW_CONTROLLER_CELL_ADVANCED") - cell.accessoryType = .disclosureIndicator - break - default: - break - } - return cell - } - - func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - switch (indexPath.section, indexPath.item) { - case (0, 0): - importAppIconFile() - case (0, 1): - navigationController?.pushViewController(AppSigningInputViewController(appSigningViewController: self, initialValue: self.name, valueToSaveTo: "name", indexPath: indexPath), animated: true) - case (0, 2): - navigationController?.pushViewController(AppSigningInputViewController(appSigningViewController: self, initialValue: self.bundleId, valueToSaveTo: "bundleId", indexPath: indexPath), animated: true) - case (0, 3): - navigationController?.pushViewController(AppSigningInputViewController(appSigningViewController: self, initialValue: self.version, valueToSaveTo: "version", indexPath: indexPath), animated: true) - case (2, 0): - navigationController?.pushViewController(AppSigningTweakViewController(appSigningViewController: self), animated: true) - case (2, 1): - navigationController?.pushViewController(AppSigningDylibViewController(appSigningViewController: self, app: getFilesForDownloadedApps(app: app as! DownloadedApps, getuuidonly: false)), animated: true) - case (3, 0): - navigationController?.pushViewController(AppSigningAdvancedViewController(appSigningViewController: self), animated: true) - default: - break - } - tableView.deselectRow(at: indexPath, animated: true) - } - - func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { - return [ - 40, - 40, - 40, - 0, - 0 - ][section] - } - - func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { - - let titles = [ - String.localized("APP_SIGNING_VIEW_CONTROLLER_CELL_TITLE_CUSTOMIZATION"), - String.localized("APP_SIGNING_VIEW_CONTROLLER_CELL_TITLE_SIGNING"), - String.localized("APP_SIGNING_VIEW_CONTROLLER_CELL_TITLE_ADVANCED"), - "", - "" - ][section] - - let headerView = InsetGroupedSectionHeader(title: titles) - return headerView - } - - func updateValue(propertyName: String, value: String?, indexPath: IndexPath) { - switch propertyName { - case "name": - name = value ?? "Unknown" - case "bundleId": - bundleId = value ?? "unknown" - case "version": - version = value ?? "unknown" - default: - Debug.shared.log(message: "Invalid property name: \(propertyName)") - } - self.tableView.reloadRows(at: [indexPath], with: .automatic) - } - - func getFilesForDownloadedApps(app: DownloadedApps, getuuidonly: Bool) -> URL { - return CoreDataManager.shared.getFilesForDownloadedApps(for: app, getuuidonly: getuuidonly) - } - - func getIconURL(for app: DownloadedApps) -> URL? { - guard let iconURLString = app.value(forKey: "iconURL") as? String, - let iconURL = URL(string: iconURLString) else { - return nil - } - - let filesURL = getFilesForDownloadedApps(app: app, getuuidonly: false) - return filesURL.appendingPathComponent(iconURL.lastPathComponent) - } -} -// MARK: - UIDocumentPickerDelegate -extension AppSigningViewController: UIDocumentPickerDelegate & UIImagePickerControllerDelegate, UINavigationControllerDelegate { - func importAppIconFile() { - let actionSheet = UIAlertController(title: "Select App Icon", message: nil, preferredStyle: .actionSheet) - - let documentPickerAction = UIAlertAction(title: "Choose from Files", style: .default) { [weak self] _ in - self?.presentDocumentPicker(fileExtension: [UTType.image]) - } - - let photoLibraryAction = UIAlertAction(title: "Choose from Photos", style: .default) { [weak self] _ in - self?.presentPhotoLibrary(mediaTypes: ["public.image"]) - } - - let cancelAction = UIAlertAction(title: String.localized("CANCEL"), style: .cancel, handler: nil) - - actionSheet.addAction(documentPickerAction) - actionSheet.addAction(photoLibraryAction) - actionSheet.addAction(cancelAction) - - if let popoverController = actionSheet.popoverPresentationController { - popoverController.sourceView = self.view - popoverController.sourceRect = CGRect(x: self.view.bounds.midX, y: self.view.bounds.midY, width: 0, height: 0) - popoverController.permittedArrowDirections = [] - } - - self.present(actionSheet, animated: true, completion: nil) - } - - // MARK: - Documents - - func presentDocumentPicker(fileExtension: [UTType]) { - let documentPicker = UIDocumentPickerViewController(forOpeningContentTypes: fileExtension, asCopy: true) - documentPicker.delegate = self - documentPicker.allowsMultipleSelection = false - present(documentPicker, animated: true, completion: nil) - } - - func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) { - guard let selectedFileURL = urls.first else { return } - let meow = CoreDataManager.shared.loadImage(from: selectedFileURL) - icon = meow?.resizeToSquare() - Debug.shared.log(message: "\(selectedFileURL)") - self.tableView.reloadData() - } - - func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) { - controller.dismiss(animated: true, completion: nil) - } - - // MARK: - Library - - func presentPhotoLibrary(mediaTypes: [String]) { - let imagePicker = UIImagePickerController() - imagePicker.delegate = self - imagePicker.sourceType = .photoLibrary - imagePicker.mediaTypes = mediaTypes - self.present(imagePicker, animated: true, completion: nil) - } - - func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { - picker.dismiss(animated: true, completion: nil) - - guard let selectedImage = info[.originalImage] as? UIImage else { return } - - icon = selectedImage.resizeToSquare() - self.tableView.reloadData() - } - - func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { - picker.dismiss(animated: true, completion: nil) - } - - - - -} diff --git a/iOS/Views/Extra/TransferPreview.swift b/iOS/Views/Extra/TransferPreview.swift index 4da83e2..0a4f597 100644 --- a/iOS/Views/Extra/TransferPreview.swift +++ b/iOS/Views/Extra/TransferPreview.swift @@ -110,19 +110,17 @@ struct TransferPreview: View { } .onAppear { - if Preferences.automaticInstall { - archivePayload(at: appPath, with: appName) { archiveURL in - if let archiveURL = archiveURL { - installer.package = archiveURL - if isSharing { - shareURL = archiveURL - showShareSheet = true - } else if case .ready = installer.status { - if Preferences.userSelectedServer { - isPresentWebView = true - } else { - UIApplication.shared.open(installer.iTunesLink) - } + archivePayload(at: appPath, with: appName) { archiveURL in + if let archiveURL = archiveURL { + installer.package = archiveURL + if isSharing { + shareURL = archiveURL + showShareSheet = true + } else if case .ready = installer.status { + if Preferences.userSelectedServer { + isPresentWebView = true + } else { + UIApplication.shared.open(installer.iTunesLink) } } } diff --git a/iOS/Views/Settings/SettingsViewController.swift b/iOS/Views/Settings/SettingsViewController.swift index 008761b..b96e3c6 100644 --- a/iOS/Views/Settings/SettingsViewController.swift +++ b/iOS/Views/Settings/SettingsViewController.swift @@ -234,7 +234,8 @@ extension SettingsViewController { let l = CertificatesViewController() navigationController?.pushViewController(l, animated: true) case "Signing Options": - let l = SigningViewHostingController() + let signingDataWrapper = SigningDataWrapper(signingOptions: UserDefaults.standard.signingOptions) + let l = SigningsOptionViewController(signingDataWrapper: signingDataWrapper) navigationController?.pushViewController(l, animated: true) case "Server Options": let l = ServerOptionsViewController() diff --git a/iOS/Views/Settings/Signing Options/SigningOptionsViewController.swift b/iOS/Views/Settings/Signing Options/SigningOptionsViewController.swift index 688f47f..7cfa523 100644 --- a/iOS/Views/Settings/Signing Options/SigningOptionsViewController.swift +++ b/iOS/Views/Settings/Signing Options/SigningOptionsViewController.swift @@ -74,25 +74,25 @@ extension SigningOptionsViewController { cell.textLabel?.text = cellText switch cellText { - case "Auto Install": - let autoInstall = SwitchViewCell() - autoInstall.textLabel?.text = "Install after signing" - autoInstall.switchControl.addTarget(self, action: #selector(autoInstallAfterSignToggled(_:)), for: .valueChanged) - autoInstall.switchControl.isOn = Preferences.autoInstallAfterSign - autoInstall.selectionStyle = .none - return autoInstall - case "Fuck PPQCheck": - let fuckPPq = SwitchViewCell() - fuckPPq.textLabel?.text = String.localized("SETTINGS_VIEW_CONTROLLER_PPQ_ALERT_TITLE") - fuckPPq.switchControl.addTarget(self, action: #selector(fuckPpqcheckToggled(_:)), for: .valueChanged) - fuckPPq.switchControl.isOn = Preferences.isFuckingPPqcheckDetectionOff - fuckPPq.selectionStyle = .none - - let infoButton = UIButton(type: .infoLight) - infoButton.addTarget(self, action: #selector(showPPQInfoAlert), for: .touchUpInside) - fuckPPq.accessoryView = infoButton - - return fuckPPq +// case "Auto Install": +// let autoInstall = SwitchViewCell() +// autoInstall.textLabel?.text = "Install after signing" +// autoInstall.switchControl.addTarget(self, action: #selector(autoInstallAfterSignToggled(_:)), for: .valueChanged) +// autoInstall.switchControl.isOn = Preferences.autoInstallAfterSign +// autoInstall.selectionStyle = .none +// return autoInstall +// case "Fuck PPQCheck": +// let fuckPPq = SwitchViewCell() +// fuckPPq.textLabel?.text = String.localized("SETTINGS_VIEW_CONTROLLER_PPQ_ALERT_TITLE") +// fuckPPq.switchControl.addTarget(self, action: #selector(fuckPpqcheckToggled(_:)), for: .valueChanged) +// fuckPPq.switchControl.isOn = Preferences.isFuckingPPqcheckDetectionOff +// fuckPPq.selectionStyle = .none +// +// let infoButton = UIButton(type: .infoLight) +// infoButton.addTarget(self, action: #selector(showPPQInfoAlert), for: .touchUpInside) +// fuckPPq.accessoryView = infoButton +// +// return fuckPPq case "PPQCheckMitigationString": cell.textLabel?.text = String.localized("SETTINGS_VIEW_CONTROLLER_CELL_CHANGE_ID") @@ -143,13 +143,13 @@ extension SigningOptionsViewController { present(alertController, animated: true, completion: nil) } - @objc func fuckPpqcheckToggled(_ sender: UISwitch) { - Preferences.isFuckingPPqcheckDetectionOff = sender.isOn - } - - @objc func autoInstallAfterSignToggled(_ sender: UISwitch) { - Preferences.autoInstallAfterSign = sender.isOn - } +// @objc func fuckPpqcheckToggled(_ sender: UISwitch) { +// Preferences.isFuckingPPqcheckDetectionOff = sender.isOn +// } +// +// @objc func autoInstallAfterSignToggled(_ sender: UISwitch) { +// Preferences.autoInstallAfterSign = sender.isOn +// } } diff --git a/iOS/Views/Signing/SigningData/SigningDataWrapper.swift b/iOS/Views/Signing/SigningData/SigningDataWrapper.swift index 37afcf3..e70244a 100644 --- a/iOS/Views/Signing/SigningData/SigningDataWrapper.swift +++ b/iOS/Views/Signing/SigningData/SigningDataWrapper.swift @@ -7,6 +7,14 @@ import Foundation +class SigningMainDataWrapper: ObservableObject { + @Published var mainOptions: MainSigningOptions + + init(mainOptions: MainSigningOptions) { + self.mainOptions = mainOptions + } +} + class SigningDataWrapper: ObservableObject { @Published var signingOptions: SigningOptions diff --git a/iOS/Views/Signing/SigningData/SigningOptions.swift b/iOS/Views/Signing/SigningData/SigningOptions.swift index 0f951f5..9fa0a15 100644 --- a/iOS/Views/Signing/SigningData/SigningOptions.swift +++ b/iOS/Views/Signing/SigningData/SigningOptions.swift @@ -14,7 +14,10 @@ struct MainSigningOptions { var iconURL: UIImage? var uuid: String? - var removeInjectPaths: [String]? + var removeInjectPaths: [String] = [] + + let forceMinimumVersionString = ["Automatic", "15.0", "14.0", "13.0"] + let forceLightDarkAppearenceString = ["Automatic", "Light", "Dark"] var certificate: Certificate? } @@ -34,12 +37,13 @@ struct SigningOptions: Codable { var forceForceFullScreen: Bool = false var forceiTunesFileSharing: Bool = true - var forceMinimumVersion: String = "Automatic" - var forceLightDarkAppearence: String = "Automatic" var forceTryToLocalize: Bool = false var removeProvisioningFile: Bool = true var removeWatchPlaceHolder: Bool = true + + var forceMinimumVersion: String = "Automatic" + var forceLightDarkAppearence: String = "Automatic" } extension UserDefaults { diff --git a/iOS/Views/Signing/SigningOptionsViewController/Identifier/AddIdentifierViewController.swift b/iOS/Views/Signing/SigningOptionsViewController/Identifier/AddIdentifierViewController.swift new file mode 100644 index 0000000..bee9f0b --- /dev/null +++ b/iOS/Views/Signing/SigningOptionsViewController/Identifier/AddIdentifierViewController.swift @@ -0,0 +1,105 @@ +// +// AddIdentifierViewController.swift +// feather +// +// Created by samara on 26.10.2024. +// + +import UIKit + +class AddIdentifierViewController: UITableViewController { + var onAdd: ((String, String) -> Void)? + + private let identifierTextField: UITextField = { + let textField = UITextField(frame: .zero) + textField.placeholder = "Identifier" + textField.borderStyle = .none + textField.translatesAutoresizingMaskIntoConstraints = false + return textField + }() + + private let replacementTextField: UITextField = { + let textField = UITextField(frame: .zero) + textField.placeholder = "Replacement" + textField.borderStyle = .none + textField.translatesAutoresizingMaskIntoConstraints = false + return textField + }() + + init() { + super.init(style: .insetGrouped) + } + + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } + + override func viewDidLoad() { + super.viewDidLoad() + title = "New Identifier" + view.backgroundColor = .systemBackground + + identifierTextField.addTarget(self, action: #selector(textFieldsDidChange), for: .editingChanged) + replacementTextField.addTarget(self, action: #selector(textFieldsDidChange), for: .editingChanged) + + navigationItem.rightBarButtonItem = UIBarButtonItem( + title: "Add", + style: .done, + target: self, + action: #selector(addButtonTapped) + ) + navigationItem.rightBarButtonItem?.isEnabled = false + } + + override func numberOfSections(in tableView: UITableView) -> Int { + return 1 + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return 2 + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = UITableViewCell(style: .default, reuseIdentifier: nil) + cell.selectionStyle = .none + + switch indexPath.row { + case 0: + if identifierTextField.superview == nil { + cell.contentView.addSubview(identifierTextField) + NSLayoutConstraint.activate([ + identifierTextField.centerYAnchor.constraint(equalTo: cell.contentView.centerYAnchor), + identifierTextField.leadingAnchor.constraint(equalTo: cell.contentView.layoutMarginsGuide.leadingAnchor), + identifierTextField.trailingAnchor.constraint(equalTo: cell.contentView.layoutMarginsGuide.trailingAnchor) + ]) + } + + case 1: + if replacementTextField.superview == nil { + cell.contentView.addSubview(replacementTextField) + NSLayoutConstraint.activate([ + replacementTextField.centerYAnchor.constraint(equalTo: cell.contentView.centerYAnchor), + replacementTextField.leadingAnchor.constraint(equalTo: cell.contentView.layoutMarginsGuide.leadingAnchor), + replacementTextField.trailingAnchor.constraint(equalTo: cell.contentView.layoutMarginsGuide.trailingAnchor) + ]) + } + default: + return cell + } + + return cell + } + + @objc private func textFieldsDidChange() { + let identifierText = identifierTextField.text ?? "" + let replacementText = replacementTextField.text ?? "" + navigationItem.rightBarButtonItem?.isEnabled = !identifierText.isEmpty && !replacementText.isEmpty + } + + @objc private func addButtonTapped() { + if let identifier = identifierTextField.text, + let replacement = replacementTextField.text, + !identifier.isEmpty, !replacement.isEmpty { + onAdd?(identifier, replacement) + navigationController?.popViewController(animated: true) + } + } +} diff --git a/iOS/Views/Signing/SigningOptionsViewController/Identifier/IdentifiersViewController.swift b/iOS/Views/Signing/SigningOptionsViewController/Identifier/IdentifiersViewController.swift new file mode 100644 index 0000000..74254e9 --- /dev/null +++ b/iOS/Views/Signing/SigningOptionsViewController/Identifier/IdentifiersViewController.swift @@ -0,0 +1,82 @@ +// +// IdentifiersViewController.swift +// feather +// +// Created by samara on 26.10.2024. +// + +import UIKit + +class IdentifiersViewController: UITableViewController { + var signingDataWrapper: SigningDataWrapper + private var newIdentifier: String = "" + private var newReplacement: String = "" + + init(signingDataWrapper: SigningDataWrapper) { + self.signingDataWrapper = signingDataWrapper + super.init(style: .insetGrouped) + title = "Identifiers" + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + + navigationItem.rightBarButtonItem = UIBarButtonItem( + barButtonSystemItem: .add, + target: self, + action: #selector(addIdentifierTapped) + ) + + tableView.register(UITableViewCell.self, forCellReuseIdentifier: "IdentifierCell") + } + + override func numberOfSections(in tableView: UITableView) -> Int { + return signingDataWrapper.signingOptions.bundleIdConfig.keys.count + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return 1 + } + + override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { + let sortedKeys = signingDataWrapper.signingOptions.bundleIdConfig.keys.sorted() + return sortedKeys[section] + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "IdentifierCell", for: indexPath) + + let key = signingDataWrapper.signingOptions.bundleIdConfig.keys.sorted()[indexPath.section] + cell.textLabel?.text = signingDataWrapper.signingOptions.bundleIdConfig[key] + cell.textLabel?.textColor = .secondaryLabel + cell.selectionStyle = .none + return cell + } + + override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { + let deleteAction = UIContextualAction(style: .destructive, title: "Delete") { [weak self] _, _, completionHandler in + self?.deleteIdentifier(at: indexPath.section) + completionHandler(true) + } + return UISwipeActionsConfiguration(actions: [deleteAction]) + } + + private func deleteIdentifier(at index: Int) { + let key = signingDataWrapper.signingOptions.bundleIdConfig.keys.sorted()[index] + signingDataWrapper.signingOptions.bundleIdConfig.removeValue(forKey: key) + tableView.reloadData() + } + + @objc private func addIdentifierTapped() { + let addVC = AddIdentifierViewController() + addVC.onAdd = { [weak self] identifier, replacement in + self?.signingDataWrapper.signingOptions.bundleIdConfig[identifier] = replacement + self?.tableView.reloadData() + } + navigationController?.pushViewController(addVC, animated: true) + } +} diff --git a/iOS/Views/Signing/SigningOptionsViewController/SigningsOptionViewController.swift b/iOS/Views/Signing/SigningOptionsViewController/SigningsOptionViewController.swift new file mode 100644 index 0000000..d329290 --- /dev/null +++ b/iOS/Views/Signing/SigningOptionsViewController/SigningsOptionViewController.swift @@ -0,0 +1,211 @@ +// +// SigningsOptionViewController.swift +// feather +// +// Created by samara on 26.10.2024. +// + +import CoreData +import UIKit + +struct TogglesOption { + let title: String + let footer: String? + var binding: Bool +} + +func toggleOptions(signingDataWrapper: SigningDataWrapper) -> [TogglesOption] { + return [ + TogglesOption( + title: "Remove all PlugIns", + footer: "Removes the PlugIns directory inside of the app, which would usually have some components for the app to function properly.", + binding: signingDataWrapper.signingOptions.removePlugins + ), + TogglesOption( + title: "Force File Sharing", + footer: "Allows other apps to open and edit the files stored in the Documents folder. This option also lets users set the app’s default save location in Settings.", + binding: signingDataWrapper.signingOptions.forceFileSharing + ), + TogglesOption( + title: "Remove UISupportedDevices", + footer: "Removes device restrictions for the application.", + binding: signingDataWrapper.signingOptions.removeSupportedDevices + ), + TogglesOption( + title: "Remove URL Scheme", + footer: "Removes any possible URL schemes (i.e. 'feather://')", + binding: signingDataWrapper.signingOptions.removeURLScheme + ), + TogglesOption( + title: "Enable ProMotion", + footer: "Enables ProMotion capabilities within the app, however on lower versions of 15.x this may not be enough.", + binding: signingDataWrapper.signingOptions.forceProMotion + ), + TogglesOption( + title: "Force Full Screen", + footer: "Forces only fullscreen capabilities within iPad apps, disallowing sharing the screen with other apps. On an external screen, the window for an app with this setting maintains its canvas size.", + binding: signingDataWrapper.signingOptions.forceForceFullScreen + ), + TogglesOption( + title: "Force iTunes File Sharing", + footer: "Forces the app to share their documents directory, allowing sharing between iTunes and Finder.", + binding: signingDataWrapper.signingOptions.forceiTunesFileSharing + ), + TogglesOption( + title: "Force Try To Localize", + footer: "Forces localization by modifying every localizable bundle within the app when trying to change a name of the app.", + binding: signingDataWrapper.signingOptions.forceTryToLocalize + ), + TogglesOption( + title: "Remove Provisioning File", + footer: "Removes .mobileprovison from appearing in your app after signing.", + binding: signingDataWrapper.signingOptions.removeProvisioningFile + ), + TogglesOption( + title: "Remove Watch Placeholder", + footer: "Removes unwanted watch placeholder which isn't supposed to be there, present in apps such as YouTube music, etc.", + binding: signingDataWrapper.signingOptions.removeWatchPlaceHolder + ) + ] +} + +class SigningsOptionViewController: UITableViewController { + + private var application: NSManagedObject? + private var appsViewController: LibraryViewController? + var signingDataWrapper: SigningDataWrapper + + private var toggleOptions: [TogglesOption] + + init(signingDataWrapper: SigningDataWrapper, application: NSManagedObject? = nil, appsViewController: LibraryViewController? = nil) { + self.signingDataWrapper = signingDataWrapper + self.application = application + self.appsViewController = appsViewController + self.toggleOptions = feather.toggleOptions(signingDataWrapper: signingDataWrapper) + super.init(style: .insetGrouped) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + setupViews() + setupNavigation() + } + + fileprivate func setupViews() { + tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell") + } + + fileprivate func setupNavigation() { + self.navigationItem.largeTitleDisplayMode = .never + self.title = "Signing Options" + self.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .save, target: self, action: #selector(save)) + } + + @objc func save() { + UserDefaults.standard.signingOptions = signingDataWrapper.signingOptions + self.navigationController?.popViewController(animated: true) + } +} + +extension SigningsOptionViewController { + + override func numberOfSections(in tableView: UITableView) -> Int { + return 1 + toggleOptions.count + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return section == 0 ? 3 : 1 + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "cell") ?? UITableViewCell(style: .default, reuseIdentifier: "cell") + + switch [indexPath.section, indexPath.row] { + case [0,0]: + let toggleSwitch = UISwitch() + cell.textLabel?.text = "Enable Protection" + toggleSwitch.isOn = signingDataWrapper.signingOptions.ppqCheckProtection + toggleSwitch.tag = 0 + toggleSwitch.addTarget(self, action: #selector(toggleOptionsSwitches(_:)), for: .valueChanged) + cell.accessoryView = toggleSwitch + case [0,1]: + cell.textLabel?.text = "Bundle Identifiers" + cell.accessoryType = .disclosureIndicator + cell.accessoryView = nil + case [0,2]: + let toggleSwitch = UISwitch() + cell.textLabel?.text = "Install after Signing" + toggleSwitch.isOn = signingDataWrapper.signingOptions.installAfterSigned + toggleSwitch.tag = 1 + toggleSwitch.addTarget(self, action: #selector(toggleOptionsSwitches(_:)), for: .valueChanged) + cell.accessoryView = toggleSwitch + default: + let toggleOption = toggleOptions[indexPath.section - 1] + cell.textLabel?.text = toggleOption.title + + let toggleSwitch = UISwitch() + toggleSwitch.isOn = toggleOption.binding + toggleSwitch.tag = indexPath.section + 1 + toggleSwitch.addTarget(self, action: #selector(toggleOptionsSwitches(_:)), for: .valueChanged) + cell.accessoryView = toggleSwitch + } + + return cell + } + + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + tableView.deselectRow(at: indexPath, animated: true) + + switch [indexPath.section, indexPath.row] { + case [0, 1]: + let l = IdentifiersViewController(signingDataWrapper: signingDataWrapper) + navigationController?.pushViewController(l, animated: true) + default: + break + } + + } + + @objc func toggleOptionsSwitches(_ sender: UISwitch) { + switch sender.tag { + case 0: + signingDataWrapper.signingOptions.ppqCheckProtection = sender.isOn + case 1: + signingDataWrapper.signingOptions.installAfterSigned = sender.isOn + case 2: + signingDataWrapper.signingOptions.removePlugins = sender.isOn + case 3: + signingDataWrapper.signingOptions.forceFileSharing = sender.isOn + case 4: + signingDataWrapper.signingOptions.removeSupportedDevices = sender.isOn + case 5: + signingDataWrapper.signingOptions.removeURLScheme = sender.isOn + case 6: + signingDataWrapper.signingOptions.forceProMotion = sender.isOn + case 7: + signingDataWrapper.signingOptions.forceForceFullScreen = sender.isOn + case 8: + signingDataWrapper.signingOptions.forceiTunesFileSharing = sender.isOn + case 9: + signingDataWrapper.signingOptions.forceTryToLocalize = sender.isOn + case 10: + signingDataWrapper.signingOptions.removeProvisioningFile = sender.isOn + case 11: + signingDataWrapper.signingOptions.removeWatchPlaceHolder = sender.isOn + default: + break + } + } + + override func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? { + guard section > 0 else { + return "Enabling protections will pre-append every bundle identifier with a random string, this is to protect the Apple ID related to your certificate from being flagged by Apple. However, if you don't care about this you can ignore." + } + let toggleOption = toggleOptions[section - 1] + return toggleOption.footer + } +} diff --git a/iOS/Views/Signing/SigningView/AddTweaksView.swift b/iOS/Views/Signing/SigningView/AddTweaksView.swift deleted file mode 100644 index 6612bab..0000000 --- a/iOS/Views/Signing/SigningView/AddTweaksView.swift +++ /dev/null @@ -1,18 +0,0 @@ -// -// AddTweaksView.swift -// feather -// -// Created by samara on 25.10.2024. -// - -import SwiftUI - -struct AddTweaksView: View { - var body: some View { - List { - Text("Add Tweaks View Content") - } - .navigationTitle("Add Default Tweaks") - .navigationBarTitleDisplayMode(.inline) - } -} diff --git a/iOS/Views/Signing/SigningView/Configuration/IdentifiersView.swift b/iOS/Views/Signing/SigningView/Configuration/IdentifiersView.swift deleted file mode 100644 index 675c5b0..0000000 --- a/iOS/Views/Signing/SigningView/Configuration/IdentifiersView.swift +++ /dev/null @@ -1,71 +0,0 @@ -// -// IdentifiersView.swift -// feather -// -// Created by samara on 25.10.2024. -// - -import SwiftUI - -struct IdentifiersView: View { - @ObservedObject var signingDataWrapper: SigningDataWrapper - @State private var showAddFields = false - @State private var newIdentifier: String = "" - @State private var newReplacement: String = "" - - init(signingDataWrapper: SigningDataWrapper) { - self.signingDataWrapper = signingDataWrapper - } - - var body: some View { - List { - Section { - Button(action: { - showAddFields.toggle() - }) { - Text("Add Identifier") - } - - if showAddFields { - TextField("New Replacement Identifier", text: $newIdentifier) - TextField("Replacement", text: $newReplacement) - - Button("Add") { addNewIdentifier() }.disabled(newIdentifier.isEmpty || newReplacement.isEmpty) - } - } - - Section { - ForEach(signingDataWrapper.signingOptions.bundleIdConfig.keys.sorted(), id: \.self) { key in - Text(key) - .fontWeight(.bold) - TextField("Identifier", text: Binding( - get: { signingDataWrapper.signingOptions.bundleIdConfig[key] ?? "" }, - set: { signingDataWrapper.signingOptions.bundleIdConfig[key] = $0 } - )) - .foregroundColor(.secondary) - } - .onDelete(perform: deleteIdentifiers) - } - } - .navigationTitle("Identifiers") - .navigationBarTitleDisplayMode(.inline) - } - - private func addNewIdentifier() { - // Add the new identifier and reset the fields - if !newIdentifier.isEmpty && !newReplacement.isEmpty { - signingDataWrapper.signingOptions.bundleIdConfig[newIdentifier] = newReplacement - newIdentifier = "" - newReplacement = "" - showAddFields = false - } - } - - private func deleteIdentifiers(at offsets: IndexSet) { - for index in offsets { - let key = signingDataWrapper.signingOptions.bundleIdConfig.keys.sorted()[index] - signingDataWrapper.signingOptions.bundleIdConfig.removeValue(forKey: key) - } - } -} - diff --git a/iOS/Views/Signing/SigningView/SigningView.swift b/iOS/Views/Signing/SigningView/SigningView.swift deleted file mode 100644 index 6b0ee83..0000000 --- a/iOS/Views/Signing/SigningView/SigningView.swift +++ /dev/null @@ -1,136 +0,0 @@ -// -// SigningsViewController.swift -// feather -// -// Created by samara on 25.10.2024. -// - -import UIKit -import SwiftUI -import CoreData - -struct SigningView: View { - private var isSigning: Bool - private var application: NSManagedObject? - private var appsViewController: LibraryViewController? - - @ObservedObject var signingDataWrapper: SigningDataWrapper - @State var mainOptions = MainSigningOptions() - - init(sign: Bool, signingDataWrapper: SigningDataWrapper, application: NSManagedObject? = nil, appsViewController: LibraryViewController? = nil) { - self.isSigning = sign - self.signingDataWrapper = signingDataWrapper - self.application = application - self.appsViewController = appsViewController - } - - var body: some View { - if isSigning { - NavigationView { - viewbody.listStyle(.sidebar) - } - } else { - viewbody.listStyle(.insetGrouped) - } - } - - private var viewbody: some View { - List { - Section { - topBody - } - - ForEach(toggleOptions, id: \.title) { option in - Section { - Toggle(option.title, isOn: option.binding) - } footer: { - Text(option.footer ?? "") - } - } - } - .navigationTitle("Signing Options") - .navigationBarTitleDisplayMode(.inline) - } - - @ViewBuilder - private var topBody: some View { - if isSigning { - Text("signing shit") - } else { - Toggle("Enable Protections", isOn: $signingDataWrapper.signingOptions.ppqCheckProtection) - - NavigationLink(destination: IdentifiersView(signingDataWrapper: signingDataWrapper)) { - Text("Bundle Identifiers") - .foregroundColor(signingDataWrapper.signingOptions.ppqCheckProtection ? .gray : .primary) - } - .disabled(signingDataWrapper.signingOptions.ppqCheckProtection) - - Toggle("Install after Signing", isOn: $signingDataWrapper.signingOptions.installAfterSigned) - - NavigationLink(destination: AddTweaksView()) { - Text("Add Default Tweaks") - } - } - } - - private var toggleOptions: [ToggleOption] { - [ - ToggleOption( - title: "Remove all PlugIns", - footer: "Removes the PlugIns directory inside of the app, which would usually have some components for the app to function properly.", - binding: $signingDataWrapper.signingOptions.removePlugins - ), - ToggleOption( - title: "Force File Sharing", - footer: "Allows other apps to open and edit the files stored in the Documents folder. This option also lets users set the app’s default save location in Settings.", - binding: $signingDataWrapper.signingOptions.forceFileSharing - ), - ToggleOption( - title: "Remove UISupportedDevices", - footer: "Removes device restrictions for the application.", - binding: $signingDataWrapper.signingOptions.removeSupportedDevices - ), - ToggleOption( - title: "Remove URL Scheme", - footer: "Removes any possible URL schemes (i.e. 'feather://')", - binding: $signingDataWrapper.signingOptions.removeURLScheme - ), - ToggleOption( - title: "Enable ProMotion", - footer: "Enables ProMotion capabilities within the app, however on lower versions of 15.x this may not be enough.", - binding: $signingDataWrapper.signingOptions.forceProMotion - ), - ToggleOption( - title: "Force Full Screen", - footer: "Forces only fullscreen capabilities within iPad apps, disallowing sharing the screen with other apps. On an external screen, the window for an app with this setting maintains its canvas size.", - binding: $signingDataWrapper.signingOptions.forceForceFullScreen - ), - ToggleOption( - title: "Force iTunes File Sharing", - footer: "Forces the app to share their documents directory, allowing sharing between iTunes and Finder.", - binding: $signingDataWrapper.signingOptions.forceiTunesFileSharing - ), - ToggleOption( - title: "Force Try To Localize", - footer: "Forces localization by modifying every localizable bundle within the app when trying to change a name of the app.", - binding: $signingDataWrapper.signingOptions.forceTryToLocalize - ), - ToggleOption( - title: "Remove Provisioning File", - footer: "Removes .mobileprovison from appearing in your app after signing.", - binding: $signingDataWrapper.signingOptions.removeProvisioningFile - ), - ToggleOption( - title: "Remove Watch Placeholder", - footer: "Removes unwanted watch placeholder which isn't supposed to be there, present in apps such as YouTube music, etc.", - binding: $signingDataWrapper.signingOptions.removeWatchPlaceHolder - ) - ] - } -} - -struct ToggleOption { - let title: String - let footer: String? - let binding: Binding -} diff --git a/iOS/Views/Signing/SigningView/SigningViewHostingController.swift b/iOS/Views/Signing/SigningView/SigningViewHostingController.swift deleted file mode 100644 index cefefa5..0000000 --- a/iOS/Views/Signing/SigningView/SigningViewHostingController.swift +++ /dev/null @@ -1,31 +0,0 @@ -// -// SigningViewHostingController.swift -// feather -// -// Created by samara on 25.10.2024. -// - -import SwiftUI - -class SigningViewHostingController: UIHostingController { - private var signingDataWrapper = SigningDataWrapper(signingOptions: UserDefaults.standard.signingOptions) - - init() { - super.init(rootView: SigningView(sign: false, signingDataWrapper: signingDataWrapper)) - } - - @objc required dynamic init?(coder aDecoder: NSCoder) { - super.init(coder: aDecoder) - } - - override func viewDidLoad() { - super.viewDidLoad() - self.navigationItem.largeTitleDisplayMode = .never - self.title = "Signing Options" - self.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .save, target: self, action: #selector(save)) - } - - @objc func save() { - UserDefaults.standard.signingOptions = signingDataWrapper.signingOptions - } -} diff --git a/iOS/Views/Apps/Signing/ActivityIndicatorViewCell.swift b/iOS/Views/Signing/SigningViewController/Components/ActivityIndicatorViewCell.swift similarity index 100% rename from iOS/Views/Apps/Signing/ActivityIndicatorViewCell.swift rename to iOS/Views/Signing/SigningViewController/Components/ActivityIndicatorViewCell.swift diff --git a/iOS/Views/Apps/Signing/IconImageViewCell.swift b/iOS/Views/Signing/SigningViewController/Components/IconImageViewCell.swift similarity index 89% rename from iOS/Views/Apps/Signing/IconImageViewCell.swift rename to iOS/Views/Signing/SigningViewController/Components/IconImageViewCell.swift index d4b5470..58a8fc8 100644 --- a/iOS/Views/Apps/Signing/IconImageViewCell.swift +++ b/iOS/Views/Signing/SigningViewController/Components/IconImageViewCell.swift @@ -15,8 +15,10 @@ class IconImageViewCell: UITableViewCell { let imageView = UIImageView() imageView.translatesAutoresizingMaskIntoConstraints = false imageView.contentMode = .scaleAspectFit - imageView.layer.cornerRadius = 9 + imageView.layer.cornerRadius = 10 imageView.layer.cornerCurve = .continuous + imageView.layer.borderWidth = 1 + imageView.layer.borderColor = UIColor.lightGray.withAlphaComponent(0.3).cgColor imageView.clipsToBounds = true return imageView }() diff --git a/iOS/Views/Apps/Signing/SwitchViewCell.swift b/iOS/Views/Signing/SigningViewController/Components/SwitchViewCell.swift similarity index 100% rename from iOS/Views/Apps/Signing/SwitchViewCell.swift rename to iOS/Views/Signing/SigningViewController/Components/SwitchViewCell.swift diff --git a/iOS/Views/Apps/Signing/TweakLibraryViewCell.swift b/iOS/Views/Signing/SigningViewController/Components/TweakLibraryViewCell.swift similarity index 100% rename from iOS/Views/Apps/Signing/TweakLibraryViewCell.swift rename to iOS/Views/Signing/SigningViewController/Components/TweakLibraryViewCell.swift diff --git a/iOS/Views/Signing/SigningViewController/SigningsAdvancedViewController.swift b/iOS/Views/Signing/SigningViewController/SigningsAdvancedViewController.swift new file mode 100644 index 0000000..51fd14b --- /dev/null +++ b/iOS/Views/Signing/SigningViewController/SigningsAdvancedViewController.swift @@ -0,0 +1,146 @@ +// +// SigningsAdvancedViewController.swift +// feather +// +// Created by samara on 27.10.2024. +// + +import UIKit + +class SigningsAdvancedViewController: UITableViewController { + + var tableData = [ + [ String.localized("APP_SIGNING_INPUT_VIEW_CONTROLLER_SECTION_TITLE_APPEARENCE") ], + [ String.localized("APP_SIGNING_INPUT_VIEW_CONTROLLER_SECTION_TITLE_MINIMUM_APP_VERSION") ], + [], + ] + + var sectionTitles = [ + String.localized("APP_SIGNING_INPUT_VIEW_CONTROLLER_SECTION_TITLE_APPEARENCE"), + String.localized("APP_SIGNING_INPUT_VIEW_CONTROLLER_SECTION_TITLE_MINIMUM_APP_VERSION"), + String.localized("APP_SIGNING_INPUT_VIEW_CONTROLLER_SECTION_TITLE_PROPERTIES"), + ] + + var signingDataWrapper: SigningDataWrapper + var mainOptions: SigningMainDataWrapper + + private var toggleOptions: [TogglesOption] + + init(signingDataWrapper: SigningDataWrapper, mainOptions: SigningMainDataWrapper) { + self.signingDataWrapper = signingDataWrapper + self.mainOptions = mainOptions + self.toggleOptions = feather.toggleOptions(signingDataWrapper: signingDataWrapper) + super.init(style: .insetGrouped) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + self.title = "Properties" + self.tableData[2] = toggleOptions.map { $0.title } + } +} + +extension SigningsAdvancedViewController { + override func numberOfSections(in tableView: UITableView) -> Int { return sectionTitles.count } + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return tableData[section].count } + override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { return sectionTitles[section] } + override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { return sectionTitles[section].isEmpty ? 0 : 40 } + + override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { + let title = sectionTitles[section] + let headerView = InsetGroupedSectionHeader(title: title) + return headerView + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let reuseIdentifier = "Cell" + var cell = UITableViewCell(style: .value1, reuseIdentifier: reuseIdentifier) + cell.accessoryType = .none + cell.selectionStyle = .gray + + let cellText = tableData[indexPath.section][indexPath.row] + cell.textLabel?.text = cellText + + + switch cellText { + case String.localized("APP_SIGNING_INPUT_VIEW_CONTROLLER_SECTION_TITLE_APPEARENCE"): + let forceLightDarkAppearence = TweakLibraryViewCell() + forceLightDarkAppearence.selectionStyle = .none + forceLightDarkAppearence.configureSegmentedControl( + with: mainOptions.mainOptions.forceLightDarkAppearenceString, + selectedIndex: 0 + ) + forceLightDarkAppearence.segmentedControl.addTarget(self, action: #selector(forceLightDarkAppearenceDidChange(_:)), for: .valueChanged) + + return forceLightDarkAppearence + case String.localized("APP_SIGNING_INPUT_VIEW_CONTROLLER_SECTION_TITLE_MINIMUM_APP_VERSION"): + let forceMinimumVersion = TweakLibraryViewCell() + forceMinimumVersion.selectionStyle = .none + forceMinimumVersion.configureSegmentedControl( + with: mainOptions.mainOptions.forceMinimumVersionString, + selectedIndex: 0 + ) + forceMinimumVersion.segmentedControl.addTarget(self, action: #selector(forceMinimumVersionDidChange(_:)), for: .valueChanged) + + return forceMinimumVersion + default: + break + } + + if indexPath.section == 2 { + let toggleOption = toggleOptions[indexPath.row] + cell.textLabel?.text = toggleOption.title + let toggleSwitch = UISwitch() + toggleSwitch.isOn = toggleOption.binding + toggleSwitch.tag = indexPath.row + toggleSwitch.addTarget(self, action: #selector(toggleOptionsSwitches(_:)), for: .valueChanged) + cell.accessoryView = toggleSwitch + } + + return cell + + } +} + +extension SigningsAdvancedViewController { + @objc private func forceLightDarkAppearenceDidChange(_ sender: UISegmentedControl) { + signingDataWrapper.signingOptions.forceLightDarkAppearence = + mainOptions.mainOptions.forceLightDarkAppearenceString[sender.selectedSegmentIndex] + } + + @objc private func forceMinimumVersionDidChange(_ sender: UISegmentedControl) { + signingDataWrapper.signingOptions.forceMinimumVersion = + mainOptions.mainOptions.forceMinimumVersionString[sender.selectedSegmentIndex] + } + + @objc func toggleOptionsSwitches(_ sender: UISwitch) { + switch sender.tag { + case 0: + signingDataWrapper.signingOptions.removePlugins = sender.isOn + case 1: + signingDataWrapper.signingOptions.forceFileSharing = sender.isOn + case 2: + signingDataWrapper.signingOptions.removeSupportedDevices = sender.isOn + case 3: + signingDataWrapper.signingOptions.removeURLScheme = sender.isOn + case 4: + signingDataWrapper.signingOptions.forceProMotion = sender.isOn + case 5: + signingDataWrapper.signingOptions.forceForceFullScreen = sender.isOn + case 6: + signingDataWrapper.signingOptions.forceiTunesFileSharing = sender.isOn + case 7: + signingDataWrapper.signingOptions.forceTryToLocalize = sender.isOn + case 8: + signingDataWrapper.signingOptions.removeProvisioningFile = sender.isOn + case 9: + signingDataWrapper.signingOptions.removeWatchPlaceHolder = sender.isOn + default: + break + } + } +} diff --git a/iOS/Views/Apps/Signing/AppSigningDylibViewController.swift b/iOS/Views/Signing/SigningViewController/SigningsDylibViewController.swift similarity index 89% rename from iOS/Views/Apps/Signing/AppSigningDylibViewController.swift rename to iOS/Views/Signing/SigningViewController/SigningsDylibViewController.swift index 1cdd322..5ae961c 100644 --- a/iOS/Views/Apps/Signing/AppSigningDylibViewController.swift +++ b/iOS/Views/Signing/SigningViewController/SigningsDylibViewController.swift @@ -7,19 +7,20 @@ import UIKit -class AppSigningDylibViewController: UITableViewController { - var appSigningViewController: AppSigningViewController +class SigningsDylibViewController: UITableViewController { var applicationPath: URL var groupedDylibs: [String: [String]] = [:] var dylibSections: [String] = ["@rpath", "@executable_path", "/usr/lib", "/System/Library", "Other"] var dylibstoremove: [String] = [] { didSet { - self.appSigningViewController.removeInjectPaths = self.dylibstoremove + self.mainOptions.mainOptions.removeInjectPaths = self.dylibstoremove } } + + var mainOptions: SigningMainDataWrapper - init(appSigningViewController: AppSigningViewController, app: URL) { - self.appSigningViewController = appSigningViewController + init(mainOptions: SigningMainDataWrapper, app: URL) { + self.mainOptions = mainOptions self.applicationPath = app super.init(style: .insetGrouped) @@ -41,7 +42,7 @@ class AppSigningDylibViewController: UITableViewController { super.viewDidLoad() setupViews() setupNavigation() - self.dylibstoremove = self.appSigningViewController.removeInjectPaths + self.dylibstoremove = self.mainOptions.mainOptions.removeInjectPaths } diff --git a/iOS/Views/Signing/SigningViewController/SigningsInputViewController.swift b/iOS/Views/Signing/SigningViewController/SigningsInputViewController.swift new file mode 100644 index 0000000..f60c51a --- /dev/null +++ b/iOS/Views/Signing/SigningViewController/SigningsInputViewController.swift @@ -0,0 +1,90 @@ +// +// SigningsInputViewController.swift +// feather +// +// Created by samara on 8/15/24. +// Copyright (c) 2024 Samara M (khcrysalis) +// + +import Foundation +import UIKit + +class SigningsInputViewController: UITableViewController { + var v: SigningsViewController + var initialValue: String + var valueToSaveTo: Int + private var changedValue: String? + + private lazy var textField: UITextField = { + let textField = UITextField(frame: .zero) + textField.translatesAutoresizingMaskIntoConstraints = false + textField.addTarget(self, action: #selector(textDidChange), for: .editingChanged) + return textField + }() + + init(v: SigningsViewController, initialValue: String, valueToSaveTo: Int) { + self.v = v + self.initialValue = initialValue + self.valueToSaveTo = valueToSaveTo + super.init(style: .insetGrouped) + } + + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } + + override func viewDidLoad() { + super.viewDidLoad() + navigationItem.largeTitleDisplayMode = .never + self.title = initialValue.capitalized + + let saveButton = UIBarButtonItem(title: String.localized("SAVE"), style: .done, target: self, action: #selector(saveButton)) + saveButton.isEnabled = false + navigationItem.rightBarButtonItem = saveButton + + tableView.register(UITableViewCell.self, forCellReuseIdentifier: "InputCell") + } + + @objc func saveButton() { + switch valueToSaveTo { + case 1: + v.mainOptions.mainOptions.name = changedValue + case 2: + v.mainOptions.mainOptions.bundleId = changedValue + case 3: + v.mainOptions.mainOptions.version = changedValue + default: + break + } + + self.navigationController?.popViewController(animated: true) + } + + @objc private func textDidChange() { + navigationItem.rightBarButtonItem?.isEnabled = !(textField.text?.isEmpty ?? true) + changedValue = textField.text + } + + override func numberOfSections(in tableView: UITableView) -> Int { return 1 } + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 1 } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "InputCell", for: indexPath) + switch indexPath.section { + case 0: + textField.text = initialValue + textField.placeholder = initialValue + + if textField.superview == nil { + cell.contentView.addSubview(textField) + NSLayoutConstraint.activate([ + textField.centerYAnchor.constraint(equalTo: cell.contentView.centerYAnchor), + textField.leadingAnchor.constraint(equalTo: cell.contentView.layoutMarginsGuide.leadingAnchor), + textField.trailingAnchor.constraint(equalTo: cell.contentView.layoutMarginsGuide.trailingAnchor) + ]) + } + + cell.selectionStyle = .none + default: break + } + return cell + } +} diff --git a/iOS/Views/Apps/Signing/AppSigningTweakViewController.swift b/iOS/Views/Signing/SigningViewController/SigningsTweakViewController.swift similarity index 88% rename from iOS/Views/Apps/Signing/AppSigningTweakViewController.swift rename to iOS/Views/Signing/SigningViewController/SigningsTweakViewController.swift index 40156e4..ec27168 100644 --- a/iOS/Views/Apps/Signing/AppSigningTweakViewController.swift +++ b/iOS/Views/Signing/SigningViewController/SigningsTweakViewController.swift @@ -10,19 +10,21 @@ import Foundation import UIKit import UniformTypeIdentifiers -class AppSigningTweakViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout { - var appSigningViewController: AppSigningViewController - var tweaksToInject: [URL] = [] { +class SigningsTweakViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout { + var tweaksToInject: [String] = [] { didSet { UIView.animate(withDuration: 0.3) { - self.appSigningViewController.toInject = self.tweaksToInject + self.signingDataWrapper.signingOptions.toInject = self.tweaksToInject self.collectionView.reloadData() } } } - init(appSigningViewController: AppSigningViewController) { - self.appSigningViewController = appSigningViewController + var signingDataWrapper: SigningDataWrapper + + init(signingDataWrapper: SigningDataWrapper) { + self.signingDataWrapper = signingDataWrapper + let layout = UICollectionViewFlowLayout() layout.scrollDirection = .vertical layout.minimumLineSpacing = 16 @@ -39,7 +41,7 @@ class AppSigningTweakViewController: UICollectionViewController, UICollectionVie navigationItem.rightBarButtonItem = UIBarButtonItem(image: UIImage(systemName: "plus"), style: .plain, target: self, action: #selector(openDocuments)) collectionView.backgroundColor = .systemBackground - self.tweaksToInject = self.appSigningViewController.toInject + self.tweaksToInject = self.signingDataWrapper.signingOptions.toInject } @objc func openDocuments() { @@ -51,7 +53,7 @@ class AppSigningTweakViewController: UICollectionViewController, UICollectionVie } } -extension AppSigningTweakViewController { +extension SigningsTweakViewController { override func numberOfSections(in collectionView: UICollectionView) -> Int { return 1 } @@ -77,7 +79,7 @@ extension AppSigningTweakViewController { override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ProductCollectionViewCell.reuseIdentifier, for: indexPath) as! ProductCollectionViewCell let tweak = tweaksToInject[indexPath.item] - cell.titleLabel.text = "\(tweak.lastPathComponent)" + cell.titleLabel.text = "\(URL(string: tweak)!.lastPathComponent)" return cell } @@ -93,7 +95,7 @@ extension AppSigningTweakViewController { } } -extension AppSigningTweakViewController: UIDocumentPickerDelegate { +extension SigningsTweakViewController: UIDocumentPickerDelegate { func importFile() { self.presentDocumentPicker(fileExtension: [ UTType(filenameExtension: "deb")!, @@ -113,8 +115,8 @@ extension AppSigningTweakViewController: UIDocumentPickerDelegate { Debug.shared.log(message: "\(selectedFileURL)") - if !tweaksToInject.contains(selectedFileURL) { - self.tweaksToInject.append(selectedFileURL) + if !tweaksToInject.contains(where: { $0 == selectedFileURL.absoluteString }) { + tweaksToInject.append(selectedFileURL.absoluteString) } collectionView.reloadData() diff --git a/iOS/Views/Signing/SigningViewController/SigningsViewController+Import-AppIcon.swift b/iOS/Views/Signing/SigningViewController/SigningsViewController+Import-AppIcon.swift new file mode 100644 index 0000000..2ede9a3 --- /dev/null +++ b/iOS/Views/Signing/SigningViewController/SigningsViewController+Import-AppIcon.swift @@ -0,0 +1,82 @@ +// +// SigningsViewController+Import-AppIcon.swift +// feather +// +// Created by samara on 27.10.2024. +// + +import UIKit +import UniformTypeIdentifiers + +extension SigningsViewController: UIDocumentPickerDelegate & UIImagePickerControllerDelegate, UINavigationControllerDelegate { + func importAppIconFile() { + let actionSheet = UIAlertController(title: "Select App Icon", message: nil, preferredStyle: .actionSheet) + + let documentPickerAction = UIAlertAction(title: "Choose from Files", style: .default) { [weak self] _ in + self?.presentDocumentPicker(fileExtension: [UTType.image]) + } + + let photoLibraryAction = UIAlertAction(title: "Choose from Photos", style: .default) { [weak self] _ in + self?.presentPhotoLibrary(mediaTypes: ["public.image"]) + } + + let cancelAction = UIAlertAction(title: String.localized("CANCEL"), style: .cancel, handler: nil) + + actionSheet.addAction(documentPickerAction) + actionSheet.addAction(photoLibraryAction) + actionSheet.addAction(cancelAction) + + if let popoverController = actionSheet.popoverPresentationController { + popoverController.sourceView = self.view + popoverController.sourceRect = CGRect(x: self.view.bounds.midX, y: self.view.bounds.midY, width: 0, height: 0) + popoverController.permittedArrowDirections = [] + } + + self.present(actionSheet, animated: true, completion: nil) + } + + // MARK: - Documents + + func presentDocumentPicker(fileExtension: [UTType]) { + let documentPicker = UIDocumentPickerViewController(forOpeningContentTypes: fileExtension, asCopy: true) + documentPicker.delegate = self + documentPicker.allowsMultipleSelection = false + present(documentPicker, animated: true, completion: nil) + } + + func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) { + guard let selectedFileURL = urls.first else { return } + let meow = CoreDataManager.shared.loadImage(from: selectedFileURL) + mainOptions.mainOptions.iconURL = meow?.resizeToSquare() + Debug.shared.log(message: "\(selectedFileURL)") + self.tableView.reloadData() + } + + func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) { + controller.dismiss(animated: true, completion: nil) + } + + // MARK: - Library + + func presentPhotoLibrary(mediaTypes: [String]) { + let imagePicker = UIImagePickerController() + imagePicker.delegate = self + imagePicker.sourceType = .photoLibrary + imagePicker.mediaTypes = mediaTypes + self.present(imagePicker, animated: true, completion: nil) + } + + func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { + picker.dismiss(animated: true, completion: nil) + + guard let selectedImage = info[.originalImage] as? UIImage else { return } + + mainOptions.mainOptions.iconURL = selectedImage.resizeToSquare() + self.tableView.reloadData() + } + + func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { + picker.dismiss(animated: true, completion: nil) + } +} + diff --git a/iOS/Views/Signing/SigningViewController/SigningsViewController.swift b/iOS/Views/Signing/SigningViewController/SigningsViewController.swift new file mode 100644 index 0000000..16332cf --- /dev/null +++ b/iOS/Views/Signing/SigningViewController/SigningsViewController.swift @@ -0,0 +1,318 @@ +// +// SigningsViewController.swift +// feather +// +// Created by samara on 26.10.2024. +// + +import UIKit +import CoreData + +struct BundleOptions { + var name: String? + var bundleId: String? + var version: String? +} + +class SigningsViewController: UIViewController { + + var tableData = [ + [ + "AppIcon", + String.localized("APPS_INFORMATION_TITLE_NAME"), + String.localized("APPS_INFORMATION_TITLE_IDENTIFIER"), + String.localized("APPS_INFORMATION_TITLE_VERSION"), + ], + [ + "Signing", +// "Change Certificate", + ], + [ + "Add Tweaks", + "Modify Dylibs", + ], + [ "Properties" ], + ] + + var sectionTitles = [ + String.localized("APP_SIGNING_VIEW_CONTROLLER_CELL_TITLE_CUSTOMIZATION"), + String.localized("APP_SIGNING_VIEW_CONTROLLER_CELL_TITLE_SIGNING"), + String.localized("APP_SIGNING_VIEW_CONTROLLER_CELL_TITLE_ADVANCED"), + "", + ] + + private var application: NSManagedObject? + private var appsViewController: LibraryViewController? + + var signingDataWrapper: SigningDataWrapper + var mainOptions = SigningMainDataWrapper(mainOptions: MainSigningOptions()) + + var bundle: BundleOptions? + + var tableView: UITableView! + private var variableBlurView: UIVariableBlurView? + private var largeButton = ActivityIndicatorButton() + private var iconCell = IconImageViewCell() + + init(signingDataWrapper: SigningDataWrapper, application: NSManagedObject, appsViewController: LibraryViewController) { + self.signingDataWrapper = signingDataWrapper + self.application = application + self.appsViewController = appsViewController + super.init(nibName: nil, bundle: nil) + + if let name = application.value(forKey: "name") as? String, + let bundleId = application.value(forKey: "bundleidentifier") as? String, + let version = application.value(forKey: "version") as? String { + self.bundle = BundleOptions(name: name, bundleId: bundleId, version: version) + } + + if let hasGotCert = CoreDataManager.shared.getCurrentCertificate() { self.mainOptions.mainOptions.certificate = hasGotCert } + if let uuid = application.value(forKey: "uuid") as? String { self.mainOptions.mainOptions.uuid = uuid } + + if signingDataWrapper.signingOptions.ppqCheckProtection && + mainOptions.mainOptions.certificate?.certData?.pPQCheck == true { + mainOptions.mainOptions.bundleId = (bundle?.bundleId)!+"."+Preferences.pPQCheckString + } + + if let currentBundleId = bundle?.bundleId, + let newBundleId = signingDataWrapper.signingOptions.bundleIdConfig[currentBundleId] { + mainOptions.mainOptions.bundleId = newBundleId + } + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + setupNavigation() + setupViews() + setupToolbar() + #if !targetEnvironment(simulator) + certAlert() + #endif + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + self.tableView.reloadData() + } + + fileprivate func setupNavigation() { + let logoImageView = UIImageView(image: UIImage(named: "feather_glyph")) + logoImageView.contentMode = .scaleAspectFit + navigationItem.titleView = logoImageView + self.navigationController?.navigationBar.prefersLargeTitles = false + + self.isModalInPresentation = true + self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: String.localized("DISMISS"), style: .done, target: self, action: #selector(closeSheet)) + } + + fileprivate func setupViews() { + self.tableView = UITableView(frame: .zero, style: .insetGrouped) + self.tableView.translatesAutoresizingMaskIntoConstraints = false + self.tableView.dataSource = self + self.tableView.delegate = self + self.tableView.showsHorizontalScrollIndicator = false + self.tableView.showsVerticalScrollIndicator = false + self.tableView.contentInset.bottom = 70 + + self.view.addSubview(tableView) + self.tableView.constraintCompletely(to: view) + } + + fileprivate func setupToolbar() { + largeButton.translatesAutoresizingMaskIntoConstraints = false + largeButton.addTarget(self, action: #selector(startSign), for: .touchUpInside) + + let gradientMask = VariableBlurViewConstants.defaultGradientMask + variableBlurView = UIVariableBlurView(frame: .zero) + variableBlurView?.gradientMask = gradientMask + variableBlurView?.transform = CGAffineTransform(rotationAngle: CGFloat.pi) + variableBlurView?.translatesAutoresizingMaskIntoConstraints = false + + view.addSubview(variableBlurView!) + view.addSubview(largeButton) + + var height = 80.0 + if UIDevice.current.userInterfaceIdiom == .pad { height = 65.0 } + + NSLayoutConstraint.activate([ + variableBlurView!.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor), + variableBlurView!.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor), + variableBlurView!.bottomAnchor.constraint(equalTo: view.bottomAnchor), + variableBlurView!.heightAnchor.constraint(equalToConstant: height), + + largeButton.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 16), + largeButton.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -16), + largeButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -17), + largeButton.heightAnchor.constraint(equalToConstant: 50) + ]) + + variableBlurView?.layer.zPosition = 3 + largeButton.layer.zPosition = 4 + } + + fileprivate func certAlert() { + if (mainOptions.mainOptions.certificate == nil) { + DispatchQueue.main.async { + let alert = UIAlertController( + title: String.localized("APP_SIGNING_VIEW_CONTROLLER_NO_CERTS_ALERT_TITLE"), + message: String.localized("APP_SIGNING_VIEW_CONTROLLER_NO_CERTS_ALERT_DESCRIPTION"), + preferredStyle: .alert + ) + alert.addAction(UIAlertAction(title: String.localized("LAME"), style: .default) { _ in + self.dismiss(animated: true) + } + ) + self.present(alert, animated: true, completion: nil) + } + } + } + + @objc func closeSheet() { + dismiss(animated: true, completion: nil) + } + + @objc func startSign() { + self.navigationItem.leftBarButtonItem = nil + largeButton.showLoadingIndicator() + signInitialApp( + bundle: bundle!, + mainOptions: mainOptions, + signingOptions: signingDataWrapper, + appPath:getFilesForDownloadedApps(app: application as! DownloadedApps, getuuidonly: false)) + { result in + switch result { + case .success(let (signedPath, signedApp)): + self.appsViewController?.fetchSources() + self.appsViewController?.tableView.reloadData() + Debug.shared.log(message: signedPath.path) + if self.signingDataWrapper.signingOptions.installAfterSigned { + self.appsViewController?.startInstallProcess(meow: signedApp, filePath: signedPath.path) + } + + case .failure(let error): + Debug.shared.log(message: "Signing failed: \(error.localizedDescription)", type: .error) + } + + self.dismiss(animated: true) + } + } +} + +extension SigningsViewController: UITableViewDataSource, UITableViewDelegate { + func numberOfSections(in tableView: UITableView) -> Int { return sectionTitles.count } + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return tableData[section].count } + func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { return sectionTitles[section] } + func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { return sectionTitles[section].isEmpty ? 0 : 40 } + + func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { + let title = sectionTitles[section] + let headerView = InsetGroupedSectionHeader(title: title) + return headerView + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let reuseIdentifier = "Cell" + let cell = UITableViewCell(style: .value1, reuseIdentifier: reuseIdentifier) + cell.accessoryType = .none + cell.selectionStyle = .gray + + let cellText = tableData[indexPath.section][indexPath.row] + cell.textLabel?.text = cellText + + switch cellText { + case "AppIcon": + let cell = iconCell + + if (mainOptions.mainOptions.iconURL != nil) { + cell.configure(with: mainOptions.mainOptions.iconURL) + } else { + cell.configure(with: CoreDataManager.shared.loadImage(from: getIconURL(for: application as! DownloadedApps))) + } + + cell.accessoryType = .disclosureIndicator + return cell + case String.localized("APPS_INFORMATION_TITLE_NAME"): + cell.textLabel?.text = String.localized("APPS_INFORMATION_TITLE_NAME") + cell.detailTextLabel?.text = mainOptions.mainOptions.name ?? bundle?.name + cell.accessoryType = .disclosureIndicator + case String.localized("APPS_INFORMATION_TITLE_IDENTIFIER"): + cell.textLabel?.text = String.localized("APPS_INFORMATION_TITLE_IDENTIFIER") + cell.detailTextLabel?.text = mainOptions.mainOptions.bundleId ?? bundle?.bundleId + cell.accessoryType = .disclosureIndicator + case String.localized("APPS_INFORMATION_TITLE_VERSION"): + cell.detailTextLabel?.text = mainOptions.mainOptions.version ?? bundle?.version + cell.accessoryType = .disclosureIndicator + case "Signing": + if let hasGotCert = mainOptions.mainOptions.certificate { + let cell = CertificateViewTableViewCell() + cell.configure(with: hasGotCert, isSelected: false) + cell.selectionStyle = .none + return cell + } else { + cell.textLabel?.text = String.localized("SETTINGS_VIEW_CONTROLLER_CELL_CURRENT_CERTIFICATE_NOSELECTED") + cell.textLabel?.textColor = .secondaryLabel + cell.selectionStyle = .none + } + case "Change Certificate", "Add Tweaks", "Modify Dylibs", "Properties": + cell.accessoryType = .disclosureIndicator + default: + break + } + + + return cell + } + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + let itemTapped = tableData[indexPath.section][indexPath.row] + switch itemTapped { + case "AppIcon": + importAppIconFile() + case String.localized("APPS_INFORMATION_TITLE_NAME"): + let l = SigningsInputViewController(v: self, initialValue: (mainOptions.mainOptions.name ?? bundle?.name)!, valueToSaveTo: indexPath.row) + navigationController?.pushViewController(l, animated: true) + case String.localized("APPS_INFORMATION_TITLE_IDENTIFIER"): + let l = SigningsInputViewController(v: self, initialValue: (mainOptions.mainOptions.bundleId ?? bundle?.bundleId)!, valueToSaveTo: indexPath.row) + navigationController?.pushViewController(l, animated: true) + case String.localized("APPS_INFORMATION_TITLE_VERSION"): + let l = SigningsInputViewController(v: self, initialValue: (mainOptions.mainOptions.version ?? bundle?.version)!, valueToSaveTo: indexPath.row) + navigationController?.pushViewController(l, animated: true) + case "Add Tweaks": + let l = SigningsTweakViewController(signingDataWrapper: signingDataWrapper) + navigationController?.pushViewController(l, animated: true) + case "Modify Dylibs": + let l = SigningsDylibViewController(mainOptions: mainOptions, app: getFilesForDownloadedApps(app: application as! DownloadedApps, getuuidonly: false)) + navigationController?.pushViewController(l, animated: true) + case "Properties": + let l = SigningsAdvancedViewController(signingDataWrapper: signingDataWrapper, mainOptions: mainOptions) + navigationController?.pushViewController(l, animated: true) + default: + break + } + + tableView.deselectRow(at: indexPath, animated: true) + } +} + +// MARK: - this sucks + +extension SigningsViewController { + + private func getFilesForDownloadedApps(app: DownloadedApps, getuuidonly: Bool) -> URL { + return CoreDataManager.shared.getFilesForDownloadedApps(for: app, getuuidonly: getuuidonly) + } + + private func getIconURL(for app: DownloadedApps) -> URL? { + guard let iconURLString = app.value(forKey: "iconURL") as? String, + let iconURL = URL(string: iconURLString) else { + return nil + } + + let filesURL = getFilesForDownloadedApps(app: app, getuuidonly: false) + return filesURL.appendingPathComponent(iconURL.lastPathComponent) + } +} diff --git a/iOS/Views/Sources/SearchResultsTableViewController.swift b/iOS/Views/Sources/SearchResultsTableViewController.swift index 0c05447..9667f6c 100644 --- a/iOS/Views/Sources/SearchResultsTableViewController.swift +++ b/iOS/Views/Sources/SearchResultsTableViewController.swift @@ -133,7 +133,7 @@ class SearchResultsTableViewController: UIViewController, UISearchResultsUpdatin if let url = sourceURLMapping[source] { let savc = SourceAppViewController() savc.name = source.name - savc.uri = url + savc.uri = [url] savc.highlightAppName = app?.name savc.highlightBundleID = app?.bundleIdentifier diff --git a/iOS/Views/Sources/SourceAppViews/SourceAppViewController.swift b/iOS/Views/Sources/SourceAppViews/SourceAppViewController.swift index 54e752f..fdb7e6f 100644 --- a/iOS/Views/Sources/SourceAppViews/SourceAppViewController.swift +++ b/iOS/Views/Sources/SourceAppViews/SourceAppViewController.swift @@ -11,15 +11,20 @@ import Nuke import AlertKit import CoreData +enum SortOption: String, Codable { + case `default` + case name + case date +} + class SourceAppViewController: UITableViewController { var apps: [StoreAppsData] = [] var oApps: [StoreAppsData] = [] var filteredApps: [StoreAppsData] = [] var name: String? { didSet { self.title = name } } - private var selectedFilterOption: AppFilterOption = .default - var uri: URL! + var uri: [URL]! var highlightAppName: String? var highlightBundleID: String? @@ -27,6 +32,8 @@ class SourceAppViewController: UITableViewController { var highlightDeveloperName: String? var highlightDescription: String? + var sortActionsGroup: UIMenu? + private let sourceGET = SourceGET() public var searchController: UISearchController! @@ -58,48 +65,48 @@ class SourceAppViewController: UITableViewController { } private func updateFilterMenu() { - let defaultAction = UIAction(title: String.localized("SOURCES_CELLS_ACTIONS_FILTER_BY_DEFAULT"), image: UIImage()) { [weak self] _ in - self?.applyFilter(.default) - } - - let nameAction = UIAction(title: String.localized("SOURCES_CELLS_ACTIONS_FILTER_BY_NAME"), image: UIImage(systemName: "textformat")) { [weak self] _ in - self?.applyFilter(.name) - } - - let dateAction = UIAction(title: String.localized("SOURCES_CELLS_ACTIONS_FILTER_BY_DATE"), image: UIImage(systemName: "calendar")) { [weak self] _ in - self?.applyFilter(.date) - } - - // Update actions with checkmarks - defaultAction.state = selectedFilterOption == .default ? .on : .off - nameAction.state = selectedFilterOption == .name ? .on : .off - dateAction.state = selectedFilterOption == .date ? .on : .off - - let filterMenu = UIMenu(title: String.localized("SOURCES_CELLS_ACTIONS_FILTER_TITLE"), children: [defaultAction, nameAction, dateAction]) + let filterMenu = UIMenu(title: String.localized("SOURCES_CELLS_ACTIONS_FILTER_TITLE"), children: createSubSortMenu()) let filterButton = UIBarButtonItem(image: UIImage(systemName: "line.3.horizontal.decrease"), menu: filterMenu) self.navigationItem.rightBarButtonItem = filterButton } - enum AppFilterOption { - case `default` - case name - case date + private func createSubSortMenu() -> [UIMenuElement] { + let sortByDAction = createSortAction(title: "Default", sortOption: .default) + let sortByNameAction = createSortAction(title: "Name", sortOption: .name) + let sortBySizeAction = createSortAction(title: "Date", sortOption: .date) + + let meowMenu = UIMenu(title: "", + image: nil, + identifier: nil, + options: .displayInline, + children: [sortByDAction, sortByNameAction, sortBySizeAction]) + + return [meowMenu] } + - func applyFilter(_ option: AppFilterOption) { - selectedFilterOption = option + func applyFilter() { + let sortOption = Preferences.currentSortOption + let ascending = Preferences.currentSortOptionAscending - switch option { + switch sortOption { case .default: - apps = oApps + apps = ascending ? oApps : oApps.reversed() case .name: - apps = apps.sorted { $0.name < $1.name } + apps = apps.sorted { ascending ? $0.name < $1.name : $0.name > $1.name } case .date: apps = apps.sorted { - guard let date0 = $0.versionDate else { return false } - guard let date1 = $1.versionDate else { return true } - return date0 < date1 + let date0 = $0.versions?.first?.date ?? $0.versionDate + let date1 = $1.versions?.first?.date ?? $1.versionDate + + if date0 == nil && date1 == nil { return ascending } + + guard let date0 = date0, let date1 = date1 else { + return date0 != nil + } + + return ascending ? date0 > date1 : date0 < date1 } } @@ -109,48 +116,95 @@ class SourceAppViewController: UITableViewController { updateFilterMenu() } + + + private func createSortAction(title: String, sortOption: SortOption) -> UIAction { + return UIAction(title: title, + image: arrowImage(for: sortOption), + identifier: UIAction.Identifier("sort\(title)"), + state: Preferences.currentSortOption == sortOption ? .on : .off, + handler: { [weak self] _ in + guard let self = self else { return } + + if Preferences.currentSortOption == sortOption { + Preferences.currentSortOptionAscending.toggle() + } else { + Preferences.currentSortOption = sortOption + updateSortOrderImage(for: sortOption) + } + applyFilter() + }) + } + + /// Arrowimages for Sort options + func arrowImage(for sortOption: SortOption) -> UIImage? { + let isAscending = Preferences.currentSortOptionAscending + let imageName = isAscending ? "chevron.up" : "chevron.down" + return sortOption == Preferences.currentSortOption ? UIImage(systemName: imageName) : nil + } + + func updateSortOrderImage(for sortOption: SortOption) { + guard let sortActionsGroup = sortActionsGroup else { + print("sortActionsGroup is nil") + return + } + + for case let action as UIAction in sortActionsGroup.children { + if action.identifier == UIAction.Identifier("sort\(sortOption)") { + if let image = arrowImage(for: sortOption) { + action.image = image + } + } + } + } fileprivate func setupNavigation() { self.navigationItem.largeTitleDisplayMode = .never } private func loadAppsData() { - guard let uri = uri else { return } - sourceGET.downloadURL(from: uri) { [weak self] result in - switch result { - case .success(let (data, _)): - let parseResult = self?.sourceGET.parse(data: data) - switch parseResult { - case .success(let sourceData): - DispatchQueue.main.async { - - self?.apps = sourceData.apps - self?.oApps = sourceData.apps - - if let fil = self?.shouldFilter() { - self?.apps = [fil].compactMap { $0 } - } - - UIView.transition(with: self!.tableView, duration: 0.3, options: .transitionCrossDissolve, animations: { - self!.activityIndicator.stopAnimating() - self?.navigationItem.titleView = nil - if (self?.highlightAppName == nil) { - self?.updateFilterMenu() - } - self?.tableView.reloadData() - }, completion: nil) + guard let urls = uri else { return } + let dispatchGroup = DispatchGroup() + var allApps: [StoreAppsData] = [] + + for uri in urls { + dispatchGroup.enter() + + sourceGET.downloadURL(from: uri) { [weak self] result in + switch result { + case .success(let (data, _)): + if let parseResult = self?.sourceGET.parse(data: data), case .success(let sourceData) = parseResult { + allApps.append(contentsOf: sourceData.apps) } case .failure(let error): - Debug.shared.log(message: "Error parsing data: \(error.localizedDescription)") - case .none: - break + Debug.shared.log(message: "Error fetching data from \(uri): \(error.localizedDescription)") } - case .failure(let error): - Debug.shared.log(message: "Error fetching data: \(error.localizedDescription)") + dispatchGroup.leave() } } + + dispatchGroup.notify(queue: .main) { [weak self] in + self?.apps = allApps + self?.oApps = allApps + + if let fil = self?.shouldFilter() { + self?.apps = [fil].compactMap { $0 } + } else { + self?.applyFilter() + } + + UIView.transition(with: self!.tableView, duration: 0.3, options: .transitionCrossDissolve, animations: { + self!.activityIndicator.stopAnimating() + self?.navigationItem.titleView = nil + if self?.highlightAppName == nil { + self?.updateFilterMenu() + } + self?.tableView.reloadData() + }, completion: nil) + } } + private func shouldFilter() -> StoreAppsData? { guard diff --git a/iOS/Views/Sources/SourcesViewController.swift b/iOS/Views/Sources/SourcesViewController.swift index 494c200..3948311 100644 --- a/iOS/Views/Sources/SourcesViewController.swift +++ b/iOS/Views/Sources/SourcesViewController.swift @@ -58,96 +58,141 @@ class SourcesViewController: UITableViewController { // MARK: - Tabelview extension SourcesViewController { - override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return sources?.count ?? 0 } + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + switch section { + case 0: + return 1 + case 1: + return sources?.count ?? 0 + default: + return 0 + } + } + + override func numberOfSections(in tableView: UITableView) -> Int { return 2 } override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { return 70 } + override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { - //String.localized("SOURCES_VIEW_CONTROLLER_NUMBER_OF_SOURCES", arguments: (sources?.count ?? 0)) - let headerWithButton = GroupedSectionHeader( - title: String.localized("SOURCES_VIEW_CONTROLLER_REPOSITORIES"), - subtitle: String.localized(sources?.count ?? 0 > 1 ? "SOURCES_VIEW_CONTROLLER_NUMBER_OF_SOURCES_PLURAL" : "SOURCES_VIEW_CONTROLLER_NUMBER_OF_SOURCES", arguments: "\(sources?.count ?? 0)"), - buttonTitle: String.localized("SOURCES_VIEW_CONTROLLER_ADD_SOURCES"), buttonAction: { - - let transferPreview = RepoViewController(sources: self.sources) - - let hostingController = UIHostingController(rootView: transferPreview) - hostingController.modalPresentationStyle = .formSheet - - if let presentationController = hostingController.presentationController as? UISheetPresentationController { - presentationController.detents = [.medium()] - } - - DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { - self.present(hostingController, animated: true) - } - - -// self.sourcesAddButtonTapped() - }) - return headerWithButton + if section == 1 { + let headerWithButton = GroupedSectionHeader( + title: String.localized("SOURCES_VIEW_CONTROLLER_REPOSITORIES"), + subtitle: String.localized(sources?.count ?? 0 > 1 ? "SOURCES_VIEW_CONTROLLER_NUMBER_OF_SOURCES_PLURAL" : "SOURCES_VIEW_CONTROLLER_NUMBER_OF_SOURCES", arguments: "\(sources?.count ?? 0)"), + buttonTitle: String.localized("SOURCES_VIEW_CONTROLLER_ADD_SOURCES"), buttonAction: { + let transferPreview = RepoViewController(sources: self.sources) + + let hostingController = UIHostingController(rootView: transferPreview) + hostingController.modalPresentationStyle = .formSheet + + if let presentationController = hostingController.presentationController as? UISheetPresentationController { + presentationController.detents = [.medium()] + } + + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { + self.present(hostingController, animated: true) + } + }) + + return headerWithButton + } else { + return nil + } } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "Cell") let source = sources![indexPath.row] - - cell.textLabel?.text = source.name ?? String.localized("UNKNOWN") + cell.textLabel?.font = UIFont.boldSystemFont(ofSize: 17) cell.detailTextLabel?.font = UIFont.systemFont(ofSize: 13) - cell.detailTextLabel?.text = source.sourceURL?.absoluteString cell.detailTextLabel?.textColor = .secondaryLabel cell.accessoryType = .disclosureIndicator cell.backgroundColor = .clear - if let thumbnailURL = source.iconURL { - SectionIcons.loadSectionImageFromURL(from: thumbnailURL, for: cell, at: indexPath, in: tableView) - } else { - SectionIcons.sectionImage(to: cell, with: UIImage(named: "unknown")!) + switch indexPath.section { + case 0: + cell.textLabel?.text = "All Repositories" + cell.detailTextLabel?.text = "See all apps from your sources" + SectionIcons.sectionIcon(to: cell, with: "books.vertical.fill", backgroundColor: Preferences.appTintColor.uiColor.withAlphaComponent(0.7)) + return cell + case 1: + cell.textLabel?.text = source.name ?? String.localized("UNKNOWN") + cell.detailTextLabel?.text = source.sourceURL?.absoluteString + + if let thumbnailURL = source.iconURL { + SectionIcons.loadSectionImageFromURL(from: thumbnailURL, for: cell, at: indexPath, in: tableView) + } else { + SectionIcons.sectionImage(to: cell, with: UIImage(named: "unknown")!) + } + return cell + default: + break } + return cell } override func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? { - let source = sources![indexPath.row] - - let configuration = UIContextMenuConfiguration(identifier: nil, actionProvider: { _ in - return UIMenu(title: "", image: nil, identifier: nil, options: [], children: [ - UIAction(title: String.localized("COPY"), image: UIImage(systemName: "doc.on.clipboard"), handler: {_ in - UIPasteboard.general.string = source.sourceURL?.absoluteString - }) - ]) - }) - return configuration + if indexPath.section == 1 { + let source = sources![indexPath.row] + + let configuration = UIContextMenuConfiguration(identifier: nil, actionProvider: { _ in + return UIMenu(title: "", image: nil, identifier: nil, options: [], children: [ + UIAction(title: String.localized("COPY"), image: UIImage(systemName: "doc.on.clipboard"), handler: {_ in + UIPasteboard.general.string = source.sourceURL?.absoluteString + }) + ]) + }) + return configuration + } else { + return nil + } } override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { - let deleteAction = UIContextualAction(style: .destructive, title: String.localized("DELETE")) { (action, view, completionHandler) in - let sourceToRm = self.sources![indexPath.row] - CoreDataManager.shared.context.delete(sourceToRm) - do { - try CoreDataManager.shared.context.save() - self.sources?.remove(at: indexPath.row) - self.searchResultsTableViewController.sources = self.sources ?? [] - self.tableView.reloadSections(IndexSet(integer: 0), with: .automatic) - } catch { - Debug.shared.log(message: "trailingSwipeActionsConfigurationForRowAt.deleteAction", type: .error) + if indexPath.section == 1 { + let deleteAction = UIContextualAction(style: .destructive, title: String.localized("DELETE")) { (action, view, completionHandler) in + let sourceToRm = self.sources![indexPath.row] + CoreDataManager.shared.context.delete(sourceToRm) + do { + try CoreDataManager.shared.context.save() + self.sources?.remove(at: indexPath.row) + self.searchResultsTableViewController.sources = self.sources ?? [] + self.tableView.reloadSections(IndexSet(integer: 1), with: .automatic) + } catch { + Debug.shared.log(message: "trailingSwipeActionsConfigurationForRowAt.deleteAction", type: .error) + } + completionHandler(true) } - completionHandler(true) + deleteAction.backgroundColor = UIColor.red + + let configuration = UISwipeActionsConfiguration(actions: [deleteAction]) + configuration.performsFirstActionWithFullSwipe = true + + return configuration + } else { + return nil } - deleteAction.backgroundColor = UIColor.red - - let configuration = UISwipeActionsConfiguration(actions: [deleteAction]) - configuration.performsFirstActionWithFullSwipe = true - - return configuration } override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { guard let sourcerow = sources?[indexPath.row] else { return } - let savc = SourceAppViewController() - savc.name = sourcerow.name - savc.uri = sourcerow.sourceURL - navigationController?.pushViewController(savc, animated: true) + + switch indexPath.section { + case 0: + let savc = SourceAppViewController() + savc.name = "All Repositories" + savc.uri = sources!.compactMap { $0.sourceURL } + navigationController?.pushViewController(savc, animated: true) + case 1: + let savc = SourceAppViewController() + savc.name = sourcerow.name + savc.uri = [sourcerow.sourceURL!] + navigationController?.pushViewController(savc, animated: true) + default: + break + } + tableView.deselectRow(at: indexPath, animated: true) } } @@ -158,7 +203,7 @@ extension SourcesViewController { sources = CoreDataManager.shared.getAZSources() searchResultsTableViewController.sources = sources ?? [] DispatchQueue.main.async { - self.tableView.reloadSections(IndexSet(integer: 0), with: .automatic) + self.tableView.reloadSections(IndexSet(integer: 1), with: .automatic) } } }