swift iOS generi c Realm manager - PullRequest
       13

swift iOS generi c Realm manager

0 голосов
/ 10 марта 2020

Я хочу создать универсальный c Менеджер областей для выполнения CRUD операций. Я могу addOrUpdate выделить один объект в локальную базу данных, используя приведенный ниже код

var profileManager: RealmManagerN<Profile>?
self.profileManager?.addOrUpdate(object: value.profile!,
                                                         completion: { error in
                                                            if let err = error {
                                                                print("Error \(err.localizedDescription)")
                                                            } else {

                                                            }
 })

Но я сталкиваюсь с проблемой при добавлении array объекта. Я попробовал ниже код

var deviceManager: RealmManagerN<Devices>?
self.deviceManager?.addOrUpdate(object: value.devices!,
                                                             completion: { error in
                                                                if let err = error {
                                                                    print("Error \(err.localizedDescription)")
                                                                } else {

                                                                }
                            })

Ниже моя Devices модель

import RealmSwift

class Devices: Object, Decodable {
    @objc dynamic var addedOn: Double = 0.0
    @objc dynamic var category: String? = nil
    @objc dynamic var deviceId = ""
    @objc dynamic var deviceName: String? = nil
    @objc dynamic var deviceType : String? = nil
    @objc dynamic var firmware: String? = nil
    @objc dynamic var isActive: Bool = true
    @objc dynamic var lastUpdate: Double = 0.0
    @objc dynamic var uuDeviceId = ""
    @objc dynamic var model: String? = nil
    @objc dynamic var typeId = 0
    @objc dynamic var userId = 0

    enum CodingKeys: String, CodingKey {
        case addedOn = "added_on"
        case category = "category"
        case deviceId = "device_id"
        case deviceName = "device_name"
        case deviceType = "device_type"
        case firmware = "firmware"
        case isActive = "is_active"
        case lastUpdate = "last_update"
        case uuDeviceId = "uu_device_id"
        case model = "model"
        case typeId = "type_id"
        case userId = "user_id"
    }
    override public static func primaryKey() -> String? {
        return "uuDeviceId"
    }
}

Примечание: value.devices это array из Devices

Я получаю эту ошибку Невозможно преобразовать значение типа «[Устройства]» в ожидаемый тип аргумента «Устройства» . Как вставить коллекцию?

РЕДАКТИРОВАТЬ : Это мой RealmManagerN.swift

import Foundation
import Realm
import RealmSwift

/**
 Realm manager class that reduces the boiler plate needed when creating a realm transaction.
 createOrUpdate, and Delete uses background thread

 - warning: This class assumes that every existing model being passed has a primaryKey set to it
 */
public class RealmManagerN<T> {
    public typealias Completion = ((_ error: Error?) -> Void)
    var realm: Realm?
    var background: RealmThread?

    init(configuration: Realm.Configuration?,
         fileUrl: URL?) {

        background = RealmThread(start: true,
                                 queue: nil)
        background?.enqueue {[weak self] in
            guard let self = self else { return }
            do {
                if let config = configuration {
                    self.realm = try Realm(configuration: config)
                } else if let fileUrl = fileUrl {
                    self.realm = try Realm(fileURL: fileUrl)
                } else {
                    self.realm = try Realm()
                }
            } catch let error {
                fatalError(error.localizedDescription)
            }
        }
    }
}

extension RealmManager {
    fileprivate func addOrUpdateWithRealm<Q: Collection>(realm: Realm,
                                                         object: Q,
                                                         completion: @escaping Completion) where Q.Element == Object  {
        do {
            try realm.write {
                realm.add(object,
                          update: .error)
                DispatchQueue.main.async {
                    completion(nil)
                }
            }
        } catch (let error) {
            DispatchQueue.main.async {
                completion(error)
            }
        }
    }

    fileprivate func addOrUpdateWithRealm<T: Object>(realm: Realm,
                                                     object: T,
                                                     completion: @escaping Completion) {
        do {
            try realm.write {
                realm.add(object,
                          update: .error)

                DispatchQueue.main.async {
                    completion(nil)
                }
            }
        } catch (let error) {
            DispatchQueue.main.async {
                completion(error)
            }
        }
    }

    fileprivate func write(rlmObject: Realm, writeBlock:()-> Void) -> Error? {
        do {
            //try to do a realm transaction
            try rlmObject.write {
                writeBlock()
            }
        } catch let error {
            //catch and return the error if occurs
            return error
        }
        //no error
        return nil
    }

    fileprivate func fetch<Q: Object>(condition: String?,
                                      completion: @escaping(_ result: Results<Q>) -> Void) {

        guard let realm = realm else { return }

        // All object inside the model passed.
        var bufferObjects = realm.objects(Q.self)

        if let cond = condition {
            // filters the result if condition exists
            bufferObjects = bufferObjects.filter(cond)
        }

        DispatchQueue.main.async {
            //if let safe = bufferObjects.copy() as? Results<Q> {
                completion(safe)
            //}
        }
    }
}

extension RealmManagerN where T: Collection, T.Element == Object {

    /// Add or Update an object to existing Model
    ///
    /// Accept any object that conforms to Collection Protocol,
    /// Takes a closure as escaping
    /// parameter.
    ///
    /// - Parameter object: [Object] to be saved.
    /// - Parameter completion: Closure called after
    ///   realm transaction
    /// - Parameter error: an optional value containing error
    public func addOrUpdate(object: T,
                            completion:@escaping Completion) {

        background?.enqueue {[weak self] in
            guard let self = self else { return }

            guard let realm = self.realm else { return }

            self.addOrUpdateWithRealm(realm: realm,
                                      object: object,
                                      completion: completion)
        }
    }

    //MARK: - File Private
    fileprivate func delete(condition: String?,
                            objects: T,
                            completion:@escaping(_ error: Error?) -> Void) {
        let group = DispatchGroup()
        var error: Error?

        background?.enqueue {[weak self] in
            group.enter()
            guard let self = self else { return }
            guard let realm = self.realm else { return }

            error = self.write(rlmObject: realm, writeBlock: {
                realm.delete(objects)
                group.leave()
            })
        }

        group.wait()
        DispatchQueue.main.async {
            completion(error)
        }
    }
}

extension RealmManagerN where T: Object {
    /// Add or Update an object to existing Model
    ///
    /// Accept any object that is a subclass of Object or RealmObject,
    /// Takes a closure as escaping
    /// parameter.
    ///
    /// - Parameter object: Object to be saved.
    /// - Parameter completion: Closure called after
    ///   realm transaction
    /// - Parameter error: an optional value containing error
    public func addOrUpdate(configuration: Realm.Configuration? = nil,
                            object: T,
                            completion: @escaping Completion) {
        background?.enqueue {[weak self] in
            guard let self = self else { return }

            guard let realm = self.realm else { return }

            self.addOrUpdateWithRealm(realm: realm,
                                      object: object,
                                      completion: completion)
        }
    }

    /// Fetches object from existing model
    ///
    ///
    /// - Parameter type: Type representing the object to be fetch, must be
    /// subclass of Object
    /// - Parameter condition: Predicate to be used when fetching
    ///   data from the Realm database (Optional: String)
    /// - Parameter completion: Closure called after the
    ///   realm transaction
    /// - Parameter result: An Array of Object as result from
    ///   the fetching
    public func fetchWith(condition: String?,
                          completion:@escaping(_ result: Results<T>) -> Void) {

        background?.enqueue {[weak self] in
            guard let self = self else { return }

            self.fetch(condition: condition,
                       completion: completion)
        }
    }

    /// Deletes an object from the existing model
    ///
    ///
    /// - Parameter configuration: Realm Configuration to be used
    /// - Parameter model: A string of any class NAME that inherits from 'Object' class
    /// - Parameter condition: Predicate to be used when deleting
    ///   data from the Realm database (Optional: String)
    /// - Parameter completion: Closure called after the
    ///   realm transaction
    /// - Parameter error: an optional value containing error
    public func deleteWithObject(_ object: T?,
                                 condition: String,
                                 completion:@escaping(_ error: Error?) -> Void) {

        background?.enqueue {[weak self] in
            guard let self = self else { return }

            self.delete(object: object,
                        condition: condition,
                        completion: completion)
        }
    }

    ///MARK: FilePrivates
    fileprivate func delete(object: T?,
                            condition: String?,
                            completion:@escaping(_ error: Error?) -> Void) {
        guard let realm = realm else { return }

        let group = DispatchGroup()
        var error: Error?

        background?.enqueue {[weak self] in
            group.enter()
            guard let self = self else { return }

            if object == nil {
                var fetched = realm.objects(T.self)

                if let cond = condition {
                    // filters the result if condition exists
                    fetched = fetched.filter(cond)
                }

                error = self.write(rlmObject: realm, writeBlock: {
                    realm.delete(fetched)
                    group.leave()
                })
            } else {
                if let object = object {
                    error = self.write(rlmObject: realm, writeBlock: {
                        realm.delete(object)
                        group.leave()
                    })
                }
            }
        }

        group.wait()
        DispatchQueue.main.async {
            completion(error)
        }
    }
}

Ниже приведен код для RealmThread

import Foundation

/// FIFO. First-In-First-Out guaranteed on exactly same thread.
class RealmThread: Thread {

    typealias Block = () -> ()

    private let condition = NSCondition()
    private(set) var queue = [Block]()
    private(set) var paused: Bool = false

    /**
     Designated initializer.
     - parameters:
     - start: Boolean whether thread should start immediately. Defaults to true.
     - queue: Initial array of blocks to add to enqueue. Executed in order of objects in array.
     */
    init(start: Bool = true, queue: [Block]? = nil) {
        super.init()
        // Add blocks initially to queue
        if let queue = queue {
            for block in queue {
                enqueue(block: block)
            }
        }
        // Start thread
        if start {
            self.start()
        }
    }

    /**
     The main entry point routine for the thread.
     You should never invoke this method directly. You should always start your thread by invoking the start method.
     Shouldn't invoke `super`.
     */
    final override func main() {

        // Infinite loops until thread is cancelled
        while true {
            // Use NSCondition. Comments are from Apple documentation on NSCondition
            // 1. Lock the condition object.
            condition.lock()

            // 2. Test a boolean predicate. (This predicate is a boolean flag or other variable in your code that indicates whether it is safe to perform the task protected by the condition.)
            // If no blocks (or paused) and not cancelled
            while (queue.count == 0 || paused) && !isCancelled  {
                // 3. If the boolean predicate is false, call the condition object’s wait or waitUntilDate: method to block the thread. Upon returning from these methods, go to step 2 to retest your boolean predicate. (Continue waiting and retesting the predicate until it is true.)
                condition.wait()
            }
            // 4. If the boolean predicate is true, perform the task.
            // If your thread supports cancellation, it should check this property periodically and exit if it ever returns true.
            if (isCancelled) {
                condition.unlock()
                return
            }

            // As per http://stackoverflow.com/a/22091859 by Marc Haisenko:
            // Execute block outside the condition, since it's also a lock!
            // We want to give other threads the possibility to enqueue
            // a new block while we're executing a block.
            let block = queue.removeFirst()
            condition.unlock()
            // Run block
            block()
        }
    }

    /**
     Add a block to be run on the thread. FIFO.
     - parameters:
     - block: The code to run.
     */
    final func enqueue(block: @escaping Block) {
        // Lock to ensure first-in gets added to array first
        condition.lock()
        // Add to queue
        queue.append(block)
        // Release from .wait()
        condition.signal()
        // Release lock
        condition.unlock()
    }

    /**
     Start the thread.
     - Warning: Don't start thread again after it has been cancelled/stopped.
     - SeeAlso: .start()
     - SeeAlso: .pause()
     */
    final override func start() {
        // Lock to let all mutations to behaviour obey FIFO
        condition.lock()
        // Unpause. Might be in pause state
        // Start
        super.start()
        // Release from .wait()
        condition.signal()
        // Release lock
        condition.unlock()
    }

    /**
     Cancels the thread.
     - Warning: Don't start thread again after it has been cancelled/stopped. Use .pause() instead.
     - SeeAlso: .start()
     - SeeAlso: .pause()
     */
    final override func cancel() {
        // Lock to let all mutations to behaviour obey FIFO
        condition.lock()
        // Cancel NSThread
        super.cancel()
        // Release from .wait()
        condition.signal()
        // Release lock
        condition.unlock()
    }

    /**
     Pause the thread. To completely stop it (i.e. remove it from the run-time), use `.cancel()`
     - Warning: Thread is still runnin,
     - SeeAlso: .start()
     - SeeAlso: .cancel()
     */
    final func pause() {
        // Lock to let all mutations to behaviour obey FIFO
        condition.lock()
        //
        paused = true
        // Release from .wait()
        condition.signal()
        // Release lock
        condition.unlock()
    }

    /**
     Resume the execution of blocks from the queue on the thread.
     - Warning: Can't resume if thread was cancelled/stopped.
     - SeeAlso: .start()
     - SeeAlso: .cancel()
     */
    final func resume() {
        // Lock to let all mutations to behaviour obey FIFO
        condition.lock()
        //
        paused = false
        // Release from .wait()
        condition.signal()
        // Release lock
        condition.unlock()
    }

    /**
     Empty the queue for any blocks that hasn't been run yet
     - SeeAlso:
     - .enqueue(block: Block)
     - .cancel()
     */
    final func emptyQueue() {
        // Lock to let all mutations to behaviour obey FIFO
        condition.lock()
        // Remove any blocks from the queue
        queue.removeAll()
        // Release from .wait()
        condition.signal()
        // Release lock
        condition.unlock()
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...