From 12ca34f40fe743312e2c7d286a24c70eddcc9619 Mon Sep 17 00:00:00 2001 From: Riley Testut Date: Tue, 4 Apr 2023 14:11:58 -0500 Subject: [PATCH] [AltStoreCore] Adds @AsyncManaged property wrapper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Same as @Managed, except it supports using Swift Concurrency to fetch values from its managedObject’s managedObjectContext. --- AltStore.xcodeproj/project.pbxproj | 6 +- AltStoreCore/Types/AsyncManaged.swift | 82 +++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 AltStoreCore/Types/AsyncManaged.swift diff --git a/AltStore.xcodeproj/project.pbxproj b/AltStore.xcodeproj/project.pbxproj index b4a85fae..a66964c4 100644 --- a/AltStore.xcodeproj/project.pbxproj +++ b/AltStore.xcodeproj/project.pbxproj @@ -341,10 +341,10 @@ 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 */; }; + 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 */; }; D51AD28029356B8000967AAA /* ALTWrappedError.m in Sources */ = {isa = PBXBuildFile; fileRef = D51AD27D29356B7B00967AAA /* ALTWrappedError.m */; }; - D519AD46292D665B004B12F9 /* Managed.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFB39B5B252BC10E00D1BE50 /* Managed.swift */; }; D52C08EE28AEC37A006C4AE5 /* AppVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = D52C08ED28AEC37A006C4AE5 /* AppVersion.swift */; }; D533E8B72727841800A9B5DD /* libAppleArchive.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = D533E8B62727841800A9B5DD /* libAppleArchive.tbd */; settings = {ATTRIBUTES = (Weak, ); }; }; D533E8BE2727BBF800A9B5DD /* libcurl.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D533E8BD2727BBF800A9B5DD /* libcurl.a */; }; @@ -375,6 +375,7 @@ D5E3FB9828FDFAD90034B72C /* NSError+AltStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6C336124197D700034FD24 /* NSError+AltStore.swift */; }; D5F2F6A92720B7C20081CCF5 /* PatchViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F2F6A82720B7C20081CCF5 /* PatchViewController.swift */; }; D5F48B4829CCF21B002B52A4 /* AltStore+Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F48B4729CCF21B002B52A4 /* AltStore+Async.swift */; }; + D5F48B4C29CD0C48002B52A4 /* AsyncManaged.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F48B4929CD0B67002B52A4 /* AsyncManaged.swift */; }; D5F5AF2E28FDD2EC00C938F5 /* TestErrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F5AF2D28FDD2EC00C938F5 /* TestErrors.swift */; }; D5F5AF7D28ECEA990067C736 /* ErrorDetailsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F5AF7C28ECEA990067C736 /* ErrorDetailsViewController.swift */; }; D5F99A1828D11DB500476A16 /* AltStore10ToAltStore11.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = D5F99A1728D11DB500476A16 /* AltStore10ToAltStore11.xcmappingmodel */; }; @@ -918,6 +919,7 @@ D5E1E7C028077DE90016FC96 /* FetchTrustedSourcesOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FetchTrustedSourcesOperation.swift; sourceTree = ""; }; D5F2F6A82720B7C20081CCF5 /* PatchViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PatchViewController.swift; sourceTree = ""; }; D5F48B4729CCF21B002B52A4 /* AltStore+Async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AltStore+Async.swift"; sourceTree = ""; }; + D5F48B4929CD0B67002B52A4 /* AsyncManaged.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncManaged.swift; sourceTree = ""; }; D5F5AF2D28FDD2EC00C938F5 /* TestErrors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestErrors.swift; sourceTree = ""; }; D5F5AF7C28ECEA990067C736 /* ErrorDetailsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorDetailsViewController.swift; sourceTree = ""; }; D5F99A1728D11DB500476A16 /* AltStore10ToAltStore11.xcmappingmodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcmappingmodel; path = AltStore10ToAltStore11.xcmappingmodel; sourceTree = ""; }; @@ -1396,6 +1398,7 @@ isa = PBXGroup; children = ( BFB39B5B252BC10E00D1BE50 /* Managed.swift */, + D5F48B4929CD0B67002B52A4 /* AsyncManaged.swift */, BF66EE8E2501AEBC007EE018 /* ALTAppPermission.h */, BF66EE912501AEBC007EE018 /* ALTAppPermission.m */, BF66EE922501AEBC007EE018 /* ALTPatreonBenefitType.h */, @@ -2594,6 +2597,7 @@ D5F48B4829CCF21B002B52A4 /* AltStore+Async.swift in Sources */, BF66EED92501AECA007EE018 /* Team.swift in Sources */, BF66EED12501AECA007EE018 /* AltStore3ToAltStore4.xcmappingmodel in Sources */, + D5F48B4C29CD0C48002B52A4 /* AsyncManaged.swift in Sources */, BFAECC5C2501B0A400528F27 /* CFNotificationName+AltStore.m in Sources */, BF66EED82501AECA007EE018 /* SecureValueTransformer.swift in Sources */, BF8B17EB250AC40000F8157F /* FileManager+SharedDirectories.swift in Sources */, diff --git a/AltStoreCore/Types/AsyncManaged.swift b/AltStoreCore/Types/AsyncManaged.swift new file mode 100644 index 00000000..1be2937b --- /dev/null +++ b/AltStoreCore/Types/AsyncManaged.swift @@ -0,0 +1,82 @@ +// +// AsyncManaged.swift +// AltStore +// +// Created by Riley Testut on 3/30/23. +// Copyright © 2023 Riley Testut. All rights reserved. +// + +import Foundation +import CoreData + +@propertyWrapper @dynamicMemberLookup +public struct AsyncManaged +{ + public var wrappedValue: ManagedObject { + didSet { + self.managedObjectContext = self.managedObject?.managedObjectContext + } + } + + public var projectedValue: AsyncManaged { + return self + } + + private var managedObjectContext: NSManagedObjectContext? + private var managedObject: NSManagedObject? { + return self.wrappedValue as? NSManagedObject + } + + public init(wrappedValue: ManagedObject) + { + self.wrappedValue = wrappedValue + self.managedObjectContext = self.managedObject?.managedObjectContext + } +} + +public extension AsyncManaged +{ + // Fetch multiple values. + func get(_ closure: @escaping (ManagedObject) -> T) async -> T + { + if let context = self.managedObjectContext + { + return await context.performAsync { + closure(self.wrappedValue) + } + } + else + { + return closure(self.wrappedValue) + } + } +} + +/// @dynamicMemberLookup +extension AsyncManaged +{ + public subscript(dynamicMember keyPath: KeyPath) -> T { + get async { + guard let context = self.managedObjectContext else { + return self.wrappedValue[keyPath: keyPath] + } + + return await context.performAsync { + return self.wrappedValue[keyPath: keyPath] + } + } + } + + // Optionals + public subscript(dynamicMember keyPath: KeyPath) -> T? where ManagedObject == Optional { + get async { + guard let context = self.managedObjectContext else { + return self.wrappedValue?[keyPath: keyPath] + } + + return await context.performAsync { + return self.wrappedValue?[keyPath: keyPath] + } + } + } +}