Skip to content

Allogy/Sync

 
 

Repository files navigation

Sync

Sync eases your everyday job of parsing a JSON response and syncing it with Core Data. Sync is a lightweight Swift library that uses a convention-over-configuration paradigm to facilitate your workflow.

Note: This is a modernized fork of 3lvis/Sync, updated for Swift 6 with full concurrency support. The original project was maintained from 2014 to 2021.

Features

  • Automatic mapping of camelCase or snake_case JSON into Core Data
  • Thread-safe saving with Swift Concurrency (async/await)
  • Diffing of changes: inserted, updated, and deleted objects (automatically purged)
  • Auto-mapping of relationships (one-to-one, one-to-many, and many-to-many)
  • Smart updates: only updates NSManagedObjects if server values differ from local ones
  • Uniquing: one Core Data entry per primary key
  • CRUD convenience methods for fetch, insert, update, and delete

Requirements

  • Swift 6.0+
  • iOS 18.0+ / macOS 15.0+
  • Xcode 16.0+

Installation

Swift Package Manager

Add Sync to your project via SPM. In Xcode, go to File > Add Package Dependencies and enter:

https://github.com/3lvis/Sync

Or add it to your Package.swift:

dependencies: [
    .package(url: "https://github.com/3lvis/Sync", from: "7.0.0")
]

Getting Started

Core Data Stack

Replace your Core Data stack with an instance of DataStack:

let dataStack = DataStack(modelName: "MyModel")

Syncing JSON

try await dataStack.sync(json, inEntityNamed: "User")
// New objects inserted, existing objects updated, missing objects deleted

With a predicate filter:

let yesterday = Date.now.addingTimeInterval(-24 * 60 * 60)
let predicate = NSPredicate(format: "createdAt > %@", yesterday as NSDate)

try await dataStack.sync(json, inEntityNamed: "User", predicate: predicate)

With a parent relationship:

try await dataStack.sync(noteChanges, inEntityNamed: "Note", parent: user)

CRUD Operations

// Fetch
let user: User? = try dataStack.fetch(userId, inEntityNamed: "User")

// Insert or Update
let user: User = try dataStack.insertOrUpdate(changes, inEntityNamed: "User")

// Update
let user: User? = try dataStack.update(userId, with: changes, inEntityNamed: "User")

// Delete
try dataStack.delete(userId, inEntityNamed: "User")

Primary Key

Sync requires your entities to have a primary key for diffing. By default, it uses id from JSON and id (or remoteID) from Core Data.

You can mark any attribute as the primary key by adding sync.isPrimaryKey with value true in the Core Data model's User Info.

Attribute Mapping

Attributes map automatically between snake_case JSON and camelCase Core Data:

  • first_name in JSON maps to firstName in Core Data
  • Reserved words are prefixed with the entity name (type becomes userType)
  • Acronyms are normalized (user_id maps to userID)

Use sync.remoteKey in User Info to customize the mapping for any attribute.

Relationship Mapping

Sync automatically maps relationships using nested JSON objects or simplified ID references:

// Full object
{ "id": 6, "name": "Shawn", "notes": [{ "id": 0, "text": "Hello" }] }

// Simplified (ID-only, requires pre-synced data)
{ "id": 6, "name": "Shawn", "notes_ids": [0, 1, 2] }

JSON Exporting

Convert NSManagedObject back to JSON:

let json = user.export()
// Keys automatically converted to snake_case

// Without relationships
let json = user.export(using: .excludedRelationships)

Documentation

Detailed documentation is available in the docs folder:

Migration Guide

Migrating from Sync 6.x (Swift 5)

Sync 7.0 is a major update that modernizes the library for Swift 6 with full concurrency support. Here's what you need to know:

Platform Requirements

The minimum deployment targets have been raised significantly:

  • iOS 18.0+ (was iOS 8.0)
  • macOS 15.0+ (was macOS 10.10)
  • watchOS and tvOS targets have been removed

If your app still supports older OS versions, continue using Sync 6.x.

Installation

CocoaPods and Carthage are no longer supported. Migrate to Swift Package Manager:

  1. Remove Sync from your Podfile/Cartfile
  2. Add the SPM dependency (see Installation)

API Changes: Completion Handlers to Async/Await

All completion-handler APIs are deprecated. Replace them with their async equivalents:

// Before (Sync 6.x)
dataStack.sync(json, inEntityNamed: "User") { error in
    if let error = error {
        print("Error: \(error)")
    }
}

// After (Sync 7.x)
do {
    try await dataStack.sync(json, inEntityNamed: "User")
} catch {
    print("Error: \(error)")
}
// Before
Sync.changes(json, inEntityNamed: "User", dataStack: dataStack) { error in
    // ...
}

// After
try await Sync.changes(json, inEntityNamed: "User", dataStack: dataStack)

The deprecated completion-handler methods are still available to ease migration, but will be removed in a future release.

Objective-C Compatibility Removed

The CompatibleOperationOptions enum and compatibleChanges methods have been removed. If you were calling Sync from Objective-C code, you will need to create a Swift wrapper or migrate that code to Swift.

Swift 6 Strict Concurrency

The library is compiled with Swift 6 strict concurrency. Key types conform to Sendable where appropriate. If you encounter concurrency warnings when interacting with Sync APIs, ensure your data is being passed safely across isolation boundaries.

License

Sync is available under the MIT license. See the LICENSE file for more info.

Acknowledgments

Sync was originally created and maintained by Elvis Nunez from 2014 to 2021, with contributions from nearly 30 developers across 130 releases.

About

JSON to Core Data and back. Swift Core Data Sync.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages

  • Swift 100.0%