diff --git a/AltStore.xcodeproj/project.pbxproj b/AltStore.xcodeproj/project.pbxproj index 02f0ca01..1beb9d70 100644 --- a/AltStore.xcodeproj/project.pbxproj +++ b/AltStore.xcodeproj/project.pbxproj @@ -341,6 +341,8 @@ BFF435D8255CBDAB00DD724F /* ALTApplication+AltStoreApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF435D7255CBDAB00DD724F /* ALTApplication+AltStoreApp.swift */; }; BFF615A82510042B00484D3B /* AltStoreCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF66EE7E2501AE50007EE018 /* AltStoreCore.framework */; }; BFF7C9342578492100E55F36 /* ALTAnisetteData.m in Sources */ = {isa = PBXBuildFile; fileRef = BFB49AA823834CF900D542D9 /* ALTAnisetteData.m */; }; + D5189C012A01BC6800F44625 /* UserInfoValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5189C002A01BC6800F44625 /* UserInfoValue.swift */; }; + D5189C022A01BC6800F44625 /* UserInfoValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5189C002A01BC6800F44625 /* UserInfoValue.swift */; }; D519AD46292D665B004B12F9 /* Managed.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFB39B5B252BC10E00D1BE50 /* Managed.swift */; }; D51AD27E29356B7B00967AAA /* ALTWrappedError.h in Headers */ = {isa = PBXBuildFile; fileRef = D51AD27C29356B7B00967AAA /* ALTWrappedError.h */; settings = {ATTRIBUTES = (Public, ); }; }; D51AD27F29356B7B00967AAA /* ALTWrappedError.m in Sources */ = {isa = PBXBuildFile; fileRef = D51AD27D29356B7B00967AAA /* ALTWrappedError.m */; }; @@ -897,6 +899,7 @@ D52C08ED28AEC37A006C4AE5 /* AppVersion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppVersion.swift; sourceTree = ""; }; D52E988928D002D30032BE6B /* AltStore 11.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "AltStore 11.xcdatamodel"; sourceTree = ""; }; C9EEAA842DA87A88A870053B /* Pods_AltStore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AltStore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D5189C002A01BC6800F44625 /* UserInfoValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserInfoValue.swift; sourceTree = ""; }; D51AD27C29356B7B00967AAA /* ALTWrappedError.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ALTWrappedError.h; sourceTree = ""; }; D51AD27D29356B7B00967AAA /* ALTWrappedError.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ALTWrappedError.m; sourceTree = ""; }; D52C08ED28AEC37A006C4AE5 /* AppVersion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppVersion.swift; sourceTree = ""; }; @@ -1936,6 +1939,7 @@ isa = PBXGroup; children = ( D5DB145828F9DC1000A8F606 /* ALTLocalizedError.swift */, + D5189C002A01BC6800F44625 /* UserInfoValue.swift */, D51AD27C29356B7B00967AAA /* ALTWrappedError.h */, D51AD27D29356B7B00967AAA /* ALTWrappedError.m */, ); @@ -2472,6 +2476,7 @@ BFECAC8224FD950B0077C41F /* ServerProtocol.swift in Sources */, BFECAC8124FD950B0077C41F /* ALTServerError+Conveniences.swift in Sources */, BFECAC7F24FD950B0077C41F /* CodableError.swift in Sources */, + D5189C012A01BC6800F44625 /* UserInfoValue.swift in Sources */, D51AD28029356B8000967AAA /* ALTWrappedError.m in Sources */, BFECAC8624FD950B0077C41F /* Result+Conveniences.swift in Sources */, BF265D1925F843A000080DC9 /* NSError+AltStore.swift in Sources */, @@ -2590,6 +2595,7 @@ BF66EED32501AECA007EE018 /* AltStore2ToAltStore3.xcmappingmodel in Sources */, BF66EEA52501AEC5007EE018 /* Benefit.swift in Sources */, BF66EED22501AECA007EE018 /* AltStore4ToAltStore5.xcmappingmodel in Sources */, + D5189C022A01BC6800F44625 /* UserInfoValue.swift in Sources */, BFAECC5B2501B0A400528F27 /* Bundle+AltStore.swift in Sources */, BF66EECD2501AECA007EE018 /* StoreAppPolicy.swift in Sources */, BF66EEE82501AED0007EE018 /* UserDefaults+AltStore.swift in Sources */, diff --git a/Shared/Errors/ALTLocalizedError.swift b/Shared/Errors/ALTLocalizedError.swift index 1d3aeaef..4dc54466 100644 --- a/Shared/Errors/ALTLocalizedError.swift +++ b/Shared/Errors/ALTLocalizedError.swift @@ -69,13 +69,15 @@ public extension ALTLocalizedError } var errorUserInfo: [String : Any] { - let userInfo: [String: Any?] = [ + var userInfo: [String: Any?] = [ NSLocalizedFailureErrorKey: self.errorFailure, ALTLocalizedTitleErrorKey: self.errorTitle, // ALTSourceFileErrorKey: self.sourceFile, // TODO: Figure out where these come from // ALTSourceLineErrorKey: self.sourceLine, ] - + + userInfo.merge(self.userInfoValues) { (_, new) in new } + return userInfo.compactMapValues { $0 } } @@ -125,6 +127,21 @@ public extension ALTLocalizedError } } +private extension ALTLocalizedError +{ + var userInfoValues: [(String, Any)] { + let userInfoValues = Mirror(reflecting: self).children.compactMap { (label, value) -> (String, Any)? in + guard let userInfoValue = value as? any UserInfoValueProtocol, + let key: any StringProtocol = userInfoValue.key ?? label?.dropFirst() // Remove leading underscore + else { return nil } + + return (String(key), userInfoValue.wrappedValue) + } + + return userInfoValues + } +} + public struct DefaultLocalizedError: ALTLocalizedError { public let code: Code diff --git a/Shared/Errors/UserInfoValue.swift b/Shared/Errors/UserInfoValue.swift new file mode 100644 index 00000000..0e3758d4 --- /dev/null +++ b/Shared/Errors/UserInfoValue.swift @@ -0,0 +1,38 @@ +// +// UserInfoValue.swift +// AltStore +// +// Created by Riley Testut on 5/2/23. +// Copyright © 2023 Riley Testut. All rights reserved. +// + +import Foundation + +protocol UserInfoValueProtocol +{ + associatedtype Value + + var key: String? { get } + var wrappedValue: Value { get } +} + +@propertyWrapper +public struct UserInfoValue: UserInfoValueProtocol +{ + public let key: String? + public var wrappedValue: Value + + // Necessary for memberwise initializers to work as expected + // https://github.com/apple/swift-evolution/blob/main/proposals/0258-property-wrappers.md#memberwise-initializers + public init(wrappedValue: Value) + { + self.wrappedValue = wrappedValue + self.key = nil + } + + public init(wrappedValue: Value, key: String) + { + self.wrappedValue = wrappedValue + self.key = key + } +}