DEV Community

Paul Allies
Paul Allies

Posted on • Updated on • Originally published at paulallies.Medium

 

CoreData and Swift

Continuing with our clean architecture, let's write a simple Todo CoreData Datasource in swift. Our applications repository method would use the a DataSource, in this case the TodoCoreDataSource. Here is a proposed files and their locations:

├── Core
└── Data
    ├── DataSource
        ├── TodoDataSource.swift
        └── CoreData
            ├── Main.xcdatamodeld
            └── TodoCoreDataSourceImpl.swift
    ├── Repository
        └── TodoRepositoryImpl.swift

├── Presentation
└── Domain
    ├── Model
    │   └── Todo.swift
    ├── Error
    │   └── TodoError.swift
    └── Repository
        └── TodoRepository.swift
Enter fullscreen mode Exit fullscreen mode

Let's first specify our Domain Model and Repository

Domain/Model/Todo.swift

struct Todo: Identifiable{
    let id: UUID
    let title: String
    let isCompleted: Bool
}

Enter fullscreen mode Exit fullscreen mode

Domain/Error/TodoError.swift

enum TodoError: Error{
    case DataSourceError, CreateError, DeleteError, UpdateError, FetchError
}
Enter fullscreen mode Exit fullscreen mode

Domain/Repository/TodoRepository.swift

protocol TodoRepository {
    func getTodos() async  -> Result<[Todo], TodoError>
    func getTodo(id: UUID) async  -> Result<Todo? , TodoError>
    func deleteTodo(_ id: UUID) async  -> Result<Bool, TodoError>
    func createTodo(_ todo: Todo) async  -> Result<Bool, TodoError>
    func updateTodo(_ todo: Todo) async  -> Result<Bool, TodoError>
}
Enter fullscreen mode Exit fullscreen mode

Data/DataSource/TodoDataSource.swift

protocol TodoDataSource{
    func getAll() async throws -> [Todo]
    func getById(_ id: UUID) async throws  -> Todo?
    func delete(_ id: UUID) async throws -> ()
    func create(todo: Todo) async throws -> ()
    func update(id: UUID, todo: Todo) async throws -> ()
}

Enter fullscreen mode Exit fullscreen mode

let's now create a CoreData Model by adding a new file and choosing Core Data -> Data Model and creating at TodoCoreDataEntity:

Domain/DataSource/CoreData/Main.xcdatamodeld

Core Data Model

To use the CoreData Main Model we need to create a data source that conforms to the TodoDataSource so that our TodoRepository can use it:

struct TodoCoreDataSourceImpl: TodoDataSource {
    let container: NSPersistentContainer

    init(){
        container = NSPersistentContainer(name: "Main")
        container.loadPersistentStores { description, error in
            if error != nil {
                fatalError("Cannot Load Core Data Model")
            }
        }
    }

    func getAll() throws -> [Todo]{
        let request = TodoCoreDataEntity.fetchRequest()
        return try container.viewContext.fetch(request).map({ todoCoreDataEntity in
            Todo(
                id: todoCoreDataEntity.id!,
                 title: todoCoreDataEntity.title!,
                isCompleted: todoCoreDataEntity.is_completed
            )
        })

    }

    func getById(_ id: UUID)  throws  -> Todo?{
        let todoCoreDataEntity = try getEntityById(id)!
        return Todo(
            id: todoCoreDataEntity.id!,
            title: todoCoreDataEntity.title!,
            isCompleted: todoCoreDataEntity.is_completed
        )

    }

    func delete(_ id: UUID) throws -> (){
        let todoCoreDataEntity = try getEntityById(id)!
        let context = container.viewContext;
        context.delete(todoCoreDataEntity)
        do{
            try context.save()
        }catch{
            context.rollback()
            fatalError("Error: \(error.localizedDescription)")
        }

    }

    func update(id: UUID, todo: Todo) throws -> (){
        let todoCoreDataEntity = try getEntityById(id)!
        todoCoreDataEntity.is_completed = todo.isCompleted
        todoCoreDataEntity.title = todo.title
        saveContext()
    }

    func create(todo: Todo) throws -> (){
        let todoCoreDataEntity = TodoCoreDataEntity(context: container.viewContext)
        todoCoreDataEntity.is_completed = todo.isCompleted
        todoCoreDataEntity.title = todo.title
        todoCoreDataEntity.id = todo.id
        saveContext()
    }


    private func getEntityById(_ id: UUID)  throws  -> TodoCoreDataEntity?{
        let request = TodoCoreDataEntity.fetchRequest()
        request.fetchLimit = 1
        request.predicate = NSPredicate(
            format: "id = %@", id.uuidString)
        let context =  container.viewContext
        let todoCoreDataEntity = try context.fetch(request)[0]
        return todoCoreDataEntity

    }

    private func saveContext(){
        let context = container.viewContext
        if context.hasChanges {
            do{
                try context.save()
            }catch{
                fatalError("Error: \(error.localizedDescription)")
            }
        }
    }

}
Enter fullscreen mode Exit fullscreen mode

and then lastly our TodoRepositoryImpl would implement our TodoRepository and call our Datasource:

struct TodoRepositoryImpl: TodoRepository{

    var dataSource: TodoDataSource

    func getTodo(id: UUID) async  -> Result<Todo?, TodoError> {
        do{
            let _todo =  try await dataSource.getById(id)
            return .success(_todo)
        }catch{
            return .failure(.FetchError)
        }

    }

    func deleteTodo(_ id: UUID) async ->  Result<Bool, TodoError>  {
        do{
            try await dataSource.delete(id)
            return .success(true)
        }catch{
            return .failure(.DeleteError)
        }

    }

    func createTodo(_ todo: Todo) async ->  Result<Bool, TodoError>   {
        do{
            try await dataSource.create(todo: todo)
            return .success(true)
        }catch{
            return .failure(.CreateError)
        }

    }

    func updateTodo(_ todo: Todo) async ->  Result<Bool, TodoError>   {
        do{
            try await dataSource.update(id: todo.id, todo:todo)
            return .success(true)
        }catch{
            return .failure(.UpdateError)
        }

    }

    func getTodos() async -> Result<[Todo], TodoError> {
        do{
            let _todos =  try await dataSource.getAll()
            return .success(_todos)
        }catch{
            return .failure(.FetchError)
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)

An Animated Guide to Node.js Event Loop

Node.js doesn’t stop from running other operations because of Libuv, a C++ library responsible for the event loop and asynchronously handling tasks such as network requests, DNS resolution, file system operations, data encryption, etc.

What happens under the hood when Node.js works on tasks such as database queries? We will explore it by following this piece of code step by step.