Created
January 12, 2017 12:12
-
-
Save anonymous/0e5e60bffc677044ab84a1994be0e90d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// DBRoot.swift | |
// italki | |
// | |
// Created by Hongli Yu on 12/10/2016. | |
// Copyright © 2016 italki. All rights reserved. | |
// | |
import Foundation | |
import RealmSwift | |
typealias ClosureType = () -> () | |
final public class DBRoot { | |
var reamlTaskThread: Thread? | |
var queueCondition: NSCondition = NSCondition() | |
var blockQueue: Array = [ClosureType]() | |
var reamlTaskRunloop: CFRunLoop? | |
init() { | |
self.config() | |
self.createTaskThread() | |
} | |
func reset() { | |
self.config() | |
} | |
// MARK: Realm Task Thread | |
/// Remember that Realm objects can only be accessed from the thread on which they were first created, so this copy will only work for Realms on the same thread. | |
/// All db opration in datamanager concurrent queue, but in the same thread | |
func createTaskThread() { | |
if self.reamlTaskThread == nil { | |
self.reamlTaskThread = Thread(target: self, | |
selector: #selector(DBRoot.taskThreadEntryPoint(_:)), | |
object: nil) | |
self.reamlTaskThread!.start() | |
} | |
} | |
@objc func taskThreadEntryPoint(_ sender: AnyObject?) { | |
autoreleasepool { | |
Thread.current.name = "realm_task_thread" | |
self.reamlTaskRunloop = CFRunLoopGetCurrent() | |
let runloop: RunLoop = RunLoop.current | |
runloop.add(NSMachPort(), forMode: RunLoopMode.commonModes) | |
runloop.run() | |
} | |
} | |
@objc func realmTaskStart() { | |
self.queueCondition.lock() | |
var block: ClosureType? | |
if self.blockQueue.first != nil { | |
block = self.blockQueue.removeFirst() | |
#if DEBUG | |
//print(block) | |
#endif | |
} | |
self.queueCondition.unlock() | |
block?() | |
} | |
func enqueueRealmTask(_ block: @escaping ClosureType) { | |
self.queueCondition.lock() | |
self.blockQueue.append(block) // how to copy a block | |
self.queueCondition.unlock() | |
} | |
func addTask(_ block: @escaping ClosureType) { | |
self.enqueueRealmTask(block) | |
while true { | |
if self.reamlTaskRunloop == nil { | |
#if DEBUG | |
print("waiting run loop ready!") | |
#endif | |
} else { | |
break | |
} | |
} | |
weak var weakSelf = self | |
CFRunLoopPerformBlock(self.reamlTaskRunloop, CFRunLoopMode.commonModes.rawValue) { | |
let strongSelf = weakSelf | |
strongSelf?.realmTaskStart() | |
} | |
CFRunLoopWakeUp(self.reamlTaskRunloop) | |
} | |
// MARK: Realm Config | |
func config() { | |
let userName: String = UserCenter.defaultCenter.currentUser.nickname | |
if userName.isValid() { | |
self.setDefaultRealmForUser(userName) | |
} | |
} | |
func setDefaultRealmForUser(_ username: String) { | |
var config = Realm.Configuration() | |
config.schemaVersion = 1 | |
config.migrationBlock = { migration, oldSchemaVersion -> Void in | |
migration.enumerateObjects(ofType: PackageDetail.className(), { (oldObject, newObject) in | |
// Add the `email` property to Realms with a schema version of 0 | |
if oldSchemaVersion < 1 { | |
newObject!["packageMessageUnreadCount"] = 0 | |
newObject!["packageMessageTotalCount"] = 0 | |
} | |
}) | |
} | |
let path = FileDirectoryManager.documentDirectory() + "/\(username).realm" | |
config.fileURL = URL(fileURLWithPath: path) | |
// Set this as the configuration used for the default Realm | |
Realm.Configuration.defaultConfiguration = config | |
} | |
// MARK: Database Actions | |
func insertPackages(_ packageViewModels: Array<PackageViewModel>?, | |
completion: @escaping (_ result: Bool, _ error: NSError?)->()?) { | |
let block = { | |
autoreleasepool { | |
#if DEBUG | |
print(Realm.Configuration.defaultConfiguration.fileURL!) | |
#endif | |
do { | |
let realm = try Realm() | |
realm.beginWrite() | |
for packageViewModel in packageViewModels! { | |
let package: Package = packageViewModel.fetchPackage() | |
realm.add(package, update: true) | |
} | |
try realm.commitWrite() | |
completion(true, nil) | |
} catch let error as NSError { | |
completion(false , error) | |
} | |
} | |
} | |
self.addTask(block) | |
} | |
func insertPackageDetail(_ packageDetailViewModel: PackageDetailViewModel?, | |
completion: @escaping (_ result: Bool, _ error: NSError?)->()?) { | |
let block = { | |
autoreleasepool { | |
do { | |
let realm = try Realm() | |
realm.beginWrite() | |
if packageDetailViewModel != nil { | |
let packageDetail: PackageDetail = packageDetailViewModel!.fetchPackageDetail() | |
realm.add(packageDetail, update: true) | |
} | |
try realm.commitWrite() | |
completion(true, nil) | |
} catch let error as NSError { | |
completion(false , error) | |
} | |
} | |
} | |
self.addTask(block) | |
} | |
func deletePackages(_ packageViewModels: Array<PackageViewModel>?, | |
completion: @escaping (_ result: Bool, _ error: NSError?)->()?) { | |
let block = { | |
autoreleasepool { | |
do { | |
let realm = try Realm() | |
realm.beginWrite() | |
for packageViewModel in packageViewModels! { | |
let package: Package = packageViewModel.fetchPackage() | |
realm.delete(package) | |
} | |
try realm.commitWrite() | |
completion(true, nil) | |
} catch let error as NSError { | |
completion(false , error) | |
} | |
} | |
} | |
self.addTask(block) | |
} | |
func updatePackages(_ packageViewModels: Array<PackageViewModel>?, | |
completion: @escaping (_ result: Bool, _ error: NSError?)->()?) { | |
let block = { | |
autoreleasepool { | |
do { | |
let realm = try Realm() | |
realm.beginWrite() | |
for packageViewModel in packageViewModels! { | |
let package: Package = packageViewModel.fetchPackage() | |
realm.add(package, update: true) | |
} | |
try realm.commitWrite() | |
completion(true, nil) | |
} catch let error as NSError { | |
completion(false , error) | |
} | |
} | |
} | |
self.addTask(block) | |
} | |
func selectPackages(_ completion: @escaping (_ result: Array<PackageViewModel>?, _ error: NSError?)->()?) { | |
let block = { | |
autoreleasepool { | |
do { | |
var retArray: Array<PackageViewModel> = [PackageViewModel]() | |
let realm = try Realm() | |
realm.beginWrite() | |
let packages = realm.objects(Package.self).sorted(byProperty: "updateTime", ascending: false) | |
try realm.commitWrite() | |
if packages.count == 0 { | |
completion(nil, nil) // MARK: the table is empty, but no error | |
return | |
} | |
for package in packages { | |
let packageViewModel: PackageViewModel = PackageViewModel(package: package) | |
retArray.append(packageViewModel) | |
} | |
completion(retArray, nil) | |
} catch let error as NSError { | |
completion(nil , error) | |
} | |
} | |
} | |
self.addTask(block) | |
} | |
func selectPackageDetail(_ identifier: String, | |
completion: @escaping (_ result: PackageDetailViewModel?, _ error: NSError?)->()?) { | |
let block = { | |
autoreleasepool { | |
do { | |
let realm = try Realm() | |
realm.beginWrite() | |
let retArray = realm.objects(PackageDetail.self).filter("identifier == %@", identifier) | |
try realm.commitWrite() | |
if retArray.count == 0 { | |
completion(nil, nil) // MARK: the table is empty, but no error | |
return | |
} | |
let packageDetailViewModel: PackageDetailViewModel = PackageDetailViewModel(packageDetail: retArray.first) | |
completion(packageDetailViewModel, nil) | |
} catch let error as NSError { | |
completion(nil , error) | |
} | |
} | |
} | |
self.addTask(block) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment