Friday, January 25, 2019

Core Data with Swift's Generics

It requires a lot of codes to do CRUD (Create, Read, Update, and Delete) operations on an entity. If there are a lot of entities, we would end up writing hundreds of lines of duplicate codes. With Swift's Generics, we only write the CRUD methods once and are reused by with any entities.

GenericDao.swift
    
import Foundation
import CoreData

public class GenericDao {
    private var context: NSManagedObjectContext!
    
    public init(_ context: NSManagedObjectContext) {
        self.context = context
    }
    
    public func newManagedObject() throws -> T {
        var savedEntity:T!
        context.performAndWait {
            savedEntity = NSEntityDescription.insertNewObject(forEntityName: T.entity().name!, into: context!) as! T
        }
        return savedEntity
    }
    
    public func saveChanges() throws {
        var error: Error?
        if (context?.hasChanges)! {
            do {
                try context?.save()
            } catch let saveError {
                error = saveError
            }
        }
        if let e = error {
            throw e
        }
    }
    
    public func getAll(includePendingChanges:Bool = false) throws -> [T] {
        var result:[T] = []
        
        var error: Error?
        context?.performAndWait {
            let req:NSFetchRequest = T.fetchRequest() as! NSFetchRequest
            req.includesPendingChanges = includePendingChanges
            do {
                result = (try context?.fetch(req) as? [T])!
            } catch let saveError {
                error = saveError
            }
        }
        if let e = error {
            throw e
        }
        
        return result
    }
    
    public func get(orderBy attrName:String? = nil, ascending:Bool = true, offset:UInt? = 0, limit:UInt? = nil, includePendingChanges:Bool = false) throws -> [T] {
        var result:[T] = []
        
        var error:Error?
        context?.performAndWait {
            let req:NSFetchRequest = T.fetchRequest() as! NSFetchRequest
            req.includesPendingChanges = includePendingChanges
            req.fetchOffset = Int(offset!)
            
            if limit != nil {
                req.fetchLimit = Int(limit!)
            }
            
            let sortDescriptor = NSSortDescriptor(key: attrName, ascending: ascending)
            req.sortDescriptors = [sortDescriptor]
            
            do {
                result = (try context?.fetch(req) as? [T])!
            } catch let saveError {
                error = saveError
            }
        }
        if let e = error {
            throw e
        }
        return result
    }
    
    public func deleteAll() throws {
        var error:Error?
        context?.performAndWait {
            let req = NSBatchDeleteRequest(fetchRequest: T.fetchRequest())
            do {
                try context?.execute(req)
            } catch let saveError {
                error = saveError
            }
        }
        if let e = error {
            throw e
        }
    }
    
    public func delete(by attrName:String, _ attrValue:[Int]) throws {
        var error:Error?
        context?.performAndWait {
            let fetchReq = T.fetchRequest()
            fetchReq.predicate = getPredicate(attrName, attrValue)
            let req = NSBatchDeleteRequest(fetchRequest: fetchReq)
            
            do {
                try context?.execute(req)
            } catch let saveError {
                error = saveError
            }
        }
        if let e = error {
            throw e
        }
    }
    
    private func getPredicate(_ attrName:String, _ attrValue:[Int]) -> NSPredicate {
        return NSPredicate(format: "\(attrName) IN %@", attrValue)
    }
}
 

No comments:

Post a Comment