Compare commits

..

17 Commits
alpha ... main

Author SHA1 Message Date
Magesh K
2b71c36ace Merge pull request #944 from SideStore/develop
Merge develop into main for 0.6.1 release
2025-04-08 22:03:54 -07:00
polymo1
591913743e Merge pull request #940 from Br0des/develop
Added StosVPN to the EM Proxy part.
2025-04-05 22:38:53 -04:00
Brodes
77d95fe278 Added StosVPN to the EM Proxy part.
yeah i'm petty >:3

Signed-off-by: Brodes <144500576+Br0des@users.noreply.github.com>
2025-04-05 20:13:07 -06:00
Joseph LaFreniere
0cd62d371a [Skip Ci] Fix typo "levaraging" -> "leveraging" (#926)
Signed-off-by: Joseph LaFreniere <git@lafreniere.xyz>
2025-03-31 15:12:25 -07:00
Magesh K
9771f6bb9a Merge pull request #920 from mahee96/develop
Migration: Fixes for migration issues when migrating from 0.5.9 to 0.6.0
2025-03-25 20:37:16 -07:00
mahee96
e553efbad5 - migration-fix: more reinforcements for 0.6.0 to 0.6.1 2025-03-24 01:32:01 -07:00
mahee96
a4dfd28a3c - [Fix]: migrations fix for coredata from v11(0.5.9) to v17_1(0.6.1) and v17(0.6.0 to v17_1(0.6.1) 2025-03-24 00:19:05 -07:00
mahee96
a7496e08e3 - [WIP]: migrations fix for coredata from v11(0.5.9) to v17_1(0.6.1) and v17(0.6.0 to v17_1(0.6.1) 2025-03-23 12:09:58 -07:00
mahee96
2f3be07b5d - fix: attempt to fix "app no longer available" issues due to fetch profiles not updating appFeatures and appGroups (possible regression from PR#846) 2025-03-23 12:00:31 -07:00
mahee96
cbde3e6495 Revert "- Fixed: attempt migrations fix from 0.5.10 to 0.6.0"
This reverts commit ae8e9a3506.
2025-03-23 11:57:16 -07:00
polymo1
117f31e158 Merge pull request #915 from neoarz/patch-3
Update SettingsViewController.swift
2025-03-15 10:13:42 -04:00
neoarz
420efcbb11 Update SettingsViewController.swift
Signed-off-by: neoarz <164915254+neoarz@users.noreply.github.com>
2025-03-15 06:30:03 -04:00
mahee96
ae8e9a3506 - Fixed: attempt migrations fix from 0.5.10 to 0.6.0 2025-03-11 04:44:23 +05:30
Zero King
4aca1dfa43 fix: typo in hasUpdate comparison
Signed-off-by: Zero King <l2dy@icloud.com>
2025-03-08 22:43:20 +05:30
Magesh K
3785891923 Merge pull request #900 from l2dy/patch-1
fix: typo in hasUpdate comparison
2025-03-04 03:33:32 +05:30
Zero King
e85db67ac7 fix: typo in hasUpdate comparison
Signed-off-by: Zero King <l2dy@icloud.com>
2025-03-02 01:47:04 +08:00
mahee96
39d0835f5b - CI: updated stable.yml for recent fixes 2025-03-01 01:30:15 +05:30
27 changed files with 3092 additions and 87 deletions

View File

@@ -59,6 +59,15 @@
A80D60D32D3DD85100CEF65D /* ReleaseTrack.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80D60D12D3D705F00CEF65D /* ReleaseTrack.swift */; };
A80D790D2D2F20AF00A40F40 /* PaginationIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80D790C2D2F20AF00A40F40 /* PaginationIntent.swift */; };
A80D790F2D2F217000A40F40 /* PaginationDataHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80D790E2D2F217000A40F40 /* PaginationDataHolder.swift */; };
A815AA952D90D2A100929A9E /* StoreApp17To17_1MigrationPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = A815AA942D90D2A100929A9E /* StoreApp17To17_1MigrationPolicy.swift */; };
A815AA972D90E16400929A9E /* ReleaseTrack17To17_1MigrationPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = A815AA962D90E16400929A9E /* ReleaseTrack17To17_1MigrationPolicy.swift */; };
A815AA992D90E5E500929A9E /* AltStore17ToAltStore17_1.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = A815AA982D90E5E500929A9E /* AltStore17ToAltStore17_1.xcmappingmodel */; };
A815AA9F2D9104DD00929A9E /* BuildInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = A815AA9E2D9104DD00929A9E /* BuildInfo.swift */; };
A815AAA12D9108BC00929A9E /* AltStore11ToAltStore17_1.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = A815AAA02D9108BC00929A9E /* AltStore11ToAltStore17_1.xcmappingmodel */; };
A815AAA72D9108CB00929A9E /* ReleaseTrack11To17_1MigrationPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = A815AAA42D9108CB00929A9E /* ReleaseTrack11To17_1MigrationPolicy.swift */; };
A815AAA82D9108CB00929A9E /* AppPermission11To17_1MigrationPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = A815AAA52D9108CB00929A9E /* AppPermission11To17_1MigrationPolicy.swift */; };
A815AAA92D9108CB00929A9E /* Source11To17_1MigrationPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = A815AAA22D9108CB00929A9E /* Source11To17_1MigrationPolicy.swift */; };
A815AAAA2D9108CB00929A9E /* StoreApp11To17_1MigrationPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = A815AAA32D9108CB00929A9E /* StoreApp11To17_1MigrationPolicy.swift */; };
A81A8CB92D68B30B0086C96F /* SingletonGenericMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = A868CFE32D319988002F1201 /* SingletonGenericMap.swift */; };
A81A8CBA2D68B3110086C96F /* TreeMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81A8CB02D68B0320086C96F /* TreeMap.swift */; };
A81A8CBD2D68B43F0086C96F /* LinkedHashMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81A8CBC2D68B43F0086C96F /* LinkedHashMap.swift */; };
@@ -69,6 +78,8 @@
A81A8CD12D68BA9B0086C96F /* TreeMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81A8CB02D68B0320086C96F /* TreeMap.swift */; };
A81A8CD22D68BAA30086C96F /* SingletonGenericMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = A868CFE32D319988002F1201 /* SingletonGenericMap.swift */; };
A81A8CD42D68BAFF0086C96F /* DataStructureTests.xctestplan in Resources */ = {isa = PBXBuildFile; fileRef = A81A8CD32D68BAFF0086C96F /* DataStructureTests.xctestplan */; };
A81BF9E52D84CB0C00768940 /* AppPermission17To17_1MigrationPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81BF9E22D84CB0C00768940 /* AppPermission17To17_1MigrationPolicy.swift */; };
A81BF9E72D84CB0C00768940 /* Source17To17_1MigrationPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81BF9E32D84CB0C00768940 /* Source17To17_1MigrationPolicy.swift */; };
A82067842D03DC0600645C0D /* OperatingSystemVersion+Comparable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5708416292448DA00D42D34 /* OperatingSystemVersion+Comparable.swift */; };
A82067C42D03E0DE00645C0D /* SemanticVersion in Frameworks */ = {isa = PBXBuildFile; productRef = A82067C32D03E0DE00645C0D /* SemanticVersion */; };
A8228B5B2D6E2C0C00F7CE0E /* (null) in Sources */ = {isa = PBXBuildFile; };
@@ -77,7 +88,6 @@
A859ED5D2D1EE827003DCC58 /* OpenSSL.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = A859ED5B2D1EE80D003DCC58 /* OpenSSL.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
A86315DF2D3EB2DE0048FA40 /* ErrorProcessing.swift in Sources */ = {isa = PBXBuildFile; fileRef = A86315DE2D3EB2D80048FA40 /* ErrorProcessing.swift */; };
A8696EE42D34512C00E96389 /* RemoveAppExtensionsOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8696EE32D34512C00E96389 /* RemoveAppExtensionsOperation.swift */; };
A881E7C72D6EF58C00954AD2 /* AltStore11ToAltStore17.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = A881E7C62D6EF58C00954AD2 /* AltStore11ToAltStore17.xcmappingmodel */; };
A881E7CB2D6EF5AB00954AD2 /* StoreApp11To17MigrationPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = A881E7CA2D6EF5AB00954AD2 /* StoreApp11To17MigrationPolicy.swift */; };
A88B8C492D35AD3200F53F9D /* OperationsLoggingContolView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A88B8C482D35AD3200F53F9D /* OperationsLoggingContolView.swift */; };
A88B8C552D35F1EC00F53F9D /* OperationsLoggingControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = A88B8C542D35F1EC00F53F9D /* OperationsLoggingControl.swift */; };
@@ -103,6 +113,7 @@
A8C6D5142D1EE8D700DF01F1 /* OpenSSL.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = A859ED5B2D1EE80D003DCC58 /* OpenSSL.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
A8C6D5172D1EE95B00DF01F1 /* OpenSSL.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = A859ED5B2D1EE80D003DCC58 /* OpenSSL.xcframework */; };
A8C6D5182D1EE95B00DF01F1 /* OpenSSL.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = A859ED5B2D1EE80D003DCC58 /* OpenSSL.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
A8C924242D8E73B1009D9953 /* AltStore11ToAltStore17.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = A8C924232D8E73B1009D9953 /* AltStore11ToAltStore17.xcmappingmodel */; };
A8D484D82D0CD306002C691D /* AltBackup.ipa in Resources */ = {isa = PBXBuildFile; fileRef = A8D484D72D0CD306002C691D /* AltBackup.ipa */; };
A8D49F532D3D2F9400844B92 /* ProcessInfo+AltStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8D49F522D3D2F9400844B92 /* ProcessInfo+AltStore.swift */; };
A8E2DB312D684E2A009E5D31 /* UITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8E2DB2E2D684E2A009E5D31 /* UITests.swift */; };
@@ -660,6 +671,15 @@
A80D60D12D3D705F00CEF65D /* ReleaseTrack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReleaseTrack.swift; sourceTree = "<group>"; };
A80D790C2D2F20AF00A40F40 /* PaginationIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginationIntent.swift; sourceTree = "<group>"; };
A80D790E2D2F217000A40F40 /* PaginationDataHolder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginationDataHolder.swift; sourceTree = "<group>"; };
A815AA942D90D2A100929A9E /* StoreApp17To17_1MigrationPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreApp17To17_1MigrationPolicy.swift; sourceTree = "<group>"; };
A815AA962D90E16400929A9E /* ReleaseTrack17To17_1MigrationPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReleaseTrack17To17_1MigrationPolicy.swift; sourceTree = "<group>"; };
A815AA982D90E5E500929A9E /* AltStore17ToAltStore17_1.xcmappingmodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcmappingmodel; path = AltStore17ToAltStore17_1.xcmappingmodel; sourceTree = "<group>"; };
A815AA9E2D9104DD00929A9E /* BuildInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BuildInfo.swift; sourceTree = "<group>"; };
A815AAA02D9108BC00929A9E /* AltStore11ToAltStore17_1.xcmappingmodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcmappingmodel; path = AltStore11ToAltStore17_1.xcmappingmodel; sourceTree = "<group>"; };
A815AAA22D9108CB00929A9E /* Source11To17_1MigrationPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Source11To17_1MigrationPolicy.swift; sourceTree = "<group>"; };
A815AAA32D9108CB00929A9E /* StoreApp11To17_1MigrationPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreApp11To17_1MigrationPolicy.swift; sourceTree = "<group>"; };
A815AAA42D9108CB00929A9E /* ReleaseTrack11To17_1MigrationPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReleaseTrack11To17_1MigrationPolicy.swift; sourceTree = "<group>"; };
A815AAA52D9108CB00929A9E /* AppPermission11To17_1MigrationPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppPermission11To17_1MigrationPolicy.swift; sourceTree = "<group>"; };
A81A8CB02D68B0320086C96F /* TreeMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TreeMap.swift; sourceTree = "<group>"; };
A81A8CB42D68B2180086C96F /* TreeMapTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TreeMapTests.swift; sourceTree = "<group>"; };
A81A8CBC2D68B43F0086C96F /* LinkedHashMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkedHashMap.swift; sourceTree = "<group>"; };
@@ -667,6 +687,9 @@
A81A8CC52D68BA610086C96F /* DataStructureTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DataStructureTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
A81A8CC72D68BA610086C96F /* DataStructuresTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataStructuresTests.swift; sourceTree = "<group>"; };
A81A8CD32D68BAFF0086C96F /* DataStructureTests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = DataStructureTests.xctestplan; sourceTree = "<group>"; };
A81BF9E12D84C9E900768940 /* AltStore 17_1.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "AltStore 17_1.xcdatamodel"; sourceTree = "<group>"; };
A81BF9E22D84CB0C00768940 /* AppPermission17To17_1MigrationPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppPermission17To17_1MigrationPolicy.swift; sourceTree = "<group>"; };
A81BF9E32D84CB0C00768940 /* Source17To17_1MigrationPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Source17To17_1MigrationPolicy.swift; sourceTree = "<group>"; };
A859ED5B2D1EE80D003DCC58 /* OpenSSL.xcframework */ = {isa = PBXFileReference; expectedSignature = "AppleDeveloperProgram:67RAULRX93:Marcin Krzyzanowski"; lastKnownFileType = wrapper.xcframework; name = OpenSSL.xcframework; path = SideStore/AltSign/Dependencies/OpenSSL/Frameworks/OpenSSL.xcframework; sourceTree = "<group>"; };
A85ACB8E2D1F31C400AA3DE7 /* AltBackup.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AltBackup.xcconfig; sourceTree = "<group>"; };
A85ACB8F2D1F31C400AA3DE7 /* AltStore.debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AltStore.debug.xcconfig; sourceTree = "<group>"; };
@@ -679,7 +702,6 @@
A86315DE2D3EB2D80048FA40 /* ErrorProcessing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorProcessing.swift; sourceTree = "<group>"; };
A868CFE32D319988002F1201 /* SingletonGenericMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingletonGenericMap.swift; sourceTree = "<group>"; };
A8696EE32D34512C00E96389 /* RemoveAppExtensionsOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoveAppExtensionsOperation.swift; sourceTree = "<group>"; };
A881E7C62D6EF58C00954AD2 /* AltStore11ToAltStore17.xcmappingmodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcmappingmodel; path = AltStore11ToAltStore17.xcmappingmodel; sourceTree = "<group>"; };
A881E7CA2D6EF5AB00954AD2 /* StoreApp11To17MigrationPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreApp11To17MigrationPolicy.swift; sourceTree = "<group>"; };
A881E8562D6FBBAF00954AD2 /* DataStructureTests.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = DataStructureTests.xcconfig; sourceTree = "<group>"; };
A88B8C482D35AD3200F53F9D /* OperationsLoggingContolView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OperationsLoggingContolView.swift; sourceTree = "<group>"; };
@@ -696,6 +718,7 @@
A8C38C2B2D206AD900E83DBD /* AbstractClassError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AbstractClassError.swift; sourceTree = "<group>"; };
A8C38C312D206B2500E83DBD /* FileOutputStream.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileOutputStream.swift; sourceTree = "<group>"; };
A8C38C372D2084D000E83DBD /* ConsoleLogView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsoleLogView.swift; sourceTree = "<group>"; };
A8C924232D8E73B1009D9953 /* AltStore11ToAltStore17.xcmappingmodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcmappingmodel; path = AltStore11ToAltStore17.xcmappingmodel; sourceTree = "<group>"; };
A8D484D72D0CD306002C691D /* AltBackup.ipa */ = {isa = PBXFileReference; lastKnownFileType = file; path = AltBackup.ipa; sourceTree = "<group>"; };
A8D49F522D3D2F9400844B92 /* ProcessInfo+AltStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProcessInfo+AltStore.swift"; sourceTree = "<group>"; };
A8E2DB212D684CBD009E5D31 /* UITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -1246,6 +1269,17 @@
path = Intents;
sourceTree = "<group>";
};
A815AAA62D9108CB00929A9E /* v11-to-v17_1 */ = {
isa = PBXGroup;
children = (
A815AAA22D9108CB00929A9E /* Source11To17_1MigrationPolicy.swift */,
A815AAA32D9108CB00929A9E /* StoreApp11To17_1MigrationPolicy.swift */,
A815AAA42D9108CB00929A9E /* ReleaseTrack11To17_1MigrationPolicy.swift */,
A815AAA52D9108CB00929A9E /* AppPermission11To17_1MigrationPolicy.swift */,
);
path = "v11-to-v17_1";
sourceTree = "<group>";
};
A81A8CB22D68B2030086C96F /* UnitTests */ = {
isa = PBXGroup;
children = (
@@ -1281,6 +1315,26 @@
path = xcconfigs;
sourceTree = "<group>";
};
A85E43732D8AF82100E89240 /* v11-to-v17 */ = {
isa = PBXGroup;
children = (
D5185B812AE1E71D00646E33 /* Source11To17MigrationPolicy.swift */,
A881E7CA2D6EF5AB00954AD2 /* StoreApp11To17MigrationPolicy.swift */,
);
path = "v11-to-v17";
sourceTree = "<group>";
};
A85E43742D8AF84200E89240 /* v17-to-v17_1 */ = {
isa = PBXGroup;
children = (
A81BF9E32D84CB0C00768940 /* Source17To17_1MigrationPolicy.swift */,
A815AA942D90D2A100929A9E /* StoreApp17To17_1MigrationPolicy.swift */,
A815AA962D90E16400929A9E /* ReleaseTrack17To17_1MigrationPolicy.swift */,
A81BF9E22D84CB0C00768940 /* AppPermission17To17_1MigrationPolicy.swift */,
);
path = "v17-to-v17_1";
sourceTree = "<group>";
};
A86315DD2D3EB2BD0048FA40 /* errors */ = {
isa = PBXGroup;
children = (
@@ -1393,6 +1447,7 @@
A8C38C272D206AA500E83DBD /* common */ = {
isa = PBXGroup;
children = (
A815AA9E2D9104DD00929A9E /* BuildInfo.swift */,
A8B516E52D2668020047047C /* DateTimeUtil.swift */,
A8C38C282D206AC100E83DBD /* OutputStream.swift */,
A8C38C312D206B2500E83DBD /* FileOutputStream.swift */,
@@ -1821,10 +1876,11 @@
isa = PBXGroup;
children = (
BF66EEAF2501AECA007EE018 /* InstalledAppPolicy.swift */,
D5185B812AE1E71D00646E33 /* Source11To17MigrationPolicy.swift */,
BF66EEAE2501AECA007EE018 /* StoreAppPolicy.swift */,
D5F99A1928D12B1400476A16 /* StoreApp10ToStoreApp11Policy.swift */,
A881E7CA2D6EF5AB00954AD2 /* StoreApp11To17MigrationPolicy.swift */,
A85E43732D8AF82100E89240 /* v11-to-v17 */,
A85E43742D8AF84200E89240 /* v17-to-v17_1 */,
A815AAA62D9108CB00929A9E /* v11-to-v17_1 */,
);
path = Policies;
sourceTree = "<group>";
@@ -1832,7 +1888,9 @@
BF66EEB02501AECA007EE018 /* Mapping Models */ = {
isa = PBXGroup;
children = (
A881E7C62D6EF58C00954AD2 /* AltStore11ToAltStore17.xcmappingmodel */,
A815AAA02D9108BC00929A9E /* AltStore11ToAltStore17_1.xcmappingmodel */,
A815AA982D90E5E500929A9E /* AltStore17ToAltStore17_1.xcmappingmodel */,
A8C924232D8E73B1009D9953 /* AltStore11ToAltStore17.xcmappingmodel */,
D5F99A1728D11DB500476A16 /* AltStore10ToAltStore11.xcmappingmodel */,
D5CA0C4D280E249E00469595 /* AltStore9ToAltStore10.xcmappingmodel */,
BFBF331A2526762200B7B8C9 /* AltStore8ToAltStore9.xcmappingmodel */,
@@ -3098,6 +3156,7 @@
A82067842D03DC0600645C0D /* OperatingSystemVersion+Comparable.swift in Sources */,
D5FB28EE2ADDF89800A1C337 /* KnownSource.swift in Sources */,
BF66EED32501AECA007EE018 /* AltStore2ToAltStore3.xcmappingmodel in Sources */,
A815AA972D90E16400929A9E /* ReleaseTrack17To17_1MigrationPolicy.swift in Sources */,
BF66EEA52501AEC5007EE018 /* Benefit.swift in Sources */,
D52B4ABF2AF183F0005991C3 /* WebViewController.swift in Sources */,
BF66EED22501AECA007EE018 /* AltStore4ToAltStore5.xcmappingmodel in Sources */,
@@ -3106,17 +3165,26 @@
BF66EECD2501AECA007EE018 /* StoreAppPolicy.swift in Sources */,
BF66EEE82501AED0007EE018 /* UserDefaults+AltStore.swift in Sources */,
BF340E9A250AD39500A192CB /* ViewApp.intentdefinition in Sources */,
A8C924242D8E73B1009D9953 /* AltStore11ToAltStore17.xcmappingmodel in Sources */,
D552EB062AF453F900A3AB4D /* URL+Normalized.swift in Sources */,
BFAECC522501B0A400528F27 /* CodableError.swift in Sources */,
A8FD915F2D046F5200322782 /* UserInfoValue.swift in Sources */,
D5F9821D2AB900060045751F /* AppScreenshot.swift in Sources */,
BF66EE9E2501AEC1007EE018 /* Fetchable.swift in Sources */,
BF66EEDF2501AECA007EE018 /* PatreonAccount.swift in Sources */,
A815AAA12D9108BC00929A9E /* AltStore11ToAltStore17_1.xcmappingmodel in Sources */,
BFAECC532501B0A400528F27 /* ServerProtocol.swift in Sources */,
A81BF9E52D84CB0C00768940 /* AppPermission17To17_1MigrationPolicy.swift in Sources */,
A81BF9E72D84CB0C00768940 /* Source17To17_1MigrationPolicy.swift in Sources */,
BFAECC572501B0A400528F27 /* ConnectionManager.swift in Sources */,
BF66EE9D2501AEC1007EE018 /* AppProtocol.swift in Sources */,
D519AD46292D665B004B12F9 /* Managed.swift in Sources */,
D52A2F972ACB40F700BDF8E3 /* Logger+AltStore.swift in Sources */,
A815AA952D90D2A100929A9E /* StoreApp17To17_1MigrationPolicy.swift in Sources */,
A815AAA72D9108CB00929A9E /* ReleaseTrack11To17_1MigrationPolicy.swift in Sources */,
A815AAA82D9108CB00929A9E /* AppPermission11To17_1MigrationPolicy.swift in Sources */,
A815AAA92D9108CB00929A9E /* Source11To17_1MigrationPolicy.swift in Sources */,
A815AAAA2D9108CB00929A9E /* StoreApp11To17_1MigrationPolicy.swift in Sources */,
BFC712C42512D5F100AB5EBE /* XPCConnection.swift in Sources */,
D5CA0C4B280E141900469595 /* ManagedPatron.swift in Sources */,
BF66EE8C2501AEB2007EE018 /* Keychain.swift in Sources */,
@@ -3179,11 +3247,11 @@
BF66EECE2501AECA007EE018 /* InstalledAppPolicy.swift in Sources */,
BF1FE359251A9FB000C3CE09 /* NSXPCConnection+MachServices.swift in Sources */,
BF66EEA62501AEC5007EE018 /* PatreonAPI.swift in Sources */,
A881E7C72D6EF58C00954AD2 /* AltStore11ToAltStore17.xcmappingmodel in Sources */,
BF66EED02501AECA007EE018 /* AltStore6ToAltStore7.xcmappingmodel in Sources */,
BF66EEDC2501AECA007EE018 /* MergePolicy.swift in Sources */,
BF66EEE22501AECA007EE018 /* InstalledExtension.swift in Sources */,
BF66EED62501AECA007EE018 /* NewsItem.swift in Sources */,
A815AA992D90E5E500929A9E /* AltStore17ToAltStore17_1.xcmappingmodel in Sources */,
BF66EEA72501AEC5007EE018 /* Campaign.swift in Sources */,
D52C8F032AFC56F000CA0BDD /* StoreCategory.swift in Sources */,
BF66EE992501AEBC007EE018 /* ALTSourceUserInfoKey.m in Sources */,
@@ -3332,6 +3400,7 @@
BF0C4EBD22A1BD8B009A2DD7 /* AppManager.swift in Sources */,
BF2901312318F7A800D88A45 /* AppBannerView.swift in Sources */,
0EE7FDC42BE8BC7900D1E390 /* ALTLocalizedError.swift in Sources */,
A815AA9F2D9104DD00929A9E /* BuildInfo.swift in Sources */,
BFF00D342501BDCF00746320 /* IntentHandler.swift in Sources */,
BFDBBD80246CB84F004ED2F3 /* RemoveAppBackupOperation.swift in Sources */,
A8C38C2A2D206AC100E83DBD /* OutputStream.swift in Sources */,
@@ -4340,6 +4409,7 @@
BF66EEB72501AECA007EE018 /* AltStore.xcdatamodeld */ = {
isa = XCVersionGroup;
children = (
A81BF9E12D84C9E900768940 /* AltStore 17_1.xcdatamodel */,
D51E83802B86926B0092FC61 /* AltStore 17.xcdatamodel */,
D52E988928D002D30032BE6B /* AltStore 11.xcdatamodel */,
D5CA0C4C280E242500469595 /* AltStore 10.xcdatamodel */,
@@ -4353,7 +4423,7 @@
BF66EEBD2501AECA007EE018 /* AltStore 2.xcdatamodel */,
BF66EEB92501AECA007EE018 /* AltStore.xcdatamodel */,
);
currentVersion = D51E83802B86926B0092FC61 /* AltStore 17.xcdatamodel */;
currentVersion = A81BF9E12D84C9E900768940 /* AltStore 17_1.xcdatamodel */;
path = AltStore.xcdatamodeld;
sourceTree = "<group>";
versionGroupType = wrapper.xcdatamodel;

View File

@@ -11,6 +11,7 @@ import CoreData
import AltStoreCore
import Roxas
import SemanticVersion
@objc(FetchSourceOperation)
final class FetchSourceOperation: ResultOperation<Source>
@@ -246,9 +247,20 @@ private extension FetchSourceOperation
#endif
}
if let previousSourceID = self.$source.identifier
let incomingSourceID = source.identifier
if let previousSourceID = self.$source.identifier,
incomingSourceID != previousSourceID
{
guard source.identifier == previousSourceID else { throw SourceError.changedID(source.identifier, previousID: previousSourceID, source: source) }
// if let version = BuildInfo().marketing_version,
// SemanticVersion(version)! <= SemanticVersion("0.6.1")!
// {
// // delete the source, so that incoming will be saved.
// self.source?.managedObjectContext?.delete(self.source!)
// }
// else
// {
throw SourceError.changedID(source.identifier, previousID: self.$source.identifier ?? "nil", source: source)
// }
}
}

View File

@@ -263,50 +263,6 @@ final class SettingsViewController: UITableViewController
}
private class BuildInfo{
private static let MARKETING_VERSION_TAG = "CFBundleShortVersionString"
private static let CURRENT_PROJECT_VERSION_TAG = kCFBundleVersionKey as String
private static let XCODE_VERSION_TAG = "DTXcode"
private static let XCODE_REVISION_TAG = "DTXcodeBuild"
let bundle: Bundle
public init(){
bundle = Bundle.main
}
enum BundleError: Swift.Error {
case invalidURL
}
public init(url: URL) throws {
guard let bundle = Bundle(url: url) else {
throw BundleError.invalidURL
}
self.bundle = bundle
}
public lazy var project_version: String? = {
let version = bundle.object(forInfoDictionaryKey: Self.CURRENT_PROJECT_VERSION_TAG) as? String
return version
}()
public lazy var marketing_version: String? = {
let version = bundle.object(forInfoDictionaryKey: Self.MARKETING_VERSION_TAG) as? String
return version
}()
public lazy var xcode: String? = {
let xcode = bundle.object(forInfoDictionaryKey: Self.XCODE_VERSION_TAG) as? String
return xcode
}()
public lazy var xcode_revision: String? = {
let revision = bundle.object(forInfoDictionaryKey: Self.XCODE_REVISION_TAG) as? String
return revision
}()
}
private extension SettingsViewController
{
@@ -993,7 +949,7 @@ extension SettingsViewController
// Option 2: Discord
alertController.addAction(UIAlertAction(title: "Discord", style: .default) { _ in
if let discordURL = URL(string: "https://discord.gg/sidestore") {
if let discordURL = URL(string: "https://discord.gg/sidestore-949183273383395328") {
let safariViewController = SFSafariViewController(url: discordURL)
safariViewController.preferredControlTintColor = .altPrimary
self.present(safariViewController, animated: true, completion: nil)

View File

@@ -514,27 +514,27 @@ extension SourcesViewController: NSFetchedResultsControllerDelegate
let context = DatabaseManager.shared.persistentContainer.newBackgroundContext()
context.performAndWait {
_ = Source.make(name: "OatmealDome's AltStore Source",
identifier: "me.oatmealdome.altstore",
groupID: "me.oatmealdome.altstore",
sourceURL: URL(string: "https://altstore.oatmealdome.me")!,
context: context)
_ = Source.make(name: "UTM Repository",
identifier: "com.utmapp.repos.UTM",
groupID: "com.utmapp.repos.UTM",
sourceURL: URL(string: "https://alt.getutm.app")!,
context: context)
_ = Source.make(name: "Flyinghead",
identifier: "com.flyinghead.source",
groupID: "com.flyinghead.source",
sourceURL: URL(string: "https://flyinghead.github.io/flycast-builds/altstore.json")!,
context: context)
_ = Source.make(name: "Provenance",
identifier: "org.provenance-emu.AltStore",
groupID: "org.provenance-emu.AltStore",
sourceURL: URL(string: "https://provenance-emu.com/apps.json")!,
context: context)
_ = Source.make(name: "PojavLauncher Repository",
identifier: "dev.crystall1ne.repos.PojavLauncher",
groupID: "dev.crystall1ne.repos.PojavLauncher",
sourceURL: URL(string: "http://alt.crystall1ne.dev")!,
context: context)

View File

@@ -3,6 +3,6 @@
<plist version="1.0">
<dict>
<key>_XCCurrentVersionName</key>
<string>AltStore 17.xcdatamodel</string>
<string>AltStore 17_1.xcdatamodel</string>
</dict>
</plist>

View File

@@ -0,0 +1,315 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="23605" systemVersion="24D70" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="v17.1">
<entity name="Account" representedClassName="Account" syncable="YES">
<attribute name="appleID" attributeType="String"/>
<attribute name="firstName" attributeType="String"/>
<attribute name="identifier" attributeType="String"/>
<attribute name="isActiveAccount" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="lastName" attributeType="String"/>
<relationship name="teams" toMany="YES" deletionRule="Cascade" destinationEntity="Team" inverseName="account" inverseEntity="Team"/>
<uniquenessConstraints>
<uniquenessConstraint>
<constraint value="identifier"/>
</uniquenessConstraint>
</uniquenessConstraints>
</entity>
<entity name="AppID" representedClassName="AppID" syncable="YES">
<attribute name="bundleIdentifier" attributeType="String"/>
<attribute name="expirationDate" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="features" attributeType="Transformable" valueTransformerName="ALTSecureValueTransformer"/>
<attribute name="identifier" attributeType="String"/>
<attribute name="name" attributeType="String"/>
<relationship name="team" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Team" inverseName="appIDs" inverseEntity="Team"/>
<uniquenessConstraints>
<uniquenessConstraint>
<constraint value="identifier"/>
</uniquenessConstraint>
</uniquenessConstraints>
</entity>
<entity name="AppPermission" representedClassName="AppPermission" syncable="YES">
<attribute name="appBundleID" optional="YES" attributeType="String"/>
<attribute name="permission" optional="YES" attributeType="String"/>
<attribute name="sourceID" optional="YES" attributeType="String"/>
<attribute name="type" attributeType="String"/>
<attribute name="usageDescription" attributeType="String"/>
<relationship name="app" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="StoreApp" inverseName="permissions" inverseEntity="StoreApp"/>
<uniquenessConstraints>
<uniquenessConstraint>
<constraint value="sourceID"/>
<constraint value="appBundleID"/>
<constraint value="type"/>
<constraint value="permission"/>
</uniquenessConstraint>
</uniquenessConstraints>
</entity>
<entity name="AppScreenshot" representedClassName="AppScreenshot" syncable="YES">
<attribute name="appBundleID" optional="YES" attributeType="String"/>
<attribute name="deviceType" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="height" optional="YES" attributeType="Integer 16" usesScalarValueType="NO"/>
<attribute name="imageURL" attributeType="URI"/>
<attribute name="sourceID" optional="YES" attributeType="String"/>
<attribute name="width" optional="YES" attributeType="Integer 16" usesScalarValueType="NO"/>
<relationship name="app" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="StoreApp" inverseName="screenshots" inverseEntity="StoreApp"/>
<uniquenessConstraints>
<uniquenessConstraint>
<constraint value="sourceID"/>
<constraint value="appBundleID"/>
<constraint value="imageURL"/>
</uniquenessConstraint>
</uniquenessConstraints>
</entity>
<entity name="AppVersion" representedClassName="AppVersion" syncable="YES">
<attribute name="appBundleID" optional="YES" attributeType="String"/>
<attribute name="buildVersion" optional="YES" attributeType="String"/>
<attribute name="channel" optional="YES" attributeType="String"/>
<attribute name="date" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="downloadURL" attributeType="URI"/>
<attribute name="localizedDescription" optional="YES" attributeType="String"/>
<attribute name="maxOSVersion" optional="YES" attributeType="String"/>
<attribute name="minOSVersion" optional="YES" attributeType="String"/>
<attribute name="sha256" optional="YES" attributeType="String"/>
<attribute name="size" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="sourceID" optional="YES" attributeType="String"/>
<attribute name="version" attributeType="String"/>
<relationship name="app" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="StoreApp" inverseName="versions" inverseEntity="StoreApp"/>
<relationship name="latestVersionApp" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="StoreApp" inverseName="latestVersion" inverseEntity="StoreApp"/>
<relationship name="releaseTrack" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="ReleaseTrack" inverseName="releases" inverseEntity="ReleaseTrack"/>
<uniquenessConstraints>
<uniquenessConstraint>
<constraint value="sourceID"/>
<constraint value="appBundleID"/>
<constraint value="version"/>
<constraint value="buildVersion"/>
</uniquenessConstraint>
</uniquenessConstraints>
</entity>
<entity name="InstalledApp" representedClassName="InstalledApp" syncable="YES">
<attribute name="buildVersion" optional="YES" attributeType="String"/>
<attribute name="bundleIdentifier" attributeType="String"/>
<attribute name="certificateSerialNumber" optional="YES" attributeType="String"/>
<attribute name="expirationDate" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="hasAlternateIcon" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="hasUpdate" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="installedDate" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="isActive" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
<attribute name="isRefreshing" transient="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="name" attributeType="String"/>
<attribute name="needsResign" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="refreshedDate" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="resignedBundleIdentifier" attributeType="String"/>
<attribute name="storeBuildVersion" optional="YES" attributeType="String"/>
<attribute name="version" attributeType="String"/>
<relationship name="appExtensions" toMany="YES" deletionRule="Cascade" destinationEntity="InstalledExtension" inverseName="parentApp" inverseEntity="InstalledExtension"/>
<relationship name="loggedErrors" toMany="YES" deletionRule="Nullify" destinationEntity="LoggedError" inverseName="installedApp" inverseEntity="LoggedError"/>
<relationship name="storeApp" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="StoreApp" inverseName="installedApp" inverseEntity="StoreApp"/>
<relationship name="team" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Team" inverseName="installedApps" inverseEntity="Team"/>
<uniquenessConstraints>
<uniquenessConstraint>
<constraint value="bundleIdentifier"/>
</uniquenessConstraint>
</uniquenessConstraints>
</entity>
<entity name="InstalledExtension" representedClassName="InstalledExtension" syncable="YES">
<attribute name="bundleIdentifier" attributeType="String"/>
<attribute name="expirationDate" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="installedDate" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="name" attributeType="String"/>
<attribute name="refreshedDate" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="resignedBundleIdentifier" attributeType="String"/>
<attribute name="version" attributeType="String"/>
<relationship name="parentApp" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="InstalledApp" inverseName="appExtensions" inverseEntity="InstalledApp"/>
</entity>
<entity name="LoggedError" representedClassName="LoggedError" syncable="YES">
<attribute name="appBundleID" attributeType="String"/>
<attribute name="appName" attributeType="String"/>
<attribute name="code" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="date" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="domain" attributeType="String"/>
<attribute name="operation" optional="YES" attributeType="String"/>
<attribute name="userInfo" attributeType="Transformable" valueTransformerName="ALTSecureValueTransformer"/>
<relationship name="installedApp" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="InstalledApp" inverseName="loggedErrors" inverseEntity="InstalledApp"/>
<relationship name="storeApp" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="StoreApp" inverseName="loggedErrors" inverseEntity="StoreApp"/>
</entity>
<entity name="NewsItem" representedClassName="NewsItem" syncable="YES">
<attribute name="appID" optional="YES" attributeType="String"/>
<attribute name="caption" attributeType="String"/>
<attribute name="date" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="externalURL" optional="YES" attributeType="URI"/>
<attribute name="identifier" attributeType="String"/>
<attribute name="imageURL" optional="YES" attributeType="URI"/>
<attribute name="isSilent" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
<attribute name="sortIndex" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="sourceIdentifier" optional="YES" attributeType="String"/>
<attribute name="tintColor" optional="YES" attributeType="Transformable" valueTransformerName="ALTSecureValueTransformer"/>
<attribute name="title" attributeType="String"/>
<relationship name="source" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Source" inverseName="newsItems" inverseEntity="Source"/>
<relationship name="storeApp" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="StoreApp" inverseName="newsItems" inverseEntity="StoreApp"/>
<uniquenessConstraints>
<uniquenessConstraint>
<constraint value="identifier"/>
<constraint value="sourceIdentifier"/>
</uniquenessConstraint>
</uniquenessConstraints>
</entity>
<entity name="PatreonAccount" representedClassName="PatreonAccount" syncable="YES">
<attribute name="firstName" optional="YES" attributeType="String"/>
<attribute name="identifier" attributeType="String"/>
<attribute name="isPatron" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="name" attributeType="String"/>
<relationship name="pledges" toMany="YES" deletionRule="Cascade" destinationEntity="Pledge" inverseName="account" inverseEntity="Pledge"/>
<uniquenessConstraints>
<uniquenessConstraint>
<constraint value="identifier"/>
</uniquenessConstraint>
</uniquenessConstraints>
</entity>
<entity name="Patron" representedClassName="ManagedPatron" syncable="YES">
<attribute name="identifier" attributeType="String"/>
<attribute name="name" attributeType="String"/>
<uniquenessConstraints>
<uniquenessConstraint>
<constraint value="identifier"/>
</uniquenessConstraint>
</uniquenessConstraints>
</entity>
<entity name="Pledge" representedClassName="Pledge" syncable="YES">
<attribute name="amount" attributeType="Decimal" defaultValueString="0"/>
<attribute name="campaignURL" attributeType="URI"/>
<attribute name="identifier" attributeType="String"/>
<relationship name="account" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PatreonAccount" inverseName="pledges" inverseEntity="PatreonAccount"/>
<relationship name="rewards" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="PledgeReward" inverseName="pledge" inverseEntity="PledgeReward"/>
<relationship name="tiers" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="PledgeTier" inverseName="pledge" inverseEntity="PledgeTier"/>
<uniquenessConstraints>
<uniquenessConstraint>
<constraint value="identifier"/>
</uniquenessConstraint>
</uniquenessConstraints>
</entity>
<entity name="PledgeReward" representedClassName="PledgeReward" syncable="YES">
<attribute name="identifier" attributeType="String"/>
<attribute name="name" attributeType="String"/>
<relationship name="pledge" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Pledge" inverseName="rewards" inverseEntity="Pledge"/>
<uniquenessConstraints>
<uniquenessConstraint>
<constraint value="identifier"/>
</uniquenessConstraint>
</uniquenessConstraints>
</entity>
<entity name="PledgeTier" representedClassName="PledgeTier" syncable="YES">
<attribute name="amount" attributeType="Decimal" defaultValueString="0.0"/>
<attribute name="identifier" attributeType="String"/>
<attribute name="name" optional="YES" attributeType="String"/>
<relationship name="pledge" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Pledge" inverseName="tiers" inverseEntity="Pledge"/>
<uniquenessConstraints>
<uniquenessConstraint>
<constraint value="identifier"/>
</uniquenessConstraint>
</uniquenessConstraints>
</entity>
<entity name="RefreshAttempt" representedClassName="RefreshAttempt" syncable="YES">
<attribute name="date" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="errorDescription" optional="YES" attributeType="String"/>
<attribute name="identifier" attributeType="String"/>
<attribute name="isSuccess" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
<uniquenessConstraints>
<uniquenessConstraint>
<constraint value="identifier"/>
</uniquenessConstraint>
</uniquenessConstraints>
</entity>
<entity name="ReleaseTrack" representedClassName="ReleaseTrack" syncable="YES">
<attribute name="appBundleID" optional="YES" attributeType="String"/>
<attribute name="sourceID" optional="YES" attributeType="String"/>
<attribute name="track" attributeType="String"/>
<relationship name="releases" optional="YES" toMany="YES" deletionRule="Cascade" ordered="YES" destinationEntity="AppVersion" inverseName="releaseTrack" inverseEntity="AppVersion"/>
<relationship name="storeApp" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="StoreApp" inverseName="releaseTracks" inverseEntity="StoreApp"/>
<uniquenessConstraints>
<uniquenessConstraint>
<constraint value="sourceID"/>
<constraint value="appBundleID"/>
<constraint value="track"/>
</uniquenessConstraint>
</uniquenessConstraints>
</entity>
<entity name="Source" representedClassName="Source" syncable="YES">
<attribute name="error" optional="YES" attributeType="Transformable" valueTransformerName="ALTSecureValueTransformer"/>
<attribute name="featuredSortID" optional="YES" attributeType="String"/>
<attribute name="groupID" optional="YES" attributeType="String"/>
<attribute name="hasFeaturedApps" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="headerImageURL" optional="YES" attributeType="URI"/>
<attribute name="iconURL" optional="YES" attributeType="URI"/>
<attribute name="identifier" attributeType="String"/>
<attribute name="localizedDescription" optional="YES" attributeType="String"/>
<attribute name="name" attributeType="String"/>
<attribute name="patreonURL" optional="YES" attributeType="URI"/>
<attribute name="sourceURL" attributeType="URI"/>
<attribute name="subtitle" optional="YES" attributeType="String"/>
<attribute name="tintColor" optional="YES" attributeType="Transformable" valueTransformerName="ALTSecureValueTransformer"/>
<attribute name="version" attributeType="Integer 64" defaultValueString="1" usesScalarValueType="YES"/>
<attribute name="websiteURL" optional="YES" attributeType="URI"/>
<relationship name="apps" toMany="YES" deletionRule="Cascade" ordered="YES" destinationEntity="StoreApp" inverseName="source" inverseEntity="StoreApp"/>
<relationship name="featuredApps" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="StoreApp" inverseName="featuringSource" inverseEntity="StoreApp"/>
<relationship name="newsItems" toMany="YES" deletionRule="Cascade" ordered="YES" destinationEntity="NewsItem" inverseName="source" inverseEntity="NewsItem"/>
<uniquenessConstraints>
<uniquenessConstraint>
<constraint value="identifier"/>
</uniquenessConstraint>
</uniquenessConstraints>
</entity>
<entity name="StoreApp" representedClassName="StoreApp" syncable="YES">
<attribute name="bundleIdentifier" attributeType="String"/>
<attribute name="category" optional="YES" attributeType="String"/>
<attribute name="developerName" attributeType="String"/>
<attribute name="downloadURL" optional="YES" attributeType="URI"/>
<attribute name="featuredSortID" optional="YES" attributeType="String"/>
<attribute name="iconURL" attributeType="URI"/>
<attribute name="isHiddenWithoutPledge" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="isPledged" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="isPledgeRequired" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="localizedDescription" attributeType="String"/>
<attribute name="marketplaceID" optional="YES" attributeType="String"/>
<attribute name="name" attributeType="String"/>
<attribute name="pledgeAmount" optional="YES" attributeType="Decimal"/>
<attribute name="pledgeCurrency" optional="YES" attributeType="String"/>
<attribute name="prefersCustomPledge" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="screenshotURLs" attributeType="Transformable" valueTransformerName="ALTSecureValueTransformer"/>
<attribute name="size" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="sortIndex" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="sourceIdentifier" optional="YES" attributeType="String"/>
<attribute name="subtitle" optional="YES" attributeType="String"/>
<attribute name="tintColor" optional="YES" attributeType="Transformable" valueTransformerName="ALTSecureValueTransformer"/>
<attribute name="version" optional="YES" attributeType="String"/>
<attribute name="versionDate" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="versionDescription" optional="YES" attributeType="String"/>
<relationship name="featuringSource" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Source" inverseName="featuredApps" inverseEntity="Source"/>
<relationship name="installedApp" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="InstalledApp" inverseName="storeApp" inverseEntity="InstalledApp"/>
<relationship name="latestVersion" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="AppVersion" inverseName="latestVersionApp" inverseEntity="AppVersion"/>
<relationship name="loggedErrors" toMany="YES" deletionRule="Nullify" destinationEntity="LoggedError" inverseName="storeApp" inverseEntity="LoggedError"/>
<relationship name="newsItems" toMany="YES" deletionRule="Nullify" destinationEntity="NewsItem" inverseName="storeApp" inverseEntity="NewsItem"/>
<relationship name="permissions" toMany="YES" deletionRule="Cascade" destinationEntity="AppPermission" inverseName="app" inverseEntity="AppPermission"/>
<relationship name="releaseTracks" optional="YES" toMany="YES" deletionRule="Cascade" ordered="YES" destinationEntity="ReleaseTrack" inverseName="storeApp" inverseEntity="ReleaseTrack"/>
<relationship name="screenshots" toMany="YES" deletionRule="Cascade" ordered="YES" destinationEntity="AppScreenshot" inverseName="app" inverseEntity="AppScreenshot"/>
<relationship name="source" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Source" inverseName="apps" inverseEntity="Source"/>
<relationship name="versions" toMany="YES" deletionRule="Cascade" ordered="YES" destinationEntity="AppVersion" inverseName="app" inverseEntity="AppVersion"/>
<uniquenessConstraints>
<uniquenessConstraint>
<constraint value="sourceIdentifier"/>
<constraint value="bundleIdentifier"/>
</uniquenessConstraint>
</uniquenessConstraints>
</entity>
<entity name="Team" representedClassName="Team" syncable="YES">
<attribute name="identifier" attributeType="String"/>
<attribute name="isActiveTeam" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="name" attributeType="String"/>
<attribute name="type" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="account" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Account" inverseName="teams" inverseEntity="Account"/>
<relationship name="appIDs" toMany="YES" deletionRule="Cascade" destinationEntity="AppID" inverseName="team" inverseEntity="AppID"/>
<relationship name="installedApps" toMany="YES" deletionRule="Nullify" destinationEntity="InstalledApp" inverseName="team" inverseEntity="InstalledApp"/>
<uniquenessConstraints>
<uniquenessConstraint>
<constraint value="identifier"/>
</uniquenessConstraint>
</uniquenessConstraints>
</entity>
</model>

View File

@@ -33,7 +33,7 @@ public class AppPermission: BaseEntity
default: return UnknownAppPermission(rawValue: self._permission)
}
}
@NSManaged @objc(permission) private var _permission: String
@NSManaged @objc(permission) private(set) public var _permission: String
// Set by StoreApp.
@NSManaged public var appBundleID: String

View File

@@ -213,6 +213,7 @@ extension MergePolicy{
}
default:
// break
// Unknown context-level conflict.
// assertionFailure("MergePolicy is only intended to work with database-level conflicts.")
assertionFailure("Context Conflict Detected: is there ambigious data in your incoming sources?\nConflict:\(conflict)")

View File

@@ -505,13 +505,14 @@ cxIAAYagXxAPTlNLZXllZEFyY2hpdmVy0QAIAAlUcm9vdIABrxESLAALAAwAGQA1ADYANwBjAGQAZQBm
<relationship name="relationshipmappings" type="0/0" destination="XDDEVRELATIONSHIPMAPPING" idrefs="z142 z119 z223 z116 z229 z139 z236 z136 z214 z216"></relationship>
</object>
<object type="XDDEVENTITYMAPPING" id="z198">
<attribute name="migrationpolicyclassname" type="string"></attribute>
<attribute name="sourcename" type="string">AppPermission</attribute>
<attribute name="mappingtypename" type="string">Undefined</attribute>
<attribute name="mappingnumber" type="int16">17</attribute>
<attribute name="destinationname" type="string">AppPermission</attribute>
<attribute name="autogenerateexpression" type="bool">1</attribute>
<relationship name="mappingmodel" type="1/1" destination="XDDEVMAPPINGMODEL" idrefs="z192"></relationship>
<relationship name="attributemappings" type="0/0" destination="XDDEVATTRIBUTEMAPPING" idrefs="z213 z240 z191 z123 z287"></relationship>
<relationship name="attributemappings" type="0/0" destination="XDDEVATTRIBUTEMAPPING" idrefs="z123 z213 z287 z240 z191"></relationship>
<relationship name="relationshipmappings" type="0/0" destination="XDDEVRELATIONSHIPMAPPING" idrefs="z288"></relationship>
</object>
<object type="XDDEVENTITYMAPPING" id="z199">

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,5 +1,5 @@
//
// SourceMigrationPolicy.swift
// Source11To17MigrationPolicy.swift
// AltStoreCore
//
// Created by Riley Testut on 10/19/23.

View File

@@ -1,5 +1,5 @@
//
// StoreAppMigration11To17Policy.swift
// StoreApp11To17MigrationPolicy.swift
// AltStore
//
// Created by Magesh K on 25/02/25.
@@ -108,7 +108,7 @@ class StoreApp11To17MigrationPolicy: NSEntityMigrationPolicy {
// Create a new ReleaseTrack entity
let context = dInstance.managedObjectContext!
let releaseTrack = NSEntityDescription.insertNewObject(forEntityName: ReleaseTrack.entity().name!, into: context)
let releaseTrack = NSEntityDescription.insertNewObject(forEntityName: ReleaseTrack.entity().name ?? ReleaseTrack.description(), into: context)
releaseTrack.setValue(defaultChannel, forKey: #keyPath(ReleaseTrack._track))
// Connect the releaseTrack to the destination StoreApp
@@ -116,7 +116,7 @@ class StoreApp11To17MigrationPolicy: NSEntityMigrationPolicy {
// Find the mapping name for AppVersion (make sure this exactly matches your mapping model)
let appVersionMappingName = findEntityMappingName(for: AppVersion.entity().name!, in: manager)
let appVersionMappingName = findEntityMappingName(for: AppVersion.entity().name ?? AppVersion.description(), in: manager)
// Create a mutable ordered set for the destination AppVersion objects
let destinationVersionsSet = NSMutableOrderedSet()
@@ -145,5 +145,5 @@ class StoreApp11To17MigrationPolicy: NSEntityMigrationPolicy {
// dInstance.setValue(NSOrderedSet(), forKey: #keyPath(StoreApp._versions))
dInstance.setValue(nil, forKey: #keyPath(StoreApp._versions))
}
}

View File

@@ -0,0 +1,28 @@
//
// AppPermission17to17_1MigrationPolicy.swift
// AltStore
//
// Created by Magesh K on 15/03/25.
// Copyright © 2025 SideStore. All rights reserved.
//
import CoreData
@objc(AppPermission11To17_1MigrationPolicy)
class AppPermission11To17_1MigrationPolicy: AppPermission17To17_1MigrationPolicy {
override func createDestinationInstances(forSource sInstance: NSManagedObject, in mapping: NSEntityMapping, manager: NSMigrationManager) throws {
// Let the default implementation create the basic destination AppPermission
try super.createDestinationInstances(forSource: sInstance, in: mapping, manager: manager)
}
override func createRelationships(
forDestination dInstance: NSManagedObject,
in mapping: NSEntityMapping,
manager: NSMigrationManager
) throws {
try super.createRelationships(forDestination: dInstance, in: mapping, manager: manager)
}
}

View File

@@ -0,0 +1,26 @@
//
// ReleaseTrack11To17_1MigrationPolicy.swift
// AltStore
//
// Created by Magesh K on 15/03/25.
// Copyright © 2025 SideStore. All rights reserved.
//
import CoreData
@objc(ReleaseTrack11To17_1MigrationPolicy)
class ReleaseTrack11To17_1MigrationPolicy: ReleaseTrack17To17_1MigrationPolicy {
override func createDestinationInstances(forSource sInstance: NSManagedObject, in mapping: NSEntityMapping, manager: NSMigrationManager) throws {
try super.createDestinationInstances(forSource: sInstance, in: mapping, manager: manager)
}
override func createRelationships(
forDestination dInstance: NSManagedObject,
in mapping: NSEntityMapping,
manager: NSMigrationManager
) throws {
try super.createRelationships(forDestination: dInstance, in: mapping, manager: manager)
}
}

View File

@@ -0,0 +1,23 @@
//
// Source11To17_1MigrationPolicy.swift
// AltStore
//
// Created by Magesh K on 15/03/25.
// Copyright © 2025 SideStore. All rights reserved.
//
import CoreData
@objc(Source11To17_1MigrationPolicy)
class Source11To17_1MigrationPolicy: Source17To17_1MigrationPolicy
{
override func createDestinationInstances(forSource sInstance: NSManagedObject, in mapping: NSEntityMapping, manager: NSMigrationManager) throws {
// Let the default implementation create the basic destination AppPermission
try super.createDestinationInstances(forSource: sInstance, in: mapping, manager: manager)
}
override func createRelationships(forDestination dInstance: NSManagedObject, in mapping: NSEntityMapping, manager: NSMigrationManager) throws
{
try super.createRelationships(forDestination: dInstance, in: mapping, manager: manager)
}
}

View File

@@ -0,0 +1,62 @@
//
// StoreApp11To17_1MigrationPolicy.swift
// AltStore
//
// Created by Magesh K on 15/03/25.
// Copyright © 2025 SideStore. All rights reserved.
//
import CoreData
fileprivate extension NSManagedObject
{
var storeAppReleaseTracks: NSOrderedSet? {
let tracks = self.value(forKey: #keyPath(StoreApp._releaseTracks)) as? NSOrderedSet
return tracks
}
}
@objc(StoreApp11To17_1MigrationPolicy)
class StoreApp11To17_1MigrationPolicy: StoreApp11To17MigrationPolicy
{
override func createDestinationInstances(forSource sInstance: NSManagedObject, in mapping: NSEntityMapping, manager: NSMigrationManager) throws {
try super.createDestinationInstances(forSource: sInstance, in: mapping, manager: manager)
}
override func createRelationships(forDestination dInstance: NSManagedObject, in mapping: NSEntityMapping, manager: NSMigrationManager) throws
{
try super.createRelationships(forDestination: dInstance, in: mapping, manager: manager)
let appBundleID = dInstance.value(forKey: #keyPath(StoreApp.bundleIdentifier))
let sourceID = dInstance.value(forKey: #keyPath(StoreApp.sourceIdentifier))
for case let track as NSManagedObject in dInstance.storeAppReleaseTracks ?? []
{
track.setValue(appBundleID, forKey: #keyPath(ReleaseTrack._appBundleID))
track.setValue(sourceID, forKey: #keyPath(ReleaseTrack._sourceID))
guard let releases = track.value(forKey: #keyPath(ReleaseTrack._releases)) as? NSOrderedSet else {
continue
}
for case let version as NSManagedObject in releases {
version.setValue(appBundleID, forKey: #keyPath(AppVersion.appBundleID))
version.setValue(sourceID, forKey: #keyPath(AppVersion.sourceID))
}
}
if let permissions = dInstance.value(forKey: #keyPath(StoreApp._permissions)) as? NSSet {
for case let permission as NSManagedObject in permissions {
permission.setValue(appBundleID, forKey: #keyPath(AppPermission.appBundleID))
permission.setValue(sourceID, forKey: #keyPath(AppPermission.sourceID))
}
}
if let screenshots = dInstance.value(forKey: #keyPath(StoreApp._screenshots)) as? NSOrderedSet {
for case let screenshot as NSManagedObject in screenshots {
screenshot.setValue(appBundleID, forKey: #keyPath(AppScreenshot.appBundleID))
screenshot.setValue(sourceID, forKey: #keyPath(AppScreenshot.sourceID))
}
}
}
}

View File

@@ -0,0 +1,100 @@
//
// AppPermission17to17_1MigrationPolicy.swift
// AltStore
//
// Created by Magesh K on 15/03/25.
// Copyright © 2025 SideStore. All rights reserved.
//
import CoreData
@objc(AppPermission17To17_1MigrationPolicy)
class AppPermission17To17_1MigrationPolicy: NSEntityMigrationPolicy {
override func createDestinationInstances(forSource sInstance: NSManagedObject, in mapping: NSEntityMapping, manager: NSMigrationManager) throws {
// Let the default implementation create the basic destination AppPermission
try super.createDestinationInstances(forSource: sInstance, in: mapping, manager: manager)
// Get the destination AppPermission instance that was created
guard let destinationPermission = manager.destinationInstances(forEntityMappingName: mapping.name, sourceInstances: [sInstance]).first else {
print("Failed to locate destination AppPermission instance")
return
}
// Extract the type value from source
if let type = sInstance.value(forKey: #keyPath(AppPermission.type)) as? String {
// this is for backwards compatibility <0.5.10
// if older type was a valid permission, then consider it as "privacy" in the newer "type".
if let permission = self.derivePermissionFromType(type) {
destinationPermission.setValue(permission, forKey: #keyPath(AppPermission._permission))
destinationPermission.setValue("privacy", forKey: #keyPath(AppPermission.type))
}
}
// set initial values copied from source as-is
// (will be updated by StoreApp and Source migration policy in its createRelationship() method)
if let storeApp = sInstance.value(forKey: #keyPath(AppPermission.app)) as? NSManagedObject{
if let appBundle = storeApp.value(forKey: #keyPath(StoreApp.bundleIdentifier)) as? String{
destinationPermission.setValue(appBundle, forKey: #keyPath(AppPermission.appBundleID))
}
if let sourceID = storeApp.value(forKey: #keyPath(StoreApp.sourceIdentifier)) as? String {
destinationPermission.setValue(sourceID, forKey: #keyPath(AppPermission.sourceID))
}
}
}
override func createRelationships(
forDestination dInstance: NSManagedObject,
in mapping: NSEntityMapping,
manager: NSMigrationManager
) throws {
try super.createRelationships(forDestination: dInstance, in: mapping, manager: manager)
// Retrieve the source storeApp from the source appPermission
guard let storeApp = dInstance.value(forKey: #keyPath(AppPermission.app)) as? NSManagedObject else {
print("Destination \(AppPermission.description()) has no storeApp")
return
}
// set initial values copied from source as-is to satisfy unique constraints
// (will be updated by StoreApp and Source migration policy in its createRelationship() method)
if let appBundle = storeApp.value(forKey: #keyPath(StoreApp.bundleIdentifier)) as? String{
dInstance.setValue(appBundle, forKey: #keyPath(AppPermission.appBundleID))
}
if let sourceID = storeApp.value(forKey: #keyPath(StoreApp.sourceIdentifier)) as? String {
dInstance.setValue(sourceID, forKey: #keyPath(AppPermission.sourceID))
}
}
// Helper method to derive permission string from type
private func derivePermissionFromType(_ type: String) -> String? {
// Based on the code in the documents, we need to map the ALTAppPermissionType to permission strings
switch type {
case "photos": return "NSPhotosUsageDescription"
case "camera": return "NSCameraUsageDescription"
case "location": return "NSLocationUsageDescription"
case "contacts": return "NSContactsUsageDescription"
case "reminders": return "NSRemindersUsageDescription"
case "music": return "NSAppleMusicUsageDescription"
case "microphone": return "NSMicrophoneUsageDescription"
case "speech-recognition": return "NSSpeechRecognitionUsageDescription"
case "background-audio": return "NSBackgroundAudioUsageDescription"
case "background-fetch": return "NSBackgroundFetchUsageDescription"
case "bluetooth": return "NSBluetoothUsageDescription"
case "network": return "NSNetworkUsageDescription"
case "calendars": return "NSCalendarsUsageDescription"
case "touchID": return "NSTouchIDUsageDescription"
case "faceID": return "NSFaceIDUsageDescription"
case "siri": return "NSSiriUsageDescription"
case "motion": return "NSMotionUsageDescription"
default: return nil // Default fallback
}
}
}

View File

@@ -0,0 +1,57 @@
//
// ReleaseTrack17To17_1MigrationPolicy.swift
// AltStore
//
// Created by Magesh K on 15/03/25.
// Copyright © 2025 SideStore. All rights reserved.
//
import CoreData
@objc(ReleaseTrack17To17_1MigrationPolicy)
class ReleaseTrack17To17_1MigrationPolicy: NSEntityMigrationPolicy {
override func createDestinationInstances(forSource sInstance: NSManagedObject, in mapping: NSEntityMapping, manager: NSMigrationManager) throws {
try super.createDestinationInstances(forSource: sInstance, in: mapping, manager: manager)
}
override func createRelationships(
forDestination dInstance: NSManagedObject,
in mapping: NSEntityMapping,
manager: NSMigrationManager
) throws {
try super.createRelationships(forDestination: dInstance, in: mapping, manager: manager)
// Retrieve the source storeApp from the source ReleaseTrack
guard let storeApp = dInstance.value(forKey: #keyPath(ReleaseTrack.storeApp)) as? NSManagedObject else {
print("Destination \(ReleaseTrack.description()) has no storeApp")
return
}
// set initial values copied from source as-is to satisfy unique constraints
// (will be updated by StoreApp and Source migration policy in its createRelationship() method)
let appBundle = storeApp.value(forKey: #keyPath(StoreApp.bundleIdentifier)) as? String
let sourceID = storeApp.value(forKey: #keyPath(StoreApp.sourceIdentifier)) as? String
if let appBundle {
dInstance.setValue(appBundle, forKey: #keyPath(ReleaseTrack._appBundleID))
}
if let sourceID {
dInstance.setValue(sourceID, forKey: #keyPath(ReleaseTrack._sourceID))
}
if let releases = dInstance.value(forKey: #keyPath(ReleaseTrack._releases)) as? NSOrderedSet {
for case let version as NSManagedObject in releases {
if let appBundle {
version.setValue(appBundle, forKey: #keyPath(AppVersion.appBundleID))
}
if let sourceID {
version.setValue(sourceID, forKey: #keyPath(AppVersion.sourceID))
}
}
}
}
}

View File

@@ -0,0 +1,191 @@
//
// Source17To17_1MigrationPolicy.swift
// AltStore
//
// Created by Magesh K on 15/03/25.
// Copyright © 2025 SideStore. All rights reserved.
//
import CoreData
fileprivate extension NSManagedObject
{
var sourceSourceURL: URL? {
let sourceURL = self.value(forKey: #keyPath(Source.sourceURL)) as? URL
return sourceURL
}
var sourceSourceId: String? {
let sourceId = self.value(forKey: #keyPath(Source.identifier)) as? String
return sourceId
}
var sourceApps: NSOrderedSet? {
let apps = self.value(forKey: #keyPath(Source._apps)) as? NSOrderedSet
return apps
}
var sourceNewsItems: NSOrderedSet? {
let newsItems = self.value(forKey: #keyPath(Source._newsItems)) as? NSOrderedSet
return newsItems
}
}
fileprivate extension NSManagedObject
{
func setSourceId(_ sourceID: String)
{
self.setValue(sourceID, forKey: #keyPath(Source.identifier))
}
func setGroupId(_ groupID: String)
{
self.setValue(groupID, forKey: #keyPath(Source.groupID))
}
func setSourceSourceUrl(_ sourceURL: URL)
{
self.setValue(sourceURL, forKey: #keyPath(Source.sourceURL))
}
func setSourceSourceID(_ sourceID: String)
{
self.setValue(sourceID, forKey: #keyPath(Source.identifier))
}
func setStoreAppSourceID(_ sourceID: String)
{
self.setValue(sourceID, forKey: #keyPath(StoreApp.sourceIdentifier))
}
func setNewsItemSourceID(_ sourceID: String)
{
self.setValue(sourceID, forKey: #keyPath(NewsItem.sourceIdentifier))
}
func setAppVersionSourceID(_ sourceID: String)
{
self.setValue(sourceID, forKey: #keyPath(AppVersion.sourceID))
}
func setAppPermissionSourceID(_ sourceID: String)
{
self.setValue(sourceID, forKey: #keyPath(AppPermission.sourceID))
}
func setAppScreenshotSourceID(_ sourceID: String)
{
self.setValue(sourceID, forKey: #keyPath(AppScreenshot.sourceID))
}
func setReleaseTracksSourceID(_ sourceID: String)
{
self.setValue(sourceID, forKey: #keyPath(ReleaseTrack._sourceID))
}
}
fileprivate extension NSManagedObject
{
var storeAppVersions: NSOrderedSet? {
let versions = self.value(forKey: #keyPath(StoreApp._versions)) as? NSOrderedSet
return versions
}
var storeAppPermissions: NSSet? {
let permissions = self.value(forKey: #keyPath(StoreApp._permissions)) as? NSSet
return permissions
}
var storeAppScreenshots: NSOrderedSet? {
let screenshots = self.value(forKey: #keyPath(StoreApp._screenshots)) as? NSOrderedSet
return screenshots
}
var storeAppReleaseTracks: NSOrderedSet? {
let tracks = self.value(forKey: #keyPath(StoreApp._releaseTracks)) as? NSOrderedSet
return tracks
}
}
@objc(Source17To17_1MigrationPolicy)
class Source17To17_1MigrationPolicy: NSEntityMigrationPolicy
{
override func createDestinationInstances(forSource sInstance: NSManagedObject, in mapping: NSEntityMapping, manager: NSMigrationManager) throws {
// Let the default implementation create the basic destination AppPermission
try super.createDestinationInstances(forSource: sInstance, in: mapping, manager: manager)
// Get the destination Source instance that was created
guard let dInstance = manager.destinationInstances(forEntityMappingName: mapping.name, sourceInstances: [sInstance]).first else {
print("Failed to locate destination Source instance")
return
}
// update new fields with initial values
if let sourceID = sInstance.value(forKey: #keyPath(Source.identifier)) as? String {
dInstance.setValue(sourceID, forKey: #keyPath(Source.groupID))
}
guard var sourceURL = dInstance.sourceSourceURL else {
return
}
// sidestore official source has been moved to sidestore.io/apps-v2.json
// if we don't switch, users will end up with 2 offical sources
let normalizedSourceURL = try? sourceURL.normalized()
if normalizedSourceURL == "apps.sidestore.io" // if using old source url (<0.5.9)
{
sourceURL = Source.altStoreSourceURL // switch to latest
dInstance.setSourceSourceUrl(sourceURL) // and use it for current
}
var sourceID = try Source.sourceID(from: sourceURL)
dInstance.setSourceId(sourceID)
// for older versions migrating to current (their sourceID is their groupID)
dInstance.setGroupId(sourceID)
if sourceID == "apps.sidestore.io" {
sourceID = Source.altStoreIdentifier
dInstance.setSourceId(sourceID)
}
}
override func createRelationships(forDestination dInstance: NSManagedObject, in mapping: NSEntityMapping, manager: NSMigrationManager) throws
{
try super.createRelationships(forDestination: dInstance, in: mapping, manager: manager)
guard let sourceID = dInstance.sourceSourceId else { return }
for case let newsItem as NSManagedObject in dInstance.sourceNewsItems ?? []
{
newsItem.setNewsItemSourceID(sourceID)
}
for case let app as NSManagedObject in dInstance.sourceApps ?? []
{
app.setStoreAppSourceID(sourceID)
for case let screenshot as NSManagedObject in app.storeAppScreenshots ?? []
{
screenshot.setAppScreenshotSourceID(sourceID)
}
for case let track as NSManagedObject in app.storeAppReleaseTracks ?? []
{
// print("Source_17_1MigrationPolicy: processing track \(track.value(forKey: "track")!)")
track.setValue(sourceID, forKey: #keyPath(ReleaseTrack._sourceID))
guard let releases = track.value(forKey: #keyPath(ReleaseTrack._releases)) as? NSOrderedSet else {
// print("Source_17_1MigrationPolicy: releases not found for track: \(track.value(forKey: "track")!)")
continue
}
for case let version as NSManagedObject in releases {
// print("Source_17_1MigrationPolicy: updating sourceID for version: \(version.value(forKey: "version")!)")
version.setValue(sourceID, forKey: #keyPath(AppVersion.sourceID))
}
}
}
}
}

View File

@@ -0,0 +1,57 @@
//
// StoreApp17To17_1MigrationPolicy.swift
// AltStore
//
// Created by Magesh K on 15/03/25.
// Copyright © 2025 SideStore. All rights reserved.
//
import CoreData
fileprivate extension NSManagedObject
{
var storeAppReleaseTracks: NSOrderedSet? {
let tracks = self.value(forKey: #keyPath(StoreApp._releaseTracks)) as? NSOrderedSet
return tracks
}
}
@objc(StoreApp17To17_1MigrationPolicy)
class StoreApp17To17_1MigrationPolicy: NSEntityMigrationPolicy
{
override func createDestinationInstances(forSource sInstance: NSManagedObject, in mapping: NSEntityMapping, manager: NSMigrationManager) throws {
try super.createDestinationInstances(forSource: sInstance, in: mapping, manager: manager)
}
override func createRelationships(forDestination dInstance: NSManagedObject, in mapping: NSEntityMapping, manager: NSMigrationManager) throws
{
try super.createRelationships(forDestination: dInstance, in: mapping, manager: manager)
let appBundleID = dInstance.value(forKey: #keyPath(StoreApp.bundleIdentifier))
for case let track as NSManagedObject in dInstance.storeAppReleaseTracks ?? []
{
track.setValue(appBundleID, forKey: #keyPath(ReleaseTrack._appBundleID))
guard let releases = track.value(forKey: #keyPath(ReleaseTrack._releases)) as? NSOrderedSet else {
continue
}
for case let version as NSManagedObject in releases {
version.setValue(appBundleID, forKey: #keyPath(AppVersion.appBundleID))
}
}
if let permissions = dInstance.value(forKey: #keyPath(StoreApp._permissions)) as? NSSet {
for case let permission as NSManagedObject in permissions {
permission.setValue(appBundleID, forKey: #keyPath(AppPermission.appBundleID))
}
}
if let screenshots = dInstance.value(forKey: #keyPath(StoreApp._screenshots)) as? NSOrderedSet {
for case let screenshot as NSManagedObject in screenshots {
screenshot.setValue(appBundleID, forKey: #keyPath(AppScreenshot.appBundleID))
}
}
}
}

View File

@@ -75,6 +75,9 @@ public class NewsItem: BaseEntity, Decodable
self.imageURL = try container.decodeIfPresent(URL.self, forKey: .imageURL)
self.externalURL = try container.decodeIfPresent(URL.self, forKey: .externalURL)
// TODO: app specific news should be moved to appEntity (via refactoring)
// This can be done by: 1. having two newsItem schema, one for source and one for StoreApp
// 2. move this appID field into the newItem schema which is under StoreApp.
self.appID = try container.decodeIfPresent(String.self, forKey: .appID)
let notify = try container.decodeIfPresent(Bool.self, forKey: .notify) ?? false

View File

@@ -14,7 +14,9 @@ public class ReleaseTrack: BaseEntity, Decodable
{
// attributes
@NSManaged @objc(track) public private(set) var _track: String?
@NSManaged @objc(appBundleID) public private(set) var _appBundleID: String?
@NSManaged @objc(sourceID) public private(set) var _sourceID: String?
// RelationShips
@NSManaged @objc(releases) public private(set) var _releases: NSOrderedSet?
@NSManaged public private(set) var storeApp: StoreApp?
@@ -89,7 +91,7 @@ public extension ReleaseTrack{
}
// update it into the appVersion
_ = version.mutateForData(channel: track, appBundleID: storeApp.bundleIdentifier)
_ = version.mutateForData(channel: track, appBundleID: storeApp.bundleIdentifier, sourceID: storeApp.sourceIdentifier)
}
}
@@ -109,6 +111,10 @@ public extension ReleaseTrack{
if key == NSExpression(forKeyPath: #keyPath(ReleaseTrack.storeApp)).keyPath
{
updateVersions(for: storeApp)
// update unique constraint attribs
self._appBundleID = storeApp?.bundleIdentifier
self._sourceID = storeApp?.sourceIdentifier
}
}
}

View File

@@ -11,7 +11,11 @@ import UIKit
public extension Source
{
static let altStoreIdentifier = try! Source.sourceID(from: Source.altStoreSourceURL)
#if ALPHA
static let altStoreGroupIdentifier = Bundle.Info.appbundleIdentifier
#else
static let altStoreGroupIdentifier = Bundle.Info.appbundleIdentifier
#endif
#if STAGING
@@ -24,13 +28,15 @@ public extension Source
#else
#if ALPHA
static let altStoreSourceURL = URL(string: "https://apps.sidestore.io/")!
static let altStoreSourceURL = URL(string: "https://sidestore.io/apps-v2.json/")!
#else
// static let altStoreSourceURL = URL(string: "https://apps.sidestore.io/")!
static let altStoreSourceURL = URL(string: "https://sidestore.io/apps-v2.json/")! // using v2 for alpha releases
static let altStoreSourceURL = URL(string: "https://sidestore.io/apps-v2.json/")!
#endif
#endif
// normalized url is the source identifier (or) p-key!
static let altStoreIdentifier = try! Source.sourceID(from: altStoreSourceURL)
}
// public struct AppPermissionFeed: Codable {
@@ -205,7 +211,8 @@ public class Source: BaseEntity, Decodable
/* Properties */
@NSManaged public var version: Int
@NSManaged public var name: String
@NSManaged public private(set) var identifier: String
@NSManaged public private(set) var identifier: String // NOTE: sourceID is just normalized sourceURL
@NSManaged public private(set) var groupID: String?
@NSManaged public var sourceURL: URL
/* Source Detail */
@@ -280,6 +287,9 @@ public class Source: BaseEntity, Decodable
case news
case featuredApps
case userInfo
// case identifier
case groupID = "identifier"
}
private override init(entity: NSEntityDescription, insertInto context: NSManagedObjectContext?)
@@ -359,6 +369,24 @@ public class Source: BaseEntity, Decodable
// Updates identifier + apps & newsItems
try self.setSourceURL(sourceURL)
// NOTE: Source ID is just normalized sourceURL. coz normalized url is the primary key which needs to be unique
// Hence if a source's URL changed, then it means it is a different source now.
// This also means that the identifier field in the source is irrelevant (if any)
// if we want grouping of sources from same author or something like that then we should have used groupID (a new field)
// shouldn't use the existing "identifier" field, hence the following is commented out
// // if an explicit identifier is present, then use it
// if let identifier = try container.decodeIfPresent(String.self, forKey: .identifier)
// {
// self.identifier = identifier
// }
// if an explicit (group)identifier is present, then use it as groupID else use sourceID as groupID too
self.groupID = try container.decodeIfPresent(String.self, forKey: .groupID) ?? self.identifier
}
catch
{
@@ -405,7 +433,7 @@ public extension Source
// TODO: Support alternate URLs
let isRecommended = recommendedSources.contains { source in
return source.identifier == self.identifier || source.sourceURL?.absoluteString.lowercased() == self.sourceURL.absoluteString
return source.identifier == self.identifier || source.sourceURL?.absoluteString.lowercased() == self.sourceURL.absoluteString.lowercased()
}
return isRecommended
}
@@ -418,14 +446,17 @@ public extension Source
}
}
internal extension Source
public extension Source
{
class func sourceID(from sourceURL: URL) throws -> String
{
let sourceID = try sourceURL.normalized()
return sourceID
}
}
internal extension Source
{
func setFeaturedApps(_ featuredApps: [StoreApp]?)
{
// Explicitly update relationships for all apps to ensure featuredApps merges correctly.
@@ -451,17 +482,23 @@ public extension Source
{
func setSourceURL(_ sourceURL: URL) throws
{
let identifier = try Source.sourceID(from: sourceURL)
self.identifier = identifier
self.sourceURL = sourceURL
// update the normalized sourceURL as the identifier
let identifier = try Source.sourceID(from: sourceURL)
try self.setSourceID(identifier)
}
func setSourceID(_ identifier: String) throws
{
self.identifier = identifier
for app in self.apps
{
app.sourceIdentifier = identifier
}
for newsItem in self.newsItems
for newsItem in self.newsItems
{
newsItem.sourceIdentifier = identifier
}
@@ -479,6 +516,7 @@ public extension Source
{
let source = Source(context: context)
source.name = "SideStore Offical"
source.groupID = Source.altStoreGroupIdentifier
source.identifier = Source.altStoreIdentifier
try! source.setSourceURL(Source.altStoreSourceURL)
@@ -491,13 +529,14 @@ public extension Source
return source
}
class func make(name: String, identifier: String, sourceURL: URL, context: NSManagedObjectContext) -> Source
class func make(name: String, groupID: String, sourceURL: URL, context: NSManagedObjectContext) -> Source
{
let source = Source(context: context)
source.name = name
source.identifier = identifier
source.sourceURL = sourceURL
source.sourceURL = sourceURL
source.identifier = try! Source.sourceID(from: sourceURL)
return source
}
}

View File

@@ -1,8 +1,8 @@
// Configuration settings file format documentation can be found at:
// https://help.apple.com/xcode/#/dev745c5c974
MARKETING_VERSION = 0.6.0
CURRENT_PROJECT_VERSION = 6000
MARKETING_VERSION = 0.6.1
CURRENT_PROJECT_VERSION = 0601
// Vars to be overwritten by `CodeSigning.xcconfig` if exists
DEVELOPMENT_TEAM = S32Z3HMYVQ

View File

@@ -28,7 +28,7 @@ Why iOS 15? Targeting such a recent version of iOS allows us to accelerate devel
SideStore is a just regular, sandboxed iOS application. The AltStore app target contains the vast majority of SideStore's functionality, including all the logic for downloading and updating apps through SideStore. SideStore makes heavy use of standard iOS frameworks and technologies most iOS developers are familiar with.
### EM Proxy
[SideServer mobile](https://github.com/jkcoxson/em_proxy) powers the defining feature of SideStore: untethered app installation. By levaraging an App Store app with additional entitlements (WireGuard) to create the VPN tunnel for us, it allows SideStore to take advantage of [Jitterbug](https://github.com/osy/Jitterbug)'s loopback method without requiring a paid developer account.
[SideServer mobile](https://github.com/jkcoxson/em_proxy) powers the defining feature of SideStore: untethered app installation. By leveraging an App Store app with additional entitlements (WireGuard or StosVPN) to create the VPN tunnel for us, it allows SideStore to take advantage of [Jitterbug](https://github.com/osy/Jitterbug)'s loopback method without requiring a paid developer account.
### Minimuxer
[Minimuxer](https://github.com/jkcoxson/minimuxer) is a lockdown muxer that can run inside iOSs sandbox. It replicates Apples usbmuxd protocol on MacOS to “discover” devices to interface with wireguard On-Device.

View File

@@ -0,0 +1,54 @@
//
// BuildInfo.swift
// AltStore
//
// Created by Magesh K on 23/03/25.
// Copyright © 2025 SideStore. All rights reserved.
//
import Foundation
public class BuildInfo{
private static let MARKETING_VERSION_TAG = "CFBundleShortVersionString"
private static let CURRENT_PROJECT_VERSION_TAG = kCFBundleVersionKey as String
private static let XCODE_VERSION_TAG = "DTXcode"
private static let XCODE_REVISION_TAG = "DTXcodeBuild"
let bundle: Bundle
public init(){
bundle = Bundle.main
}
enum BundleError: Swift.Error {
case invalidURL
}
public init(url: URL) throws {
guard let bundle = Bundle(url: url) else {
throw BundleError.invalidURL
}
self.bundle = bundle
}
public lazy var project_version: String? = {
let version = bundle.object(forInfoDictionaryKey: Self.CURRENT_PROJECT_VERSION_TAG) as? String
return version
}()
public lazy var marketing_version: String? = {
let version = bundle.object(forInfoDictionaryKey: Self.MARKETING_VERSION_TAG) as? String
return version
}()
public lazy var xcode: String? = {
let xcode = bundle.object(forInfoDictionaryKey: Self.XCODE_VERSION_TAG) as? String
return xcode
}()
public lazy var xcode_revision: String? = {
let revision = bundle.object(forInfoDictionaryKey: Self.XCODE_REVISION_TAG) as? String
return revision
}()
}