הגירת סכמת Core Data באפליקציות iOS – שיטות עבודה מומלצות ופתרונות

כאשר עדכון מודל Core Data גורם לקריסת האפליקציה בעת ההפעלה, המשתמשים מאבדים נתונים והצוות מאבד זמן על תיקונים דחופים. אנחנו מבצעים הגירת סכמת Core Data במפתח מלא: אנו בודקים את המודל הנוכחי, מגדירים versioning, ומיישמים תרחישי העברת נתונים פשוטים ומורכבים כאחד. הצוות שלנו מטפל בכל המחזור—מאבחון ועד תמיכה שוטפת—כך שהאפליקציה שלך תעבוד באופן אמין לאחר כל שינוי.

פיתוח ותמיכה בכל סוגי היישומים הניידים:

יישומים ניידים למידע ובידור
אפליקציות חדשות, משחקים, מדריכים, קטלוגים מקוונים, אפליקציות מזג אוויר, אפליקציות כושר ובריאות, אפליקציות נסיעות, אפליקציות חינוכיות, רשתות חברתיות ומסנג'רים, חידונים, בלוגים ופודקאסטים, פורומים, אגרגטורים
יישומים ניידים למסחר אלקטרוני
חנויות מקוונות, אפליקציות B2B, מרקטפלייסים, בורסות מקוונות, שירותי קאשבק, בורסות, פלטפורמות דרופשיפינג, תוכניות נאמנות, משלוחי מזון ומוצרים, מערכות תשלום.
יישומים ניידים לניהול תהליכים עסקיים
מערכות CRM, מערכות ERP, ניהול פרויקטים, כלים לצוותי מכירות, ניהול פיננסי, ניהול ייצור, ניהול לוגיסטיקה ומשלוחים, ניהול משאבי אנוש, מערכות ניטור נתונים
יישומים ניידים לשירותים אלקטרוניים
פלטפורמות מודעות מסווגות, בתי ספר מקוונים, בתי קולנוע מקוונים, פלטפורמות שירות אלקטרוניות, פלטפורמות קאשבק, אירוח וידאו, פורטלים נושאיים, פלטפורמות הזמנות ותזמון מקוונות, פלטפורמות מסחר מקוונות

אלה הם רק חלק מסוגי היישומים הניידים שאנו עובדים איתם, ולכל אחד מהם עשויים להיות מאפיינים ופונקציונליות ספציפיים משלו, המותאמים לצרכים ולמטרות הספציפיות של הלקוח.

השירותים שאנו מציעים
מציג 1 מתוך 1כל 1734 השירותים
הגירת סכמת Core Data באפליקציות iOS – שיטות עבודה מומלצות ופתרונות
בינוני
~2-3 ימים

הכישורים שלנו:

שאלות נפוצות

העבודות האחרונות

  • פיתוח אפליקציית מובייל עבור FEEDME
    פיתוח אפליקציית מובייל עבור FEEDME
    971
  • פיתוח אפליקציית מובייל עבור XOOMER
    פיתוח אפליקציית מובייל עבור XOOMER
    869
  • פיתוח אפליקציית מובייל עבור RHL
    פיתוח אפליקציית מובייל עבור RHL
    1289
  • פיתוח אפליקציית מובייל עבור ZIPPY
    פיתוח אפליקציית מובייל עבור ZIPPY
    1143
  • פיתוח אפליקציית מובייל עבור Affhome
    פיתוח אפליקציית מובייל עבור Affhome
    1076
  • פיתוח אפליקציית מובייל עבור חברת FLAVORS
    פיתוח אפליקציית מובייל עבור חברת FLAVORS
    677

לעתים קרובות אנו נתקלים במצבים שבהם loadPersistentStores מחזיר NSMigrationError—והאפליקציה קורסת בעת ההפעלה. ב-80% מהמקרים, הסיבה היא גרסת מודל שגויה: מפתח הוסיף תכונה חדשה ב-.xcdatamodeld, שכח ליצור גרסה חדשה, והאפליקציה מזהה אי-התאמה בין הקוד למאגר. עבור המשתמש, מדובר בקריסה בעת ההפעלה. עבור הצוות, זה תיקון חירום בשעה 2 בלילה. במשך 5 שנים, השלמנו בהצלחה מעל 50 הגירות Core Data עבור לקוחות מתוך 100 האפליקציות המובילות ב-App Store, וחסכנו להם בממוצע 30% מזמן התחזוקה.

אילו בעיות פותרת הגירת Core Data?

הגירת Core Data פותרת שתי משימות עיקריות: שימור נתונים קיימים בעת עדכון הסכמה והבטחת תאימות בין גרסאות האפליקציה. בלעדיה, כל שינוי במודל מוביל לאובדן נתונים או לקריסה. הגירה קלה (Lightweight) מטפלת אוטומטית בשינויים פשוטים (הוספת תכונות אופציונליות, הסרת שדות), בעוד שהגירה כבדה (Heavyweight) מטפלת בטרנספורמציות מורכבות. לדוגמה, שינוי סוג שדה מ-Float ל-Int64 דורש מדיניות מותאמת אישית.

הגירה קלה: מתי היא עובדת וכיצד להגדיר אותה

הגירה קלה (NSInferMappingModelAutomatically) פועלת אוטומטית עבור הוספת תכונה חדשה עם optional = true או ערך ברירת מחדל, הסרת תכונה, או שינוי שם באמצעות Renaming Identifier. היא מופעלת עם שורת קוד אחת:

let options: [String: Any] = [
    NSMigratePersistentStoresAutomaticallyOption: true,
    NSInferMappingModelAutomaticallyOption: true
]
try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: storeURL, options: options)

עבור let options: [String: Any] = [ NSMigratePersistentStoresAutomaticallyOption: true, NSInferMappingModelAutomaticallyOption: true ] try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: storeURL, options: options) :

container.persistentStoreDescriptions.first?.shouldMigrateStoreAutomatically = true
container.persistentStoreDescriptions.first?.shouldInferMappingModelAutomatically = true

חשוב: לעולם אל תערוך גרסת מודל קיימת אם האפליקציה כבר בפרודקשן. במקום זאת, צור גרסה חדשה דרך Editor → Add Model Version והגדר אותה כגרסה הנוכחית (Current Version).

הגירה כבדה: כאשר האוטומציה אינה מספיקה

אם סוג תכונה השתנה, שדה חובה לא-אופציונלי נוסף ללא ברירת מחדל, או שנדרשת טרנספורמציה של נתונים—נדרשת NSPersistentContainer מותאמת אישית. דוגמה להמרת מחרוזת למספר:

class TransactionMigrationPolicy: NSEntityMigrationPolicy {
    override func createDestinationInstances(
        forSource sourceInstance: NSManagedObject,
        in mapping: NSEntityMapping,
        manager: NSMigrationManager
    ) throws {
        let destination = NSEntityDescription.insertNewObject(
            forEntityName: mapping.destinationEntityName!,
            into: manager.destinationContext
        )
        destination.setValue(sourceInstance.value(forKey: "amount"), forKey: "amount")
        let categoryString = sourceInstance.value(forKey: "category") as? String ?? ""
        destination.setValue(CategoryMapper.intValue(for: categoryString), forKey: "categoryRaw")
        manager.associate(sourceInstance: sourceInstance, withDestinationInstance: destination, for: mapping)
    }
}

ה-Mapping Model נוצר ב-Xcode: New File → Mapping Model. שם מציינים אילו ישויות משתמשות במדיניות המותאמת אישית.

השוואה: הגירה קלה לעומת כבדה

פרמטר קלה כבדה
מהירות מיידית עבור נתונים קטנים עד מספר שניות במאגרים גדולים
אוטומציה 100% דורשת קוד Policy
מורכבות השינוי פשוטה (הוספה/הסרה/שינוי שם) כל שינוי (טרנספורמציה, מיזוג, פיצול)
סיכונים מינימליים גבוהים (נדרש גיבוי)
כמות קוד שורת אפשרויות אחת 50–200 שורות
פיתוח מהיר פי 10 ארוך יותר

הגירה קלה מהירה פי 10 מהגירה כבדה ויציבה יותר עבור שינויים פשוטים. אם אינך בטוח לגבי האסטרטגיה, בקש ייעוץ—נעזור לך לבחור בגישה האופטימלית.

כיצד פועלת הגירה פרוגרסיבית

אם משתמש לא עדכן מ-v1 ל-v5, Core Data לא משרשרת הגירות אוטומטית. נדרש מנהל (manager) כדי להחיל ברצף את כל הגרסאות מהנוכחית ליעד:

class MigrationManager {
    func migrateStore(at storeURL: URL) throws {
        var currentURL = storeURL
        while true {
            guard let sourceModel = NSManagedObjectModel.mergedModel(from: nil, forStoreMetadata: metadata(at: currentURL)),
                  let destinationModel = nextModel(after: sourceModel) else {
                break
            }
            let mappingModel = try NSMappingModel.inferredMappingModel(
                forSourceModel: sourceModel,
                destinationModel: destinationModel
            )
            let migrator = NSMigrationManager(sourceModel: sourceModel, destinationModel: destinationModel)
            let tempURL = storeURL.appendingPathExtension("migration")
            try migrator.migrateStore(from: currentURL, type: .sqlite, to: tempURL, type: .sqlite, mapping: mappingModel)
            try FileManager.default.removeItem(at: currentURL)
            try FileManager.default.moveItem(at: tempURL, to: storeURL)
        }
    }
}

ההגירה מתבצעת לפני אתחול container.persistentStoreDescriptions.first?.shouldMigrateStoreAutomatically = true container.persistentStoreDescriptions.first?.shouldInferMappingModelAutomatically = true —במסך הפתיחה (splash screen) עם מחוון התקדמות. הגירה פרוגרסיבית מפחיתה את זמן ההשבתה ב-70% בהשוואה ליצירה מחדש מלאה של מסד הנתונים.

מדוע גיבוי הוא קריטי

בצע תמיד גיבוי לפני הגירה כבדה:

let backupURL = storeURL.deletingLastPathComponent()
    .appendingPathComponent("backup_\(Date().timeIntervalSince1970).sqlite")
try FileManager.default.copyItem(at: storeURL, to: backupURL)

אם ההגירה נכשלת, שחזר את הגיבוי. זה קריטי עבור נתונים שלא ניתן לשחזר.

כיצד לבצע הגירה: הוראות שלב-אחר-שלב

  1. בצע ביקורת על המודל הנוכחי: בדוק את היסטוריית הגרסאות וסוגי השינויים.
  2. צור גרסת מודל חדשה ב-Xcode (Editor → Add Model Version).
  3. הגדר אותה כגרסה הנוכחית (Current Version).
  4. הגדר הגירה קלה (הוסף דגלים) או צור Mapping Model עבור הגירה כבדה.
  5. כתוב NSEntityMigrationPolicy מותאם אישית אם נדרשת טרנספורמציה של נתונים.
  6. יישם הגירה פרוגרסיבית (אם המשתמש דילג על גרסאות).
  7. בצע גיבוי של קובץ ה-SQLite.
  8. הרץ את ההגירה לפני אתחול הקונטיינר במסך הפתיחה.
  9. בדוק עם class TransactionMigrationPolicy: NSEntityMigrationPolicy { override func createDestinationInstances( forSource sourceInstance: NSManagedObject, in mapping: NSEntityMapping, manager: NSMigrationManager ) throws { let destination = NSEntityDescription.insertNewObject( forEntityName: mapping.destinationEntityName!, into: manager.destinationContext ) destination.setValue(sourceInstance.value(forKey: "amount"), forKey: "amount") let categoryString = sourceInstance.value(forKey: "category") as? String ?? "" destination.setValue(CategoryMapper.intValue(for: categoryString), forKey: "categoryRaw") manager.associate(sourceInstance: sourceInstance, withDestinationInstance: destination, for: mapping) } } אמיתי בסימולטור ובמכשיר.

שגיאות נפוצות

שגיאה סיבה פתרון
class MigrationManager { func migrateStore(at storeURL: URL) throws { var currentURL = storeURL while true { guard let sourceModel = NSManagedObjectModel.mergedModel(from: nil, forStoreMetadata: metadata(at: currentURL)), let destinationModel = nextModel(after: sourceModel) else { break } let mappingModel = try NSMappingModel.inferredMappingModel( forSourceModel: sourceModel, destinationModel: destinationModel ) let migrator = NSMigrationManager(sourceModel: sourceModel, destinationModel: destinationModel) let tempURL = storeURL.appendingPathExtension("migration") try migrator.migrateStore(from: currentURL, type: .sqlite, to: tempURL, type: .sqlite, mapping: mappingModel) try FileManager.default.removeItem(at: currentURL) try FileManager.default.moveItem(at: tempURL, to: storeURL) } } } אי-התאמה בסכמה צור גרסת מודל חדשה
מסך פתיחה תקוע הגירה כבדה על ה-thread הראשי בצע ברקע עם מחוון התקדמות
אובדן נתונים אין גיבוי תמיד העתק את המאגר לפני הגירה
NSPersistentContainer עריכת הגרסה הנוכחית השתמש ב-Add Model Version

מה כלול וזמני ביצוע

  • ביקורת על המודל הנוכחי והיסטוריית הגרסאות
  • יצירת גרסאות let backupURL = storeURL.deletingLastPathComponent() .appendingPathComponent("backup_\(Date().timeIntervalSince1970).sqlite") try FileManager.default.copyItem(at: storeURL, to: backupURL) חדשות
  • הגירה קלה או כבדה בהתאם לשינויים
  • NSEntityMigrationPolicy מותאם אישית לטרנספורמציה של נתונים
  • הגירה פרוגרסיבית על פני מספר גרסאות
  • גיבוי לפני הגירה

הגירה קלה (הוספת תכונות) אורכת 0.5 ימים (החל מ-$500). הגירה כבדה עם מדיניות מותאמת אישית ומעברים פרוגרסיביים אורכת 2–3 ימים (החל מ-$1500). צור קשר לביקורת על המודל שלך—נבחר את אסטרטגיית ההגירה האופטימלית. בקש ייעוץ וקבל תוכנית עבודה מפורטת.

קרא עוד על הגירת Core Data בתיעוד הרשמי של Apple.