mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-09 06:43:25 +01:00
* Change error from Swift.Error to NSError
* Adds ResultOperation.localizedFailure
* Finish Riley's monster commit
3b38d725d7
May the Gods have mercy on my soul.
* Fix format strings I broke
* Include "Enable JIT" errors in Error Log
* Fix minimuxer status checking
* [skip ci] Update the no wifi message to include VPN
* Opens Error Log when tapping ToastView
* Fixes Error Log context menu covering cell content
* Fixes Error Log context menu appearing while scrolling
* Fixes incorrect Search FAQ URL
* Fix Error Log showing UIAlertController on iOS 14+
* Fix Error Log not showing UIAlertController on iOS <=13
* Fix wrong color in AuthenticationViewController
* Fix typo
* Fixes logging non-AltServerErrors as AltServerError.underlyingError
* Limits quitting other AltStore/SideStore processes to database migrations
* Skips logging cancelled errors
* Replaces StoreApp.latestVersion with latestSupportedVersion + latestAvailableVersion
We now store the latest supported version as a relationship on StoreApp, rather than the latest available version. This allows us to reference the latest supported version in predicates and sort descriptors.
However, we kept the underlying Core Data property name the same to avoid extra migration.
* Conforms OperatingSystemVersion to Comparable
* Parses AppVersion.minOSVersion/maxOSVersion from source JSON
* Supports non-NSManagedObjects for @Managed properties
This allows us to use @Managed with properties that may or may not be NSManagedObjects at runtime (e.g. protocols). If they are, Managed will keep strong reference to context like before.
* Supports optional @Managed properties
* Conforms AppVersion to AppProtocol
* Verifies min/max OS version before downloading app + asks user to download older app version if necessary
* Improves error message when file does not exist at AppVersion.downloadURL
* Removes unnecessary StoreApp convenience properties
* Removes unnecessary StoreApp convenience properties as well as fix other issues
* Remove Settings bundle, add SwiftUI view instead
Fix refresh all shortcut intent
* Update AuthenticationOperation.swift
Signed-off-by: June Park <rjp2030@outlook.com>
* Fix build issues given by develop
* Add availability check to fix CI build(?)
* If it's gonna be that way...
---------
Signed-off-by: June Park <rjp2030@outlook.com>
Co-authored-by: nythepegasus <nythepegasus84@gmail.com>
Co-authored-by: Riley Testut <riley@rileytestut.com>
Co-authored-by: ny <me@nythepegas.us>
289 lines
14 KiB
Swift
289 lines
14 KiB
Swift
//
|
|
// OperationError.swift
|
|
// AltStore
|
|
//
|
|
// Created by Riley Testut on 6/7/19.
|
|
// Copyright © 2019 Riley Testut. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
import AltSign
|
|
import AltStoreCore
|
|
import minimuxer
|
|
|
|
extension OperationError
|
|
{
|
|
enum Code: Int, ALTErrorCode, CaseIterable {
|
|
typealias Error = OperationError
|
|
|
|
// General
|
|
case unknown = 1000
|
|
case unknownResult
|
|
case cancelled
|
|
case timedOut
|
|
case unableToConnectSideJIT
|
|
case unableToRespondSideJITDevice
|
|
case wrongSideJITIP
|
|
case SideJITIssue // (error: String)
|
|
case refreshsidejit
|
|
case notAuthenticated
|
|
case appNotFound
|
|
case unknownUDID
|
|
case invalidApp
|
|
case invalidParameters
|
|
case maximumAppIDLimitReached//((application: ALTApplication, requiredAppIDs: Int, availableAppIDs: Int, nextExpirationDate: Date)
|
|
case noSources
|
|
case openAppFailed//(name: String)
|
|
case missingAppGroup
|
|
|
|
// Connection
|
|
case noWiFi = 1200
|
|
case tooNewError
|
|
case anisetteV1Error//(message: String)
|
|
case provisioningError//(result: String, message: String?)
|
|
case anisetteV3Error//(message: String)
|
|
|
|
case cacheClearError//(errors: [String])
|
|
}
|
|
|
|
static let unknownResult: OperationError = .init(code: .unknownResult)
|
|
static let cancelled: OperationError = .init(code: .cancelled)
|
|
static let timedOut: OperationError = .init(code: .timedOut)
|
|
static let unableToConnectSideJIT: OperationError = .init(code: .unableToConnectSideJIT)
|
|
static let unableToRespondSideJITDevice: OperationError = .init(code: .unableToRespondSideJITDevice)
|
|
static let wrongSideJITIP: OperationError = .init(code: .wrongSideJITIP)
|
|
static let notAuthenticated: OperationError = .init(code: .notAuthenticated)
|
|
static let unknownUDID: OperationError = .init(code: .unknownUDID)
|
|
static let invalidApp: OperationError = .init(code: .invalidApp)
|
|
static let invalidParameters: OperationError = .init(code: .invalidParameters)
|
|
static let noSources: OperationError = .init(code: .noSources)
|
|
static let missingAppGroup: OperationError = .init(code: .missingAppGroup)
|
|
|
|
static let noWiFi: OperationError = .init(code: .noWiFi)
|
|
static let tooNewError: OperationError = .init(code: .tooNewError)
|
|
static let provisioningError: OperationError = .init(code: .provisioningError)
|
|
static let anisetteV1Error: OperationError = .init(code: .anisetteV1Error)
|
|
static let anisetteV3Error: OperationError = .init(code: .anisetteV3Error)
|
|
|
|
static let cacheClearError: OperationError = .init(code: .cacheClearError)
|
|
|
|
static func unknown(failureReason: String? = nil, file: String = #fileID, line: UInt = #line) -> OperationError {
|
|
OperationError(code: .unknown, failureReason: failureReason, sourceFile: file, sourceLine: line)
|
|
}
|
|
|
|
static func appNotFound(name: String?) -> OperationError {
|
|
OperationError(code: .appNotFound, appName: name)
|
|
}
|
|
|
|
static func openAppFailed(name: String?) -> OperationError {
|
|
OperationError(code: .openAppFailed, appName: name)
|
|
}
|
|
|
|
static func SideJITIssue(error: String?) -> OperationError {
|
|
var o = OperationError(code: .SideJITIssue)
|
|
o.errorFailure = error
|
|
return o
|
|
}
|
|
|
|
static func maximumAppIDLimitReached(appName: String, requiredAppIDs: Int, availableAppIDs: Int, expirationDate: Date) -> OperationError {
|
|
OperationError(code: .maximumAppIDLimitReached, appName: appName, requiredAppIDs: requiredAppIDs, availableAppIDs: availableAppIDs, expirationDate: expirationDate)
|
|
}
|
|
|
|
static func provisioningError(result: String, message: String?) -> OperationError {
|
|
var o = OperationError(code: .provisioningError, failureReason: result)
|
|
o.errorTitle = message
|
|
return o
|
|
}
|
|
|
|
static func cacheClearError(errors: [String]) -> OperationError {
|
|
OperationError(code: .cacheClearError, failureReason: errors.joined(separator: "\n"))
|
|
}
|
|
|
|
static func anisetteV1Error(message: String) -> OperationError {
|
|
OperationError(code: .anisetteV1Error, failureReason: message)
|
|
}
|
|
|
|
static func anisetteV3Error(message: String) -> OperationError {
|
|
OperationError(code: .anisetteV3Error, failureReason: message)
|
|
}
|
|
|
|
}
|
|
|
|
|
|
struct OperationError: ALTLocalizedError {
|
|
|
|
let code: Code
|
|
|
|
var errorTitle: String?
|
|
var errorFailure: String?
|
|
|
|
var appName: String?
|
|
|
|
var requiredAppIDs: Int?
|
|
var availableAppIDs: Int?
|
|
var expirationDate: Date?
|
|
|
|
var sourceFile: String?
|
|
var sourceLine: UInt?
|
|
|
|
private var _failureReason: String?
|
|
|
|
private init(code: Code, failureReason: String? = nil,
|
|
appName: String? = nil, requiredAppIDs: Int? = nil, availableAppIDs: Int? = nil,
|
|
expirationDate: Date? = nil, sourceFile: String? = nil, sourceLine: UInt? = nil){
|
|
self.code = code
|
|
self._failureReason = failureReason
|
|
|
|
self.appName = appName
|
|
self.requiredAppIDs = requiredAppIDs
|
|
self.availableAppIDs = availableAppIDs
|
|
self.expirationDate = expirationDate
|
|
self.sourceFile = sourceFile
|
|
self.sourceLine = sourceLine
|
|
}
|
|
|
|
var errorFailureReason: String {
|
|
switch self.code {
|
|
case .unknown:
|
|
var failureReason = self._failureReason ?? NSLocalizedString("An unknown error occurred.", comment: "")
|
|
guard let sourceFile, let sourceLine else { return failureReason }
|
|
failureReason += " (\(sourceFile) line \(sourceLine)"
|
|
return failureReason
|
|
case .unknownResult: return NSLocalizedString("The operation returned an unknown result.", comment: "")
|
|
case .cancelled: return NSLocalizedString("The operation was cancelled.", comment: "")
|
|
case .timedOut: return NSLocalizedString("The operation timed out.", comment: "")
|
|
case .notAuthenticated: return NSLocalizedString("You are not signed in.", comment: "")
|
|
case .unknownUDID: return NSLocalizedString("SideStore could not determine this device's UDID.", comment: "")
|
|
case .invalidApp: return NSLocalizedString("The app is in an invalid format.", comment: "")
|
|
case .invalidParameters: return NSLocalizedString("Invalid parameters.", comment: "")
|
|
case .maximumAppIDLimitReached: return NSLocalizedString("Cannot register more than 10 App IDs within a 7 day period.", comment: "")
|
|
case .noSources: return NSLocalizedString("There are no SideStore sources.", comment: "")
|
|
case .missingAppGroup: return NSLocalizedString("SideStore's shared app group could not be accessed.", comment: "")
|
|
case .appNotFound:
|
|
let appName = self.appName ?? NSLocalizedString("The app", comment: "")
|
|
return String(format: NSLocalizedString("%@ could not be found.", comment: ""), appName)
|
|
case .openAppFailed:
|
|
let appName = self.appName ?? NSLocalizedString("The app", comment: "")
|
|
return String(format: NSLocalizedString("SideStore was denied permission to launch %@.", comment: ""), appName)
|
|
case .noWiFi: return NSLocalizedString("You do not appear to be connected to WiFi and/or the WireGuard VPN!\nSideStore will never be able to install or refresh applications without WiFi and the WireGuard VPN.", comment: "")
|
|
case .tooNewError: return NSLocalizedString("iOS 17 has changed how JIT is enabled therefore SideStore cannot enable it at this time, sorry for any inconvenience.\nWe will let everyone know once we have a solution!", comment: "")
|
|
case .unableToConnectSideJIT: return NSLocalizedString("Unable to connect to SideJITServer Please check that you are on the Same Wi-Fi and your Firewall has been set correctly", comment: "")
|
|
case .unableToRespondSideJITDevice: return NSLocalizedString("SideJITServer is unable to connect to your iDevice Please make sure you have paired your Device by doing 'SideJITServer -y' or try Refreshing SideJITServer from Settings", comment: "")
|
|
case .wrongSideJITIP: return NSLocalizedString("Incorrect SideJITServer IP Please make sure that you are on the Samw Wifi as SideJITServer", comment: "")
|
|
case .refreshsidejit: return NSLocalizedString("Unable to find App Please try Refreshing SideJITServer from Settings", comment: "")
|
|
case .anisetteV1Error: return NSLocalizedString("An error occurred when getting anisette data from a V1 server: %@. Try using another anisette server.", comment: "")
|
|
case .provisioningError: return NSLocalizedString("An error occurred when provisioning: %@ %@. Please try again. If the issue persists, report it on GitHub Issues!", comment: "")
|
|
case .anisetteV3Error: return NSLocalizedString("An error occurred when getting anisette data from a V3 server: %@. Please try again. If the issue persists, report it on GitHub Issues!", comment: "")
|
|
case .cacheClearError: return NSLocalizedString("An error occurred while clearing cache: %@", comment: "")
|
|
case .SideJITIssue: return NSLocalizedString("An error occurred while using SideJIT: %@", comment: "")
|
|
}
|
|
}
|
|
|
|
var recoverySuggestion: String? {
|
|
switch self.code
|
|
{
|
|
case .noWiFi: return NSLocalizedString("Make sure the VPN is toggled on and you are connected to any WiFi network!", comment: "")
|
|
case .maximumAppIDLimitReached:
|
|
let baseMessage = NSLocalizedString("Delete sideloaded apps to free up App ID slots.", comment: "")
|
|
guard let appName, let requiredAppIDs, let availableAppIDs, let expirationDate else { return baseMessage }
|
|
var message: String
|
|
|
|
if requiredAppIDs > 1
|
|
{
|
|
let availableText: String
|
|
|
|
switch availableAppIDs
|
|
{
|
|
case 0: availableText = NSLocalizedString("none are available", comment: "")
|
|
case 1: availableText = NSLocalizedString("only 1 is available", comment: "")
|
|
default: availableText = String(format: NSLocalizedString("only %@ are available", comment: ""), NSNumber(value: availableAppIDs))
|
|
}
|
|
|
|
let prefixMessage = String(format: NSLocalizedString("%@ requires %@ App IDs, but %@.", comment: ""), appName, NSNumber(value: requiredAppIDs), availableText)
|
|
message = prefixMessage + " " + baseMessage + "\n\n"
|
|
}
|
|
else
|
|
{
|
|
message = baseMessage + " "
|
|
}
|
|
|
|
let dateComponents = Calendar.current.dateComponents([.day, .hour, .minute], from: Date(), to: expirationDate)
|
|
let dateFormatter = DateComponentsFormatter()
|
|
dateFormatter.maximumUnitCount = 1
|
|
dateFormatter.unitsStyle = .full
|
|
|
|
let remainingTime = dateFormatter.string(from: dateComponents)!
|
|
|
|
message += String(format: NSLocalizedString("You can register another App ID in %@.", comment: ""), remainingTime)
|
|
|
|
return message
|
|
|
|
default: return nil
|
|
}
|
|
}
|
|
}
|
|
|
|
extension MinimuxerError: LocalizedError {
|
|
public var failureReason: String? {
|
|
switch self {
|
|
case .NoDevice:
|
|
return NSLocalizedString("Cannot fetch the device from the muxer", comment: "")
|
|
case .NoConnection:
|
|
return NSLocalizedString("Unable to connect to the device, make sure Wireguard is enabled and you're connected to WiFi", comment: "")
|
|
case .PairingFile:
|
|
return NSLocalizedString("Invalid pairing file. Your pairing file either didn't have a UDID, or it wasn't a valid plist. Please use jitterbugpair to generate it", comment: "")
|
|
|
|
case .CreateDebug:
|
|
return self.createService(name: "debug")
|
|
case .LookupApps:
|
|
return self.getFromDevice(name: "installed apps")
|
|
case .FindApp:
|
|
return self.getFromDevice(name: "path to the app")
|
|
case .BundlePath:
|
|
return self.getFromDevice(name: "bundle path")
|
|
case .MaxPacket:
|
|
return self.setArgument(name: "max packet")
|
|
case .WorkingDirectory:
|
|
return self.setArgument(name: "working directory")
|
|
case .Argv:
|
|
return self.setArgument(name: "argv")
|
|
case .LaunchSuccess:
|
|
return self.getFromDevice(name: "launch success")
|
|
case .Detach:
|
|
return NSLocalizedString("Unable to detach from the app's process", comment: "")
|
|
case .Attach:
|
|
return NSLocalizedString("Unable to attach to the app's process", comment: "")
|
|
|
|
case .CreateInstproxy:
|
|
return self.createService(name: "instproxy")
|
|
case .CreateAfc:
|
|
return self.createService(name: "AFC")
|
|
case .RwAfc:
|
|
return NSLocalizedString("AFC was unable to manage files on the device", comment: "")
|
|
case .InstallApp(let message):
|
|
return NSLocalizedString("Unable to install the app: \(message.toString())", comment: "")
|
|
case .UninstallApp:
|
|
return NSLocalizedString("Unable to uninstall the app", comment: "")
|
|
|
|
case .CreateMisagent:
|
|
return self.createService(name: "misagent")
|
|
case .ProfileInstall:
|
|
return NSLocalizedString("Unable to manage profiles on the device", comment: "")
|
|
case .ProfileRemove:
|
|
return NSLocalizedString("Unable to manage profiles on the device", comment: "")
|
|
}
|
|
}
|
|
|
|
fileprivate func createService(name: String) -> String {
|
|
return String(format: NSLocalizedString("Cannot start a %@ server on the device.", comment: ""), name)
|
|
}
|
|
|
|
fileprivate func getFromDevice(name: String) -> String {
|
|
return String(format: NSLocalizedString("Cannot fetch %@ from the device.", comment: ""), name)
|
|
}
|
|
|
|
fileprivate func setArgument(name: String) -> String {
|
|
return String(format: NSLocalizedString("Cannot set %@ on the device.", comment: ""), name)
|
|
}
|
|
}
|