From 16c71be7f94a018c26235f2b475c3ba028c3351b Mon Sep 17 00:00:00 2001 From: Riley Testut Date: Thu, 11 May 2023 15:03:29 -0500 Subject: [PATCH] [AltStoreCore] Flattens optional values when @Managed/@AsyncManaged.wrappedValue is also optional --- AltStore.xcodeproj/project.pbxproj | 4 ++++ AltStoreCore/Protocols/OptionalProtocol.swift | 19 +++++++++++++++++++ AltStoreCore/Types/AsyncManaged.swift | 13 ++++++++++++- AltStoreCore/Types/Managed.swift | 12 +++++++++++- 4 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 AltStoreCore/Protocols/OptionalProtocol.swift diff --git a/AltStore.xcodeproj/project.pbxproj b/AltStore.xcodeproj/project.pbxproj index 1beb9d70..2985be6a 100644 --- a/AltStore.xcodeproj/project.pbxproj +++ b/AltStore.xcodeproj/project.pbxproj @@ -357,6 +357,7 @@ D561B2ED28EF5A4F006752E4 /* AltSign-Dynamic in Embed Frameworks */ = {isa = PBXBuildFile; productRef = D561B2EA28EF5A4F006752E4 /* AltSign-Dynamic */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; D5708417292448DA00D42D34 /* OperatingSystemVersion+Comparable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5708416292448DA00D42D34 /* OperatingSystemVersion+Comparable.swift */; }; D570841A2924680D00D42D34 /* OperatingSystemVersion+Comparable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5708416292448DA00D42D34 /* OperatingSystemVersion+Comparable.swift */; }; + D5728CA72A0D79D30014E73C /* OptionalProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5728CA62A0D79D30014E73C /* OptionalProtocol.swift */; }; D57968CB29CB99EF00539069 /* VibrantButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57968CA29CB99EF00539069 /* VibrantButton.swift */; }; D57DF638271E32F000677701 /* PatchApp.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D57DF637271E32F000677701 /* PatchApp.storyboard */; }; D57DF63F271E51E400677701 /* ALTAppPatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = D57DF63E271E51E400677701 /* ALTAppPatcher.m */; }; @@ -912,6 +913,7 @@ D54DED1328CBC44B008B27A0 /* ErrorLogTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorLogTableViewCell.swift; sourceTree = ""; }; D55E163528776CB000A627A1 /* ComplicationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComplicationView.swift; sourceTree = ""; }; D5708416292448DA00D42D34 /* OperatingSystemVersion+Comparable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OperatingSystemVersion+Comparable.swift"; sourceTree = ""; }; + D5728CA62A0D79D30014E73C /* OptionalProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OptionalProtocol.swift; sourceTree = ""; }; D57968CA29CB99EF00539069 /* VibrantButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VibrantButton.swift; sourceTree = ""; }; D57DF637271E32F000677701 /* PatchApp.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = PatchApp.storyboard; sourceTree = ""; }; D57DF63D271E51E400677701 /* ALTAppPatcher.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ALTAppPatcher.h; sourceTree = ""; }; @@ -1439,6 +1441,7 @@ children = ( BF66EE9B2501AEC1007EE018 /* AppProtocol.swift */, BF66EE9C2501AEC1007EE018 /* Fetchable.swift */, + D5728CA62A0D79D30014E73C /* OptionalProtocol.swift */, ); path = Protocols; sourceTree = ""; @@ -2636,6 +2639,7 @@ BF66EEEB2501AED0007EE018 /* UIApplication+AppExtension.swift in Sources */, D5F48B4829CCF21B002B52A4 /* AltStore+Async.swift in Sources */, BF66EED92501AECA007EE018 /* Team.swift in Sources */, + D5728CA72A0D79D30014E73C /* OptionalProtocol.swift in Sources */, BF66EED12501AECA007EE018 /* AltStore3ToAltStore4.xcmappingmodel in Sources */, D5F48B4C29CD0C48002B52A4 /* AsyncManaged.swift in Sources */, BFAECC5C2501B0A400528F27 /* CFNotificationName+AltStore.m in Sources */, diff --git a/AltStoreCore/Protocols/OptionalProtocol.swift b/AltStoreCore/Protocols/OptionalProtocol.swift new file mode 100644 index 00000000..c9b8fb68 --- /dev/null +++ b/AltStoreCore/Protocols/OptionalProtocol.swift @@ -0,0 +1,19 @@ +// +// OptionalProtocol.swift +// AltStoreCore +// +// Created by Riley Testut on 5/11/23. +// Copyright © 2023 Riley Testut. All rights reserved. +// + +import Foundation + +// Public so we can use as generic constraint. +public protocol OptionalProtocol +{ + associatedtype Wrapped + + static var none: Self { get } +} + +extension Optional: OptionalProtocol {} diff --git a/AltStoreCore/Types/AsyncManaged.swift b/AltStoreCore/Types/AsyncManaged.swift index a97341f2..6622a794 100644 --- a/AltStoreCore/Types/AsyncManaged.swift +++ b/AltStoreCore/Types/AsyncManaged.swift @@ -71,6 +71,7 @@ public extension AsyncManaged /// @dynamicMemberLookup public extension AsyncManaged { + // Non-optional values subscript(dynamicMember keyPath: KeyPath) -> T { get async { let result = await self.perform { $0[keyPath: keyPath] } @@ -78,7 +79,7 @@ public extension AsyncManaged } } - // Optionals + // Optional wrapped value subscript(dynamicMember keyPath: KeyPath) -> T? where ManagedObject == Optional { get async { guard let wrappedValue else { return nil } @@ -87,4 +88,14 @@ public extension AsyncManaged return result } } + + // Optional wrapped value + optional property (flattened) + subscript(dynamicMember keyPath: KeyPath) -> T where ManagedObject == Optional, T: OptionalProtocol { + get async { + guard let wrappedValue else { return T.none } + + let result = await self.perform { _ in wrappedValue[keyPath: keyPath] } + return result + } + } } diff --git a/AltStoreCore/Types/Managed.swift b/AltStoreCore/Types/Managed.swift index d591e5ae..64ae4e42 100644 --- a/AltStoreCore/Types/Managed.swift +++ b/AltStoreCore/Types/Managed.swift @@ -80,13 +80,14 @@ public extension Managed /// @dynamicMemberLookup public extension Managed { + // Non-optional values subscript(dynamicMember keyPath: KeyPath) -> T { let result = self.perform { $0[keyPath: keyPath] } return result } - // Optionals + // Optional wrapped value subscript(dynamicMember keyPath: KeyPath) -> T? where ManagedObject == Optional { guard let wrappedValue else { return nil } @@ -94,4 +95,13 @@ public extension Managed let result = self.perform { _ in wrappedValue[keyPath: keyPath] } return result } + + // Optional wrapped value + optional property (flattened) + subscript(dynamicMember keyPath: KeyPath) -> T where ManagedObject == Optional, T: OptionalProtocol + { + guard let wrappedValue else { return T.none } + + let result = self.perform { _ in wrappedValue[keyPath: keyPath] } + return result + } }