/CoreDataCombineKit

A light weight library for manipulating the main Core Data actions with Combine framework compatibility

Primary LanguageSwift


Core Data Combine Kit

A light weight library for manipulating the main Core Data actions with Combine framework compatibility.



Dependency managers

Cocoapods compatible Swift Package Manager compatible


  • Swift 5.5: iOS 13.1+ / macOS 10.15+

Setting up

You need two things:

  • A CoreDataStorageContext.
  • And a CoreDataRepository.

1- The simplest way to initialize CoreDataCombineKit is using the CoreDataManager if you have one data model file.

  • Initialize coreDataStorageContext with your data model file name.
  • Bass the context to the CoreDataManager by calling Setup method in the AppDelegate.
import UIKit
import CoreDataCombineKit

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        
        let coreDataContext: CoreDataStorageContext = .init(
            fileName: "UserManagement",
            bundle: .main,
            storeType: .sqLiteStoreType
        )
        
        CoreDataManager.setup(coreDataStorageContext: coreDataContext)
        return true
    }
}

2- Or you can initialize your own CoreDataStorageContainer on the CoreDataRepo container like:

import UIKit
import CoreDataCombineKit
import Combine

class ExampleViewController: UIViewController {
    private let coreDataContext: CoreDataStorageContext = .init(
            fileName: "UserManagement",
            bundle: .main,
            storeType: .sqLiteStoreType
        )
    private lazy var usersRepo = CoreDataRepository<User>(coreDataStorageContext: coreDataContext)
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
}

Usage

  • Suppose we have an entity called User.

Adding new entity:

func addNewUser() {
        usersRepo.insert { user in
            user.id = UUID()
            user.name = "Jhon"
        }
        .sink { completion in
            guard case .failure(let error) = completion else { return }
            print(error.localizedDescription)

        } receiveValue: { _ in
            print("Saving Done")
        }
        .store(in: &cancellables)
    }

Fetching all entities:

func fetchAllUsers() {
     usersRepo.fetch()
        .sink { completion in
            guard case .failure(let error) = completion else { return }
            print(error.localizedDescription)
                
        } receiveValue: { [weak self] users in
            guard let self = self else { return }
            // Do what you want
        }
        .store(in: &cancellables)
    }

Fetching all entities with sort:

func fetchAllUsersWithSorting() {
     usersRepo.fetch(sortDescriptors: [.init(keyPath: \User.name, ascending: true)])
        .sink { completion in
            guard case .failure(let error) = completion else { return }
            print(error.localizedDescription)
            
        } receiveValue: { [weak self] users in
            guard let self = self else { return }
            // Do what you want
        }
        .store(in: &cancellables)
    }

Fetching an entity with id:

func fetchSingleUser() {
     usersRepo.fetchEntity(with: user.id)
        .sink { completion in
            guard case .failure(let error) = completion else { return }
            print(error.localizedDescription)
                
        } receiveValue: { [weak self] user in
            guard let self = self else { return }
            // Do what you want
        }
        .store(in: &cancellables)
    }

Update an entity:

func updateUser() {
    user.name = "Updated name"

    usersRepo.update(user)
        .sink(receiveCompletion: { completion in
            guard case .failure(let error) = completion else { return }
             print(error.localizedDescription)
            
        }, receiveValue: {
            // Do what you want
        })
        .store(in: &cancellables)
    }

Delete an entity:

func deleteSingleUser() {
    usersRepo.delete(with: user.objectID)
        .sink(receiveCompletion: { completion in
            guard case .failure(let error) = completion else { return }
             print(error.localizedDescription)
            
        }, receiveValue: {
            // Do what you want
        })
        .store(in: &cancellables)
    }

Delete all entities:

func deleteAllUsers() {
    usersRepo.deleteAll()
        .sink(receiveCompletion: { completion in
            guard case .failure(let error) = completion else { return }
             print(error.localizedDescription)
            
        }, receiveValue: {
            // Do what you want
        })
        .store(in: &cancellables)
    }

Architecture

All fetching requests execusting on the main thread with the main context, But insert, update and delete requests are executing on a background thread.


Installation

  • Requires:
    • iOS 13.1 SDK and above
    • Swift 5.0 (Xcode 13+)
  • Dependencies:
    • None
  • Other notes:
    • The fetch requests executes on the main context (Main Thread).
    • Insert, Update, Delete and DeleteAll all are executing on the background context (Background Thread).

Install with CocoaPods

In your Podfile, add

pod 'CoreDataCombineKit'

and run

pod install

if you are using Macbook M1, maybe you will face an error while executing the previous command, so you can try the following commands

sudo arch -x86_64 gem install ffi
arch -x86_64 pod install

Authors