Я хочу создать универсальный 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()
}
}