CoreData based Swift ORM

Overview

Swift ORM

SWORM

Features

  1. Pure swift objects - no more subclasses of NSManagedObject
  2. Extensible attribute system - store any type in CoreData storage by implementing a simple protocol in type extension
  3. Strongly typed data queries
  4. Progressive migrations

Motivation

CoreData is hard. It was originally designed as a data layer for applications with a focus on I/O performance, but hardware has become more powerful over time and the complexity of CoreData still persists (lol). In modern applications, building CoreData-based data layer is expensive and often unreasonable decision.

Even the NSPersistentContainer doesn't relieve us of the need to keep track of the lifecycle of the managed objects associated with the context and remember to read / write on the context queue. In addition, an application often has a second set of data models, similar to managed objects and code for converting between the two sets of models.

Apple is aware of all of this and in modern guides prefers data persistence based on Codable models.

At the same time, CoreData has many advantages - a powerful visual editor for data models, automatic migrations, a simplified (compared to SQL) query system, secure multi-threaded access to data out of the box, and so on.

Sworm is a tool that hides the complexity of CoreData from the developer, but keeps the advantages.

How to use

Basic usage

Attributes

Queries

Read and write your data graph

Proper setup of PersistentContainer

Progressive migrations

Examples

See tests

Installation

Use SPM:

dependencies: [
    .package(url: "https://github.com/prisma-ai/Sworm.git", .upToNextMajor(from: "1.0.0"))
]

Code style

swiftformat --self insert --swiftversion 5.3 .

Comments
  • delete operation throws build error

    delete operation throws build error

    I created a generic repository that gets a PersistentContainer injected (I followed you instructions up to there):

    public class BaseRepository<T>: Repository where T: ManagedObjectConvertible {
    	
    	public var persistentContainer: PersistentContainer
    	
    	init(persistentContainer: PersistentContainer) {
    		
    		self.persistentContainer = persistentContainer
    	}
    	
    	public func allItems() throws -> [T] {
    
    		try self.persistentContainer.perform { context in
    			
    			try context.fetch(T.all).map { try $0.decode() }
    		}
    	}
    	
    	public func create(_ item: T) throws {
    		try self.persistentContainer.perform { context in
    			
    			try context.insert(item)
    		}
    	}
    	
    	public func remove(_ item: T) throws {
    		try self.persistentContainer.perform { context in
    			
    			try context.delete(item)
    		}
    	}
    } 
    

    However, the "remove" function where the delete operation on the context is used, throws a build error:

    Cannot convert value of type 'T' to expected argument type 'ManagedObject<PlainObject>'
    

    I tried around, but I cannot figure out how to fix this. Do you have any ideas?

    The calling part looks like this (a repository factory):

    	public func makeRepository<R>(for storyType: StoreType) -> R where R: Repository {
    
    		switch storyType {
    			case .country:
    				return BaseRepository<Country>(persistentContainer: persistentContainer) as! R
    				
    			case .location:
    				return BaseRepository<Location>(persistentContainer: persistentContainer) as! R
    		}
    	}
    

    where

    public protocol Repository {
    	associatedtype T: ManagedObjectConvertible
    	
    	var persistentContainer: PersistentContainer { get }
    	
    	func allItems() throws -> [T]
    }
    

    Thanks for your support!

    opened by innoreq 9
  • Relations cannot be set

    Relations cannot be set

    Hello again,

    I'm struggling with relations and their handling. The use case is:

    • There are companies, that have a many-to-many-relationship with persons
    • I create a company, and I create some persons.
    • I want to assign the persons to the company.

    Actually, pretty simple.

    The test case is:

    		let domainStore: DomainStore = .init(kind: .temporary)
    
    		let companyStore = GenericAPI<Company>(domainStore: domainStore)
    		let personStore = GenericAPI<Person>(domainStore: domainStore)
    		
    		companyStore.refresh()
    		personStore.refresh()
    		
    		XCTAssertEqual(companyStore.list, [])
    		XCTAssertEqual(personStore.list, [])
    
    		// Add some persons.
    		var new1: Person = personStore.add()
    		new1.name = "Paul"
    		personStore.save(new1)
    		
    		var new2: Person = personStore.add()
    		new2.name = "Peter"
    		personStore.save(new2)
    		
    		XCTAssertEqual(personStore.list.count, 2)
    		
    		// Add a company.
    		var new3: Company = companyStore.add()
    		new3.name = "The AG"
    		companyStore.save(new3)
    				
    		XCTAssertEqual(companyStore.list.count, 1)
    		XCTAssertEqual(companyStore.list[0].name, "The AG")
    		
    		new3.persons = [new1, new2]
    

    The error occurts in the last line, where the compiler says: "Value of type Company has no member persons."

    Now, Company is defined such:

    public struct Company: DomainType {
    	
    	public init(uuid: UUID, name: String) {
    		self.uuid = uuid
    		self.name = name
    	}
    	
    	public var uuid: UUID
    	public var name: String
    }
    
    extension Company {
    	
    	public static var idKeyPath: KeyPath<Company, UUID> {
    		\.uuid
    	}
    	
    	public init() {
    		self.uuid = .init()
    		self.name = .init()
    	}
    	
    	public static let entityName = "CompanyMO"
    	
    	public static let attributes: Set<Attribute<Company>> = [
    		
    		.init(\.uuid, "uuid"),
    		.init(\.name, "name"),
    	]
    	
    	public struct Relations {
    		public init() {}
    		public let persons = ToManyRelation<Person>("persons")
    	}
    	
    	public static let relations = Relations()
    }
    
    

    So, actually everything as stated in your directions and test cases, where a Book has authors.

    The public init() was kind of desperation task, cause I thought it may be a visiblity problem (the domain types are defined in a package), but it did not change anything.

    So it seems I'm doing something wrong - but what?

    Thanks in advance for your help!

    opened by innoreq 4
  • Is it possible to use Swarm as change tracker in CoreData?

    Is it possible to use Swarm as change tracker in CoreData?

    Hi

    Is it possible to use Swarm as change tracker in CoreData? If not Could you give advise how implement this functionality in cooperation with Sworm?

    Thank you

    opened by shalom-aviv 3
  • Q: How to update an existing item?

    Q: How to update an existing item?

    Just for better understanding: There is no "update" method in Sworm, just insert and delete. How is an existing object updated?

    My use case is an editor sheet, that provides "cancel" and "save" operations. This includes (a) the need for a rollback, and (b) the need for an update.

    The missing "update" operation may be interpreted in a way that Sworm updates "on-the-fly" whenever an attribute changes.

    So, how does Sworm work in this context? How should an update intentionally be handled?

    opened by innoreq 3
  • Cold you please add sample for

    Cold you please add sample for "read / write data models or attributes separately."

    .... 
    In addition to accessing graphics, ManagedObject has a set of paired `encode` / `decode` methods 
    that allow you to read / write data models or attributes separately.
    

    from here

    opened by shalom-aviv 2
  • Key path value type cannot be converted to contextual type

    Key path value type cannot be converted to contextual type

    I'm following the guidance in basic usage docs and I have hit a snag with the package.

    The following throws a compile time error of Key path value type 'WritableKeyPath<Forecast, Date>' cannot be converted to contextual type 'KeyPath<Forecast, Date>'.

    Any tips?

    XCode 14.1 targeting iOS 16

    import Foundation
    import Sworm
    
    struct Forecast {
        let date: Date    
        let area: Area
        
        init(date: Date, area: Area) {
            self.date = date
            self.area = area
        }
    }
    
    extension Forecast : ManagedObjectConvertible {
        init() {
        }
        
        static let entityName = "Forecast"
    
        static let attributes: Set<Attribute<Forecast>> = [
            .init(\.date, "date") // Key path value type 'WritableKeyPath<Forecast, Date>' cannot be converted to contextual type 'KeyPath<Forecast, Date>'
        ]
    
        struct Relations {
            let area = ToOneRelation<Area>("area")
        }
    
        static let relations = Relations()
    }
    
    
    opened by darbio 1
Releases(1.1.0)
Owner
Prisma Labs
Prisma Labs
A Swift framework that wraps CoreData, hides context complexity, and helps facilitate best practices.

Cadmium is a Core Data framework for Swift that enforces best practices and raises exceptions for common Core Data pitfalls exactly where you make the

Jason Fieldman 123 Oct 18, 2022
🎯 PredicateKit allows Swift developers to write expressive and type-safe predicates for CoreData using key-paths, comparisons and logical operators, literal values, and functions.

?? PredicateKit PredicateKit is an alternative to NSPredicate allowing you to write expressive and type-safe predicates for CoreData using key-paths,

Faiçal Tchirou 352 Jan 3, 2023
A small set of utilities to make working with CoreData and Swift a bit easier.

SuperRecord =================== SUPPORTS SWIFT 2.0 from Version >= 1.4 ** SUPPORTS SWIFT 1.2 from Version <= 1.3 Both iOS and WatchOS A Swift CoreData

Michael Armstrong 372 Jul 19, 2022
A Swift framework that wraps CoreData, hides context complexity, and helps facilitate best practices.

Cadmium is a Core Data framework for Swift that enforces best practices and raises exceptions for common Core Data pitfalls exactly where you make them.

Jason Fieldman 123 Oct 18, 2022
CoreData + UI/Unit Tests + Pure Swift

What is it? ???? It's a Todo iOS Application with offline support by the use of Core Data which has been developed as a code challenge. It's written p

iMamad 1 Jun 1, 2022
DataKernel is a minimalistic wrapper around CoreData stack to ease persistence operations.

DataKernel What is DataKernel? DataKernel is a minimalistic wrapper around CoreData stack to ease persistence operations. It is heavily inspired by Su

Denis 16 Jun 2, 2022
SugarRecord is a persistence wrapper designed to make working with persistence solutions like CoreData in a much easier way.

What is SugarRecord? SugarRecord is a persistence wrapper designed to make working with persistence solutions like CoreData in a much easier way. Than

Modo 2.1k Dec 29, 2022
App agenda with CoreData

iOS Agenda App with CORE DATA Ejemplo de código para una aplicación de agenda, gestionando el modelo de datos con CoreData. Built using XCode 13.0 (Sw

Manu Martinez 1 Oct 19, 2021
Domain Specific Language to safely build predicates and requests to fetch a CoreData store

SafeFetching This library offers a DSL (Domain Specific Language) to safely build predicates and requests to fetch a CoreData store. Also a wrapper ar

Alexis Bridoux 13 Sep 13, 2022
iOS app built with UIKit and programatic auto-layouts to learn and apply knowledge. Offline storage using CoreData and Caching

PurpleImage Search Pixabay for images and save them offline to share and view To use: Clone the GitHub project, build and run in Xcode. The API is com

Frederico Kückelhaus 0 May 10, 2022
Simple IOS notes app written programmatically without storyboard using UIKit and CoreData

Notes Simple Notes app. Swift, UIKit, CoreData Description Simple IOS notes app

null 4 Dec 23, 2022
Aplikasi iOS To Do List dengan Storyboard & Local CoreData (Database Xcode)

ToDoList Aplikasi iOS ToDoList adalah sebuah aplikasi CRUD sederhana berbasis iOS yang digunakan untuk membuat sebuah list item. Aplikasi ini dibuat m

DK 7 Aug 1, 2022
Super awesome Swift minion for Core Data (iOS, macOS, tvOS)

⚠️ Since this repository is going to be archived soon, I suggest migrating to NSPersistentContainer instead (available since iOS 10). For other conven

Marko Tadić 306 Sep 23, 2022
A powerful and elegant Core Data framework for Swift.

A powerful and elegant Core Data framework for Swift. Usage Beta version. New docs soon... Simple do that: let query = persistentContainer.viewContext

null 782 Nov 6, 2022
CloudCore is a framework that manages syncing between iCloud (CloudKit) and Core Data written on native Swift.

CloudCore CloudCore is a framework that manages syncing between iCloud (CloudKit) and Core Data written on native Swift. Features Leveraging NSPersist

deeje cooley 123 Dec 31, 2022
Unleashing the real power of Core Data with the elegance and safety of Swift

Unleashing the real power of Core Data with the elegance and safety of Swift Dependency managers Contact Swift 5.4: iOS 11+ / macOS 10.13+ / watchOS 4

John Estropia 3.7k Jan 9, 2023
Swift client library to interact with Supabase Storage

storage-swift Swift Client library to interact with Supabase Storage.

Supabase 21 Dec 1, 2022
HitList is a Swift App shows the implementation of Core Data.

HitList HitList is a Swift App shows the implementation of Core Data. It is the demo app of Ray Wenderlich's tech blog. For details please reference G

Kushal Shingote 2 Dec 9, 2022
A type-safe, fluent Swift library for working with Core Data

Core Data Query Interface (CDQI) is a type-safe, fluent, intuitive library for working with Core Data in Swift. CDQI tremendously reduces the amount o

null 31 Oct 26, 2022