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

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