commit 6f7258a589cec43c12358361443463b1bd3d069a Author: Ivan Vavilov Date: Tue Jun 13 14:06:20 2017 +0300 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..15ab66f --- /dev/null +++ b/.gitignore @@ -0,0 +1,37 @@ +# OS X +.DS_Store + +# Xcode +build/ +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata/ +*.xccheckout +profile +*.moved-aside +DerivedData +*.hmap +*.ipa + +# Bundler +.bundle + +Carthage +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control +# +# Note: if you ignore the Pods directory, make sure to uncomment +# `pod install` in .travis.yml +# +Pods/ + +Source/.idea +Example/pod_install.command +Example/pod_update.command diff --git a/DAO.podspec b/DAO.podspec new file mode 100644 index 0000000..609c5f4 --- /dev/null +++ b/DAO.podspec @@ -0,0 +1,22 @@ +Pod::Spec.new do |s| + s.name = 'DAO' + s.version = '1.0.0' + s.summary = 'DAO Library' + s.description = 'Library provides easy way to cache entities.' + s.homepage = 'https://github.com/RedMadRobot/DAO' + s.license = { :type => 'MIT', :file => 'LICENSE' } + s.author = { 'vani2' => 'iv@redmadrobot.com' } + s.source = { :git => 'git@github.com:RedMadRobot/DAO.git' } + s.platform = :ios, '9.0' + s.source_files = 'DAO/Classes/Core/**/*' + + s.subspec 'Realm' do |r| + r.source_files = "DAO/Classes/RealmDAO/**/*" + r.dependency "RealmSwift" + end + + s.subspec 'CoreData' do |cd| + cd.source_files = "DAO/Classes/CoreData/**/*" + end + +end diff --git a/DAO/Classes/.gitkeep b/DAO/Classes/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/DAO/Classes/Core/DAO.swift b/DAO/Classes/Core/DAO.swift new file mode 100644 index 0000000..c503280 --- /dev/null +++ b/DAO/Classes/Core/DAO.swift @@ -0,0 +1,105 @@ +// +// DAO.swift +// DAO +// +// Created by Igor Bulyga on 04.02.16. +// Copyright © 2016 RedMadRobot LLC. All rights reserved. +// + +import Foundation + + +/// Parent class for DAO pattern implemetation (aka interface). +open class DAO { + + // MARK: - Insert/update + + + /// Saving new entity or update existing. + /// + /// - Parameter entity: entity should be saved. + /// - Throws: error if entity can not be saved. + open func persist(_ entity: Model) throws { + preconditionFailure() + } + + + /// Saving new entities or update existing. + /// + /// - Parameter entities: entities should be saved. + /// - Throws: error if any entity can not be saved. + open func persist(_ entities: [Model]) throws { + preconditionFailure() + } + + + // MARK: - Read + + /// Read entity from database of `Model` type. + /// + /// - Parameter entityId: entity identifier. + /// - Returns: instance of existant entity or nil. + open func read(_ entityId: String) -> Model? { + preconditionFailure() + } + + + /// Read all entities from database of `Model` type. + /// + /// - Returns: array of entities. + open func read() -> [Model] { + preconditionFailure() + } + + + /// Read all entities from database of `Model` type filtered by predicate. + /// + /// - Parameter predicate: predicate to filter entities. + /// - Returns: filtered array of entities. + open func read(predicatedBy predicate: NSPredicate?) -> [Model] { + preconditionFailure() + } + + + /// Read all entities from database of `Model` type ordered by field. + /// + /// - Parameters: + /// - field: ordering field. + /// - ascending: ascending flag (descending otherwise). + /// - Returns: ordered array of entities. + open func read(orderedBy field: String?, ascending: Bool) -> [Model] { + preconditionFailure() + } + + + /// Read all entities from database of `Model` type filtered by predicate and ordered by field. + /// + /// - Parameters: + /// - predicate: predicate to filter entities. + /// - field: ordering field. + /// - ascending: ascending flag (descending otherwise). + /// - Returns: filtered and ordered array of entities. + open func read(predicatedBy predicate: NSPredicate?, orderedBy field: String?, + ascending: Bool) -> [Model] { + preconditionFailure() + } + + + // MARK: - Delete + + /// Delete all entities of `Model` type. + /// + /// - Throws: error if any entity can not be deleted. + open func erase() throws { + preconditionFailure() + } + + + /// Delete entity of `Model` type by identifier. + /// + /// - Throws: error if any entity can not be deleted. + open func erase(_ entityId: String) throws { + preconditionFailure() + } + +} diff --git a/DAO/Classes/Core/Entity.swift b/DAO/Classes/Core/Entity.swift new file mode 100644 index 0000000..c6c4826 --- /dev/null +++ b/DAO/Classes/Core/Entity.swift @@ -0,0 +1,57 @@ +// +// Entity.swift +// DAO +// +// Created by Igor Bulyga on 05.02.16. +// Copyright © 2016 RedMadRobot LLC. All rights reserved. +// + + +import Foundation + + +/// Parent class for all entities. +open class Entity: Hashable { + + /// Hash value for compare entities. + open var hashValue: Int { + get { + return self.entityId.hashValue + } + } + + + /// Unique entity identifer. + open var entityId: String = "" + + + required public init() {} + + + /// Creates an instance with identifier. + /// + /// - Parameter entityId: unique entity identifier. + public init(entityId: String) { + self.entityId = entityId + } + + + /// Function to redefine it in children for proper equality. + /// + /// - Parameter other: entity compare with. + /// - Returns: result of comparison. + open func equals(_ other: T) -> Bool where T: Entity { + return self.entityId == other.entityId + } +} + + +/// Custom operator `==` for `Entity` and subclasses. +/// +/// - Parameters: +/// - lhs: left entity to compare. +/// - rhs: right entity to compare. +/// - Returns: result of comparison. +public func ==(lhs: T, rhs: T) -> Bool where T: Entity { + return lhs.equals(rhs) +} diff --git a/DAO/Classes/CoreData/Configuration/CoreDataConfiguration.swift b/DAO/Classes/CoreData/Configuration/CoreDataConfiguration.swift new file mode 100644 index 0000000..85d4c28 --- /dev/null +++ b/DAO/Classes/CoreData/Configuration/CoreDataConfiguration.swift @@ -0,0 +1,45 @@ +// +// CoreDataConfiguration.swift +// DAO +// +// Created by Ivan Vavilov on 12/05/2017. +// Copyright © 2017 RedMadRobot LLC. All rights reserved. +// + +import Foundation +import CoreData + + +/// `CoreData DAO` configuration. +/// Incapsulates basic settings. +/// Used to initialize `CoreData DAO`. +public struct CoreDataConfiguration { + + /// Name of container also is filename for `*.xcdatamodelid` file. + public let containerName: String + + /// Store type like in `CoreData`. `NSInMemoryStoreType`, for instance. + public let storeType: String + + /// Options for persistence store + public let options: [String: NSObject] + + + /// Create an instance with specified `containerName`, `storeType`, `options`. + /// + /// - Parameters: + /// - containerName: name. See above. + /// - storeType: store type. See above. + /// - options: persistence store options. + public init( + containerName: String, + storeType: String = NSSQLiteStoreType, + options: [String : NSObject] = + [NSMigratePersistentStoresAutomaticallyOption: true as NSObject, + NSInferMappingModelAutomaticallyOption: true as NSObject]) { + self.containerName = containerName + self.storeType = storeType + self.options = options + } + +} diff --git a/DAO/Classes/CoreData/DAO/CoreDataDAO.swift b/DAO/Classes/CoreData/DAO/CoreDataDAO.swift new file mode 100644 index 0000000..d4d000a --- /dev/null +++ b/DAO/Classes/CoreData/DAO/CoreDataDAO.swift @@ -0,0 +1,342 @@ +// +// CoreDataDAO.swift +// DAO +// +// Created by Ivan Vavilov on 2/4/16. +// Copyright © 2016 RedMadRobot LLC. All rights reserved. +// + +import CoreData + + +/// `DAO` pattern implementation for `CoreData`. +open class CoreDataDAO : DAO { + + // MARK: - Private + + /// Translator for current `CDModel` and `Model` types. + private var translator: CoreDataTranslator + + + /// Persistent store cooridnator. Can be configured by `CoreDataConfiguration`. + private let persistentStoreCoordinator: NSPersistentStoreCoordinator + + + /// Managed object context. Context is created every transaction due to current queue – + /// main or background. + private var context: NSManagedObjectContext { + let context = Thread.isMainThread ? + NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType) : + NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType) + + context.persistentStoreCoordinator = persistentStoreCoordinator + context.shouldDeleteInaccessibleFaults = true + if #available(iOS 10.0, *) { + context.automaticallyMergesChangesFromParent = true + } + return context + } + + + //MARK: - Public + + /// Creates an instance with specified `translator` and `configuration`. + /// + /// - Parameters: + /// - translator: translator for current `CDModel` and `Model` types. + /// - configuration: configuration. See also `CoreDataConfiguration`. + /// - Throws: error if loading or adding persistence store is failed. + public init(_ translator: CoreDataTranslator, + configuration: CoreDataConfiguration) throws { + self.translator = translator + + if #available(iOS 10, *) { + let persistentContainer = NSPersistentContainer(name: configuration.containerName) + + persistentContainer.persistentStoreDescriptions + .forEach { description in + configuration.options + .forEach { + description.setOption($0.value, forKey: $0.key) + } + description.type = configuration.storeType + } + + var error: Error? + + persistentContainer.loadPersistentStores { _, e in + error = e + } + + if let error = error { throw error } + + persistentStoreCoordinator = persistentContainer.persistentStoreCoordinator + } else { + let url = Bundle(for: CDModel.self).url( + forResource: configuration.containerName, + withExtension: "momd")! + + persistentStoreCoordinator = NSPersistentStoreCoordinator( + managedObjectModel: NSManagedObjectModel(contentsOf: url)!) + + try persistentStoreCoordinator.addPersistentStore( + ofType: configuration.storeType, + configurationName: nil, + at: CoreDataDAO.url(storeName: "\(configuration.containerName).db"), + options: configuration.options) + } + + super.init() + } + + + //MARK: - DAO + + override open func persist(_ entity: Model) throws { + var error: Error? + + let context = self.context + + context.performAndWait { [weak self] in + guard let `self` = self else { return } + + do { + if self.isEntryExist(entity.entityId, inContext: context) { + try self.update(entity, inContext: context) + } else { + try self.create(entity, inContext: context) + } + } catch let e { + error = e + } + } + + if let error = error { throw error } + } + + + open override func persist(_ entities: [Model]) throws { + var error: Error? + + let context = self.context + + context.performAndWait { [weak self] in + guard let `self` = self else { return } + + entities.forEach{ entity in + if self.isEntryExist(entity.entityId, inContext: context) { + let existingEntries = self.fetchEntries(entity.entityId, inContext: context) + existingEntries.forEach { + self.translator.fill($0, fromEntity: entity, in: context) + } + } else if let entry = NSEntityDescription.insertNewObject( + forEntityName: self.translator.entryClassName, + into: context) as? CDModel { + + self.translator.fill(entry, fromEntity: entity, in: context) + } + } + + do { + try context.save() + } catch let e { + error = e + context.rollback() + } + } + + if let error = error { throw error } + } + + + open override func read(_ entityId: String) -> Model? { + guard let entries = try? context.fetch(request(entityId)), + !entries.isEmpty, + let entry = entries.first + else { + return nil + } + + let entity = Model() + translator.fill(entity, fromEntry: entry) + + return entity + } + + + open override func read() -> [Model] { + return read(predicatedBy: nil) + } + + + open override func read(predicatedBy predicate: NSPredicate?) -> [Model] { + return read(predicatedBy: predicate, orderedBy: nil, ascending: false) + } + + + open override func read( + predicatedBy predicate: NSPredicate?, + orderedBy field: String?, + ascending: Bool) -> [Model] { + + let sortDescriptors = field != nil ? [NSSortDescriptor(key: field, ascending: ascending)] : [] + + return fetchEntries(predicate, sortDescriptors: sortDescriptors, inContext: context) + .flatMap { + let entity = Model() + + self.translator.fill(entity, fromEntry: $0) + + return entity + } + } + + + open override func read(orderedBy field: String?, ascending: Bool) -> [Model] { + return read(predicatedBy: nil, orderedBy: field, ascending: ascending) + } + + + override open func erase() throws { + var error: Error? + + let context = self.context + + context.performAndWait { [weak self] in + guard let `self` = self else { return } + + self.fetchEntries(inContext: context) + .forEach { + context.delete($0) + } + + do { + try context.save() + } catch let e { + error = e + context.rollback() + } + } + + if let error = error { throw error } + } + + + override open func erase(_ entityId: String) throws { + var error: Error? + + let context = self.context + + context.performAndWait { [weak self] in + guard let `self` = self else { return } + + self.fetchEntries(entityId, inContext: context) + .forEach { + context.delete($0) + } + do { + try context.save() + } catch let e { + error = e + context.rollback() + } + + } + + if let error = error { throw error } + } + + + //MARK: - Private + + private func fetchEntries( + _ entryId: String, + inContext context: NSManagedObjectContext) -> [CDModel] { + if let entries = try? context.fetch(request(entryId)) { + return entries + } else { + return [CDModel]() + } + } + + + private func fetchEntries( + _ predicate: NSPredicate? = nil, + sortDescriptors: [NSSortDescriptor] = [], + inContext context: NSManagedObjectContext) -> [CDModel] { + if let entries = try? context.fetch(request(predicate, sortDescriptors: sortDescriptors)) { + return entries + } else { + return [CDModel]() + } + } + + + private func request(_ entryId: String) -> NSFetchRequest { + let request = NSFetchRequest(entityName: translator.entryClassName) + request.predicate = NSPredicate(format: "entryId == %@", argumentArray: [entryId]) + + return request + } + + + private func request(_ predicate: NSPredicate?, + sortDescriptors: [NSSortDescriptor]) -> NSFetchRequest { + let request = NSFetchRequest(entityName: translator.entryClassName) + request.predicate = predicate + request.sortDescriptors = sortDescriptors + + return request + } + + + //MARK: - Transactions + + private func isEntryExist( + _ entryId: String, + inContext context: NSManagedObjectContext) -> Bool { + let existingEntries = fetchEntries(entryId, inContext: context) + return existingEntries.count > 0 + } + + + private func update(_ entity: Model, inContext context: NSManagedObjectContext) throws { + let existingEntries = fetchEntries(entity.entityId, inContext: context) + + existingEntries.forEach { + translator.fill($0, fromEntity: entity, in: context) + } + + try context.save() + } + + + private func create(_ entity: Model, inContext context: NSManagedObjectContext) throws { + guard let entry = NSEntityDescription.insertNewObject( + forEntityName: translator.entryClassName, + into: context) as? CDModel + else { + return + } + + translator.fill(entry, fromEntity: entity, in: context) + + try context.save() + } + + + // MARK: - Helper + + private class func url(storeName: String) -> URL { + var url: URL! = nil + if let documentsDirectory = NSSearchPathForDirectoriesInDomains( + .documentDirectory, + .userDomainMask, true).first { + let storeAbsolutePath = (documentsDirectory as NSString).appendingPathComponent(storeName) + url = URL(fileURLWithPath: storeAbsolutePath) as URL! + } + + return url + } + +} diff --git a/DAO/Classes/CoreData/Translator/CoreDataTranslator.swift b/DAO/Classes/CoreData/Translator/CoreDataTranslator.swift new file mode 100644 index 0000000..f4b0c13 --- /dev/null +++ b/DAO/Classes/CoreData/Translator/CoreDataTranslator.swift @@ -0,0 +1,93 @@ +// +// RealmTranslator.swift +// DAO +// +// Created by Ivan Vavilov on 06.02.16. +// Copyright © 2016 RedMadRobot LLC. All rights reserved. +// + + +import Foundation +import CoreData + + +/// Parent class for `CoreData` translators. +/// Translators fill properties of new/existant entities from entries and other way. +open class CoreDataTranslator { + + /// Helper property for `CoreDataDAO`. + open var entryClassName: String { + return NSStringFromClass(CDModel.self).components(separatedBy: ".").last! + } + + + /// Creates an instance of class. + required public init() { } + + + /// All properties of entity will be overridden by entry properties. + /// + /// - Parameters: + /// - entity: instance of `Model` type. + /// - fromEntry: instance of `CDModel` type. + open func fill(_ entity: Model, fromEntry: CDModel) { + fatalError("Abstact method") + } + + + /// All properties of entry will be overridden by entity properties. + /// + /// - Parameters: + /// - entry: instance of `CDModel` type. + /// - fromEntity: instance of `Model` type. + /// - context: managed object context for current transaction. + open func fill(_ entry: CDModel, fromEntity: Model, in context: NSManagedObjectContext) { + fatalError("Abstact method") + } + + + /// All properties of entities will be overridden by entries properties. + /// For simplicity create new entries w/o changing existent. + /// + /// - Parameters: + /// - entries: array of instances of `CDModel` type. + /// - fromEntities: array of instances of `Model` type. + /// - context: managed object context for current transaction. + open func fill( + _ entries: inout Set, + fromEntities: [Model], + in context: NSManagedObjectContext) { + + fromEntities + .flatMap { entity -> (CDModel, Model)? in + if let entry = NSEntityDescription.insertNewObject( + forEntityName: self.entryClassName, + into: context) as? CDModel { + entries.insert(entry) + return (entry, entity) + } else { + return nil + } + } + .forEach { + self.fill($0.0, fromEntity: $0.1, in: context) + } + } + + + /// All properties of entries will be overridden by entities properties. + /// + /// - Parameters: + /// - entities: array of instances of `CDModel` type. + /// - fromEntries: array of instances of `CDModel` type. + open func fill(_ entities: inout [Model], fromEntries: Set?) { + entities.removeAll() + + fromEntries?.forEach { + let model = Model() + entities.append(model) + self.fill(model, fromEntry: $0) + } + } + +} diff --git a/DAO/Classes/RealmDAO/Configuration/RealmConfiguration.swift b/DAO/Classes/RealmDAO/Configuration/RealmConfiguration.swift new file mode 100644 index 0000000..90e72fb --- /dev/null +++ b/DAO/Classes/RealmDAO/Configuration/RealmConfiguration.swift @@ -0,0 +1,40 @@ +// +// Created by Ivan Vavilov on 5/22/17. +// Copyright (c) 2017 RedMadRobot LLC. All rights reserved. +// + +import Foundation +import RealmSwift + +/// `Realm DAO` configuration. +/// Incapsulates basic settings. +/// Used to initialize `Realm DAO`. +public struct RealmConfiguration { + + /// Name of database file name. + public let databaseFileName: String + + /// Version of database. + public let databaseVersion: UInt64 + + /// Migration block for manual migration. + public let migrationBlock: MigrationBlock? + + + /// Create an instance with specified `databaseFileName`, `databaseVersion`, `migrationBlock`. + /// + /// - Parameters: + /// - databaseFileName: name. See above. + /// - databaseVersion: version. See above. + /// - migrationBlock: migration block. See above. + public init( + databaseFileName: String = "Database.realm", + databaseVersion: UInt64 = 1, + migrationBlock: MigrationBlock? = nil) { + + self.databaseFileName = databaseFileName + self.databaseVersion = databaseVersion + self.migrationBlock = migrationBlock + } + +} diff --git a/DAO/Classes/RealmDAO/DAO/RealmDAO.swift b/DAO/Classes/RealmDAO/DAO/RealmDAO.swift new file mode 100644 index 0000000..5ac3394 --- /dev/null +++ b/DAO/Classes/RealmDAO/DAO/RealmDAO.swift @@ -0,0 +1,261 @@ +// +// RealmDAO.swift +// DAO +// +// Created by Igor Bulyga on 04.02.16. +// Copyright © 2016 RedMadRobot LLC. All rights reserved. +// + + +import Foundation +import Realm +import RealmSwift + +/// `DAO` pattern implementation for `Realm`. +open class RealmDAO: DAO { + + // MARK: - Private + + /// Translator for current `RLMEntry` and `RealmModel` types. + private let translator: RealmTranslator + + + // MARK: - Public + + /// Creates an instance with specified `translator` and `configuration`. + /// + /// - Parameters: + /// - translator: translator for current `Model` and `RealmModel` types. + /// - configuration: configuration. See also `RealmConfiguration`. + public init(_ translator: RealmTranslator, + configuration: RealmConfiguration) { + self.translator = translator + super.init() + loadDefaultRealm(configuration: configuration) + } + + + /// Creates an instance with specified `translator` and default configuration. + /// + /// - Parameters: + /// - translator: translator for current `Model` and `RealmModel` types. + public convenience init(_ translator: RealmTranslator) { + self.init(translator, + configuration: RealmConfiguration()) + } + + + //MARK: - DAO + + override open func persist(_ entity: Model) throws { + if let entry = readFromRealm(entity.entityId) { + try autoreleasepool { + realm().beginWrite() + translator.fill(entry, fromEntity: entity) + try realm().commitWrite() + } + } else { + let entry = RealmModel() + translator.fill(entry, fromEntity: entity) + try write(entry) + } + } + + + open override func persist(_ entities: [Model]) throws { + let entries = List(entities.map { + self.readFromRealm($0.entityId) ?? RealmModel() + }) + + translator.fill(entries, fromEntities: entities) + + try write(entries) + } + + + override open func read(_ entityId: String) -> Model? { + guard let entry = readFromRealm(entityId) else { + return nil + } + + let entity = Model() + translator.fill(entity, fromEntry: entry) + + return entity + } + + + open override func read() -> [Model] { + return readFromRealm().map { + let entity = Model() + self.translator.fill(entity, fromEntry: $0) + return entity + } + } + + + open override func read(predicatedBy predicate: NSPredicate?) -> [Model] { + return read(predicatedBy: predicate, orderedBy: nil) + } + + + open override func read(orderedBy field: String?, + ascending: Bool) -> [Model] { + return read(predicatedBy: nil, orderedBy: field, ascending: ascending) + } + + + open override func read(predicatedBy predicate: NSPredicate?, + orderedBy field: String?, + ascending: Bool = true) -> [Model] { + var entries = readFromRealm(predicate) + + if let field = field { + entries = entries.sorted(byKeyPath: field, ascending: ascending) + } + + return entries.map { + let entity = Model() + self.translator.fill(entity, fromEntry: $0) + return entity + } + } + + + override open func erase() throws { + let results = readFromRealm() + let entries: List = List() + + entries.append(objectsIn: results.map { + $0 as RealmModel + }) + + try self.delete(entries) + } + + + override open func erase(_ entityId: String) throws { + guard let entry = readFromRealm(entityId) else { + return + } + try delete(entry) + } + + + // MARK: - Private + + private func write(_ entry: RealmModel) throws { + try self.realm().write { + self.realm().create(RealmModel.self, value: entry, update: true) + } + } + + + private func write(_ entries: List) throws { + try self.realm().write { + entries.forEach { (e: RealmModel) -> () in + self.realm().create(RealmModel.self, value: e, update: true) + } + } + } + + + private func readFromRealm(_ entryId: String) -> RealmModel? { + return self.realm().object(ofType: RealmModel.self, forPrimaryKey: entryId) + } + + + private func readFromRealm(_ predicate: NSPredicate? = nil) -> Results { + let results: Results = self.realm().objects(RealmModel.self) + guard let predicate = predicate else { + return results + } + return results.filter(predicate) + } + + + private func delete(_ entry: RealmModel) throws { + try self.realm().write { + cascadeDelete(entry) + } + } + + + private func delete(_ entries: List) throws { + try self.realm().write { + cascadeDelete(entries) + } + } + + + private func cascadeDelete(_ object: AnyObject?) { + if let deletable = object as? CascadeDeletionProtocol { + deletable.objectsToDelete.forEach { child in + cascadeDelete(child) + } + } + + if let realmArray = object as? ListBase { + for i in 0.. Realm { + return try! Realm() + } + + + private func defaultRealmPathIsEqualToPath(_ path: URL?) -> Bool { + guard let path = path else { + return false + } + return Realm.Configuration.defaultConfiguration.fileURL == path + } + + + private func loadDefaultRealm(configuration: RealmConfiguration) { + guard let path = self.pathForFileName(configuration.databaseFileName) else { + fatalError("Cant find path for DB with filename: \(configuration.databaseFileName)" + + " v.\(configuration.databaseVersion)") + } + if defaultRealmPathIsEqualToPath(path) { + return + } + + assignDefaultRealmPath(path) + migrateDefaultRealmToCurrentVersion(configuration: configuration) + } + + + private func pathForFileName(_ fileName: String) -> URL? { + let documentDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first as NSString? + guard let realmPath = documentDirectory?.appendingPathComponent(fileName) else { + return nil + } + return URL(string: realmPath) + } + + + private func assignDefaultRealmPath(_ path: URL) { + var configuration = Realm.Configuration.defaultConfiguration + configuration.fileURL = path + Realm.Configuration.defaultConfiguration = configuration + } + + + private func migrateDefaultRealmToCurrentVersion(configuration: RealmConfiguration) { + var config = Realm.Configuration.defaultConfiguration + config.schemaVersion = configuration.databaseVersion + config.migrationBlock = configuration.migrationBlock + Realm.Configuration.defaultConfiguration = config + } + +} diff --git a/DAO/Classes/RealmDAO/Model/CascadeDeletionProtocol.swift b/DAO/Classes/RealmDAO/Model/CascadeDeletionProtocol.swift new file mode 100644 index 0000000..b68f348 --- /dev/null +++ b/DAO/Classes/RealmDAO/Model/CascadeDeletionProtocol.swift @@ -0,0 +1,19 @@ +// +// CascadeDeletionProtocol.swift +// DAO +// +// Created by Igor Bulyga on 24.06.16. +// Copyright © 2016 RedMadRobot LLC. All rights reserved. +// + + +import Foundation +import RealmSwift + + +/// Protocol to implement cascade deletion of related entities +public protocol CascadeDeletionProtocol { + + var objectsToDelete: [Object] { get } + +} diff --git a/DAO/Classes/RealmDAO/Model/RLMEntry.swift b/DAO/Classes/RealmDAO/Model/RLMEntry.swift new file mode 100644 index 0000000..a192fd8 --- /dev/null +++ b/DAO/Classes/RealmDAO/Model/RLMEntry.swift @@ -0,0 +1,63 @@ +// +// RLMEntry.swift +// DAO +// +// Created by Igor Bulyga on 04.02.16. +// Copyright © 2016 RedMadRobot LLC. All rights reserved. +// + + +import Foundation +import RealmSwift +import Realm + + +/// Parent class for `Realm` entries. +open class RLMEntry: Object { + + /// Entry identifier. Must be unique. + dynamic open var entryId: String + + + /// Creates an instance with specified `entryId`. + /// + /// - Parameter entryId: entry identifier. + public init(entryId: String) { + self.entryId = entryId + super.init() + } + + + /// Creates an instance with emppty `entryId`. + public required init() { + self.entryId = "" + super.init() + } + + + /// Creates an instance with specified `realm` and `schema`. + /// + /// - Parameters: + /// - realm: instance of RLMRealm. + /// - schema: instance of RLMObjectSchema. + required public init(realm: RLMRealm, schema: RLMObjectSchema) { + self.entryId = "" + super.init(realm: realm, schema: schema) + } + + + /// Creates an instance with specified `value` and `schema`. + /// + /// - Parameters: + /// - value: value. + /// - schema: instance of `RLMSchema`. + required public init(value: Any, schema: RLMSchema) { + fatalError("init(value:schema:) has not been implemented") + } + + + override open class func primaryKey() -> String? { + return "entryId" + } + +} diff --git a/DAO/Classes/RealmDAO/Model/RLMTypes.swift b/DAO/Classes/RealmDAO/Model/RLMTypes.swift new file mode 100644 index 0000000..6aa194c --- /dev/null +++ b/DAO/Classes/RealmDAO/Model/RLMTypes.swift @@ -0,0 +1,157 @@ +// +// RLMTypes.swift +// DAO +// +// Created by Ivan Vavilov on 5/17/17. +// Copyright © 2017 RedMadRobot LLC. All rights reserved. +// + +import UIKit +import RealmSwift +import Realm + + +/// Protocol for implement wrappers for standard types that Realm can't save now. +/// Use it if you want to save collection of standard type in Realm. +public protocol RLMPrimitiveType: class { + + associatedtype A + + var value: A { get set } + + init(val: A) +} + + +/// String wrapper +open class RLMString: Object, RLMPrimitiveType { + + public required init(val: String) { + value = val + super.init() + } + + public required init(value: Any, schema: RLMSchema) { + fatalError("init(value:schema:) has not been implemented") + } + + public required init(realm: RLMRealm, schema: RLMObjectSchema) { + super.init(realm: realm, schema: schema) + } + + required public init() { + super.init() + } + + public typealias A = String + + public dynamic var value: String = "" + +} + + +/// Integer wrapper +open class RLMInteger: Object, RLMPrimitiveType { + + public required init(val: Int) { + value = val + super.init() + } + + public required init(value: Any, schema: RLMSchema) { + fatalError("init(value:schema:) has not been implemented") + } + + public required init(realm: RLMRealm, schema: RLMObjectSchema) { + super.init(realm: realm, schema: schema) + } + + required public init() { + super.init() + } + + public typealias A = Int + + public dynamic var value: Int = 0 + +} + + +/// Double wrapper +open class RLMDouble: Object, RLMPrimitiveType { + + public required init(val: Double) { + value = val + super.init() + } + + public required init(value: Any, schema: RLMSchema) { + fatalError("init(value:schema:) has not been implemented") + } + + public required init(realm: RLMRealm, schema: RLMObjectSchema) { + super.init(realm: realm, schema: schema) + } + + required public init() { + super.init() + } + + public typealias A = Double + + public dynamic var value: Double = 0.0 + +} + + +/// Date wrapper +open class RLMDate: Object, RLMPrimitiveType { + + public required init(val: Date) { + super.init() + } + + public required init(value: Any, schema: RLMSchema) { + fatalError("init(value:schema:) has not been implemented") + } + + public required init(realm: RLMRealm, schema: RLMObjectSchema) { + super.init(realm: realm, schema: schema) + } + + required public init() { + super.init() + } + + public typealias A = Date + + public dynamic var value: Date = Date() + +} + + +/// Data wrapper +open class RLMData: Object, RLMPrimitiveType { + + public required init(val: Data) { + value = val + super.init() + } + + public required init(value: Any, schema: RLMSchema) { + fatalError("init(value:schema:) has not been implemented") + } + + public required init(realm: RLMRealm, schema: RLMObjectSchema) { + super.init(realm: realm, schema: schema) + } + + required public init() { + super.init() + } + + public typealias A = Data + + public dynamic var value: Data = Data() + +} diff --git a/DAO/Classes/RealmDAO/Translator/RealmTranslator.swift b/DAO/Classes/RealmDAO/Translator/RealmTranslator.swift new file mode 100644 index 0000000..17ac67f --- /dev/null +++ b/DAO/Classes/RealmDAO/Translator/RealmTranslator.swift @@ -0,0 +1,73 @@ +// +// RealmTranslator.swift +// DAO +// +// Created by Igor Bulyga on 04.02.16. +// Copyright © 2016 RedMadRobot LLC. All rights reserved. +// + + +import Foundation +import RealmSwift + + +open class RealmTranslator { + + public required init() {} + + + open func fill(_ entry: RealmModel, fromEntity: Model) { + fatalError("Abstract method") + } + + + open func fill(_ entity: Model, fromEntry: RealmModel) { + fatalError("Abstract method") + } + + + /// All properties of entities will be overridden by entries properties. + /// If entry doesn't exist, it'll be created. + /// + /// - Parameters: + /// - entries: list of instances of `RealmModel` type. + /// - fromEntities: array of instances of `Model` type. + open func fill(_ entries: List, fromEntities: [Model]) { + let oldEntries = entries.map { $0 } + + fromEntities + .map { entity -> (RealmModel, Model) in + let entry = oldEntries + .filter { $0.entryId == entity.entityId } + .first + + if let entry = entry { + return (entry, entity) + } else { + let entry = RealmModel() + entries.append(entry) + return (entry, entity) + } + } + .forEach { + self.fill($0.0, fromEntity: $0.1) + } + } + + + /// All properties of entries will be overridden by entities properties. + /// + /// - Parameters: + /// - entities: array of instances of `Model` type. + /// - fromEntries: list of instances of `RealmModel` type. + open func fill( _ entities: inout [Model], fromEntries: List) { + entities.removeAll() + + fromEntries.forEach { + let model = Model() + entities.append(model) + self.fill(model, fromEntry: $0) + } + } + +} diff --git a/Example/CoreDataDAOTests/CoreDataDAOEntityTests.swift b/Example/CoreDataDAOTests/CoreDataDAOEntityTests.swift new file mode 100644 index 0000000..06530c3 --- /dev/null +++ b/Example/CoreDataDAOTests/CoreDataDAOEntityTests.swift @@ -0,0 +1,168 @@ +// +// CoreDataDAOEntityTests.swift +// DAO +// +// Created by Ivan Vavilov on 4/25/17. +// Copyright © 2017 RedMadRobot LLC. All rights reserved. +// + +import XCTest +import DAO +import CoreData +@testable import DAO_Example + + +final class CoreDataDAOEntityTests: XCTestCase { + + let dao = try! CoreDataDAO( + CDEntityTranslator(), + configuration: CoreDataConfiguration( + containerName: "Model", + storeType: NSInMemoryStoreType)) + + + func testReadById() { + let entity = Entity(entityId: "2") + + do { + try dao.persist(entity) + } catch _ { + XCTFail("Read by id is failed") + } + + + if let savedEntity = dao.read("2") { + XCTAssertEqual(savedEntity.entityId, entity.entityId) + } else { + XCTFail("Read by id is failed") + } + } + + + func testAsyncReadById() { + let entity = Entity(entityId: "2_back") + let exp = expectation(description: "") + + DispatchQueue.global().async { + do { + try self.dao.persist(entity) + } catch _ { + XCTFail("Async read by id is failed") + } + + + DispatchQueue.main.async { + if let savedEntity = self.dao.read("2_back") { + XCTAssertEqual(savedEntity.entityId, entity.entityId) + } else { + XCTFail("Async read by id is failed") + } + exp.fulfill() + } + } + + waitForExpectations(timeout: 5) { error in + if error != nil { + XCTFail("Async read by id is failed") + } + XCTAssert(true) + } + } + + + func testPersist() { + let entity = Entity(entityId: "1") + do { + try dao.persist(entity) + } catch _ { + XCTFail("Persist is failed") + } + + + XCTAssert(true) + } + + + func testPersistAll() { + let firstEntity = Entity(entityId: "2") + let secondEntity = Entity(entityId: "3") + + do { + try dao.persist([firstEntity, secondEntity]) + } catch _ { + XCTFail("Persist All is failed") + } + + XCTAssert(true) + } + + + func testAsyncPersist() { + let exp = expectation(description: "") + + DispatchQueue.global().async { + let entity = Entity(entityId: "1_back") + + do { + try self.dao.persist(entity) + } catch _ { + XCTFail("Saving entity in background is failed") + } + + exp.fulfill() + } + + waitForExpectations(timeout: 5) { error in + if error != nil { + XCTFail("Saving entity in background is failed") + } + XCTAssert(true) + } + } + + + func testEraseById() { + let entity = Entity(entityId: "3") + + do { + try dao.persist(entity) + try dao.erase("3") + } catch _ { + XCTFail("Erase is failed") + } + + XCTAssert(true) + } + + + func testAsyncEraseById() { + let entity = Entity(entityId: "2_back") + + do { + try dao.persist(entity) + } catch _ { + XCTFail("Async erase by id is failed") + } + + + let exp = expectation(description: "") + + DispatchQueue.global().async { + do { + try self.dao.erase("2_back") + } catch _ { + XCTFail("Async erase by id is failed") + } + + exp.fulfill() + } + + waitForExpectations(timeout: 5) { error in + if error != nil { + XCTFail("Async erase by id is failed") + } + XCTAssert(true) + } + } + +} diff --git a/Example/CoreDataDAOTests/CoreDataDAOFoldersTests.swift b/Example/CoreDataDAOTests/CoreDataDAOFoldersTests.swift new file mode 100644 index 0000000..420f355 --- /dev/null +++ b/Example/CoreDataDAOTests/CoreDataDAOFoldersTests.swift @@ -0,0 +1,43 @@ +// +// CoreDataDAOFoldersTests.swift +// DAO +// +// Created by Ivan Vavilov on 5/23/17. +// Copyright © 2017 RedMadRobot LLC. All rights reserved. +// + +import XCTest +import DAO +import CoreData +@testable import DAO_Example + + +final class CoreDataDAOFoldersTests: XCTestCase { + + let dao = try! CoreDataDAO( + CDFolderTranslator(), + configuration: CoreDataConfiguration( + containerName: "Model", + storeType: NSInMemoryStoreType)) + + + func testPersistMessages() { + let message1 = Message(entityId: "abc", text: "text1") + let message2 = Message(entityId: "bcc", text: "text2") + + let folder = Folder(entityId: "fld", name: "Home", messages: [message1, message2]) + + do { + try dao.persist(folder) + } catch _ { + XCTFail("Persist folder is failed") + } + + if let savedFolder = dao.read(folder.entityId) { + XCTAssertEqual(folder.messages.count, savedFolder.messages.count) + } else { + XCTFail("Persist folder is failed") + } + } + +} diff --git a/Example/CoreDataDAOTests/CoreDataDAOManyDAOTests.swift b/Example/CoreDataDAOTests/CoreDataDAOManyDAOTests.swift new file mode 100644 index 0000000..a37108e --- /dev/null +++ b/Example/CoreDataDAOTests/CoreDataDAOManyDAOTests.swift @@ -0,0 +1,45 @@ +// +// CoreDataDAOManyDAOTests.swift +// DAO +// +// Created by Ivan Vavilov on 5/22/17. +// Copyright © 2017 RedMadRobot LLC. All rights reserved. +// + +import XCTest +import DAO +import CoreData +@testable import DAO_Example + + +final class CoreDataDAOManyDAOTests: XCTestCase { + + let messagesDAO = try! CoreDataDAO( + CDMessageTranslator(), + configuration: CoreDataConfiguration( + containerName: "Model", + storeType: NSInMemoryStoreType)) + + let folderDAO = try! CoreDataDAO( + CDFolderTranslator(), + configuration: CoreDataConfiguration( + containerName: "Model", + storeType: NSInMemoryStoreType)) + + + func testPersistMessage() { + let message = Message(entityId: "abc", text: "text") + let folder = Folder(entityId: "fld", name: "folder", messages: []) + + do { + try messagesDAO.persist(message) + try folderDAO.persist(folder) + } catch _ { + XCTFail("Persist message is failed") + } + + XCTAssertEqual(message, messagesDAO.read(message.entityId)) + XCTAssertEqual(folder, folderDAO.read(folder.entityId)) + } + +} diff --git a/Example/CoreDataDAOTests/CoreDataDAOMessagesTests.swift b/Example/CoreDataDAOTests/CoreDataDAOMessagesTests.swift new file mode 100644 index 0000000..bb66476 --- /dev/null +++ b/Example/CoreDataDAOTests/CoreDataDAOMessagesTests.swift @@ -0,0 +1,65 @@ +// +// CoreDataDAOMessagesTests.swift +// DAO +// +// Created by Ivan Vavilov on 5/2/17. +// Copyright © 2017 RedMadRobot LLC. All rights reserved. +// + +import XCTest +import DAO +import CoreData +@testable import DAO_Example + + +final class CoreDataDAOMessagesTests: XCTestCase { + + let dao = try! CoreDataDAO( + CDMessageTranslator(), + configuration: CoreDataConfiguration( + containerName: "Model", + storeType: NSInMemoryStoreType)) + + + func testPersistMessage() { + let message = Message(entityId: "abc", text: "text") + + do { + try dao.persist(message) + } catch _ { + XCTFail("Persist message is failed") + } + + XCTAssertEqual(message, dao.read(message.entityId)) + } + + + func testReadMessage() { + let message = Message(entityId: "def", text: "text 2") + + do { + try dao.persist(message) + } catch _ { + XCTFail("Read message is failed") + } + + XCTAssertEqual(message, dao.read("def")) + } + + + func testEraseMessage() { + let message = Message(entityId: "ghi", text: "text 2") + + do { + try dao.persist(message) + try dao.erase("ghi") + } catch _ { + XCTFail("Erase message is failed") + } + + XCTAssertNil(dao.read("ghi")) + } + + + +} diff --git a/Example/CoreDataDAOTests/CoreDataDAOTests-Bridging-Header.h b/Example/CoreDataDAOTests/CoreDataDAOTests-Bridging-Header.h new file mode 100644 index 0000000..1b2cb5d --- /dev/null +++ b/Example/CoreDataDAOTests/CoreDataDAOTests-Bridging-Header.h @@ -0,0 +1,4 @@ +// +// Use this file to import your target's public headers that you would like to expose to Swift. +// + diff --git a/Example/CoreDataDAOTests/Info.plist b/Example/CoreDataDAOTests/Info.plist new file mode 100644 index 0000000..6c6c23c --- /dev/null +++ b/Example/CoreDataDAOTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/Example/DAO.xcodeproj/project.pbxproj b/Example/DAO.xcodeproj/project.pbxproj new file mode 100644 index 0000000..0347f4b --- /dev/null +++ b/Example/DAO.xcodeproj/project.pbxproj @@ -0,0 +1,957 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD51AFB9204008FA782 /* AppDelegate.swift */; }; + 63EBBC5B24D75059B9ADA532 /* Pods_RealmDAOTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0DCA2A834AEC013372828BE4 /* Pods_RealmDAOTests.framework */; }; + 78A211B7C1B6D6AF282EBB71 /* Pods_DAO_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2A944DFD46BDAF9137BA01D6 /* Pods_DAO_Example.framework */; }; + AA10ECB91EEFE058001228A1 /* RealmDAOBookTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA10ECB51EEFE058001228A1 /* RealmDAOBookTests.swift */; }; + AA10ECBA1EEFE058001228A1 /* RealmDAOEntityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA10ECB61EEFE058001228A1 /* RealmDAOEntityTests.swift */; }; + AA10ECBB1EEFE058001228A1 /* RealmDAOFolderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA10ECB71EEFE058001228A1 /* RealmDAOFolderTests.swift */; }; + AA10ECBC1EEFE058001228A1 /* RealmDAOMessagesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA10ECB81EEFE058001228A1 /* RealmDAOMessagesTests.swift */; }; + AA35256B1EEFDBE2009C4375 /* CDEntityTranslator.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA3525531EEFDBE2009C4375 /* CDEntityTranslator.swift */; }; + AA35256C1EEFDBE2009C4375 /* CDFolderTranslator.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA3525541EEFDBE2009C4375 /* CDFolderTranslator.swift */; }; + AA35256D1EEFDBE2009C4375 /* CDMessageTranslator.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA3525551EEFDBE2009C4375 /* CDMessageTranslator.swift */; }; + AA35256E1EEFDBE2009C4375 /* RLMBookTranslator.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA3525571EEFDBE2009C4375 /* RLMBookTranslator.swift */; }; + AA35256F1EEFDBE2009C4375 /* RLMEntityTranslator.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA3525581EEFDBE2009C4375 /* RLMEntityTranslator.swift */; }; + AA3525701EEFDBE2009C4375 /* RLMFolderTranslator.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA3525591EEFDBE2009C4375 /* RLMFolderTranslator.swift */; }; + AA3525711EEFDBE2009C4375 /* RLMMessageTranslator.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA35255A1EEFDBE2009C4375 /* RLMMessageTranslator.swift */; }; + AA3525721EEFDBE2009C4375 /* Book.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA35255C1EEFDBE2009C4375 /* Book.swift */; }; + AA3525731EEFDBE2009C4375 /* CDEntity+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA35255E1EEFDBE2009C4375 /* CDEntity+CoreDataProperties.swift */; }; + AA3525741EEFDBE2009C4375 /* CDEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA35255F1EEFDBE2009C4375 /* CDEntity.swift */; }; + AA3525751EEFDBE2009C4375 /* CDFolder+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA3525601EEFDBE2009C4375 /* CDFolder+CoreDataProperties.swift */; }; + AA3525761EEFDBE2009C4375 /* CDFolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA3525611EEFDBE2009C4375 /* CDFolder.swift */; }; + AA3525771EEFDBE2009C4375 /* CDMessage+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA3525621EEFDBE2009C4375 /* CDMessage+CoreDataProperties.swift */; }; + AA3525781EEFDBE2009C4375 /* CDMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA3525631EEFDBE2009C4375 /* CDMessage.swift */; }; + AA3525791EEFDBE2009C4375 /* Folder.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA3525641EEFDBE2009C4375 /* Folder.swift */; }; + AA35257A1EEFDBE2009C4375 /* Message.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA3525651EEFDBE2009C4375 /* Message.swift */; }; + AA35257B1EEFDBE2009C4375 /* DBBook.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA3525671EEFDBE2009C4375 /* DBBook.swift */; }; + AA35257C1EEFDBE2009C4375 /* DBEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA3525681EEFDBE2009C4375 /* DBEntity.swift */; }; + AA35257D1EEFDBE2009C4375 /* DBFolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA3525691EEFDBE2009C4375 /* DBFolder.swift */; }; + AA35257E1EEFDBE2009C4375 /* DBMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA35256A1EEFDBE2009C4375 /* DBMessage.swift */; }; + AA3525821EEFDC24009C4375 /* Model.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = AA3525801EEFDC24009C4375 /* Model.xcdatamodeld */; }; + AA3525961EEFDD13009C4375 /* CoreDataDAOEntityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA3525921EEFDD13009C4375 /* CoreDataDAOEntityTests.swift */; }; + AA3525971EEFDD13009C4375 /* CoreDataDAOFoldersTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA3525931EEFDD13009C4375 /* CoreDataDAOFoldersTests.swift */; }; + AA3525981EEFDD13009C4375 /* CoreDataDAOManyDAOTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA3525941EEFDD13009C4375 /* CoreDataDAOManyDAOTests.swift */; }; + AA3525991EEFDD13009C4375 /* CoreDataDAOMessagesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA3525951EEFDD13009C4375 /* CoreDataDAOMessagesTests.swift */; }; + E6862F802F1820084726DEB8 /* Pods_CoreDataDAOTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F42144986137E1707B5FAC0 /* Pods_CoreDataDAOTests.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + AA10ECAF1EEFE039001228A1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 607FACC81AFB9204008FA782 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 607FACCF1AFB9204008FA782; + remoteInfo = DAO_Example; + }; + AA35258C1EEFDCE6009C4375 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 607FACC81AFB9204008FA782 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 607FACCF1AFB9204008FA782; + remoteInfo = DAO_Example; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 048D2FC052C450E7600E2FA9 /* Pods-RealmDAOTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RealmDAOTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-RealmDAOTests/Pods-RealmDAOTests.release.xcconfig"; sourceTree = ""; }; + 0DCA2A834AEC013372828BE4 /* Pods_RealmDAOTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RealmDAOTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 1DD31E834DFA3BE6198A94A4 /* Pods-CoreDataDAOTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CoreDataDAOTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-CoreDataDAOTests/Pods-CoreDataDAOTests.release.xcconfig"; sourceTree = ""; }; + 2A944DFD46BDAF9137BA01D6 /* Pods_DAO_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_DAO_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 33B58A69D9690C75477DB06B /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; + 607FACD01AFB9204008FA782 /* DAO_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DAO_Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 607FACD41AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 607FACD51AFB9204008FA782 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 6B14B1EA7DC79FC4DC1DE526 /* Pods-DAO_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DAO_Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-DAO_Example/Pods-DAO_Example.release.xcconfig"; sourceTree = ""; }; + 6F42144986137E1707B5FAC0 /* Pods_CoreDataDAOTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_CoreDataDAOTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 7AA81098C68FE90BF55455CC /* DAO.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = DAO.podspec; path = ../DAO.podspec; sourceTree = ""; }; + 8FB85F7F7AE5E16EDA09AF5A /* Pods-RealmDAOTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RealmDAOTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RealmDAOTests/Pods-RealmDAOTests.debug.xcconfig"; sourceTree = ""; }; + 9096125064B44D5035341130 /* Pods-DAO_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DAO_Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-DAO_Example/Pods-DAO_Example.debug.xcconfig"; sourceTree = ""; }; + A0C0C1D67BC9E557747CBD93 /* Pods-DAO_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DAO_Tests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-DAO_Tests/Pods-DAO_Tests.debug.xcconfig"; sourceTree = ""; }; + AA10ECAA1EEFE039001228A1 /* RealmDAOTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RealmDAOTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + AA10ECAE1EEFE039001228A1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + AA10ECB41EEFE058001228A1 /* RealmDAOTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RealmDAOTests-Bridging-Header.h"; sourceTree = ""; }; + AA10ECB51EEFE058001228A1 /* RealmDAOBookTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealmDAOBookTests.swift; sourceTree = ""; }; + AA10ECB61EEFE058001228A1 /* RealmDAOEntityTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealmDAOEntityTests.swift; sourceTree = ""; }; + AA10ECB71EEFE058001228A1 /* RealmDAOFolderTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealmDAOFolderTests.swift; sourceTree = ""; }; + AA10ECB81EEFE058001228A1 /* RealmDAOMessagesTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealmDAOMessagesTests.swift; sourceTree = ""; }; + AA3525531EEFDBE2009C4375 /* CDEntityTranslator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CDEntityTranslator.swift; sourceTree = ""; }; + AA3525541EEFDBE2009C4375 /* CDFolderTranslator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CDFolderTranslator.swift; sourceTree = ""; }; + AA3525551EEFDBE2009C4375 /* CDMessageTranslator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CDMessageTranslator.swift; sourceTree = ""; }; + AA3525571EEFDBE2009C4375 /* RLMBookTranslator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RLMBookTranslator.swift; sourceTree = ""; }; + AA3525581EEFDBE2009C4375 /* RLMEntityTranslator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RLMEntityTranslator.swift; sourceTree = ""; }; + AA3525591EEFDBE2009C4375 /* RLMFolderTranslator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RLMFolderTranslator.swift; sourceTree = ""; }; + AA35255A1EEFDBE2009C4375 /* RLMMessageTranslator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RLMMessageTranslator.swift; sourceTree = ""; }; + AA35255C1EEFDBE2009C4375 /* Book.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Book.swift; sourceTree = ""; }; + AA35255E1EEFDBE2009C4375 /* CDEntity+CoreDataProperties.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CDEntity+CoreDataProperties.swift"; sourceTree = ""; }; + AA35255F1EEFDBE2009C4375 /* CDEntity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CDEntity.swift; sourceTree = ""; }; + AA3525601EEFDBE2009C4375 /* CDFolder+CoreDataProperties.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CDFolder+CoreDataProperties.swift"; sourceTree = ""; }; + AA3525611EEFDBE2009C4375 /* CDFolder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CDFolder.swift; sourceTree = ""; }; + AA3525621EEFDBE2009C4375 /* CDMessage+CoreDataProperties.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CDMessage+CoreDataProperties.swift"; sourceTree = ""; }; + AA3525631EEFDBE2009C4375 /* CDMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CDMessage.swift; sourceTree = ""; }; + AA3525641EEFDBE2009C4375 /* Folder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Folder.swift; sourceTree = ""; }; + AA3525651EEFDBE2009C4375 /* Message.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Message.swift; sourceTree = ""; }; + AA3525671EEFDBE2009C4375 /* DBBook.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DBBook.swift; sourceTree = ""; }; + AA3525681EEFDBE2009C4375 /* DBEntity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DBEntity.swift; sourceTree = ""; }; + AA3525691EEFDBE2009C4375 /* DBFolder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DBFolder.swift; sourceTree = ""; }; + AA35256A1EEFDBE2009C4375 /* DBMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DBMessage.swift; sourceTree = ""; }; + AA3525811EEFDC24009C4375 /* Model.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Model.xcdatamodel; sourceTree = ""; }; + AA3525871EEFDCE6009C4375 /* CoreDataDAOTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CoreDataDAOTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + AA35258B1EEFDCE6009C4375 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + AA3525911EEFDD13009C4375 /* CoreDataDAOTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CoreDataDAOTests-Bridging-Header.h"; sourceTree = ""; }; + AA3525921EEFDD13009C4375 /* CoreDataDAOEntityTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreDataDAOEntityTests.swift; sourceTree = ""; }; + AA3525931EEFDD13009C4375 /* CoreDataDAOFoldersTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreDataDAOFoldersTests.swift; sourceTree = ""; }; + AA3525941EEFDD13009C4375 /* CoreDataDAOManyDAOTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreDataDAOManyDAOTests.swift; sourceTree = ""; }; + AA3525951EEFDD13009C4375 /* CoreDataDAOMessagesTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreDataDAOMessagesTests.swift; sourceTree = ""; }; + B3F6075B730F33239A8C0C16 /* Pods-DAO_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DAO_Tests.release.xcconfig"; path = "Pods/Target Support Files/Pods-DAO_Tests/Pods-DAO_Tests.release.xcconfig"; sourceTree = ""; }; + B83929C10B53857ABAEBA293 /* Pods-CoreDataDAOTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CoreDataDAOTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-CoreDataDAOTests/Pods-CoreDataDAOTests.debug.xcconfig"; sourceTree = ""; }; + BF445E2FB5382CAF4125954E /* Pods_DAO_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_DAO_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + F8C399142A0F0FA7C064D093 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 607FACCD1AFB9204008FA782 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 78A211B7C1B6D6AF282EBB71 /* Pods_DAO_Example.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + AA10ECA71EEFE039001228A1 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 63EBBC5B24D75059B9ADA532 /* Pods_RealmDAOTests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + AA3525841EEFDCE6009C4375 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + E6862F802F1820084726DEB8 /* Pods_CoreDataDAOTests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 607FACC71AFB9204008FA782 = { + isa = PBXGroup; + children = ( + 607FACD21AFB9204008FA782 /* Example for DAO */, + AA3525881EEFDCE6009C4375 /* CoreDataDAOTests */, + AA10ECAB1EEFE039001228A1 /* RealmDAOTests */, + CB0F3F47EAF7977E58A370FB /* Frameworks */, + EF4D865297EF284754A73C8C /* Pods */, + 607FACF51AFB993E008FA782 /* Podspec Metadata */, + 607FACD11AFB9204008FA782 /* Products */, + ); + sourceTree = ""; + }; + 607FACD11AFB9204008FA782 /* Products */ = { + isa = PBXGroup; + children = ( + 607FACD01AFB9204008FA782 /* DAO_Example.app */, + AA3525871EEFDCE6009C4375 /* CoreDataDAOTests.xctest */, + AA10ECAA1EEFE039001228A1 /* RealmDAOTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 607FACD21AFB9204008FA782 /* Example for DAO */ = { + isa = PBXGroup; + children = ( + 607FACD51AFB9204008FA782 /* AppDelegate.swift */, + AA35254E1EEFDBE2009C4375 /* Classes */, + AA35257F1EEFDC24009C4375 /* Resources */, + 607FACD31AFB9204008FA782 /* Supporting Files */, + ); + name = "Example for DAO"; + path = DAO; + sourceTree = ""; + }; + 607FACD31AFB9204008FA782 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 607FACD41AFB9204008FA782 /* Info.plist */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 607FACF51AFB993E008FA782 /* Podspec Metadata */ = { + isa = PBXGroup; + children = ( + 7AA81098C68FE90BF55455CC /* DAO.podspec */, + 33B58A69D9690C75477DB06B /* README.md */, + F8C399142A0F0FA7C064D093 /* LICENSE */, + ); + name = "Podspec Metadata"; + sourceTree = ""; + }; + AA10ECAB1EEFE039001228A1 /* RealmDAOTests */ = { + isa = PBXGroup; + children = ( + AA10ECAE1EEFE039001228A1 /* Info.plist */, + AA10ECB51EEFE058001228A1 /* RealmDAOBookTests.swift */, + AA10ECB61EEFE058001228A1 /* RealmDAOEntityTests.swift */, + AA10ECB71EEFE058001228A1 /* RealmDAOFolderTests.swift */, + AA10ECB81EEFE058001228A1 /* RealmDAOMessagesTests.swift */, + AA10ECB41EEFE058001228A1 /* RealmDAOTests-Bridging-Header.h */, + ); + path = RealmDAOTests; + sourceTree = ""; + }; + AA35254E1EEFDBE2009C4375 /* Classes */ = { + isa = PBXGroup; + children = ( + AA35254F1EEFDBE2009C4375 /* Business Logic */, + AA35255B1EEFDBE2009C4375 /* Model */, + ); + path = Classes; + sourceTree = ""; + }; + AA35254F1EEFDBE2009C4375 /* Business Logic */ = { + isa = PBXGroup; + children = ( + AA3525501EEFDBE2009C4375 /* Helper */, + ); + path = "Business Logic"; + sourceTree = ""; + }; + AA3525501EEFDBE2009C4375 /* Helper */ = { + isa = PBXGroup; + children = ( + AA3525511EEFDBE2009C4375 /* DAO */, + ); + path = Helper; + sourceTree = ""; + }; + AA3525511EEFDBE2009C4375 /* DAO */ = { + isa = PBXGroup; + children = ( + AA3525521EEFDBE2009C4375 /* CoreDataTranslator */, + AA3525561EEFDBE2009C4375 /* RealmTranslator */, + ); + path = DAO; + sourceTree = ""; + }; + AA3525521EEFDBE2009C4375 /* CoreDataTranslator */ = { + isa = PBXGroup; + children = ( + AA3525531EEFDBE2009C4375 /* CDEntityTranslator.swift */, + AA3525541EEFDBE2009C4375 /* CDFolderTranslator.swift */, + AA3525551EEFDBE2009C4375 /* CDMessageTranslator.swift */, + ); + path = CoreDataTranslator; + sourceTree = ""; + }; + AA3525561EEFDBE2009C4375 /* RealmTranslator */ = { + isa = PBXGroup; + children = ( + AA3525571EEFDBE2009C4375 /* RLMBookTranslator.swift */, + AA3525581EEFDBE2009C4375 /* RLMEntityTranslator.swift */, + AA3525591EEFDBE2009C4375 /* RLMFolderTranslator.swift */, + AA35255A1EEFDBE2009C4375 /* RLMMessageTranslator.swift */, + ); + path = RealmTranslator; + sourceTree = ""; + }; + AA35255B1EEFDBE2009C4375 /* Model */ = { + isa = PBXGroup; + children = ( + AA35255C1EEFDBE2009C4375 /* Book.swift */, + AA35255D1EEFDBE2009C4375 /* CoreDataDatabase */, + AA3525641EEFDBE2009C4375 /* Folder.swift */, + AA3525651EEFDBE2009C4375 /* Message.swift */, + AA3525661EEFDBE2009C4375 /* RealmDatabase */, + ); + path = Model; + sourceTree = ""; + }; + AA35255D1EEFDBE2009C4375 /* CoreDataDatabase */ = { + isa = PBXGroup; + children = ( + AA35255F1EEFDBE2009C4375 /* CDEntity.swift */, + AA35255E1EEFDBE2009C4375 /* CDEntity+CoreDataProperties.swift */, + AA3525611EEFDBE2009C4375 /* CDFolder.swift */, + AA3525601EEFDBE2009C4375 /* CDFolder+CoreDataProperties.swift */, + AA3525631EEFDBE2009C4375 /* CDMessage.swift */, + AA3525621EEFDBE2009C4375 /* CDMessage+CoreDataProperties.swift */, + ); + path = CoreDataDatabase; + sourceTree = ""; + }; + AA3525661EEFDBE2009C4375 /* RealmDatabase */ = { + isa = PBXGroup; + children = ( + AA3525671EEFDBE2009C4375 /* DBBook.swift */, + AA3525681EEFDBE2009C4375 /* DBEntity.swift */, + AA3525691EEFDBE2009C4375 /* DBFolder.swift */, + AA35256A1EEFDBE2009C4375 /* DBMessage.swift */, + ); + path = RealmDatabase; + sourceTree = ""; + }; + AA35257F1EEFDC24009C4375 /* Resources */ = { + isa = PBXGroup; + children = ( + AA3525801EEFDC24009C4375 /* Model.xcdatamodeld */, + ); + path = Resources; + sourceTree = ""; + }; + AA3525881EEFDCE6009C4375 /* CoreDataDAOTests */ = { + isa = PBXGroup; + children = ( + AA3525921EEFDD13009C4375 /* CoreDataDAOEntityTests.swift */, + AA3525931EEFDD13009C4375 /* CoreDataDAOFoldersTests.swift */, + AA3525941EEFDD13009C4375 /* CoreDataDAOManyDAOTests.swift */, + AA3525951EEFDD13009C4375 /* CoreDataDAOMessagesTests.swift */, + AA3525911EEFDD13009C4375 /* CoreDataDAOTests-Bridging-Header.h */, + AA35258B1EEFDCE6009C4375 /* Info.plist */, + ); + path = CoreDataDAOTests; + sourceTree = ""; + }; + CB0F3F47EAF7977E58A370FB /* Frameworks */ = { + isa = PBXGroup; + children = ( + 2A944DFD46BDAF9137BA01D6 /* Pods_DAO_Example.framework */, + BF445E2FB5382CAF4125954E /* Pods_DAO_Tests.framework */, + 6F42144986137E1707B5FAC0 /* Pods_CoreDataDAOTests.framework */, + 0DCA2A834AEC013372828BE4 /* Pods_RealmDAOTests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + EF4D865297EF284754A73C8C /* Pods */ = { + isa = PBXGroup; + children = ( + 9096125064B44D5035341130 /* Pods-DAO_Example.debug.xcconfig */, + 6B14B1EA7DC79FC4DC1DE526 /* Pods-DAO_Example.release.xcconfig */, + A0C0C1D67BC9E557747CBD93 /* Pods-DAO_Tests.debug.xcconfig */, + B3F6075B730F33239A8C0C16 /* Pods-DAO_Tests.release.xcconfig */, + B83929C10B53857ABAEBA293 /* Pods-CoreDataDAOTests.debug.xcconfig */, + 1DD31E834DFA3BE6198A94A4 /* Pods-CoreDataDAOTests.release.xcconfig */, + 8FB85F7F7AE5E16EDA09AF5A /* Pods-RealmDAOTests.debug.xcconfig */, + 048D2FC052C450E7600E2FA9 /* Pods-RealmDAOTests.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 607FACCF1AFB9204008FA782 /* DAO_Example */ = { + isa = PBXNativeTarget; + buildConfigurationList = 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "DAO_Example" */; + buildPhases = ( + 396DD212B4355BA1D08DC7E7 /* [CP] Check Pods Manifest.lock */, + 607FACCC1AFB9204008FA782 /* Sources */, + 607FACCD1AFB9204008FA782 /* Frameworks */, + 607FACCE1AFB9204008FA782 /* Resources */, + A3C342A12354181FB1CDC888 /* [CP] Embed Pods Frameworks */, + FBB72B6CB6EF1FFF9E33FE93 /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = DAO_Example; + productName = DAO; + productReference = 607FACD01AFB9204008FA782 /* DAO_Example.app */; + productType = "com.apple.product-type.application"; + }; + AA10ECA91EEFE039001228A1 /* RealmDAOTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = AA10ECB11EEFE039001228A1 /* Build configuration list for PBXNativeTarget "RealmDAOTests" */; + buildPhases = ( + 847A3AC11FE1C324C80B4DFC /* [CP] Check Pods Manifest.lock */, + AA10ECA61EEFE039001228A1 /* Sources */, + AA10ECA71EEFE039001228A1 /* Frameworks */, + AA10ECA81EEFE039001228A1 /* Resources */, + C31918995F93713C33A15BCA /* [CP] Embed Pods Frameworks */, + 525D7AFD3BD0F596CBD437B0 /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + AA10ECB01EEFE039001228A1 /* PBXTargetDependency */, + ); + name = RealmDAOTests; + productName = RealmDAOTests; + productReference = AA10ECAA1EEFE039001228A1 /* RealmDAOTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + AA3525861EEFDCE6009C4375 /* CoreDataDAOTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = AA35258E1EEFDCE6009C4375 /* Build configuration list for PBXNativeTarget "CoreDataDAOTests" */; + buildPhases = ( + EC8476E3A4C369B6B36446A1 /* [CP] Check Pods Manifest.lock */, + AA3525831EEFDCE6009C4375 /* Sources */, + AA3525841EEFDCE6009C4375 /* Frameworks */, + AA3525851EEFDCE6009C4375 /* Resources */, + 6E99BAE18868857CA3143787 /* [CP] Embed Pods Frameworks */, + E809FAD87FAC0879AF2B934A /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + AA35258D1EEFDCE6009C4375 /* PBXTargetDependency */, + ); + name = CoreDataDAOTests; + productName = CoreDataDAOTests; + productReference = AA3525871EEFDCE6009C4375 /* CoreDataDAOTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 607FACC81AFB9204008FA782 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0830; + LastUpgradeCheck = 0820; + ORGANIZATIONNAME = CocoaPods; + TargetAttributes = { + 607FACCF1AFB9204008FA782 = { + CreatedOnToolsVersion = 6.3.1; + LastSwiftMigration = 0820; + }; + AA10ECA91EEFE039001228A1 = { + CreatedOnToolsVersion = 8.3.2; + DevelopmentTeam = 42LRQS6X44; + LastSwiftMigration = 0830; + ProvisioningStyle = Automatic; + TestTargetID = 607FACCF1AFB9204008FA782; + }; + AA3525861EEFDCE6009C4375 = { + CreatedOnToolsVersion = 8.3.2; + DevelopmentTeam = 42LRQS6X44; + LastSwiftMigration = 0830; + ProvisioningStyle = Automatic; + TestTargetID = 607FACCF1AFB9204008FA782; + }; + }; + }; + buildConfigurationList = 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "DAO" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 607FACC71AFB9204008FA782; + productRefGroup = 607FACD11AFB9204008FA782 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 607FACCF1AFB9204008FA782 /* DAO_Example */, + AA3525861EEFDCE6009C4375 /* CoreDataDAOTests */, + AA10ECA91EEFE039001228A1 /* RealmDAOTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 607FACCE1AFB9204008FA782 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + AA10ECA81EEFE039001228A1 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + AA3525851EEFDCE6009C4375 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 396DD212B4355BA1D08DC7E7 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; + 525D7AFD3BD0F596CBD437B0 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-RealmDAOTests/Pods-RealmDAOTests-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + 6E99BAE18868857CA3143787 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-CoreDataDAOTests/Pods-CoreDataDAOTests-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 847A3AC11FE1C324C80B4DFC /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; + A3C342A12354181FB1CDC888 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-DAO_Example/Pods-DAO_Example-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + C31918995F93713C33A15BCA /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-RealmDAOTests/Pods-RealmDAOTests-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + E809FAD87FAC0879AF2B934A /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-CoreDataDAOTests/Pods-CoreDataDAOTests-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + EC8476E3A4C369B6B36446A1 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; + FBB72B6CB6EF1FFF9E33FE93 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-DAO_Example/Pods-DAO_Example-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 607FACCC1AFB9204008FA782 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + AA35256E1EEFDBE2009C4375 /* RLMBookTranslator.swift in Sources */, + AA3525731EEFDBE2009C4375 /* CDEntity+CoreDataProperties.swift in Sources */, + AA3525771EEFDBE2009C4375 /* CDMessage+CoreDataProperties.swift in Sources */, + AA35257D1EEFDBE2009C4375 /* DBFolder.swift in Sources */, + AA35257C1EEFDBE2009C4375 /* DBEntity.swift in Sources */, + AA35257A1EEFDBE2009C4375 /* Message.swift in Sources */, + AA3525791EEFDBE2009C4375 /* Folder.swift in Sources */, + AA3525741EEFDBE2009C4375 /* CDEntity.swift in Sources */, + AA3525781EEFDBE2009C4375 /* CDMessage.swift in Sources */, + AA3525821EEFDC24009C4375 /* Model.xcdatamodeld in Sources */, + 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */, + AA35257B1EEFDBE2009C4375 /* DBBook.swift in Sources */, + AA3525701EEFDBE2009C4375 /* RLMFolderTranslator.swift in Sources */, + AA35257E1EEFDBE2009C4375 /* DBMessage.swift in Sources */, + AA35256F1EEFDBE2009C4375 /* RLMEntityTranslator.swift in Sources */, + AA3525761EEFDBE2009C4375 /* CDFolder.swift in Sources */, + AA35256C1EEFDBE2009C4375 /* CDFolderTranslator.swift in Sources */, + AA3525721EEFDBE2009C4375 /* Book.swift in Sources */, + AA35256D1EEFDBE2009C4375 /* CDMessageTranslator.swift in Sources */, + AA35256B1EEFDBE2009C4375 /* CDEntityTranslator.swift in Sources */, + AA3525751EEFDBE2009C4375 /* CDFolder+CoreDataProperties.swift in Sources */, + AA3525711EEFDBE2009C4375 /* RLMMessageTranslator.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + AA10ECA61EEFE039001228A1 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + AA10ECB91EEFE058001228A1 /* RealmDAOBookTests.swift in Sources */, + AA10ECBA1EEFE058001228A1 /* RealmDAOEntityTests.swift in Sources */, + AA10ECBC1EEFE058001228A1 /* RealmDAOMessagesTests.swift in Sources */, + AA10ECBB1EEFE058001228A1 /* RealmDAOFolderTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + AA3525831EEFDCE6009C4375 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + AA3525961EEFDD13009C4375 /* CoreDataDAOEntityTests.swift in Sources */, + AA3525981EEFDD13009C4375 /* CoreDataDAOManyDAOTests.swift in Sources */, + AA3525971EEFDD13009C4375 /* CoreDataDAOFoldersTests.swift in Sources */, + AA3525991EEFDD13009C4375 /* CoreDataDAOMessagesTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + AA10ECB01EEFE039001228A1 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 607FACCF1AFB9204008FA782 /* DAO_Example */; + targetProxy = AA10ECAF1EEFE039001228A1 /* PBXContainerItemProxy */; + }; + AA35258D1EEFDCE6009C4375 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 607FACCF1AFB9204008FA782 /* DAO_Example */; + targetProxy = AA35258C1EEFDCE6009C4375 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 607FACED1AFB9204008FA782 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 607FACEE1AFB9204008FA782 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 607FACF01AFB9204008FA782 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9096125064B44D5035341130 /* Pods-DAO_Example.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = DAO/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + MODULE_NAME = ExampleApp; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; + }; + name = Debug; + }; + 607FACF11AFB9204008FA782 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 6B14B1EA7DC79FC4DC1DE526 /* Pods-DAO_Example.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = DAO/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + MODULE_NAME = ExampleApp; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; + }; + name = Release; + }; + AA10ECB21EEFE039001228A1 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 8FB85F7F7AE5E16EDA09AF5A /* Pods-RealmDAOTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_ENABLE_MODULES = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = 42LRQS6X44; + INFOPLIST_FILE = RealmDAOTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 10.3; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.redmadrobot.RealmDAOTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OBJC_BRIDGING_HEADER = "RealmDAOTests/RealmDAOTests-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/DAO_Example.app/DAO_Example"; + }; + name = Debug; + }; + AA10ECB31EEFE039001228A1 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 048D2FC052C450E7600E2FA9 /* Pods-RealmDAOTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_ENABLE_MODULES = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + DEVELOPMENT_TEAM = 42LRQS6X44; + INFOPLIST_FILE = RealmDAOTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 10.3; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.redmadrobot.RealmDAOTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "RealmDAOTests/RealmDAOTests-Bridging-Header.h"; + SWIFT_VERSION = 3.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/DAO_Example.app/DAO_Example"; + }; + name = Release; + }; + AA35258F1EEFDCE6009C4375 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = B83929C10B53857ABAEBA293 /* Pods-CoreDataDAOTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_ENABLE_MODULES = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = 42LRQS6X44; + INFOPLIST_FILE = CoreDataDAOTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 10.3; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.redmadrobot.CoreDataDAOTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OBJC_BRIDGING_HEADER = "CoreDataDAOTests/CoreDataDAOTests-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/DAO_Example.app/DAO_Example"; + }; + name = Debug; + }; + AA3525901EEFDCE6009C4375 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 1DD31E834DFA3BE6198A94A4 /* Pods-CoreDataDAOTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_ENABLE_MODULES = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + DEVELOPMENT_TEAM = 42LRQS6X44; + INFOPLIST_FILE = CoreDataDAOTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 10.3; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.redmadrobot.CoreDataDAOTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "CoreDataDAOTests/CoreDataDAOTests-Bridging-Header.h"; + SWIFT_VERSION = 3.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/DAO_Example.app/DAO_Example"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "DAO" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 607FACED1AFB9204008FA782 /* Debug */, + 607FACEE1AFB9204008FA782 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "DAO_Example" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 607FACF01AFB9204008FA782 /* Debug */, + 607FACF11AFB9204008FA782 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + AA10ECB11EEFE039001228A1 /* Build configuration list for PBXNativeTarget "RealmDAOTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + AA10ECB21EEFE039001228A1 /* Debug */, + AA10ECB31EEFE039001228A1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + AA35258E1EEFDCE6009C4375 /* Build configuration list for PBXNativeTarget "CoreDataDAOTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + AA35258F1EEFDCE6009C4375 /* Debug */, + AA3525901EEFDCE6009C4375 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCVersionGroup section */ + AA3525801EEFDC24009C4375 /* Model.xcdatamodeld */ = { + isa = XCVersionGroup; + children = ( + AA3525811EEFDC24009C4375 /* Model.xcdatamodel */, + ); + currentVersion = AA3525811EEFDC24009C4375 /* Model.xcdatamodel */; + path = Model.xcdatamodeld; + sourceTree = ""; + versionGroupType = wrapper.xcdatamodel; + }; +/* End XCVersionGroup section */ + }; + rootObject = 607FACC81AFB9204008FA782 /* Project object */; +} diff --git a/Example/DAO.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Example/DAO.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..f947249 --- /dev/null +++ b/Example/DAO.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Example/DAO.xcodeproj/xcshareddata/xcschemes/DAO-Example.xcscheme b/Example/DAO.xcodeproj/xcshareddata/xcschemes/DAO-Example.xcscheme new file mode 100644 index 0000000..bc5889e --- /dev/null +++ b/Example/DAO.xcodeproj/xcshareddata/xcschemes/DAO-Example.xcscheme @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Example/DAO.xcworkspace/contents.xcworkspacedata b/Example/DAO.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..a5db114 --- /dev/null +++ b/Example/DAO.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/Example/DAO/AppDelegate.swift b/Example/DAO/AppDelegate.swift new file mode 100644 index 0000000..eb8914f --- /dev/null +++ b/Example/DAO/AppDelegate.swift @@ -0,0 +1,19 @@ +// +// AppDelegate.swift +// DAO +// +// Created by Ivan Vavilov on 06/13/2017. +// Copyright (c) 2017 RedMadRobot. All rights reserved. +// + +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { + return true + } + +} + diff --git a/Example/DAO/Classes/Business Logic/Helper/DAO/CoreDataTranslator/CDEntityTranslator.swift b/Example/DAO/Classes/Business Logic/Helper/DAO/CoreDataTranslator/CDEntityTranslator.swift new file mode 100755 index 0000000..9a9d179 --- /dev/null +++ b/Example/DAO/Classes/Business Logic/Helper/DAO/CoreDataTranslator/CDEntityTranslator.swift @@ -0,0 +1,28 @@ +// +// CDEntityTranslator.swift +// DAO +// +// Created by Ivan Vavilov on 2/9/16. +// Copyright © 2016 RedMadRobot LLC. All rights reserved. +// + +import DAO +import CoreData + +class CDEntityTranslator: CoreDataTranslator { + + override func fill(_ entity: Entity, fromEntry: CDEntity) { + entity.entityId = fromEntry.entryId + } + + required init() { + + } + + override func fill( + _ entry: CDEntity, + fromEntity entity: Entity, + in context: NSManagedObjectContext) { + entry.entryId = entity.entityId + } +} diff --git a/Example/DAO/Classes/Business Logic/Helper/DAO/CoreDataTranslator/CDFolderTranslator.swift b/Example/DAO/Classes/Business Logic/Helper/DAO/CoreDataTranslator/CDFolderTranslator.swift new file mode 100644 index 0000000..2e66bf6 --- /dev/null +++ b/Example/DAO/Classes/Business Logic/Helper/DAO/CoreDataTranslator/CDFolderTranslator.swift @@ -0,0 +1,43 @@ +// +// CDFolderTranslator.swift +// DAO +// +// Created by Ivan Vavilov on 5/2/17. +// Copyright © 2017 RedMadRobot LLC. All rights reserved. +// + +import DAO +import CoreData + + +class CDFolderTranslator: CoreDataTranslator { + + required init() {} + + + override func fill(_ entity: Folder, fromEntry entry: CDFolder) { + entity.entityId = entry.entryId + entity.name = entry.name + + CDMessageTranslator().fill(&entity.messages, fromEntries: entry.messages as? Set) + } + + + override func fill( + _ entry: CDFolder, + fromEntity entity: Folder, + in context: NSManagedObjectContext) { + entry.entryId = entity.entityId + entry.name = entity.name + + var messages = Set() + CDMessageTranslator().fill(&messages, fromEntities: entity.messages, in: context) + + if let m = entry.messages { + entry.removeFromMessages(m) + } + + entry.addToMessages(messages as NSSet) + } + +} diff --git a/Example/DAO/Classes/Business Logic/Helper/DAO/CoreDataTranslator/CDMessageTranslator.swift b/Example/DAO/Classes/Business Logic/Helper/DAO/CoreDataTranslator/CDMessageTranslator.swift new file mode 100644 index 0000000..e8db05c --- /dev/null +++ b/Example/DAO/Classes/Business Logic/Helper/DAO/CoreDataTranslator/CDMessageTranslator.swift @@ -0,0 +1,32 @@ +// +// CDMessageTranslator.swift +// DAO +// +// Created by Ivan Vavilov on 5/2/17. +// Copyright © 2017 RedMadRobot LLC. All rights reserved. +// + +import DAO +import CoreData + + +class CDMessageTranslator: CoreDataTranslator { + + override func fill(_ entity: Message?, fromEntry entry: CDMessage) { + entity?.entityId = entry.entryId + entity?.text = entry.text + } + + + required init() {} + + + override func fill( + _ entry: CDMessage, + fromEntity entity: Message, + in context: NSManagedObjectContext) { + entry.entryId = entity.entityId + entry.text = entity.text + } + +} diff --git a/Example/DAO/Classes/Business Logic/Helper/DAO/RealmTranslator/RLMBookTranslator.swift b/Example/DAO/Classes/Business Logic/Helper/DAO/RealmTranslator/RLMBookTranslator.swift new file mode 100644 index 0000000..88e6d0e --- /dev/null +++ b/Example/DAO/Classes/Business Logic/Helper/DAO/RealmTranslator/RLMBookTranslator.swift @@ -0,0 +1,50 @@ +// +// RLMBookTranslator.swift +// DAO +// +// Created by Ivan Vavilov on 5/17/17. +// Copyright © 2017 RedMadRobot LLC. All rights reserved. +// + +import UIKit +import DAO + +class RLMBookTranslator: RealmTranslator { + + required init() {} + + + override func fill(_ entity: Book, fromEntry: DBBook) { + entity.entityId = fromEntry.entryId + entity.name = fromEntry.name + entity.authors = fromEntry.authors.map { $0.value } + entity.dates = fromEntry.dates.map { $0.value } + entity.pages = fromEntry.pages.map { $0.value } + entity.attachments = fromEntry.attachments.map { $0.value } + } + + + override func fill(_ entry: DBBook, fromEntity: Book) { + if entry.entryId != fromEntity.entityId { + entry.entryId = fromEntity.entityId + } + + entry.name = fromEntity.name + + entry.authors.removeAll() + entry.dates.removeAll() + entry.pages.removeAll() + entry.attachments.removeAll() + + let authors = fromEntity.authors.map { RLMString(val: $0) } + let dates = fromEntity.dates.map { RLMDate(val: $0) } + let pages = fromEntity.pages.map { RLMInteger(val: $0) } + let attachments = fromEntity.attachments.map { RLMData(val: $0) } + + entry.authors.append(objectsIn: authors) + entry.dates.append(objectsIn: dates) + entry.pages.append(objectsIn: pages) + entry.attachments.append(objectsIn: attachments) + } + +} diff --git a/Example/DAO/Classes/Business Logic/Helper/DAO/RealmTranslator/RLMEntityTranslator.swift b/Example/DAO/Classes/Business Logic/Helper/DAO/RealmTranslator/RLMEntityTranslator.swift new file mode 100644 index 0000000..1cb704c --- /dev/null +++ b/Example/DAO/Classes/Business Logic/Helper/DAO/RealmTranslator/RLMEntityTranslator.swift @@ -0,0 +1,30 @@ +// +// RealmTranslator.swift +// DAO +// +// Created by Igor Bulyga on 05.02.16. +// Copyright © 2016 RedMadRobot LLC. All rights reserved. +// + + +import DAO + +class RLMEntityTranslator: RealmTranslator { + + required init() { + + } + + + override func fill(_ entity: Entity, fromEntry: DBEntity) { + entity.entityId = fromEntry.entryId + } + + + override func fill(_ entry: DBEntity, fromEntity: Entity) { + if entry.entryId != fromEntity.entityId { + entry.entryId = fromEntity.entityId + } + } + +} diff --git a/Example/DAO/Classes/Business Logic/Helper/DAO/RealmTranslator/RLMFolderTranslator.swift b/Example/DAO/Classes/Business Logic/Helper/DAO/RealmTranslator/RLMFolderTranslator.swift new file mode 100644 index 0000000..30b0651 --- /dev/null +++ b/Example/DAO/Classes/Business Logic/Helper/DAO/RealmTranslator/RLMFolderTranslator.swift @@ -0,0 +1,34 @@ +// +// RLMFolderTranslator.swift +// DAO +// +// Created by Igor Bulyga on 09.02.16. +// Copyright © 2016 RedMadRobot LLC. All rights reserved. +// + +import DAO +import RealmSwift + +class RLMFolderTranslator: RealmTranslator { + + required init() {} + + + override func fill(_ entity: Folder, fromEntry: DBFolder) { + entity.entityId = fromEntry.entryId + entity.name = fromEntry.name + + RLMMessageTranslator().fill(&entity.messages, fromEntries: fromEntry.messages) + } + + + override func fill(_ entry: DBFolder, fromEntity: Folder) { + if entry.entryId != fromEntity.entityId { + entry.entryId = fromEntity.entityId + } + entry.name = fromEntity.name + + RLMMessageTranslator().fill(entry.messages, fromEntities: fromEntity.messages) + } + +} diff --git a/Example/DAO/Classes/Business Logic/Helper/DAO/RealmTranslator/RLMMessageTranslator.swift b/Example/DAO/Classes/Business Logic/Helper/DAO/RealmTranslator/RLMMessageTranslator.swift new file mode 100644 index 0000000..fe88a8f --- /dev/null +++ b/Example/DAO/Classes/Business Logic/Helper/DAO/RealmTranslator/RLMMessageTranslator.swift @@ -0,0 +1,30 @@ +// +// RLMMessageTranslator.swift +// DAO +// +// Created by Igor Bulyga on 09.02.16. +// Copyright © 2016 RedMadRobot LLC. All rights reserved. +// + +import DAO + +class RLMMessageTranslator: RealmTranslator { + + required init() { + + } + + override func fill(_ entity: Message, fromEntry: DBMessage) { + entity.entityId = fromEntry.entryId + entity.text = fromEntry.text + } + + + override func fill(_ entry: DBMessage, fromEntity: Message) { + if entry.entryId != fromEntity.entityId { + entry.entryId = fromEntity.entityId + } + entry.text = fromEntity.text + } + +} diff --git a/Example/DAO/Classes/Model/Book.swift b/Example/DAO/Classes/Model/Book.swift new file mode 100644 index 0000000..b31deed --- /dev/null +++ b/Example/DAO/Classes/Model/Book.swift @@ -0,0 +1,47 @@ +// +// Book.swift +// DAO +// +// Created by Ivan Vavilov on 5/17/17. +// Copyright © 2017 RedMadRobot LLC. All rights reserved. +// + +import UIKit +import DAO + + +class Book: Entity { + + var name: String + + var authors: [String] + + var dates: [Date] + + var pages: [Int] + + var attachments: [Data] + + + init(entityId: String, name: String, authors: [String], dates: [Date], pages: [Int], attachments: [Data]) { + self.name = name + self.authors = authors + self.dates = dates + self.pages = pages + self.attachments = attachments + + super.init(entityId: entityId) + } + + + required init() { + self.name = "" + self.authors = [] + self.dates = [] + self.pages = [] + self.attachments = [] + + super.init() + } + +} diff --git a/Example/DAO/Classes/Model/CoreDataDatabase/CDEntity+CoreDataProperties.swift b/Example/DAO/Classes/Model/CoreDataDatabase/CDEntity+CoreDataProperties.swift new file mode 100755 index 0000000..bbeea0b --- /dev/null +++ b/Example/DAO/Classes/Model/CoreDataDatabase/CDEntity+CoreDataProperties.swift @@ -0,0 +1,20 @@ +// +// CDEntity+CoreDataProperties.swift +// DAO +// +// Created by Ivan Vavilov on 09/02/16. +// Copyright © 2016 RedMadRobot LLC. All rights reserved. +// +// Choose "Create NSManagedObject Subclass…" from the Core Data editor menu +// to delete and recreate this implementation file for your updated model. +// + +import Foundation +import CoreData + + +extension CDEntity { + + @NSManaged var entryId: String + +} diff --git a/Example/DAO/Classes/Model/CoreDataDatabase/CDEntity.swift b/Example/DAO/Classes/Model/CoreDataDatabase/CDEntity.swift new file mode 100755 index 0000000..bd1f629 --- /dev/null +++ b/Example/DAO/Classes/Model/CoreDataDatabase/CDEntity.swift @@ -0,0 +1,18 @@ +// +// CDEntity.swift +// DAO +// +// Created by Ivan Vavilov on 09/02/16. +// Copyright © 2016 RedMadRobot LLC. All rights reserved. +// + +import Foundation +import CoreData + + +@objc(CDEntity) +class CDEntity: NSManagedObject { + + // Insert code here to add functionality to your managed object subclass + +} diff --git a/Example/DAO/Classes/Model/CoreDataDatabase/CDFolder+CoreDataProperties.swift b/Example/DAO/Classes/Model/CoreDataDatabase/CDFolder+CoreDataProperties.swift new file mode 100644 index 0000000..b66a956 --- /dev/null +++ b/Example/DAO/Classes/Model/CoreDataDatabase/CDFolder+CoreDataProperties.swift @@ -0,0 +1,40 @@ +// +// CDFolder+CoreDataProperties.swift +// DAO +// +// Created by Ivan Vavilov on 5/2/17. +// Copyright © 2017 RedMadRobot LLC. All rights reserved. +// + +import Foundation +import CoreData + + +extension CDFolder { + + @nonobjc + class func fetchRequest() -> NSFetchRequest { + return NSFetchRequest(entityName: "CDFolder") + } + + @NSManaged var name: String + @NSManaged var messages: NSSet? + +} + +// MARK: Generated accessors for messages +extension CDFolder { + + @objc(addMessagesObject:) + @NSManaged func addToMessages(_ value: CDMessage) + + @objc(removeMessagesObject:) + @NSManaged func removeFromMessages(_ value: CDMessage) + + @objc(addMessages:) + @NSManaged func addToMessages(_ values: NSSet) + + @objc(removeMessages:) + @NSManaged func removeFromMessages(_ values: NSSet) + +} diff --git a/Example/DAO/Classes/Model/CoreDataDatabase/CDFolder.swift b/Example/DAO/Classes/Model/CoreDataDatabase/CDFolder.swift new file mode 100644 index 0000000..b380a13 --- /dev/null +++ b/Example/DAO/Classes/Model/CoreDataDatabase/CDFolder.swift @@ -0,0 +1,15 @@ +// +// CDFolder.swift +// DAO +// +// Created by Ivan Vavilov on 5/2/17. +// Copyright © 2017 RedMadRobot LLC. All rights reserved. +// + +import Foundation +import CoreData + +@objc(CDFolder) +class CDFolder: CDEntity { + +} diff --git a/Example/DAO/Classes/Model/CoreDataDatabase/CDMessage+CoreDataProperties.swift b/Example/DAO/Classes/Model/CoreDataDatabase/CDMessage+CoreDataProperties.swift new file mode 100644 index 0000000..2b2fdfc --- /dev/null +++ b/Example/DAO/Classes/Model/CoreDataDatabase/CDMessage+CoreDataProperties.swift @@ -0,0 +1,22 @@ +// +// CDMessage+CoreDataProperties.swift +// DAO +// +// Created by Ivan Vavilov on 5/2/17. +// Copyright © 2017 RedMadRobot LLC. All rights reserved. +// + +import Foundation +import CoreData + + +extension CDMessage { + + @nonobjc + class func fetchRequest() -> NSFetchRequest { + return NSFetchRequest(entityName: "CDMessage") + } + + @NSManaged var text: String? + +} diff --git a/Example/DAO/Classes/Model/CoreDataDatabase/CDMessage.swift b/Example/DAO/Classes/Model/CoreDataDatabase/CDMessage.swift new file mode 100644 index 0000000..9d85a50 --- /dev/null +++ b/Example/DAO/Classes/Model/CoreDataDatabase/CDMessage.swift @@ -0,0 +1,15 @@ +// +// CDMessage.swift +// DAO +// +// Created by Ivan Vavilov on 5/2/17. +// Copyright © 2017 RedMadRobot LLC. All rights reserved. +// + +import Foundation +import CoreData + +@objc(CDMessage) +class CDMessage: CDEntity { + +} diff --git a/Example/DAO/Classes/Model/Folder.swift b/Example/DAO/Classes/Model/Folder.swift new file mode 100644 index 0000000..8988297 --- /dev/null +++ b/Example/DAO/Classes/Model/Folder.swift @@ -0,0 +1,45 @@ +// +// Folder.swift +// DAO +// +// Created by Igor Bulyga on 09.02.16. +// Copyright © 2016 RedMadRobot LLC. All rights reserved. +// + + +import DAO + + +class Folder: Entity { + + var name: String = "" + var messages = [Message]() + + + init(entityId: String, name: String, messages: [Message]) { + self.name = name + self.messages = messages + + super.init(entityId: entityId) + } + + + required init() { + super.init() + } + + + override func equals(_ other: T) -> Bool where T : Folder { + return (super.equals(other)) && self.name == other.name && self.messagesArrayEquals(other.messages) + } + + + fileprivate func messagesArrayEquals(_ otherMessages: [Message]) -> Bool { + if (self.messages.count != otherMessages.count) { return false } + + for message in otherMessages { + if (!messages.contains(message)) { return false } + } + return true + } +} diff --git a/Example/DAO/Classes/Model/Message.swift b/Example/DAO/Classes/Model/Message.swift new file mode 100644 index 0000000..abd951d --- /dev/null +++ b/Example/DAO/Classes/Model/Message.swift @@ -0,0 +1,31 @@ +// +// Message.swift +// DAO +// +// Created by Igor Bulyga on 05.02.16. +// Copyright © 2016 RedMadRobot LLC. All rights reserved. +// + +import DAO + + +class Message: Entity { + + var text: String? + + init(entityId: String, text: String?) + { + self.text = text + super.init(entityId: entityId) + } + + required init() + { + super.init() + } + + override func equals(_ other: T) -> Bool where T: Message + { + return super.equals(other) && self.text == other.text + } +} diff --git a/Example/DAO/Classes/Model/RealmDatabase/DBBook.swift b/Example/DAO/Classes/Model/RealmDatabase/DBBook.swift new file mode 100644 index 0000000..95562d6 --- /dev/null +++ b/Example/DAO/Classes/Model/RealmDatabase/DBBook.swift @@ -0,0 +1,34 @@ +// +// DBBook.swift +// DAO +// +// Created by Ivan Vavilov on 5/17/17. +// Copyright © 2017 RedMadRobot LLC. All rights reserved. +// + +import DAO +import RealmSwift + + +final class DBBook: DBEntity, CascadeDeletionProtocol { + + dynamic var name: String = "" + + let authors = List() + + let dates = List() + + let pages = List() + + let attachments = List() + + var objectsToDelete: [Object] { + let authors = Array(self.authors) as [Object] + let dates = Array(self.dates) as [Object] + let pages = Array(self.pages) as [Object] + let attachments = Array(self.attachments) as [Object] + + return authors + dates + pages + attachments + } + +} diff --git a/Example/DAO/Classes/Model/RealmDatabase/DBEntity.swift b/Example/DAO/Classes/Model/RealmDatabase/DBEntity.swift new file mode 100644 index 0000000..3d3b870 --- /dev/null +++ b/Example/DAO/Classes/Model/RealmDatabase/DBEntity.swift @@ -0,0 +1,22 @@ +// +// DBEntity.swift +// DAO +// +// Created by Igor Bulyga on 05.02.16. +// Copyright © 2016 RedMadRobot LLC. All rights reserved. +// + +import DAO + + +class DBEntity: RLMEntry { + + class func entityWithId(_ entityId: String) -> DBEntity + { + let entity = DBEntity() + entity.entryId = entityId + return entity + } + + +} diff --git a/Example/DAO/Classes/Model/RealmDatabase/DBFolder.swift b/Example/DAO/Classes/Model/RealmDatabase/DBFolder.swift new file mode 100644 index 0000000..16640c2 --- /dev/null +++ b/Example/DAO/Classes/Model/RealmDatabase/DBFolder.swift @@ -0,0 +1,29 @@ +// +// DBFolder.swift +// DAO +// +// Created by Igor Bulyga on 09.02.16. +// Copyright © 2016 RedMadRobot LLC. All rights reserved. +// + +import DAO +import RealmSwift + +class DBFolder: DBEntity, CascadeDeletionProtocol { + + dynamic var name: String = "" + var messages = List() + + + class func folderWithId(_ entryId: String, name: String, messages: List) -> DBFolder { + let folder = DBFolder() + folder.entryId = entryId + folder.name = name + folder.messages.append(objectsIn: messages) + return folder + } + + var objectsToDelete: [Object] { + return Array(messages) + } +} diff --git a/Example/DAO/Classes/Model/RealmDatabase/DBMessage.swift b/Example/DAO/Classes/Model/RealmDatabase/DBMessage.swift new file mode 100644 index 0000000..9aaa4ce --- /dev/null +++ b/Example/DAO/Classes/Model/RealmDatabase/DBMessage.swift @@ -0,0 +1,21 @@ +// +// DBMessage.swift +// DAO +// +// Created by Igor Bulyga on 09.02.16. +// Copyright © 2016 RedMadRobot LLC. All rights reserved. +// + +import DAO + +class DBMessage: DBEntity { + + dynamic var text: String? + + class func messageWithId(_ entityId: String, text: String?) -> DBMessage { + let message = DBMessage() + message.entryId = entityId + message.text = text + return message + } +} diff --git a/Example/DAO/Info.plist b/Example/DAO/Info.plist new file mode 100644 index 0000000..ce67f51 --- /dev/null +++ b/Example/DAO/Info.plist @@ -0,0 +1,35 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + + + diff --git a/Example/DAO/Resources/Model.xcdatamodeld/Model.xcdatamodel/contents b/Example/DAO/Resources/Model.xcdatamodeld/Model.xcdatamodel/contents new file mode 100644 index 0000000..a49140c --- /dev/null +++ b/Example/DAO/Resources/Model.xcdatamodeld/Model.xcdatamodel/contents @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Example/Podfile b/Example/Podfile new file mode 100644 index 0000000..ea4c174 --- /dev/null +++ b/Example/Podfile @@ -0,0 +1,13 @@ +use_frameworks! + +target 'DAO_Example' do + pod 'DAO', :path => '../' + + target 'CoreDataDAOTests' do + inherit! :search_paths + end + + target 'RealmDAOTests' do + inherit! :search_paths + end +end diff --git a/Example/Podfile.lock b/Example/Podfile.lock new file mode 100644 index 0000000..a7fd569 --- /dev/null +++ b/Example/Podfile.lock @@ -0,0 +1,28 @@ +PODS: + - DAO (1.0.0): + - DAO/CoreData (= 1.0.0) + - DAO/Realm (= 1.0.0) + - DAO/CoreData (1.0.0) + - DAO/Realm (1.0.0): + - RealmSwift + - Realm (2.8.1): + - Realm/Headers (= 2.8.1) + - Realm/Headers (2.8.1) + - RealmSwift (2.8.1): + - Realm (= 2.8.1) + +DEPENDENCIES: + - DAO (from `../`) + +EXTERNAL SOURCES: + DAO: + :path: ../ + +SPEC CHECKSUMS: + DAO: 29f9c48e72c0a4ca6ed59a87718bb18978b682e0 + Realm: 2627602ad6818451f0cb8c2a6e072f7f10a5f360 + RealmSwift: 4764ca7657f2193c256fb032c0b123926f70dbcd + +PODFILE CHECKSUM: 304d7b9f54070467c355fe795db431630f017f61 + +COCOAPODS: 1.2.1 diff --git a/Example/RealmDAOTests/Info.plist b/Example/RealmDAOTests/Info.plist new file mode 100644 index 0000000..6c6c23c --- /dev/null +++ b/Example/RealmDAOTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/Example/RealmDAOTests/RealmDAOBookTests.swift b/Example/RealmDAOTests/RealmDAOBookTests.swift new file mode 100644 index 0000000..83bdb6f --- /dev/null +++ b/Example/RealmDAOTests/RealmDAOBookTests.swift @@ -0,0 +1,43 @@ +// +// RealmDAOBookTests.swift +// DAO +// +// Created by Ivan Vavilov on 5/17/17. +// Copyright © 2017 RedMadRobot LLC. All rights reserved. +// + +import XCTest +import DAO +@testable import DAO_Example + + +class RealmDAOBookTests: XCTestCase { + + let dao = RealmDAO(RLMBookTranslator()) + + + func testPersist() { + let book = Book(entityId: "book1", name: "Swift", authors: ["Chris Lattner"], + dates: [Date(timeIntervalSince1970: 1000000)], + pages: [100, 200], attachments: [Data()]) + + do { + try dao.persist(book) + } catch { + XCTFail("Persist is failed") + } + + if let savedBook = dao.read(book.entityId) { + XCTAssertEqual(book, savedBook) + XCTAssertFalse(savedBook.authors.isEmpty) + XCTAssertEqual(savedBook.authors.first, "Chris Lattner") + XCTAssertFalse(savedBook.dates.isEmpty) + XCTAssertFalse(savedBook.pages.isEmpty) + XCTAssertEqual(savedBook.pages.count, 2) + XCTAssertFalse(savedBook.attachments.isEmpty) + } else { + XCTFail("Persist is failed") + } + } + +} diff --git a/Example/RealmDAOTests/RealmDAOEntityTests.swift b/Example/RealmDAOTests/RealmDAOEntityTests.swift new file mode 100644 index 0000000..c6b9f54 --- /dev/null +++ b/Example/RealmDAOTests/RealmDAOEntityTests.swift @@ -0,0 +1,156 @@ +// +// RealmDAOEraseTests.swift +// DAO +// +// Created by Ivan Vavilov on 4/27/17. +// Copyright © 2017 RedMadRobot LLC. All rights reserved. +// + +import XCTest +import DAO +@testable import DAO_Example + + +final class RealmDAOEntityTests: XCTestCase { + + let dao = RealmDAO(RLMEntityTranslator()) + + + func testReadById() { + let entity = Entity(entityId: "2") + do { + try dao.persist(entity) + } catch { + XCTFail("Persist is failed") + } + + if let savedEntity = dao.read("2") { + XCTAssertEqual(entity, savedEntity) + } else { + XCTFail("Read is failed") + } + } + + + func testAsyncReadById() { + let entity = Entity(entityId: "2_back") + let exp = expectation(description: "") + + DispatchQueue.global().async { + do { + try self.dao.persist(entity) + } catch { + XCTFail("Async read by id is failed") + } + + DispatchQueue.main.async { + if let savedEntity = self.dao.read("2_back") { + XCTAssertEqual(savedEntity.entityId, entity.entityId) + } else { + XCTFail("Async read by id is failed") + } + exp.fulfill() + } + } + + waitForExpectations(timeout: 5) { error in + if error != nil { + XCTFail("Async read by id is failed") + } + XCTAssert(true) + } + } + + + func testPersist() { + let entity = Entity(entityId: "1") + do { + try dao.persist(entity) + } catch { + XCTFail("Saving entity is failed") + } + + XCTAssert(true) + } + + + func testPersistAll() { + let firstEntity = Entity(entityId: "2") + let secondEntity = Entity(entityId: "3") + + do { + try dao.persist([firstEntity, secondEntity]) + } catch { + XCTFail("Saving entities is failed") + } + + XCTAssert(true) + } + + + func testAsyncPersist() { + let exp = expectation(description: "") + + DispatchQueue.global().async { + let entity = Entity(entityId: "1_back") + do { + try self.dao.persist(entity) + } catch { + XCTFail("Saving entity in background is failed") + } + + exp.fulfill() + } + + waitForExpectations(timeout: 5) { error in + if error != nil { + XCTFail("Saving entity in background is failed") + } + XCTAssert(true) + } + } + + + func testEraseById() { + let entity = Entity(entityId: "3") + do { + try dao.persist(entity) + try dao.erase("3") + } catch { + XCTFail("Erase is failed") + } + + XCTAssertNil(dao.read("3")) + } + + + func testAsyncEraseById() { + let entity = Entity(entityId: "2_back") + + do { + try dao.persist(entity) + } catch { + XCTFail("Async erase by id is failed") + } + + let exp = expectation(description: "") + + DispatchQueue.global().async { + do { + try self.dao.erase("2_back") + } catch { + XCTFail("Async erase by id is failed") + } + + exp.fulfill() + } + + waitForExpectations(timeout: 5) { error in + if error != nil { + XCTFail("Async erase by id is failed") + } + XCTAssert(true) + } + } + +} diff --git a/Example/RealmDAOTests/RealmDAOFolderTests.swift b/Example/RealmDAOTests/RealmDAOFolderTests.swift new file mode 100644 index 0000000..3721445 --- /dev/null +++ b/Example/RealmDAOTests/RealmDAOFolderTests.swift @@ -0,0 +1,45 @@ +// +// RealmDAOFolderTests.swift +// DAO +// +// Created by Ivan Vavilov on 4/28/17. +// Copyright © 2017 RedMadRobot LLC. All rights reserved. +// + +import XCTest +import DAO +@testable import DAO_Example + + +final class RealmDAOFolderTests: XCTestCase { + + let messageDAO = RealmDAO(RLMMessageTranslator()) + + let folderDAO = RealmDAO(RLMFolderTranslator()) + + + func testCascadeErase() { + let message = Message(entityId: "V.message", text: "V.message.text") + let folder = Folder(entityId: "V", name: "Delete", messages: [message]) + + do { + try folderDAO.persist(folder) + } catch { + XCTFail("Persist folder is failed") + } + + let savedMessage = messageDAO.read("V.message") + + XCTAssertNotNil(savedMessage) + + do { + try folderDAO.erase("V") + } catch { + XCTFail("Erase folder is failed") + } + + XCTAssertNil(messageDAO.read("V.message")) + } + + +} diff --git a/Example/RealmDAOTests/RealmDAOMessagesTests.swift b/Example/RealmDAOTests/RealmDAOMessagesTests.swift new file mode 100644 index 0000000..171c151 --- /dev/null +++ b/Example/RealmDAOTests/RealmDAOMessagesTests.swift @@ -0,0 +1,56 @@ +// +// RealmDAOMessagesTests.swift +// DAO +// +// Created by Ivan Vavilov on 4/28/17. +// Copyright © 2017 RedMadRobot LLC. All rights reserved. +// + +import XCTest +import DAO +@testable import DAO_Example + + +final class RealmDAOMessagesTests: XCTestCase { + + let dao = RealmDAO(RLMMessageTranslator()) + + + func testPersistMessage() { + let message = Message(entityId: "abc", text: "text") + + do { + try dao.persist(message) + } catch { + XCTFail("Persist is failed") + } + + XCTAssertEqual(message, dao.read(message.entityId)) + } + + + func testReadMessage() { + let message = Message(entityId: "def", text: "text 2") + do { + try dao.persist(message) + } catch { + XCTFail("Persist is failed") + } + + XCTAssertEqual(message, dao.read("def")) + } + + + func testEraseMessage() { + let message = Message(entityId: "ghi", text: "text 2") + do { + try dao.persist(message) + try dao.erase("ghi") + } catch { + XCTFail("Persist or erase is failed") + } + + XCTAssertNil(dao.read("ghi")) + } + +} diff --git a/Example/RealmDAOTests/RealmDAOTests-Bridging-Header.h b/Example/RealmDAOTests/RealmDAOTests-Bridging-Header.h new file mode 100644 index 0000000..1b2cb5d --- /dev/null +++ b/Example/RealmDAOTests/RealmDAOTests-Bridging-Header.h @@ -0,0 +1,4 @@ +// +// Use this file to import your target's public headers that you would like to expose to Swift. +// + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..45450a8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2017 RedMadRobot + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..4af0579 --- /dev/null +++ b/README.md @@ -0,0 +1,66 @@ +CoreDAO +======= + +An implementation of [DAO pattern](http://www.oracle.com/technetwork/java/dataaccessobject-138824.html) for CoreData and Realm. +Helps you think less about database in your application. + +## Features + +- Use your persistence layer synchronously for CRUD operations. +- Abstraction of database objects (entries) from application objects (entities). +- Abstraction from concurrency. + +## Install + +Cocoapods + +For using with CoreData: + +```ruby +pod 'CoreDAO/CoreData' +``` + +Or with Realm: + +```ruby +pod 'CoreDAO/Realm' +``` + +## Usage + +```swift +// Create DAO instance +let dao = RealmDAO(RLMMessageTranslator()) + +//... + +// Create message entity +let message = Message(entityId: "abc", text: "text") + +// Save message to database +try? dao.persist(message) + +// Read saved message from database +let savedMessage = dao.read(message.entityId) + +// Delete message from database +try? dao.erase(message.entityId) +``` + +Please look at the example project for more information. + +## When not recommended to use + +- If you have big and complex database schema. Many entities, many relationships. +- If you want to use many features of database. Realm Mobile Platform, for instance is not comaptible with this implementation. +- If you have thousands of objects (> 10-20K). Performance can be the issue. + +## Requirements + +- XCode 8 +- Swift 3 +- iOS 9 + +## Authors + +Ivan Vavilov - iv@redmadrobot.com diff --git a/_Pods.xcodeproj b/_Pods.xcodeproj new file mode 120000 index 0000000..3c5a8e7 --- /dev/null +++ b/_Pods.xcodeproj @@ -0,0 +1 @@ +Example/Pods/Pods.xcodeproj \ No newline at end of file