diff --git a/Shared/Data/CoreData/CoreDataManager+Certificates.swift b/Shared/Data/CoreData/CoreDataManager+Certificates.swift index f370ce7..bb987da 100644 --- a/Shared/Data/CoreData/CoreDataManager+Certificates.swift +++ b/Shared/Data/CoreData/CoreDataManager+Certificates.swift @@ -513,9 +513,9 @@ extension CoreDataManager { } } - /// Clear the update state for a signed app + /// Clear the update state for a signed app (alternative implementation) /// - Parameter signedApp: The app to update - func clearUpdateState(for signedApp: SignedApps) throws { + func clearUpdateStateForCertificate(for signedApp: SignedApps) throws { let ctx = try context // Make sure we have the app in the right context @@ -526,7 +526,7 @@ extension CoreDataManager { domain: "CoreDataManager", code: 1016, userInfo: [NSLocalizedDescriptionKey: "App not found in context"] - ) + ) as Error } appInContext = fetchedApp } else { diff --git a/Shared/Management/ImageCache.swift b/Shared/Management/ImageCache.swift index 6ee933c..f7d24e1 100644 --- a/Shared/Management/ImageCache.swift +++ b/Shared/Management/ImageCache.swift @@ -12,6 +12,21 @@ final class ImageCache { /// Shared instance of the image cache static let shared = ImageCache() + + /// Save an image to the cache for a specific URL + /// - Parameters: + /// - image: The image to save + /// - url: The URL associated with the image + func saveImage(_ image: UIImage, for url: URL) { + // Save to memory cache + let key = url.absoluteString as NSString + memoryCache.setObject(image, forKey: key) + + // Save to disk in the background + diskQueue.async { [weak self] in + self?.saveImageToDisk(image: image, url: url) + } + } // MARK: - Cache Storage diff --git a/iOS/Views/Apps/LibraryViewController+IconFix.swift b/iOS/Views/Apps/LibraryViewController+IconFix.swift index 2f4b657..9e017db 100644 --- a/iOS/Views/Apps/LibraryViewController+IconFix.swift +++ b/iOS/Views/Apps/LibraryViewController+IconFix.swift @@ -85,8 +85,8 @@ extension LibraryViewController { let image = UIImage(contentsOfFile: imagePath.path) { setImageWithLEDEffect(cell: cell, image: image) - // Save to CoreData cache for future use - CoreDataManager.shared.saveImage(image, at: imagePath) + // Save to image cache for future use + ImageCache.shared.saveImage(image, for: imagePath) return } diff --git a/iOS/Views/Apps/LibraryViewController.swift b/iOS/Views/Apps/LibraryViewController.swift index c03dcf0..2fe1d7f 100644 --- a/iOS/Views/Apps/LibraryViewController.swift +++ b/iOS/Views/Apps/LibraryViewController.swift @@ -527,7 +527,7 @@ extension LibraryViewController { guard let self = self else { return } self.popupVC.dismiss(animated: true) do { - try CoreDataManager.shared.clearUpdateState(for: signedApp) + try CoreDataManager.shared.clearUpdateStateCompat(for: signedApp) self.tableView.reloadRows(at: [indexPath], with: .none) } catch { backdoor.Debug.shared.log(message: "Error clearing update state: \(error)", type: LogType.error) diff --git a/iOS/Views/Extra/PopupViewController+MenuFix.swift b/iOS/Views/Extra/PopupViewController+MenuFix.swift index ad9a0e6..7afe352 100644 --- a/iOS/Views/Extra/PopupViewController+MenuFix.swift +++ b/iOS/Views/Extra/PopupViewController+MenuFix.swift @@ -93,7 +93,10 @@ extension PopupViewController { // Prevent user from dismissing by dragging (forces use of buttons) if hasUpdate { - sheet.prefersModalPresentation = true + if #available(iOS 15.0, *) { + sheet.prefersGrabberVisible = false + sheet.detents = [.medium()] + } } } } @@ -282,7 +285,14 @@ extension PopupViewController { // Refresh sheet presentation if needed if let presentationController = presentationController as? UISheetPresentationController { // Force update the presentation controller's layout - presentationController.invalidateDetents() + if #available(iOS 16.0, *) { + presentationController.invalidateDetents() + } else { + // Fall back for iOS 15 + if let sheet = presentationController as? UISheetPresentationController { + sheet.detents = [.medium(), .large()] + } + } } // Ensure buttons are still correctly displayed @@ -296,7 +306,14 @@ extension PopupViewController { // Fix presentation issues that might occur when app returns to foreground if let presentationController = presentationController as? UISheetPresentationController { // Force update the presentation controller's layout - presentationController.invalidateDetents() + if #available(iOS 16.0, *) { + presentationController.invalidateDetents() + } else { + // Fall back for iOS 15 + if let sheet = presentationController as? UISheetPresentationController { + sheet.detents = [.medium(), .large()] + } + } // Ensure buttons are still correctly displayed view.setNeedsLayout() diff --git a/iOS/Views/Home/Core/DirectoryViewControllerExtensions.swift b/iOS/Views/Home/Core/DirectoryViewControllerExtensions.swift index 4d6d080..07c2055 100644 --- a/iOS/Views/Home/Core/DirectoryViewControllerExtensions.swift +++ b/iOS/Views/Home/Core/DirectoryViewControllerExtensions.swift @@ -48,7 +48,12 @@ extension DirectoryViewController { // Import file option let importAction = UIAlertAction(title: "Import File", style: .default) { [weak self] _ in guard let self = self else { return } - self.importFile() + if let homeVC = self as? HomeViewController { + homeVC.importFile() + } else { + // Handle the case where self is not a HomeViewController + Debug.shared.log(message: "importFile called on non-HomeViewController", type: .warning) + } } importAction.setValue(UIImage(systemName: "square.and.arrow.down"), forKey: "image") diff --git a/iOS/Views/Home/Core/HomeViewController+FileUploadFix.swift b/iOS/Views/Home/Core/HomeViewController+FileUploadFix.swift index 0569d12..be4df73 100644 --- a/iOS/Views/Home/Core/HomeViewController+FileUploadFix.swift +++ b/iOS/Views/Home/Core/HomeViewController+FileUploadFix.swift @@ -153,7 +153,7 @@ extension HomeViewController { /// Process a single imported file private func processImportedFile(url: URL) throws { // Get a unique filename that won't conflict with existing files - let fileName = getUniqueFileName(for: url.lastPathComponent) + let fileName = HomeViewController.getUniqueFileNameShared(for: url.lastPathComponent) let destinationURL = documentsDirectory.appendingPathComponent(fileName) Debug.shared.log(message: "Processing import: \(url.path) to \(destinationURL.path)", type: .info) @@ -274,22 +274,29 @@ extension HomeViewController { } } -/// LED indicator types if not already defined -fileprivate enum LEDIndicatorType { - case success - case error - case warning - case info - - var backgroundColor: UIColor { - switch self { - case .success: return UIColor.systemGreen.withAlphaComponent(0.8) - case .error: return UIColor.systemRed.withAlphaComponent(0.8) - case .warning: return UIColor.systemOrange.withAlphaComponent(0.8) - case .info: return UIColor.systemBlue.withAlphaComponent(0.8) +// Using the LED effect from UIView+LED extension instead +extension HomeViewController { + func showUploadStatusIndicator(type: UploadStatus) { + let color: UIColor + switch type { + case .success: color = UIColor.systemGreen + case .error: color = UIColor.systemRed + case .warning: color = UIColor.systemOrange + case .info: color = UIColor.systemBlue } + + // Apply LED effect using the existing extension + self.view.addLEDEffect(color: color, intensity: 0.8, spread: 5, animated: true) } + enum UploadStatus { + case success + case error + case warning + case info + } +} + var glowColor: UIColor { switch self { case .success: return .systemGreen diff --git a/iOS/Views/Home/Core/HomeViewController.swift b/iOS/Views/Home/Core/HomeViewController.swift index a461a2d..0050e62 100644 --- a/iOS/Views/Home/Core/HomeViewController.swift +++ b/iOS/Views/Home/Core/HomeViewController.swift @@ -331,7 +331,7 @@ class HomeViewController: UIViewController, UISearchResultsUpdating, UIDocumentP activityIndicator.startAnimating() // Generate a unique name if a file with the same name exists - let fileName = getUniqueFileName(for: url.lastPathComponent) + let fileName = HomeViewController.getUniqueFileNameShared(for: url.lastPathComponent) let destinationURL = documentsDirectory.appendingPathComponent(fileName) Debug.shared.log(message: "Importing file from \(url.path) to \(destinationURL.path)", type: .info) @@ -446,11 +446,17 @@ class HomeViewController: UIViewController, UISearchResultsUpdating, UIDocumentP /// Generates a unique filename if the original already exists /// - Parameter filename: The original filename /// - Returns: A unique filename - private func getUniqueFileName(for filename: String) -> String { - let fileURL = documentsDirectory.appendingPathComponent(filename) + // Changed to static method so it can be shared + static func getUniqueFileNameShared(for filename: String) -> String { + // Get documents directory since we're in a static context + guard let documentsDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first?.appendingPathComponent("files") else { + return filename + "_unique" + } + + let fileURL = documentsDir.appendingPathComponent(filename) // If the file doesn't exist, return the original name - if !fileManager.fileExists(atPath: fileURL.path) { + if !FileManager.default.fileExists(atPath: fileURL.path) { return filename } @@ -1203,7 +1209,7 @@ class HomeViewController: UIViewController, UISearchResultsUpdating, UIDocumentP // Check if destination already exists if self.fileManager.fileExists(atPath: destinationURL.path) { // Create a unique name if needed - let uniqueName = self.getUniqueFileName(for: destinationName) + let uniqueName = HomeViewController.getUniqueFileNameShared(for: destinationName) let uniqueURL = self.documentsDirectory.appendingPathComponent(uniqueName) // Create extraction directory diff --git a/iOS/Views/Signing/SigningData/SigningOptions.swift b/iOS/Views/Signing/SigningData/SigningOptions.swift index 4e2f55c..f48d7f5 100644 --- a/iOS/Views/Signing/SigningData/SigningOptions.swift +++ b/iOS/Views/Signing/SigningData/SigningOptions.swift @@ -57,8 +57,22 @@ struct SigningOptions: Codable { // Added missing properties var useOfflineCertificates: Bool = false - var customEntitlements: [String: Any]? = nil + + // Note: These properties need special handling for Codable conformance + private var _customEntitlements: [String: String]? = nil var additionalData: [String: String]? = nil + + // Use computed property for type that doesn't conform to Codable + var customEntitlements: [String: Any]? { + get { + return _customEntitlements as [String: Any]? + } + set { + if let newValue = newValue as? [String: String] { + _customEntitlements = newValue + } + } + } } extension UserDefaults { diff --git a/iOS/Views/Signing/SigningViewController/EntitlementsEditorViewController.swift b/iOS/Views/Signing/SigningViewController/EntitlementsEditorViewController.swift index 5cd4942..36fdd4d 100644 --- a/iOS/Views/Signing/SigningViewController/EntitlementsEditorViewController.swift +++ b/iOS/Views/Signing/SigningViewController/EntitlementsEditorViewController.swift @@ -468,17 +468,48 @@ extension Array { // MARK: - SigningDataWrapper Extension -extension SigningDataWrapper.SigningOptions { - /// Custom entitlements dictionary - var customEntitlements: [String: Any]? { - get { - return additionalData["customEntitlements"] as? [String: Any] +// Extension to provide customEntitlements access +extension SigningDataWrapper { + // Add an accessor to get to the SigningOptions + var signingOptionsWithEntitlements: SigningOptions { + return signingOptions + } +} + +// Helper extension to store entitlements +extension SigningOptions { + /// Helper to access entitlements through additionalData + func getEntitlementsFromAdditionalData() -> [String: Any]? { + guard let entitlementsJson = additionalData?["customEntitlements"] else { + return customEntitlements + } + + if let data = entitlementsJson.data(using: .utf8), + let dict = try? JSONSerialization.jsonObject(with: data) as? [String: Any] { + return dict + } + + return customEntitlements + } + + /// Helper to store entitlements in additionalData + mutating func setEntitlementsToAdditionalData(_ entitlements: [String: Any]?) { + if additionalData == nil { + additionalData = [:] } - set { - if additionalData == nil { - additionalData = [:] + // Convert to JSON string for storage + if let entitlements = entitlements { + do { + let data = try JSONSerialization.data(withJSONObject: entitlements, options: []) + if let jsonString = String(data: data, encoding: .utf8) { + additionalData?["customEntitlements"] = jsonString + } + } catch { + Debug.shared.log(message: "Failed to serialize entitlements: \(error)", type: .error) + } + } else { + additionalData?["customEntitlements"] = nil } - additionalData?["customEntitlements"] = newValue } } } diff --git a/iOS/Views/Terminal/TerminalViewController.swift b/iOS/Views/Terminal/TerminalViewController.swift index 4467088..1c2f462 100644 --- a/iOS/Views/Terminal/TerminalViewController.swift +++ b/iOS/Views/Terminal/TerminalViewController.swift @@ -863,11 +863,10 @@ extension TerminalService { /// Get the current session ID func getCurrentSessionId(completion: @escaping (String?) -> Void) { // Get current session ID from the service - // This is a placeholder method - implement according to your TerminalService - // It should return the active session ID or fetch it if needed + // Use the sessionId property directly - if let currentSessionId = self.currentSessionId { - completion(currentSessionId) + if let sessionId = self.sessionId { + completion(sessionId) return }