diff --git a/AltStore.xcodeproj/project.pbxproj b/AltStore.xcodeproj/project.pbxproj index 63395945..b4a85fae 100644 --- a/AltStore.xcodeproj/project.pbxproj +++ b/AltStore.xcodeproj/project.pbxproj @@ -374,6 +374,7 @@ D5E1E7C128077DE90016FC96 /* FetchTrustedSourcesOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5E1E7C028077DE90016FC96 /* FetchTrustedSourcesOperation.swift */; }; 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 */; }; 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 */; }; @@ -916,6 +917,7 @@ D5DB145828F9DC1000A8F606 /* ALTLocalizedError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ALTLocalizedError.swift; sourceTree = ""; }; 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 = ""; }; 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 = ""; }; @@ -2589,6 +2591,7 @@ D5F99A1828D11DB500476A16 /* AltStore10ToAltStore11.xcmappingmodel in Sources */, BF66EEE92501AED0007EE018 /* JSONDecoder+Properties.swift in Sources */, BF66EEEB2501AED0007EE018 /* UIApplication+AppExtension.swift in Sources */, + D5F48B4829CCF21B002B52A4 /* AltStore+Async.swift in Sources */, BF66EED92501AECA007EE018 /* Team.swift in Sources */, BF66EED12501AECA007EE018 /* AltStore3ToAltStore4.xcmappingmodel in Sources */, BFAECC5C2501B0A400528F27 /* CFNotificationName+AltStore.m in Sources */, diff --git a/AltStoreCore/Extensions/AltStore+Async.swift b/AltStoreCore/Extensions/AltStore+Async.swift new file mode 100644 index 00000000..5e247b2b --- /dev/null +++ b/AltStoreCore/Extensions/AltStore+Async.swift @@ -0,0 +1,61 @@ +// +// AltStore+Async.swift +// AltStoreCore +// +// Created by Riley Testut on 3/23/23. +// Copyright © 2023 Riley Testut. All rights reserved. +// + +import UIKit +import CoreData + +public extension NSManagedObjectContext +{ + // Non-Throwing + func performAsync(_ closure: @escaping () -> T) async -> T + { + let result: T + + if #available(iOS 15, *) + { + result = await self.perform(schedule: .immediate) { + closure() + } + } + else + { + result = await withCheckedContinuation { (continuation: CheckedContinuation) in + self.perform { + let result = closure() + continuation.resume(returning: result) + } + } + } + + return result + } + + // Throwing + func performAsync(_ closure: @escaping () throws -> T) async throws -> T + { + let result: T + + if #available(iOS 15, *) + { + result = try await self.perform(schedule: .immediate) { + try closure() + } + } + else + { + result = try await withCheckedThrowingContinuation { (continuation: CheckedContinuation) in + self.perform { + let result = Result { try closure() } + continuation.resume(with: result) + } + } + } + + return result + } +}