- [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)

This commit is contained in:
mahee96
2025-03-23 11:56:17 -07:00
parent 2f3be07b5d
commit a7496e08e3
21 changed files with 2903 additions and 40 deletions

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,21 @@
//
// AppPermission11To17_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 {
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,21 @@
//
// 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,21 @@
//
// Source17To17_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 {
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,21 @@
//
// StoreApp11To17_1MigrationPolicy.swift
// AltStore
//
// Created by Magesh K on 15/03/25.
// Copyright © 2025 SideStore. All rights reserved.
//
import CoreData
@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)
}
}

View File

@@ -0,0 +1,101 @@
//
// 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 {
// In the new model, "permission" is the actual permission string, which needs to be derived from the old "type"
let permission = self.derivePermissionFromType(type)
destinationPermission.setValue(permission, forKey: #keyPath(AppPermission._permission))
}
// 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 {
// Retrieve the corresponding source instance for the destination StoreApp
let sourceInstances = manager.sourceInstances(forEntityMappingName: mapping.name, destinationInstances: [dInstance])
guard let sInstance = sourceInstances.first else {
print("No source instance found for destination: \(dInstance)")
return
}
// Retrieve the source storeApp from the source appPermission
guard let storeApp = sInstance.value(forKey: #keyPath(AppPermission.app)) as? NSManagedObject else {
print("Source \(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"
case "entitlement": return type // For entitlements, we might keep the raw value
case "privacy": return type // For privacy permissions, we might keep the raw value
default: return type // Default fallback
}
}
}

View File

@@ -0,0 +1,70 @@
//
// 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 {
// 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 ReleaseTrack instance")
return
}
if let track = sInstance.value(forKey: #keyPath(ReleaseTrack._track)) as? String {
destinationPermission.setValue(track, forKey: #keyPath(ReleaseTrack._track))
}
// set initial values migrated from source as-is
if let storeApp = sInstance.value(forKey: #keyPath(ReleaseTrack.storeApp)) as? NSManagedObject{
if let appBundle = storeApp.value(forKey: #keyPath(StoreApp.bundleIdentifier)) as? String{
destinationPermission.setValue(appBundle, forKey: #keyPath(ReleaseTrack._appBundleID))
}
if let sourceID = storeApp.value(forKey: #keyPath(StoreApp.sourceIdentifier)) as? String {
destinationPermission.setValue(sourceID, forKey: #keyPath(ReleaseTrack._sourceID))
}
}
}
override func createRelationships(
forDestination dInstance: NSManagedObject,
in mapping: NSEntityMapping,
manager: NSMigrationManager
) throws {
// Retrieve the corresponding source instance for the destination StoreApp
let sourceInstances = manager.sourceInstances(forEntityMappingName: mapping.name, destinationInstances: [dInstance])
guard let sInstance = sourceInstances.first else {
print("No source instance found for destination: \(dInstance)")
return
}
// Retrieve the source storeApp from the source ReleaseTrack
guard let storeApp = sInstance.value(forKey: #keyPath(ReleaseTrack.storeApp)) as? NSManagedObject else {
print("Source \(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)
if let appBundle = storeApp.value(forKey: #keyPath(StoreApp.bundleIdentifier)) as? String{
dInstance.setValue(appBundle, forKey: #keyPath(ReleaseTrack._appBundleID))
}
if let sourceID = storeApp.value(forKey: #keyPath(StoreApp.sourceIdentifier)) as? String {
dInstance.setValue(sourceID, forKey: #keyPath(ReleaseTrack._sourceID))
}
}
}

View File

@@ -0,0 +1,185 @@
//
// 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 app as NSManagedObject in dInstance.sourceApps ?? []
{
app.setStoreAppSourceID(sourceID)
for case let version as NSManagedObject in app.storeAppVersions ?? []
{
version.setAppVersionSourceID(sourceID)
}
for case let permission as NSManagedObject in app.storeAppPermissions ?? []
{
permission.setAppPermissionSourceID(sourceID)
}
for case let screenshot as NSManagedObject in app.storeAppScreenshots ?? []
{
screenshot.setAppScreenshotSourceID(sourceID)
}
for case let tracks as NSManagedObject in app.storeAppReleaseTracks ?? []
{
tracks.setReleaseTracksSourceID(sourceID)
}
}
}
}