├── Cartfile ├── Example ├── Support │ ├── Assets.xcassets │ │ ├── Contents.json │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Info.plist │ └── Base.lproj │ │ └── LaunchScreen.storyboard └── Sources │ ├── Extension │ ├── UITableView-Extension.swift │ ├── ClassName-Extension.swift │ └── UIViewController-Extension.swift │ ├── DB │ ├── Managers │ │ ├── UserRealmManager.swift │ │ └── InMemoryRealmManager.swift │ └── Proxies │ │ ├── UserInMemoryRealmProxy.swift │ │ └── UserRealmProxy.swift │ ├── AppDelegate.swift │ ├── Models │ └── User.swift │ ├── AppNavigator.swift │ ├── Cells │ └── UserTableViewCell.swift │ └── ViewControllers │ ├── EditViewController.swift │ ├── MultipleAddViewController.swift │ ├── SingleAddViewController.swift │ └── TableViewController.swift ├── RealmWrapper.h ├── .gitignore ├── Tests ├── Mocks │ ├── DB │ │ ├── Managers │ │ │ ├── MockUserRealmManager.swift │ │ │ └── MockInMemoryRealmManager.swift │ │ └── Proxies │ │ │ ├── MockUserInMemoryRealmProxy.swift │ │ │ └── MockUserRealmProxy.swift │ └── Models │ │ └── MockUser.swift ├── Info.plist ├── RealmNotificationTests.swift ├── RealmCRUDTests.swift └── RealmTransactionTests.swift ├── .travis.yml ├── Package.swift ├── RealmWrapper.podspec ├── Info.plist ├── LICENSE ├── Sources └── RealmWrapper │ ├── RealmProxiable.swift │ ├── RealmQuery.swift │ └── RealmManageable.swift ├── RealmWrapper.xcodeproj ├── xcshareddata │ └── xcschemes │ │ ├── RealmWrapperTests.xcscheme │ │ ├── RealmWrapperExample.xcscheme │ │ └── RealmWrapper.xcscheme └── project.pbxproj └── README.md /Cartfile: -------------------------------------------------------------------------------- 1 | github "realm/realm-cocoa" ~> 5.5.2 -------------------------------------------------------------------------------- /Example/Support/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /RealmWrapper.h: -------------------------------------------------------------------------------- 1 | @import UIKit; 2 | 3 | FOUNDATION_EXPORT double RealmWrapperVersionNumber; 4 | FOUNDATION_EXPORT const unsigned char RealmWrapperVersionString[]; 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.xcuserstate 3 | project.xcworkspace/ 4 | xcuserdata/ 5 | 6 | # Podfiles 7 | Pods/ 8 | 9 | # Carthage 10 | Carthage/Build 11 | Carthage/Checkouts 12 | Cartfile.resolved 13 | 14 | # Swift Package Manager 15 | .swiftpm/ 16 | Package.resolved -------------------------------------------------------------------------------- /Example/Sources/Extension/UITableView-Extension.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | extension UITableView { 4 | func registerWithCellReuseIdentifier(_ cellClass: CellClass.Type) { 5 | self.register(cellClass, forCellReuseIdentifier: cellClass.className) 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Example/Sources/DB/Managers/UserRealmManager.swift: -------------------------------------------------------------------------------- 1 | import RealmSwift 2 | import RealmWrapper 3 | 4 | final class UserRealmManager: RealmManageable { 5 | 6 | var isUseInMemory: Bool { 7 | return false 8 | } 9 | var schemaVersion: UInt64 { 10 | return 1 11 | } 12 | var fileName: String { 13 | return "user" 14 | } 15 | var objectTypes: [Object.Type]? { 16 | return [User.self] 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /Example/Sources/DB/Managers/InMemoryRealmManager.swift: -------------------------------------------------------------------------------- 1 | import RealmSwift 2 | import RealmWrapper 3 | 4 | final class InMemoryRealmManager: RealmManageable { 5 | 6 | var isUseInMemory: Bool { 7 | return true 8 | } 9 | var schemaVersion: UInt64 { 10 | return 1 11 | } 12 | var fileName: String { 13 | return "inMemory" 14 | } 15 | var objectTypes: [Object.Type]? { 16 | return [User.self] 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /Tests/Mocks/DB/Managers/MockUserRealmManager.swift: -------------------------------------------------------------------------------- 1 | import RealmSwift 2 | import RealmWrapper 3 | 4 | final class MockUserRealmManager: RealmManageable { 5 | 6 | var isUseInMemory: Bool { 7 | return false 8 | } 9 | var schemaVersion: UInt64 { 10 | return 1 11 | } 12 | var fileName: String { 13 | return "mock-user" 14 | } 15 | var objectTypes: [Object.Type]? { 16 | return [MockUser.self] 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /Tests/Mocks/DB/Managers/MockInMemoryRealmManager.swift: -------------------------------------------------------------------------------- 1 | import RealmSwift 2 | import RealmWrapper 3 | 4 | final class MockInMemoryRealmManager: RealmManageable { 5 | 6 | var isUseInMemory: Bool { 7 | return true 8 | } 9 | var schemaVersion: UInt64 { 10 | return 1 11 | } 12 | var fileName: String { 13 | return "inMemory" 14 | } 15 | var objectTypes: [Object.Type]? { 16 | return [MockUser.self] 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /Example/Sources/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | @UIApplicationMain 4 | class AppDelegate: UIResponder, UIApplicationDelegate { 5 | 6 | var window: UIWindow? 7 | 8 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 9 | let window = UIWindow(frame: UIScreen.main.bounds) 10 | self.window = window 11 | 12 | AppNavigator.shared.start(with: window) 13 | return true 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /Example/Sources/Extension/ClassName-Extension.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | protocol ClassName { 4 | static var className: String { get } 5 | } 6 | 7 | // Swift Objects 8 | extension ClassName { 9 | static var className: String { 10 | return String(describing: self).components(separatedBy: " ").first! 11 | } 12 | } 13 | 14 | // Bridge to Obj-C 15 | extension NSObject: ClassName { 16 | class var className: String { 17 | return String(describing: self).components(separatedBy: " ").first! 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Example/Sources/Extension/UIViewController-Extension.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | extension UIViewController { 4 | func alert(title: String? = nil, message: String? = nil, preferredStyle: UIAlertController.Style = .alert, actions: [UIAlertAction]?, completion: (() -> Void)? = nil) { 5 | let alert = UIAlertController(title: title, message: message, preferredStyle: preferredStyle) 6 | if let actions = actions { 7 | actions.forEach({ (action) in 8 | alert.addAction(action) 9 | }) 10 | } 11 | present(alert, animated: true, completion: completion) 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /Example/Sources/Models/User.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | import RealmSwift 4 | 5 | @objcMembers 6 | class User: Object { 7 | 8 | dynamic var age: Int = 0 9 | dynamic var date: Int = 0 10 | dynamic var id: String? 11 | dynamic var name: String? 12 | 13 | convenience init(name: String, age: Int) { 14 | self.init() 15 | 16 | id = UUID().uuidString 17 | date = Date().timeIntervalSince1970.hashValue 18 | self.name = name 19 | self.age = age 20 | } 21 | 22 | override static func primaryKey() -> String? { 23 | return "id" 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /Tests/Mocks/Models/MockUser.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | import RealmSwift 4 | 5 | @objcMembers 6 | class MockUser: Object { 7 | 8 | dynamic var age: Int = 0 9 | dynamic var date: Int = 0 10 | dynamic var id: String? 11 | dynamic var name: String? 12 | 13 | convenience init(name: String, age: Int) { 14 | self.init() 15 | 16 | id = UUID().uuidString 17 | date = Date().timeIntervalSince1970.hashValue 18 | self.name = name 19 | self.age = age 20 | } 21 | 22 | override static func primaryKey() -> String? { 23 | return "id" 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /Example/Sources/AppNavigator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppNavigator.swift 3 | // RealmWrapperExample 4 | // 5 | // Created by DongHeeKang on 20/02/2019. 6 | // Copyright © 2019 k-lpmg. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | final class AppNavigator { 12 | 13 | static let shared = AppNavigator() 14 | 15 | // MARK: - Internal methods 16 | 17 | func start(with window: UIWindow) { 18 | let controller = TableViewController() 19 | let navigationController = UINavigationController(rootViewController: controller) 20 | window.rootViewController = navigationController 21 | window.backgroundColor = .white 22 | window.makeKeyAndVisible() 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | osx_image: xcode12.5 2 | language: objective-c 3 | env: 4 | global: 5 | - PROJECT="RealmWrapper.xcodeproj" 6 | - SCHEME="RealmWrapper" 7 | - IOS_SDK="iphonesimulator" 8 | matrix: 9 | - SDK="$IOS_SDK" DESTINATION="platform=iOS Simulator,OS=14.5,name=iPhone 12 Pro Max" 10 | 11 | before_install: 12 | - brew update 13 | - if brew outdated | grep -qx carthage; then brew upgrade carthage; fi 14 | - carthage bootstrap --platform iOS 15 | 16 | install: 17 | - swift --version 18 | 19 | before_script: 20 | - set -o pipefail 21 | 22 | script: 23 | - travis_retry xcodebuild clean build test 24 | -project "$PROJECT" 25 | -scheme "$SCHEME" 26 | -sdk "$SDK" 27 | -destination "$DESTINATION" | xcpretty -c 28 | -------------------------------------------------------------------------------- /Example/Sources/Cells/UserTableViewCell.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | class UserTableViewCell: UITableViewCell { 4 | 5 | // MARK: - Properties 6 | 7 | var model: User? { 8 | didSet { 9 | guard let model = model else {return} 10 | 11 | textLabel?.text = model.name 12 | detailTextLabel?.text = model.age.description 13 | } 14 | } 15 | 16 | // MARK: - Con(De)structor 17 | 18 | override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { 19 | super.init(style: .subtitle, reuseIdentifier: reuseIdentifier) 20 | } 21 | 22 | required init?(coder aDecoder: NSCoder) { 23 | fatalError("init(coder:) has not been implemented") 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.2 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "RealmWrapper", 6 | platforms: [ 7 | .iOS(.v11), 8 | ], 9 | products: [ 10 | .library(name: "RealmWrapper", targets: ["RealmWrapper"]), 11 | ], 12 | dependencies: [ 13 | .package(name: "Realm", url: "https://github.com/realm/realm-cocoa", .upToNextMajor(from: "5.0.0")) 14 | ], 15 | targets: [ 16 | .target( 17 | name: "RealmWrapper", 18 | dependencies: [ 19 | .product(name: "RealmSwift", package: "Realm"), 20 | ] 21 | ), 22 | .testTarget( 23 | name: "RealmWrapperTests", 24 | dependencies: ["RealmWrapper"], 25 | path: "./Tests" 26 | ) 27 | ] 28 | ) 29 | -------------------------------------------------------------------------------- /RealmWrapper.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "RealmWrapper" 3 | s.version = "1.4.3" 4 | s.summary = "Simple wrapper for RealmSwift" 5 | s.description = "This is simple wrapper that wrapper is easy to use transaction synchronous processing and realm notifications." 6 | s.homepage = "https://github.com/k-lpmg/RealmWrapper" 7 | s.license = { :type => 'MIT', :file => 'LICENSE' } 8 | s.author = { "DongHee Kang" => "kanglpmg@gmail.com" } 9 | s.source = { :git => "https://github.com/k-lpmg/RealmWrapper.git", :tag => s.version.to_s } 10 | s.documentation_url = "https://github.com/k-lpmg/RealmWrapper/blob/master/README.md" 11 | 12 | s.ios.source_files = "Sources/**/*.swift" 13 | s.ios.deployment_target = "9.0" 14 | 15 | s.swift_version = '5.0' 16 | 17 | s.dependency 'RealmSwift' 18 | end 19 | -------------------------------------------------------------------------------- /Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.4.3 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 DongHee Kang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Sources/RealmWrapper/RealmProxiable.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | import RealmSwift 4 | 5 | public enum QueryFilter { 6 | case string(String) 7 | case predicate(NSPredicate) 8 | } 9 | 10 | public protocol RealmProxiable { 11 | associatedtype RealmManager where RealmManager: RealmManageable 12 | var rm: RealmManager { get } 13 | } 14 | 15 | public extension RealmProxiable { 16 | 17 | var rm: RealmManager { 18 | return RealmManager() 19 | } 20 | 21 | func query( 22 | _ type: T.Type = T.self, 23 | filter: QueryFilter? = nil, 24 | sortProperty: String? = nil, 25 | ordering: OrderingType = .ascending 26 | ) -> RealmQuery { 27 | guard let realm = try? Realm(configuration: rm.createConfiguration()) else { 28 | return RealmQuery(results: nil) 29 | } 30 | 31 | var results = realm.objects(type) 32 | if let filter = filter { 33 | switch filter { 34 | case let .string(stringValue): 35 | results = results.filter(stringValue) 36 | case let .predicate(predicateValue): 37 | results = results.filter(predicateValue) 38 | } 39 | } 40 | if let sortProperty = sortProperty { 41 | results = results.sorted(byKeyPath: sortProperty, ascending: ordering == .ascending) 42 | } 43 | 44 | return RealmQuery(results: results) 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /Example/Support/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.4.3 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIRequiredDeviceCapabilities 26 | 27 | armv7 28 | 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | 33 | UISupportedInterfaceOrientations~ipad 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationPortraitUpsideDown 37 | UIInterfaceOrientationLandscapeLeft 38 | UIInterfaceOrientationLandscapeRight 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Example/Sources/DB/Proxies/UserInMemoryRealmProxy.swift: -------------------------------------------------------------------------------- 1 | import RealmWrapper 2 | 3 | struct UserInMemoryRealmProxy: RealmProxiable { 4 | 5 | // MARK: - Properties 6 | 7 | var users: RealmQuery { 8 | return query(sortProperty: "date", ordering: .ascending) 9 | } 10 | 11 | // MARK: Methods 12 | 13 | func append(_ user: User) { 14 | rm.transaction(writeHandler: { (realm) in 15 | realm.add(user, update: .all) 16 | }) 17 | } 18 | 19 | func append(_ users: [User], isSync: Bool, completion: (() -> Void)? = nil) { 20 | rm.transaction(isSync: isSync, writeHandler: { (realm) in 21 | realm.add(users, update: .all) 22 | }) { (realm, error) in 23 | completion?() 24 | } 25 | } 26 | 27 | func delete(_ user: User) { 28 | rm.transaction(writeHandler: { (realm) in 29 | realm.delete(user) 30 | }) 31 | } 32 | 33 | func updateName(id: String, name: String, age: Int) { 34 | guard let user = userFromId(id) else {return} 35 | 36 | rm.transaction(writeHandler: { (realm) in 37 | user.name = name 38 | user.age = age 39 | realm.add(user, update: .all) 40 | }) 41 | } 42 | 43 | func userFromId(_ id: String) -> User? { 44 | return query(filter: .string("id == '\(id)'")).results?.first 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /Tests/Mocks/DB/Proxies/MockUserInMemoryRealmProxy.swift: -------------------------------------------------------------------------------- 1 | import RealmWrapper 2 | 3 | struct MockUserInMemoryRealmProxy: RealmProxiable { 4 | 5 | // MARK: - Properties 6 | 7 | var users: RealmQuery { 8 | return query(sortProperty: "date", ordering: .ascending) 9 | } 10 | 11 | // MARK: Methods 12 | 13 | func append(_ user: MockUser) { 14 | rm.transaction(writeHandler: { (realm) in 15 | realm.add(user, update: .all) 16 | }) 17 | } 18 | 19 | func append(_ users: [MockUser], isSync: Bool, completion: (() -> Void)? = nil) { 20 | rm.transaction(isSync: isSync, writeHandler: { (realm) in 21 | realm.add(users, update: .all) 22 | }) { (realm, error) in 23 | completion?() 24 | } 25 | } 26 | 27 | func delete(_ user: MockUser) { 28 | rm.transaction(writeHandler: { (realm) in 29 | realm.delete(user) 30 | }) 31 | } 32 | 33 | func updateName(id: String, name: String, age: Int) { 34 | guard let user = userFromId(id) else {return} 35 | 36 | rm.transaction(writeHandler: { (realm) in 37 | user.name = name 38 | user.age = age 39 | realm.add(user, update: .all) 40 | }) 41 | } 42 | 43 | func userFromId(_ id: String) -> MockUser? { 44 | return query(filter: .string("id == '\(id)'")).results?.first 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /Example/Sources/DB/Proxies/UserRealmProxy.swift: -------------------------------------------------------------------------------- 1 | import RealmWrapper 2 | 3 | struct UserRealmProxy: RealmProxiable { 4 | 5 | // MARK: - Properties 6 | 7 | var users: RealmQuery { 8 | return query(sortProperty: "date", ordering: .ascending) 9 | } 10 | 11 | // MARK: Methods 12 | 13 | func append(_ user: User) { 14 | rm.transaction(writeHandler: { (realm) in 15 | realm.add(user, update: .all) 16 | }) 17 | } 18 | 19 | func append(_ users: [User], isSync: Bool, completion: (() -> Void)? = nil) { 20 | rm.transaction(isSync: isSync, writeHandler: { (realm) in 21 | realm.add(users, update: .all) 22 | }) { (realm, error) in 23 | completion?() 24 | } 25 | } 26 | 27 | func delete(_ user: User) { 28 | rm.transaction(writeHandler: { (realm) in 29 | realm.delete(user) 30 | }) 31 | } 32 | 33 | func deleteAll() { 34 | guard let usersResults = users.results else { return } 35 | rm.transaction(writeHandler: { (realm) in 36 | realm.delete(usersResults) 37 | }) 38 | } 39 | 40 | func updateName(id: String, name: String, age: Int) { 41 | guard let user = userFromId(id) else {return} 42 | 43 | rm.transaction(writeHandler: { (realm) in 44 | user.name = name 45 | user.age = age 46 | realm.add(user, update: .all) 47 | }) 48 | } 49 | 50 | func userFromId(_ id: String) -> User? { 51 | return query(filter: .string("id == '\(id)'")).results?.first 52 | } 53 | 54 | func userWithFilter(_ filter: String) -> RealmQuery { 55 | return query(filter: .string(filter)) 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /Tests/Mocks/DB/Proxies/MockUserRealmProxy.swift: -------------------------------------------------------------------------------- 1 | import RealmWrapper 2 | 3 | struct MockUserRealmProxy: RealmProxiable { 4 | 5 | // MARK: - Properties 6 | 7 | var users: RealmQuery { 8 | return query(sortProperty: "date", ordering: .ascending) 9 | } 10 | 11 | // MARK: Methods 12 | 13 | func append(_ user: MockUser) { 14 | rm.transaction(writeHandler: { (realm) in 15 | realm.add(user, update: .all) 16 | }) 17 | } 18 | 19 | func append(_ users: [MockUser], isSync: Bool, completion: (() -> Void)? = nil) { 20 | rm.transaction(isSync: isSync, writeHandler: { (realm) in 21 | realm.add(users, update: .all) 22 | }) { (realm, error) in 23 | completion?() 24 | } 25 | } 26 | 27 | func delete(_ user: MockUser) { 28 | rm.transaction(writeHandler: { (realm) in 29 | realm.delete(user) 30 | }) 31 | } 32 | 33 | func deleteAll() { 34 | guard let usersResults = users.results else { return } 35 | rm.transaction(writeHandler: { (realm) in 36 | realm.delete(usersResults) 37 | }) 38 | } 39 | 40 | func updateName(id: String, name: String, age: Int) { 41 | guard let user = userFromId(id) else {return} 42 | 43 | rm.transaction(writeHandler: { (realm) in 44 | user.name = name 45 | user.age = age 46 | realm.add(user, update: .all) 47 | }) 48 | } 49 | 50 | func userFromId(_ id: String) -> MockUser? { 51 | return query(filter: .string("id == '\(id)'")).results?.first 52 | } 53 | 54 | func userWithFilter(_ filter: String) -> RealmQuery { 55 | return query(filter: .string(filter)) 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /Example/Support/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /Example/Support/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /RealmWrapper.xcodeproj/xcshareddata/xcschemes/RealmWrapperTests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 14 | 15 | 17 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 39 | 40 | 41 | 42 | 48 | 49 | 51 | 52 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /Tests/RealmNotificationTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import RealmWrapper 3 | 4 | import RealmSwift 5 | 6 | class RealmNotificationTests: XCTestCase { 7 | 8 | override func setUp() { 9 | super.setUp() 10 | 11 | MockUserRealmProxy().deleteAll() 12 | } 13 | 14 | func testCreateNotification() { 15 | let expectation = self.expectation(description: "testCreateNotification") 16 | 17 | let users = MockUserRealmProxy().users 18 | users.addInsertNotificationBlock(self) { (self, insertions) in 19 | XCTAssertTrue(insertions.count > 0) 20 | 21 | expectation.fulfill() 22 | } 23 | .registerNotification() 24 | 25 | let user = MockUser(name: "user1", age: 1) 26 | MockUserRealmProxy().append(user) 27 | 28 | waitForExpectations(timeout: 60) 29 | } 30 | 31 | func testUpdateNotification() { 32 | let expectation = self.expectation(description: "testUpdateNotification") 33 | 34 | let users = MockUserRealmProxy().users 35 | users.addModificateNotificationBlock(self) { (self, modifications) in 36 | XCTAssertTrue(modifications.count > 0) 37 | 38 | expectation.fulfill() 39 | } 40 | .registerNotification() 41 | 42 | let user = MockUser(name: "user1", age: 1) 43 | let userId = user.id 44 | MockUserRealmProxy().append(user) 45 | 46 | XCTAssertNotNil(userId) 47 | MockUserRealmProxy().updateName(id: userId!, name: "user2", age: 2) 48 | 49 | waitForExpectations(timeout: 60) 50 | } 51 | 52 | func testDeleteNotification() { 53 | let expectation = self.expectation(description: "testDeleteNotification") 54 | 55 | let users = MockUserRealmProxy().users 56 | users.addDeleteNotificationBlock(self) { (self, deletions) in 57 | XCTAssertTrue(deletions.count > 0) 58 | 59 | expectation.fulfill() 60 | } 61 | .registerNotification() 62 | 63 | let user = MockUser(name: "user2", age: 2) 64 | MockUserRealmProxy().append(user) 65 | 66 | MockUserRealmProxy().delete(user) 67 | 68 | waitForExpectations(timeout: 60) 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /Tests/RealmCRUDTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import RealmWrapper 3 | 4 | import RealmSwift 5 | 6 | class RealmCRUDTests: XCTestCase { 7 | 8 | var users = MockUserRealmProxy().users 9 | 10 | override func setUp() { 11 | super.setUp() 12 | 13 | MockUserRealmProxy().deleteAll() 14 | XCTAssertEqual(users.count, 0) 15 | } 16 | 17 | func testCreate() { 18 | let user = MockUser(name: "user1", age: 1) 19 | MockUserRealmProxy().append(user) 20 | 21 | XCTAssertEqual(MockUserRealmProxy().users.count, 1) 22 | } 23 | 24 | func testRead() { 25 | let user1 = MockUser(name: "user1", age: 1) 26 | let user2 = MockUser(name: "user2", age: 2) 27 | let user3 = MockUser(name: "user3", age: 3) 28 | let user4 = MockUser(name: "user4", age: 4) 29 | let user5 = MockUser(name: "user5", age: 5) 30 | let user6 = MockUser(name: "user6", age: 6) 31 | 32 | MockUserRealmProxy().append(user1) 33 | MockUserRealmProxy().append(user2) 34 | MockUserRealmProxy().append(user3) 35 | MockUserRealmProxy().append(user4) 36 | MockUserRealmProxy().append(user5) 37 | MockUserRealmProxy().append(user6) 38 | 39 | let evenUser = MockUserRealmProxy().userWithFilter("age > 3") 40 | XCTAssertEqual(users.count, 6) 41 | XCTAssertEqual(evenUser.count, 3) 42 | } 43 | 44 | func testUpdate() { 45 | let name1 = "user1" 46 | let age1 = 1 47 | let user = MockUser(name: name1, age: age1) 48 | 49 | XCTAssertNotNil(user.id) 50 | let userId = user.id 51 | 52 | MockUserRealmProxy().append(user) 53 | XCTAssertEqual(users.count, 1) 54 | XCTAssertEqual(users.results?.first?.id, userId) 55 | XCTAssertEqual(users.results?.first?.name, name1) 56 | XCTAssertEqual(users.results?.first?.age, age1) 57 | 58 | MockUserRealmProxy().updateName(id: userId!, name: "user2", age: 2) 59 | XCTAssertEqual(users.count, 1) 60 | XCTAssertEqual(users.results?.first?.id, userId) 61 | XCTAssertNotEqual(users.results?.first?.name, name1) 62 | XCTAssertNotEqual(users.results?.first?.age, age1) 63 | } 64 | 65 | func testDelete() { 66 | let user = MockUser(name: "user1", age: 1) 67 | 68 | MockUserRealmProxy().append(user) 69 | XCTAssertEqual(users.count, 1) 70 | 71 | MockUserRealmProxy().delete(user) 72 | XCTAssertEqual(users.count, 0) 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /Tests/RealmTransactionTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | import RealmSwift 4 | 5 | class RealmTransactionTests: XCTestCase { 6 | 7 | let userCount = 100000 8 | 9 | override func setUp() { 10 | super.setUp() 11 | 12 | MockUserRealmManager().clear() 13 | } 14 | 15 | func testDefaultTransaction() { 16 | MockUserRealmManager().transaction(writeHandler: { (realm) in 17 | self.addUser(count: self.userCount, realm: realm) 18 | 19 | let users = realm.objects(MockUser.self) 20 | XCTAssertEqual(users.count, self.userCount) 21 | }) 22 | } 23 | 24 | func testDefaultAsyncTransaction() { 25 | let expectation = self.expectation(description: "testCreateNotification") 26 | 27 | MockUserRealmManager().transaction(isSync: false, writeHandler: { (realm) in 28 | self.addUser(count: self.userCount, realm: realm) 29 | }) { (realm, error) in 30 | XCTAssertNil(error) 31 | XCTAssertNotNil(realm) 32 | 33 | let users = realm!.objects(MockUser.self) 34 | XCTAssertEqual(users.count, self.userCount) 35 | 36 | expectation.fulfill() 37 | } 38 | 39 | waitForExpectations(timeout: 60) 40 | } 41 | 42 | func testCustomSyncTransaction() { 43 | MockUserRealmManager().transaction(writeQueue: DispatchQueue(label: "testCustomSyncTransaction"), writeHandler: { (realm) in 44 | self.addUser(count: self.userCount, realm: realm) 45 | 46 | let users = realm.objects(MockUser.self) 47 | XCTAssertEqual(users.count, self.userCount) 48 | }) 49 | } 50 | 51 | func testCustomAsyncTransaction() { 52 | let expectation = self.expectation(description: "testCreateNotification") 53 | 54 | MockUserRealmManager().transaction(writeQueue: DispatchQueue(label: "testCustomAsyncTransaction"), isSync: false, writeHandler: { (realm) in 55 | self.addUser(count: self.userCount, realm: realm) 56 | }) { (realm, error) in 57 | XCTAssertNil(error) 58 | XCTAssertNotNil(realm) 59 | 60 | let users = realm!.objects(MockUser.self) 61 | XCTAssertEqual(users.count, self.userCount) 62 | 63 | expectation.fulfill() 64 | } 65 | 66 | waitForExpectations(timeout: 60) 67 | } 68 | 69 | // MARK: - Private methods 70 | 71 | private func addUser(count: Int, realm: Realm) { 72 | for i in 0.. 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /RealmWrapper.xcodeproj/xcshareddata/xcschemes/RealmWrapper.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 65 | 71 | 72 | 73 | 74 | 75 | 76 | 82 | 83 | 89 | 90 | 91 | 92 | 94 | 95 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /Sources/RealmWrapper/RealmQuery.swift: -------------------------------------------------------------------------------- 1 | import RealmSwift 2 | 3 | //import Foundation 4 | #if os(iOS) 5 | import UIKit 6 | #elseif os(macOS) 7 | import AppKit 8 | #endif 9 | 10 | public typealias RealmQueryChanged = ([IndexPath]) -> Void 11 | 12 | final public class RealmQuery { 13 | 14 | // MARK: - Properties 15 | 16 | private var deleteNotificationBlock: RealmQueryChanged? 17 | private var insertNotificationBlock: RealmQueryChanged? 18 | private var modificateNotificationBlock: RealmQueryChanged? 19 | 20 | private(set) var notificationToken: NotificationToken? 21 | private(set) var section: Int? 22 | 23 | public var results: Results? 24 | public var count: Int { 25 | return results?.count ?? 0 26 | } 27 | 28 | // MARK: - Subscript 29 | 30 | public subscript(index: Int) -> T? { 31 | return results?[index] 32 | } 33 | 34 | // MARK: - Con(De)structor 35 | 36 | init(results: Results?) { 37 | self.results = results 38 | } 39 | 40 | deinit { 41 | clearNotification() 42 | } 43 | 44 | // MARK: - Public methods 45 | 46 | public func addDeleteNotificationBlock( 47 | _ object: Object, 48 | block: @escaping (Object, [IndexPath]) -> Void 49 | ) -> Self { 50 | deleteNotificationBlock = { [weak object] (insertions) in 51 | guard let weakObject = object else {return} 52 | 53 | block(weakObject, insertions) 54 | } 55 | return self 56 | } 57 | 58 | public func addInsertNotificationBlock( 59 | _ object: Object, 60 | block: @escaping (Object, [IndexPath]) -> Void 61 | ) -> Self { 62 | insertNotificationBlock = { [weak object] (insertions) in 63 | guard let weakObject = object else {return} 64 | 65 | block(weakObject, insertions) 66 | } 67 | return self 68 | } 69 | 70 | public func addModificateNotificationBlock( 71 | _ object: Object, 72 | block: @escaping (Object, [IndexPath]) -> Void 73 | ) -> Self { 74 | modificateNotificationBlock = { [weak object] (insertions) in 75 | guard let weakObject = object else {return} 76 | 77 | block(weakObject, insertions) 78 | } 79 | return self 80 | } 81 | 82 | public func clearNotification() { 83 | notificationToken?.invalidate() 84 | } 85 | 86 | public func registerNotification() { 87 | guard let results = results else { return } 88 | 89 | clearNotification() 90 | notificationToken = results.observe { [weak self] (change) in 91 | guard let weakSelf = self else {return} 92 | 93 | switch change { 94 | case .update(_, let deletions, let insertions, let modifications): 95 | let indexPathsForDeletions = weakSelf.indexPathsFromInt(deletions) 96 | let indexPathsForInsertions = weakSelf.indexPathsFromInt(insertions) 97 | let indexPathsForModifications = weakSelf.indexPathsFromInt(modifications) 98 | 99 | if !deletions.isEmpty { 100 | weakSelf.deleteNotificationBlock?(indexPathsForDeletions) 101 | } 102 | if !insertions.isEmpty { 103 | weakSelf.insertNotificationBlock?(indexPathsForInsertions) 104 | } 105 | if !modifications.isEmpty { 106 | weakSelf.modificateNotificationBlock?(indexPathsForModifications) 107 | } 108 | default: 109 | break 110 | } 111 | } 112 | } 113 | 114 | public func setSection(_ section: Int) -> Self { 115 | self.section = section 116 | return self 117 | } 118 | 119 | // MARK: - Private methods 120 | 121 | private func indexPathsFromInt(_ data: [Int]) -> [IndexPath] { 122 | var indexPaths = [IndexPath]() 123 | data.forEach { (datum) in 124 | #if os(iOS) 125 | indexPaths.append(IndexPath(row: datum, section: section ?? 0)) 126 | #elseif os(macOS) 127 | indexPaths.append(IndexPath(item: datum, section: section ?? 0)) 128 | #endif 129 | } 130 | return indexPaths 131 | } 132 | 133 | } 134 | -------------------------------------------------------------------------------- /Sources/RealmWrapper/RealmManageable.swift: -------------------------------------------------------------------------------- 1 | import RealmSwift 2 | import Realm.Private 3 | 4 | public typealias RealmWriteHandler = (Realm) -> Void 5 | public typealias RealmCompletionHandler = (Realm?, Error?) -> Void 6 | 7 | public enum OrderingType { 8 | case ascending, descending 9 | } 10 | 11 | public protocol RealmManageable { 12 | 13 | // MARK: - Properties 14 | 15 | var deleteRealmIfMigrationNeeded: Bool { get } 16 | var isUseInMemory: Bool { get } 17 | var readOnly: Bool { get } 18 | var schemaVersion: UInt64 { get } 19 | var fileName: String { get } 20 | var appGroupIdentifier: String? { get } 21 | var encryptionKey: Data? { get } 22 | var shouldCompactOnLaunch: ((Int, Int) -> Bool)? { get } 23 | var migrationBlock: MigrationBlock? { get } 24 | var syncConfiguration: SyncConfiguration? { get } 25 | var objectTypes: [Object.Type]? { get } 26 | 27 | // MARK: - Constructor 28 | 29 | init() 30 | 31 | } 32 | 33 | public extension RealmManageable { 34 | 35 | // MARK: - Constants 36 | 37 | static var defaultQueue: DispatchQueue { 38 | let label = "com.k-lpmg.RealmWrapper.RealmManageable.defaultQueue" 39 | return DispatchQueue(label: label) 40 | } 41 | 42 | // MARK: - Properties 43 | 44 | var deleteRealmIfMigrationNeeded: Bool { 45 | return false 46 | } 47 | var readOnly: Bool { 48 | return false 49 | } 50 | var appGroupIdentifier: String? { 51 | return nil 52 | } 53 | var encryptionKey: Data? { 54 | return nil 55 | } 56 | var shouldCompactOnLaunch: ((Int, Int) -> Bool)? { 57 | return nil 58 | } 59 | var migrationBlock: MigrationBlock? { 60 | return nil 61 | } 62 | var syncConfiguration: SyncConfiguration? { 63 | return nil 64 | } 65 | var objectTypes: [Object.Type]? { 66 | return nil 67 | } 68 | 69 | // MARK: - Public methods 70 | 71 | func clear( 72 | writeQueue: DispatchQueue = Self.defaultQueue, 73 | isSync: Bool = true, 74 | completion: RealmCompletionHandler? = nil 75 | ) { 76 | transaction(writeQueue: writeQueue, isSync: isSync, writeHandler: { (realm) in 77 | realm.deleteAll() 78 | }) { (realm, error) in 79 | completion?(realm, error) 80 | } 81 | } 82 | 83 | func createConfiguration() -> Realm.Configuration { 84 | var config = Realm.Configuration() 85 | config.schemaVersion = schemaVersion 86 | config.migrationBlock = migrationBlock 87 | config.deleteRealmIfMigrationNeeded = deleteRealmIfMigrationNeeded 88 | config.readOnly = readOnly 89 | config.encryptionKey = encryptionKey 90 | config.shouldCompactOnLaunch = shouldCompactOnLaunch 91 | config.syncConfiguration = syncConfiguration 92 | 93 | let file = "\(fileName).realm" 94 | if isUseInMemory { 95 | config.inMemoryIdentifier = "inMemory-\(file)" 96 | } else { 97 | if let appGroupIdentifier = appGroupIdentifier { 98 | config.fileURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupIdentifier)?.appendingPathComponent(file) 99 | } else { 100 | config.fileURL = URL(fileURLWithPath: RLMRealmPathForFile(file)) 101 | } 102 | config.objectTypes = objectTypes 103 | } 104 | return config 105 | } 106 | 107 | func transaction( 108 | writeQueue: DispatchQueue = Self.defaultQueue, 109 | isSync: Bool = true, 110 | writeHandler: @escaping RealmWriteHandler, 111 | completionQueue: DispatchQueue = DispatchQueue.main, 112 | completion: RealmCompletionHandler? = nil 113 | ) { 114 | guard isSync else { 115 | writeQueue.async { 116 | self.perform(writeHandler: writeHandler, completionQueue: completionQueue, completion: completion) 117 | } 118 | return 119 | } 120 | 121 | writeQueue.sync { 122 | perform(writeHandler: writeHandler, completionQueue: completionQueue, completion: completion) 123 | } 124 | } 125 | 126 | // MARK: - Private methods 127 | 128 | private func perform( 129 | writeHandler: @escaping RealmWriteHandler, 130 | completionQueue: DispatchQueue, 131 | completion: RealmCompletionHandler? 132 | ) { 133 | do { 134 | let configuration = createConfiguration() 135 | let realm = try Realm(configuration: configuration) 136 | try realm.write { 137 | writeHandler(realm) 138 | } 139 | 140 | Realm.asyncOpen(configuration: configuration, callbackQueue: completionQueue) { (realm, error) in 141 | realm?.refresh() 142 | completion?(realm, error) 143 | } 144 | } catch { 145 | print("RealmManager not write to database: \(error)") 146 | completion?(nil, error) 147 | } 148 | } 149 | 150 | } 151 | -------------------------------------------------------------------------------- /Example/Sources/ViewControllers/EditViewController.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | final class EditViewController: UIViewController { 4 | 5 | // MARK: - Properties 6 | 7 | private var isUseInMemory: Bool = false 8 | private var id: String! 9 | private var user: User? 10 | private var isNameValid: Bool = true { 11 | didSet { 12 | saveButtonItem.isEnabled = isNameValid && isAgeValid 13 | } 14 | } 15 | private var isAgeValid: Bool = true { 16 | didSet { 17 | saveButtonItem.isEnabled = isNameValid && isAgeValid 18 | } 19 | } 20 | private let nameLabel: UILabel = { 21 | let label = UILabel() 22 | label.translatesAutoresizingMaskIntoConstraints = false 23 | label.font = UIFont.systemFont(ofSize: 15, weight: .bold) 24 | label.text = "Name" 25 | return label 26 | }() 27 | private let nameTextField: UITextField = { 28 | let textField = UITextField() 29 | textField.translatesAutoresizingMaskIntoConstraints = false 30 | textField.placeholder = "Please enter user's name." 31 | return textField 32 | }() 33 | private let ageLabel: UILabel = { 34 | let label = UILabel() 35 | label.translatesAutoresizingMaskIntoConstraints = false 36 | label.font = UIFont.systemFont(ofSize: 15, weight: .bold) 37 | label.text = "Age" 38 | return label 39 | }() 40 | private let ageTextField: UITextField = { 41 | let textField = UITextField() 42 | textField.translatesAutoresizingMaskIntoConstraints = false 43 | textField.placeholder = "Please enter user's age." 44 | textField.keyboardType = .numberPad 45 | return textField 46 | }() 47 | 48 | private lazy var saveButtonItem: UIBarButtonItem = { 49 | let buttonItem = UIBarButtonItem(barButtonSystemItem: .save, target: self, action: #selector(saveButtonDidClicked)) 50 | return buttonItem 51 | }() 52 | 53 | // MARK: - Con(De)structor 54 | 55 | convenience init(isUseInMemory: Bool = false, id: String) { 56 | self.init() 57 | 58 | self.isUseInMemory = isUseInMemory 59 | self.id = id 60 | if isUseInMemory { 61 | user = UserInMemoryRealmProxy().userFromId(id) 62 | } else { 63 | user = UserRealmProxy().userFromId(id) 64 | } 65 | } 66 | 67 | // MARK: - Overridden: UIViewController 68 | 69 | override func viewDidLoad() { 70 | super.viewDidLoad() 71 | 72 | setNavigation() 73 | setProperties() 74 | setSelector() 75 | view.addSubview(nameLabel) 76 | view.addSubview(nameTextField) 77 | view.addSubview(ageLabel) 78 | view.addSubview(ageTextField) 79 | layout() 80 | } 81 | 82 | // MARK: - Private methods 83 | 84 | private func setNavigation() { 85 | title = "Edit User" 86 | if #available(iOS 11.0, *) { 87 | navigationItem.largeTitleDisplayMode = .never 88 | } 89 | navigationItem.rightBarButtonItem = saveButtonItem 90 | } 91 | 92 | private func setProperties() { 93 | view.backgroundColor = .white 94 | nameTextField.text = user?.name 95 | ageTextField.text = user?.age.description 96 | } 97 | 98 | private func setSelector() { 99 | nameTextField.addTarget(self, action: #selector(nameTextFieldEditingChanged(sender:)), for: .editingChanged) 100 | ageTextField.addTarget(self, action: #selector(ageTextFieldEditingChanged(sender:)), for: .editingChanged) 101 | } 102 | 103 | // MARK: - Private selector 104 | 105 | @objc private func saveButtonDidClicked() { 106 | guard let name = nameTextField.text, let ageText = ageTextField.text, let age = Int(ageText) else {return} 107 | 108 | if isUseInMemory { 109 | UserInMemoryRealmProxy().updateName(id: id, name: name, age: age) 110 | } else { 111 | UserRealmProxy().updateName(id: id, name: name, age: age) 112 | } 113 | navigationController?.popViewController(animated: true) 114 | } 115 | 116 | @objc private func nameTextFieldEditingChanged(sender: UITextField) { 117 | guard let text = sender.text else {return} 118 | 119 | isNameValid = !text.isEmpty 120 | } 121 | 122 | @objc private func ageTextFieldEditingChanged(sender: UITextField) { 123 | guard let text = sender.text else {return} 124 | 125 | isAgeValid = !text.isEmpty 126 | } 127 | 128 | } 129 | 130 | // MARK: - Layout 131 | 132 | extension EditViewController { 133 | 134 | private func layout() { 135 | nameLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 32).isActive = true 136 | nameLabel.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor, constant: 32).isActive = true 137 | nameLabel.widthAnchor.constraint(equalToConstant: 50).isActive = true 138 | 139 | nameTextField.leadingAnchor.constraint(equalTo: nameLabel.trailingAnchor, constant: 16).isActive = true 140 | nameTextField.topAnchor.constraint(equalTo: nameLabel.topAnchor).isActive = true 141 | nameTextField.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -32).isActive = true 142 | 143 | ageLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 32).isActive = true 144 | ageLabel.topAnchor.constraint(equalTo: nameLabel.bottomAnchor, constant: 16).isActive = true 145 | ageLabel.widthAnchor.constraint(equalToConstant: 50).isActive = true 146 | 147 | ageTextField.leadingAnchor.constraint(equalTo: ageLabel.trailingAnchor, constant: 16).isActive = true 148 | ageTextField.topAnchor.constraint(equalTo: ageLabel.topAnchor).isActive = true 149 | ageTextField.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -32).isActive = true 150 | } 151 | 152 | } 153 | -------------------------------------------------------------------------------- /Example/Sources/ViewControllers/MultipleAddViewController.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | import RealmSwift 4 | import RealmWrapper 5 | 6 | final class MultipleAddViewController: UIViewController { 7 | 8 | // MARK: - Properties 9 | 10 | private var isCountValid: Bool = false { 11 | didSet { 12 | saveButtonItem.isEnabled = isCountValid 13 | } 14 | } 15 | private let useSyncLabel: UILabel = { 16 | let label = UILabel() 17 | label.translatesAutoresizingMaskIntoConstraints = false 18 | label.font = UIFont.systemFont(ofSize: 15, weight: .bold) 19 | label.text = "Use Sync" 20 | return label 21 | }() 22 | private let useSyncSwitch: UISwitch = { 23 | let uiSwitch = UISwitch() 24 | uiSwitch.translatesAutoresizingMaskIntoConstraints = false 25 | return uiSwitch 26 | }() 27 | private let useInMemoryLabel: UILabel = { 28 | let label = UILabel() 29 | label.translatesAutoresizingMaskIntoConstraints = false 30 | label.font = UIFont.systemFont(ofSize: 15, weight: .bold) 31 | label.text = "Use InMemory" 32 | return label 33 | }() 34 | private let useInMemorySwitch: UISwitch = { 35 | let uiSwitch = UISwitch() 36 | uiSwitch.translatesAutoresizingMaskIntoConstraints = false 37 | return uiSwitch 38 | }() 39 | private let countLabel: UILabel = { 40 | let label = UILabel() 41 | label.translatesAutoresizingMaskIntoConstraints = false 42 | label.font = UIFont.systemFont(ofSize: 15, weight: .bold) 43 | label.text = "Count" 44 | return label 45 | }() 46 | private let countTextField: UITextField = { 47 | let textField = UITextField() 48 | textField.translatesAutoresizingMaskIntoConstraints = false 49 | textField.keyboardType = .numberPad 50 | textField.textAlignment = .right 51 | textField.layer.borderColor = UIColor.gray.cgColor 52 | textField.layer.borderWidth = 0.5 53 | return textField 54 | }() 55 | 56 | private lazy var saveButtonItem: UIBarButtonItem = { 57 | let buttonItem = UIBarButtonItem(barButtonSystemItem: .save, target: self, action: #selector(saveButtonDidClicked)) 58 | buttonItem.isEnabled = false 59 | return buttonItem 60 | }() 61 | 62 | // MARK: - Overridden: UIViewController 63 | 64 | override func viewDidLoad() { 65 | super.viewDidLoad() 66 | 67 | setNavigation() 68 | setProperties() 69 | setSelector() 70 | view.addSubview(useSyncLabel) 71 | view.addSubview(useSyncSwitch) 72 | view.addSubview(useInMemoryLabel) 73 | view.addSubview(useInMemorySwitch) 74 | view.addSubview(countLabel) 75 | view.addSubview(countTextField) 76 | layout() 77 | } 78 | 79 | // MARK: - Private methods 80 | 81 | private func setNavigation() { 82 | title = "Add multiple user" 83 | if #available(iOS 11.0, *) { 84 | navigationItem.largeTitleDisplayMode = .never 85 | } 86 | navigationItem.rightBarButtonItem = saveButtonItem 87 | } 88 | 89 | private func setProperties() { 90 | view.backgroundColor = .white 91 | } 92 | 93 | private func setSelector() { 94 | countTextField.addTarget(self, action: #selector(countTextFieldEditingChanged(sender:)), for: .editingChanged) 95 | } 96 | 97 | // MARK: - Private selector 98 | 99 | @objc private func countTextFieldEditingChanged(sender: UITextField) { 100 | guard let text = sender.text else {return} 101 | 102 | isCountValid = !text.isEmpty 103 | } 104 | 105 | @objc private func saveButtonDidClicked() { 106 | guard let text = countTextField.text, let count = Int(text) else {return} 107 | 108 | let useInMemory = useInMemorySwitch.isOn 109 | let useSync = useSyncSwitch.isOn 110 | 111 | DispatchQueue.global().async { 112 | var users = [User]() 113 | (0...count).forEach({ (i) in 114 | let user = User(name: "\(i)", age: i) 115 | users.append(user) 116 | 117 | print("Appended users count : \(users.count)") 118 | }) 119 | 120 | if useInMemory { 121 | UserInMemoryRealmProxy().append(users, isSync: useSync) 122 | } else { 123 | UserRealmProxy().append(users, isSync: useSync) 124 | } 125 | } 126 | navigationController?.popViewController(animated: true) 127 | } 128 | 129 | } 130 | 131 | // MARK: - Layout 132 | 133 | extension MultipleAddViewController { 134 | 135 | private func layout() { 136 | useSyncLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16).isActive = true 137 | useSyncLabel.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor, constant: 32).isActive = true 138 | useSyncLabel.widthAnchor.constraint(equalToConstant: 120).isActive = true 139 | 140 | useSyncSwitch.topAnchor.constraint(equalTo: useSyncLabel.topAnchor).isActive = true 141 | useSyncSwitch.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16).isActive = true 142 | 143 | useInMemoryLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16).isActive = true 144 | useInMemoryLabel.topAnchor.constraint(equalTo: useSyncLabel.bottomAnchor, constant: 32).isActive = true 145 | useInMemoryLabel.widthAnchor.constraint(equalToConstant: 120).isActive = true 146 | 147 | useInMemorySwitch.topAnchor.constraint(equalTo: useInMemoryLabel.topAnchor).isActive = true 148 | useInMemorySwitch.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16).isActive = true 149 | 150 | countLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16).isActive = true 151 | countLabel.topAnchor.constraint(equalTo: useInMemoryLabel.bottomAnchor, constant: 32).isActive = true 152 | countLabel.widthAnchor.constraint(equalToConstant: 120).isActive = true 153 | 154 | countTextField.topAnchor.constraint(equalTo: countLabel.topAnchor).isActive = true 155 | countTextField.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16).isActive = true 156 | countTextField.widthAnchor.constraint(equalToConstant: 85).isActive = true 157 | countTextField.heightAnchor.constraint(equalToConstant: 32).isActive = true 158 | } 159 | 160 | } 161 | 162 | -------------------------------------------------------------------------------- /Example/Sources/ViewControllers/SingleAddViewController.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | import RealmSwift 4 | import RealmWrapper 5 | 6 | final class SingleAddViewController: UIViewController { 7 | 8 | // MARK: - Properties 9 | 10 | private var isNameValid: Bool = false { 11 | didSet { 12 | saveButtonItem.isEnabled = isNameValid && isAgeValid 13 | } 14 | } 15 | private var isAgeValid: Bool = false { 16 | didSet { 17 | saveButtonItem.isEnabled = isNameValid && isAgeValid 18 | } 19 | } 20 | private let useInMemoryLabel: UILabel = { 21 | let label = UILabel() 22 | label.translatesAutoresizingMaskIntoConstraints = false 23 | label.font = UIFont.systemFont(ofSize: 15, weight: .bold) 24 | label.text = "Use InMemory" 25 | return label 26 | }() 27 | private let useInMemorySwitch: UISwitch = { 28 | let uiSwitch = UISwitch() 29 | uiSwitch.translatesAutoresizingMaskIntoConstraints = false 30 | return uiSwitch 31 | }() 32 | private let nameLabel: UILabel = { 33 | let label = UILabel() 34 | label.translatesAutoresizingMaskIntoConstraints = false 35 | label.font = UIFont.systemFont(ofSize: 15, weight: .bold) 36 | label.text = "Name" 37 | return label 38 | }() 39 | private let nameTextField: UITextField = { 40 | let textField = UITextField() 41 | textField.translatesAutoresizingMaskIntoConstraints = false 42 | textField.placeholder = "Please enter user's name." 43 | textField.textAlignment = .right 44 | textField.layer.borderColor = UIColor.gray.cgColor 45 | textField.layer.borderWidth = 0.5 46 | return textField 47 | }() 48 | private let ageLabel: UILabel = { 49 | let label = UILabel() 50 | label.translatesAutoresizingMaskIntoConstraints = false 51 | label.font = UIFont.systemFont(ofSize: 15, weight: .bold) 52 | label.text = "Age" 53 | return label 54 | }() 55 | private let ageTextField: UITextField = { 56 | let textField = UITextField() 57 | textField.translatesAutoresizingMaskIntoConstraints = false 58 | textField.placeholder = "Please enter user's age." 59 | textField.keyboardType = .numberPad 60 | textField.textAlignment = .right 61 | textField.layer.borderColor = UIColor.gray.cgColor 62 | textField.layer.borderWidth = 0.5 63 | return textField 64 | }() 65 | 66 | private lazy var saveButtonItem: UIBarButtonItem = { 67 | let buttonItem = UIBarButtonItem(barButtonSystemItem: .save, target: self, action: #selector(saveButtonDidClicked)) 68 | buttonItem.isEnabled = false 69 | return buttonItem 70 | }() 71 | 72 | // MARK: - Overridden: UIViewController 73 | 74 | override func viewDidLoad() { 75 | super.viewDidLoad() 76 | 77 | setNavigation() 78 | setProperties() 79 | setSelector() 80 | view.addSubview(useInMemoryLabel) 81 | view.addSubview(useInMemorySwitch) 82 | view.addSubview(nameLabel) 83 | view.addSubview(nameTextField) 84 | view.addSubview(ageLabel) 85 | view.addSubview(ageTextField) 86 | layout() 87 | } 88 | 89 | // MARK: - Private methods 90 | 91 | private func setNavigation() { 92 | title = "Add single user" 93 | if #available(iOS 11.0, *) { 94 | navigationItem.largeTitleDisplayMode = .never 95 | } 96 | navigationItem.rightBarButtonItem = saveButtonItem 97 | } 98 | 99 | private func setProperties() { 100 | view.backgroundColor = .white 101 | } 102 | 103 | private func setSelector() { 104 | nameTextField.addTarget(self, action: #selector(nameTextFieldEditingChanged(sender:)), for: .editingChanged) 105 | ageTextField.addTarget(self, action: #selector(ageTextFieldEditingChanged(sender:)), for: .editingChanged) 106 | } 107 | 108 | // MARK: - Private selector 109 | 110 | @objc private func saveButtonDidClicked() { 111 | guard let name = nameTextField.text, let ageText = ageTextField.text, let age = Int(ageText) else {return} 112 | 113 | let user = User(name: name, age: age) 114 | if useInMemorySwitch.isOn { 115 | UserInMemoryRealmProxy().append(user) 116 | } else { 117 | UserRealmProxy().append(user) 118 | } 119 | navigationController?.popViewController(animated: true) 120 | } 121 | 122 | @objc private func nameTextFieldEditingChanged(sender: UITextField) { 123 | guard let text = sender.text else {return} 124 | 125 | isNameValid = !text.isEmpty 126 | } 127 | 128 | @objc private func ageTextFieldEditingChanged(sender: UITextField) { 129 | guard let text = sender.text else {return} 130 | 131 | isAgeValid = !text.isEmpty 132 | } 133 | 134 | } 135 | 136 | // MARK: - Layout 137 | 138 | extension SingleAddViewController { 139 | 140 | private func layout() { 141 | useInMemoryLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16).isActive = true 142 | useInMemoryLabel.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor, constant: 32).isActive = true 143 | useInMemoryLabel.widthAnchor.constraint(equalToConstant: 120).isActive = true 144 | 145 | useInMemorySwitch.topAnchor.constraint(equalTo: useInMemoryLabel.topAnchor).isActive = true 146 | useInMemorySwitch.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16).isActive = true 147 | 148 | nameLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16).isActive = true 149 | nameLabel.topAnchor.constraint(equalTo: useInMemoryLabel.bottomAnchor, constant: 32).isActive = true 150 | nameLabel.widthAnchor.constraint(equalToConstant: 120).isActive = true 151 | 152 | nameTextField.leadingAnchor.constraint(equalTo: nameLabel.trailingAnchor, constant: 8).isActive = true 153 | nameTextField.topAnchor.constraint(equalTo: nameLabel.topAnchor).isActive = true 154 | nameTextField.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16).isActive = true 155 | nameTextField.heightAnchor.constraint(equalToConstant: 32).isActive = true 156 | 157 | ageLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16).isActive = true 158 | ageLabel.topAnchor.constraint(equalTo: nameLabel.bottomAnchor, constant: 32).isActive = true 159 | ageLabel.widthAnchor.constraint(equalToConstant: 120).isActive = true 160 | 161 | ageTextField.leadingAnchor.constraint(equalTo: ageLabel.trailingAnchor, constant: 8).isActive = true 162 | ageTextField.topAnchor.constraint(equalTo: ageLabel.topAnchor).isActive = true 163 | ageTextField.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16).isActive = true 164 | ageTextField.heightAnchor.constraint(equalToConstant: 32).isActive = true 165 | } 166 | 167 | } 168 | 169 | -------------------------------------------------------------------------------- /Example/Sources/ViewControllers/TableViewController.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | import RealmSwift 4 | import RealmWrapper 5 | 6 | final class TableViewController: UITableViewController { 7 | 8 | // MARK: - Properties 9 | 10 | private lazy var users: RealmQuery = UserRealmProxy().users 11 | private lazy var usersInMemory: RealmQuery = UserInMemoryRealmProxy().users 12 | 13 | private lazy var menuButtonItem: UIBarButtonItem = { 14 | let buttonItem = UIBarButtonItem(title: "Menu", style: .plain, target: self, action: #selector(settingButtonItemClicked)) 15 | return buttonItem 16 | }() 17 | private lazy var addButtonItem: UIBarButtonItem = { 18 | let buttonItem = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(addButtonItemClicked)) 19 | return buttonItem 20 | }() 21 | 22 | // MARK: - Overridden: UITableViewController 23 | 24 | override func viewDidLoad() { 25 | super.viewDidLoad() 26 | 27 | setNavigation() 28 | setProperties() 29 | setRealmNotification() 30 | } 31 | 32 | // MARK: - Private methods 33 | 34 | private func setNavigation() { 35 | if #available(iOS 11.0, *) { 36 | navigationController?.navigationBar.prefersLargeTitles = true 37 | title = "People List" 38 | } 39 | navigationItem.leftBarButtonItem = menuButtonItem 40 | navigationItem.rightBarButtonItem = addButtonItem 41 | } 42 | 43 | private func setProperties() { 44 | tableView.estimatedSectionHeaderHeight = 20 45 | tableView.sectionHeaderHeight = UITableView.automaticDimension 46 | tableView.registerWithCellReuseIdentifier(UserTableViewCell.self) 47 | 48 | refreshControl = UIRefreshControl() 49 | refreshControl?.addTarget(self, action: #selector(handleRefresh), for: .valueChanged) 50 | } 51 | 52 | private func setRealmNotification() { 53 | users.setSection(0) 54 | .addInsertNotificationBlock(self) { (self, insertions) in 55 | self.tableView.reloadData() 56 | } 57 | .addModificateNotificationBlock(self) { (self, modifications) in 58 | self.tableView.reloadRows(at: modifications, with: .fade) 59 | } 60 | .addDeleteNotificationBlock(self) { (self, deletions) in 61 | self.tableView.deleteRows(at: deletions, with: .fade) 62 | } 63 | .registerNotification() 64 | 65 | usersInMemory.setSection(1) 66 | .addInsertNotificationBlock(self) { (self, insertions) in 67 | self.tableView.reloadData() 68 | } 69 | .addModificateNotificationBlock(self) { (self, modifications) in 70 | self.tableView.reloadRows(at: modifications, with: .fade) 71 | } 72 | .addDeleteNotificationBlock(self) { (self, deletions) in 73 | self.tableView.deleteRows(at: deletions, with: .fade) 74 | } 75 | .registerNotification() 76 | } 77 | 78 | private func getUserFrom(indexPath: IndexPath) -> User? { 79 | return indexPath.section == 0 ? users[indexPath.row] : usersInMemory[indexPath.row] 80 | } 81 | 82 | // MARk: - Private selector 83 | 84 | @objc private func settingButtonItemClicked() { 85 | let editOnAction = UIAlertAction(title: "edit on", style: .default) { [unowned self] (_) in 86 | self.tableView.isEditing = true 87 | } 88 | let editOffAction = UIAlertAction(title: "edit off", style: .default) { [unowned self] (_) in 89 | self.tableView.isEditing = false 90 | } 91 | let userRealmClearAction = UIAlertAction(title: "clear user realm", style: .default) { (_) in 92 | UserRealmManager().clear(isSync: false) 93 | } 94 | let inMemoryRealmClear = UIAlertAction(title: "clear inMemory realm", style: .default) { (_) in 95 | InMemoryRealmManager().clear(isSync: false) 96 | } 97 | let cancelAction = UIAlertAction(title: "cancel", style: .cancel) 98 | var actions = [UIAlertAction]() 99 | actions.append(tableView.isEditing ? editOffAction : editOnAction) 100 | actions.append(userRealmClearAction) 101 | actions.append(inMemoryRealmClear) 102 | actions.append(cancelAction) 103 | alert(preferredStyle: .actionSheet, actions: actions) 104 | } 105 | 106 | @objc private func addButtonItemClicked() { 107 | let singleAddAction = UIAlertAction(title: "single add", style: .default) { [unowned self] (_) in 108 | self.navigationController?.pushViewController(SingleAddViewController(), animated: true) 109 | } 110 | let multipleAddAction = UIAlertAction(title: "multiple add", style: .default) { [unowned self] (_) in 111 | self.navigationController?.pushViewController(MultipleAddViewController(), animated: true) 112 | } 113 | let cancelAction = UIAlertAction(title: "cancel", style: .cancel) 114 | alert(preferredStyle: .actionSheet, actions: [singleAddAction, multipleAddAction, cancelAction]) 115 | } 116 | 117 | @objc private func handleRefresh() { 118 | tableView.reloadData() 119 | refreshControl?.endRefreshing() 120 | } 121 | 122 | } 123 | 124 | // MARk: - UITableViewDataSource 125 | 126 | extension TableViewController { 127 | 128 | override func numberOfSections(in tableView: UITableView) -> Int { 129 | return 2 130 | } 131 | 132 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 133 | guard section == 0 else {return usersInMemory.count} 134 | return users.count 135 | } 136 | 137 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 138 | let cell = tableView.dequeueReusableCell(withIdentifier: UserTableViewCell.className, for: indexPath) as! UserTableViewCell 139 | cell.model = getUserFrom(indexPath: indexPath) 140 | return cell 141 | } 142 | 143 | } 144 | 145 | // MARK: - UITableViewDelegate 146 | 147 | extension TableViewController { 148 | 149 | override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { 150 | let headerView = UIView(frame: CGRect(x: 0, y: 0, width: tableView.bounds.width, height: tableView.estimatedSectionHeaderHeight)) 151 | headerView.backgroundColor = UIColor.groupTableViewBackground 152 | 153 | let label: UILabel = { 154 | let label = UILabel() 155 | label.translatesAutoresizingMaskIntoConstraints = false 156 | label.font = UIFont.systemFont(ofSize: 17) 157 | return label 158 | }() 159 | label.text = section == 0 ? "user realm" : "inMemory realm" 160 | headerView.addSubview(label) 161 | label.leadingAnchor.constraint(equalTo: headerView.leadingAnchor, constant: 16).isActive = true 162 | label.topAnchor.constraint(equalTo: headerView.topAnchor, constant: 4).isActive = true 163 | label.trailingAnchor.constraint(equalTo: headerView.trailingAnchor).isActive = true 164 | label.bottomAnchor.constraint(equalTo: headerView.bottomAnchor, constant: -4).isActive = true 165 | 166 | return headerView 167 | } 168 | 169 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 170 | guard let id = getUserFrom(indexPath: indexPath)?.id else {return} 171 | 172 | let controller = EditViewController(isUseInMemory: indexPath.section != 0, id: id) 173 | navigationController?.pushViewController(controller, animated: true) 174 | tableView.deselectRow(at: indexPath, animated: true) 175 | } 176 | 177 | override func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle { 178 | return .delete 179 | } 180 | 181 | override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { 182 | guard let user = getUserFrom(indexPath: indexPath) else { return } 183 | 184 | if indexPath.section == 0 { 185 | UserInMemoryRealmProxy().delete(user) 186 | } else { 187 | UserRealmProxy().delete(user) 188 | } 189 | } 190 | 191 | } 192 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RealmWrapper 2 | 3 | [![Cocoapods](https://img.shields.io/cocoapods/v/RealmWrapper.svg?style=flat)](https://cocoapods.org/pods/RealmWrapper) 4 | [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 5 | ![Swift](https://img.shields.io/badge/Swift-5.0-orange.svg) 6 | [![GitHub license](https://img.shields.io/badge/license-MIT-lightgrey.svg?style=flat)](https://raw.githubusercontent.com/k-lpmg/RealmWrapper/master/LICENSE) 7 | [![Build Status](https://travis-ci.org/k-lpmg/RealmWrapper.svg?branch=master)](https://travis-ci.org/k-lpmg/RealmWrapper) 8 | 9 | RealmWrapper is wrapper library for [RealmSwift](https://github.com/realm/realm-cocoa/tree/master/RealmSwift) in [realm-cocoa](https://github.com/realm/realm-cocoa) 10 | 11 | If you use [RealmWrapper](https://github.com/k-lpmg/RealmWrapper), you can easily use UI update through Notification and Transaction processing. 12 | Also, you do not have to worry about the retain cycle when using self in the Notification block. 13 | 14 | - [RealmWrapper](#realmwrapper) 15 | - [At a Glance](#at-a-glance) 16 | - [Threading](#threading) 17 | - [Getting Started](#getting-started) 18 | - [Usage](#usage) 19 | - [RealmManageable Property](#realmmanageable-property) 20 | - [Example](#example) 21 | - [Installation](#installation) 22 | - [CocoaPods (iOS 9+)](#cocoapods-ios-9) 23 | - [Carthage (iOS 9+)](#carthage-ios-9) 24 | - [Swift Package Manager (Swift 5.2+, iOS 11+)](#swift-package-manager-swift-52-ios-11) 25 | - [LICENSE](#license) 26 | 27 | 28 | ## At a Glance 29 | 30 | If you use [RealmSwift](https://github.com/realm/realm-cocoa/tree/master/RealmSwift), you have to be careful about try statement and thread processing every transaction. 31 | However, In [RealmManageable](https://github.com/k-lpmg/RealmWrapper/blob/master/Sources/RealmManageable.swift) which manages one realm file, transaction processing is performed using Realm-only DispatchQueue. 32 | [RealmProxiable](https://github.com/k-lpmg/RealmWrapper/blob/master/Sources/RealmProxiable.swift), which owns [RealmManageable](https://github.com/k-lpmg/RealmWrapper/blob/master/Sources/RealmManageable.swift), can easily add, modify or delete models without having to worry about try statement and thread processing. 33 | 34 | > User Model 35 | ```swift 36 | @objcMembers 37 | class User: Object { 38 | dynamic var id: String? 39 | dynamic var name: String? 40 | 41 | override static func primaryKey() -> String? { 42 | return "id" 43 | } 44 | } 45 | 46 | var user = User() 47 | user.id = UUID().uuidString 48 | user.name = "Kevin" 49 | ``` 50 | 51 | > Using RealmSwift 52 | ```swift 53 | let realm = try! Realm(configuration: Realm.Configuration(fileURL: URL(fileURLWithPath: RLMRealmPathForFile("user.realm")), schemaVersion: 1, objectTypes: [User.self])) 54 | try! realm.write { 55 | realm.add(user) 56 | } 57 | ``` 58 | 59 | > Using RealmWrapper 60 | ```swift 61 | UserRealmManager().transaction(writeHandler: { (realm) in 62 | realm.add(user) 63 | }) 64 | ``` 65 | 66 | ```swift 67 | UserRealmProxy().append(user) 68 | ``` 69 | 70 | 71 | ## Threading 72 | 73 | - By default, you can use the transaction function to process a Realm Transaction in MainThread 74 | ```swift 75 | UserRealmManager().transaction(writeHandler: { (realm) in 76 | realm.add(user) 77 | }) 78 | ``` 79 | 80 | - It can be implemented by a background thread using DispatchQueue and isSync parameters. 81 | ```swift 82 | UserRealmManager().transaction(isSync: false, writeHandler: { (realm) in 83 | realm.add(user) 84 | }) 85 | ``` 86 | 87 | ```swift 88 | UserRealmManager().transaction(writeQueue: DispatchQueue(label: "background"), isSync: false, writeHandler: { (realm) in 89 | realm.add(user) 90 | }) 91 | ``` 92 | 93 | - You can add completion closure. 94 | ```swift 95 | UserRealmManager().transaction(isSync: false, writeHandler: { (realm) in 96 | realm.add(user) 97 | }) { (realm, error) in 98 | self.tableView.reloadData() 99 | } 100 | ``` 101 | 102 | 103 | ## Getting Started 104 | 105 | 1. Create a `RealmManager` that manages one realm file. 106 | 107 | ```swift 108 | final class UserRealmManager: RealmManageable { 109 | 110 | var isUseInMemory: Bool { 111 | return false 112 | } 113 | var schemaVersion: UInt64 { 114 | return 1 115 | } 116 | var fileName: String { 117 | return "user" 118 | } 119 | var objectTypes: [Object.Type]? { 120 | return [User.self] 121 | } 122 | 123 | } 124 | ``` 125 | 126 | 2. Create a `RealmProxy` that is responsible for the CRUD function to be accessed by the Controller. 127 | 128 | ```swift 129 | struct UserRealmProxy: RealmProxiable { 130 | 131 | var users: RealmQuery { 132 | return query(sortProperty: "date", ordering: .ascending) 133 | } 134 | 135 | func append(_ user: User) { 136 | rm.transaction(writeHandler: { (realm) in 137 | realm.add(user, update: .all) 138 | }) 139 | } 140 | 141 | func delete(_ user: User) { 142 | rm.transaction(writeHandler: { (realm) in 143 | realm.delete(user) 144 | }) 145 | } 146 | 147 | func updateName(id: String, name: String, age: Int) { 148 | guard let user = userFromId(id) else {return} 149 | 150 | rm.transaction(writeHandler: { (realm) in 151 | user.name = name 152 | user.age = age 153 | realm.add(user, update: .all) 154 | }) 155 | } 156 | 157 | func userFromId(_ id: String) -> User? { 158 | return query(filter: "id == '\(id)'").results.first 159 | } 160 | 161 | } 162 | 163 | var user = User() 164 | user.id = UUID().uuidString 165 | user.name = "Kevin" 166 | 167 | UserRealmProxy().append(user) 168 | UserRealmProxy().delete(user) 169 | UserRealmProxy().updateName(id: user.id, name: "Kris") 170 | ``` 171 | 172 | 3. If you want to register the notification of the status of the Realm object in the controller, you can register the notification in [RealmQuery](https://github.com/k-lpmg/RealmWrapper/blob/master/Sources/RealmWrapper/RealmQuery.swift). 173 | 174 | ```swift 175 | let users: RealmQuery = UserRealmProxy().users 176 | 177 | users.setSection(0) 178 | .addInsertNotificationBlock(self) { (self, insertions) in 179 | self.tableView.reloadData() 180 | } 181 | .addModificateNotificationBlock(self) { (self, modifications) in 182 | self.tableView.reloadRows(at: modifications, with: .fade) 183 | } 184 | .addDeleteNotificationBlock(self) { (self, deletions) in 185 | self.tableView.deleteRows(at: deletions, with: .fade) 186 | } 187 | .registerNotification() 188 | ``` 189 | 190 | Also, since [RealmQuery](https://github.com/k-lpmg/RealmWrapper/blob/master/Sources/RealmWrapper/RealmQuery.swift) is doing weak handling to prevent the retain cycle, you can use closures without worrying about the retain cycle if you pass only self. 191 | 192 | ```swift 193 | 194 | public func addDeleteNotificationBlock(_ object: Object, block: @escaping (Object, [IndexPath]) -> Void) -> Self { 195 | deleteNotificationBlock = { [weak object] (deletions) in 196 | guard let weakObject = object else {return} 197 | 198 | block(weakObject, deletions) 199 | } 200 | 201 | return self 202 | } 203 | ``` 204 | 205 | 206 | ## Usage 207 | 208 | #### RealmManageable Property 209 | 210 | | Property | Type | Default | Description | 211 | | -------- | ---- | ------- | ---------- | 212 | | `isUseInMemory` | `Bool` | `required` |`Use InMemory Realm` | 213 | | `fileName` | `Bool` | `required` |`Realm File Name` | 214 | | `appGroupIdentifier` | `String?` | `nil` |`Value for Realm file directory for App Extension`| 215 | 216 | 217 | ## Example 218 | 219 | 1. run carthage update 220 | ```console 221 | $ carthage update --platform iOS 222 | ``` 223 | 224 | 2. open RealmWrapper.xcodeproj 225 | ```console 226 | $ open RealmWrapper.xcodeproj 227 | ``` 228 | 229 | 3. run RealmWrapperExample 230 | 231 | 232 | ## Installation 233 | 234 | #### CocoaPods (iOS 9+) 235 | 236 | ```ruby 237 | platform :ios, '9.0' 238 | use_frameworks! 239 | 240 | target '' do 241 | pod 'RealmWrapper' 242 | end 243 | ``` 244 | 245 | #### Carthage (iOS 9+) 246 | 247 | ```ruby 248 | github "k-lpmg/RealmWrapper" 249 | ``` 250 | 251 | #### Swift Package Manager (Swift 5.2+, iOS 11+) 252 | 253 | ```swift 254 | .package(url: "https://github.com/k-lpmg/RealmWrapper.git", .upToNextMajor(from: "1.4.4")), 255 | ``` 256 | 257 | ## LICENSE 258 | 259 | These works are available under the MIT license. See the [LICENSE][license] file 260 | for more info. 261 | 262 | [license]: LICENSE 263 | -------------------------------------------------------------------------------- /RealmWrapper.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 0AC3889122EC432A005EF115 /* RealmCRUDTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AC3888E22EC432A005EF115 /* RealmCRUDTests.swift */; }; 11 | 0AC3889722EC4918005EF115 /* MockUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AC3889622EC4918005EF115 /* MockUser.swift */; }; 12 | 0AC3889922EC4932005EF115 /* MockInMemoryRealmManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AC3889822EC4932005EF115 /* MockInMemoryRealmManager.swift */; }; 13 | 0AC3889B22EC493B005EF115 /* MockUserRealmManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AC3889A22EC493B005EF115 /* MockUserRealmManager.swift */; }; 14 | 0AC3889D22EC4960005EF115 /* MockUserInMemoryRealmProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AC3889C22EC4960005EF115 /* MockUserInMemoryRealmProxy.swift */; }; 15 | 0AC3889F22EC496B005EF115 /* MockUserRealmProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AC3889E22EC496B005EF115 /* MockUserRealmProxy.swift */; }; 16 | 0AC388A122EC4A4A005EF115 /* RealmNotificationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AC388A022EC4A4A005EF115 /* RealmNotificationTests.swift */; }; 17 | 0AC388A322EC4A77005EF115 /* RealmTransactionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AC388A222EC4A77005EF115 /* RealmTransactionTests.swift */; }; 18 | 6507D60A20E7903400A31564 /* Realm.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6507D60920E7903400A31564 /* Realm.framework */; }; 19 | 65104393210C430B00A5CF9D /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 65104392210C430B00A5CF9D /* README.md */; }; 20 | 652783C220E7A5AA00F067B6 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 652783A820E7A5AA00F067B6 /* Assets.xcassets */; }; 21 | 652783C320E7A5AA00F067B6 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 652783A920E7A5AA00F067B6 /* LaunchScreen.storyboard */; }; 22 | 652783CE20E7A5AA00F067B6 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 652783BB20E7A5AA00F067B6 /* AppDelegate.swift */; }; 23 | 652783DB20E7A6EB00F067B6 /* TableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 652783DA20E7A6EB00F067B6 /* TableViewController.swift */; }; 24 | 652783DD20E7A6F400F067B6 /* SingleAddViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 652783DC20E7A6F400F067B6 /* SingleAddViewController.swift */; }; 25 | 652783DF20E7A6FB00F067B6 /* EditViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 652783DE20E7A6FB00F067B6 /* EditViewController.swift */; }; 26 | 652783E820E7A8FA00F067B6 /* ClassName-Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 652783E720E7A8FA00F067B6 /* ClassName-Extension.swift */; }; 27 | 652783EA20E7A90400F067B6 /* UITableView-Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 652783E920E7A90400F067B6 /* UITableView-Extension.swift */; }; 28 | 652783EC20E7A93000F067B6 /* InMemoryRealmManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 652783EB20E7A93000F067B6 /* InMemoryRealmManager.swift */; }; 29 | 652783EE20E7A93900F067B6 /* UserRealmManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 652783ED20E7A93900F067B6 /* UserRealmManager.swift */; }; 30 | 652783F020E7A95500F067B6 /* UserTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 652783EF20E7A95500F067B6 /* UserTableViewCell.swift */; }; 31 | 652783F220E7A9BC00F067B6 /* UserInMemoryRealmProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 652783F120E7A9BC00F067B6 /* UserInMemoryRealmProxy.swift */; }; 32 | 652783F420E7A9CC00F067B6 /* UserRealmProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 652783F320E7A9CC00F067B6 /* UserRealmProxy.swift */; }; 33 | 652F13B320E7EDD000C138DA /* Realm.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6507D60920E7903400A31564 /* Realm.framework */; }; 34 | 652F13B420E7EDD900C138DA /* RealmSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6551D14920E78D55004DB378 /* RealmSwift.framework */; }; 35 | 652F13B620E7EE1B00C138DA /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = 652783E020E7A71600F067B6 /* User.swift */; }; 36 | 6551D12520E78A12004DB378 /* RealmWrapper.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6551D11B20E78A12004DB378 /* RealmWrapper.framework */; }; 37 | 6551D14A20E78D55004DB378 /* RealmSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6551D14920E78D55004DB378 /* RealmSwift.framework */; }; 38 | 656201FD21225796000E209D /* RealmWrapper.h in Headers */ = {isa = PBXBuildFile; fileRef = 656201F821225796000E209D /* RealmWrapper.h */; settings = {ATTRIBUTES = (Public, ); }; }; 39 | 65E54905221D462800074FAD /* MultipleAddViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65E54904221D462800074FAD /* MultipleAddViewController.swift */; }; 40 | 65E54907221D465900074FAD /* UIViewController-Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65E54906221D465900074FAD /* UIViewController-Extension.swift */; }; 41 | 65E54912221D611700074FAD /* AppNavigator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65E54911221D611700074FAD /* AppNavigator.swift */; }; 42 | 65F564B621E4ED7100C4860F /* RealmQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65F564B321E4ED7100C4860F /* RealmQuery.swift */; }; 43 | 65F564B721E4ED7100C4860F /* RealmManageable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65F564B421E4ED7100C4860F /* RealmManageable.swift */; }; 44 | 65F564B821E4ED7100C4860F /* RealmProxiable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65F564B521E4ED7100C4860F /* RealmProxiable.swift */; }; 45 | /* End PBXBuildFile section */ 46 | 47 | /* Begin PBXContainerItemProxy section */ 48 | 6538B582210EEBDE008688B9 /* PBXContainerItemProxy */ = { 49 | isa = PBXContainerItemProxy; 50 | containerPortal = 6551D11220E78A12004DB378 /* Project object */; 51 | proxyType = 1; 52 | remoteGlobalIDString = 6507D61620E798DA00A31564; 53 | remoteInfo = RealmWrapperExample; 54 | }; 55 | 6551D12620E78A12004DB378 /* PBXContainerItemProxy */ = { 56 | isa = PBXContainerItemProxy; 57 | containerPortal = 6551D11220E78A12004DB378 /* Project object */; 58 | proxyType = 1; 59 | remoteGlobalIDString = 6551D11A20E78A12004DB378; 60 | remoteInfo = RealmWrapper; 61 | }; 62 | /* End PBXContainerItemProxy section */ 63 | 64 | /* Begin PBXFileReference section */ 65 | 0AC3888E22EC432A005EF115 /* RealmCRUDTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealmCRUDTests.swift; sourceTree = ""; }; 66 | 0AC3889622EC4918005EF115 /* MockUser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockUser.swift; sourceTree = ""; }; 67 | 0AC3889822EC4932005EF115 /* MockInMemoryRealmManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockInMemoryRealmManager.swift; sourceTree = ""; }; 68 | 0AC3889A22EC493B005EF115 /* MockUserRealmManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockUserRealmManager.swift; sourceTree = ""; }; 69 | 0AC3889C22EC4960005EF115 /* MockUserInMemoryRealmProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockUserInMemoryRealmProxy.swift; sourceTree = ""; }; 70 | 0AC3889E22EC496B005EF115 /* MockUserRealmProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockUserRealmProxy.swift; sourceTree = ""; }; 71 | 0AC388A022EC4A4A005EF115 /* RealmNotificationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealmNotificationTests.swift; sourceTree = ""; }; 72 | 0AC388A222EC4A77005EF115 /* RealmTransactionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealmTransactionTests.swift; sourceTree = ""; }; 73 | 6507D60920E7903400A31564 /* Realm.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Realm.framework; path = Carthage/Build/iOS/Realm.framework; sourceTree = ""; }; 74 | 6507D61720E798DA00A31564 /* RealmWrapperExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RealmWrapperExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 75 | 65104392210C430B00A5CF9D /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 76 | 652783A820E7A5AA00F067B6 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 77 | 652783AA20E7A5AA00F067B6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 78 | 652783AD20E7A5AA00F067B6 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 79 | 652783BB20E7A5AA00F067B6 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 80 | 652783DA20E7A6EB00F067B6 /* TableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableViewController.swift; sourceTree = ""; }; 81 | 652783DC20E7A6F400F067B6 /* SingleAddViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingleAddViewController.swift; sourceTree = ""; }; 82 | 652783DE20E7A6FB00F067B6 /* EditViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditViewController.swift; sourceTree = ""; }; 83 | 652783E020E7A71600F067B6 /* User.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = ""; }; 84 | 652783E720E7A8FA00F067B6 /* ClassName-Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ClassName-Extension.swift"; sourceTree = ""; }; 85 | 652783E920E7A90400F067B6 /* UITableView-Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITableView-Extension.swift"; sourceTree = ""; }; 86 | 652783EB20E7A93000F067B6 /* InMemoryRealmManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InMemoryRealmManager.swift; sourceTree = ""; }; 87 | 652783ED20E7A93900F067B6 /* UserRealmManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserRealmManager.swift; sourceTree = ""; }; 88 | 652783EF20E7A95500F067B6 /* UserTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserTableViewCell.swift; sourceTree = ""; }; 89 | 652783F120E7A9BC00F067B6 /* UserInMemoryRealmProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserInMemoryRealmProxy.swift; sourceTree = ""; }; 90 | 652783F320E7A9CC00F067B6 /* UserRealmProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserRealmProxy.swift; sourceTree = ""; }; 91 | 6551D11B20E78A12004DB378 /* RealmWrapper.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RealmWrapper.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 92 | 6551D12420E78A12004DB378 /* RealmWrapperTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RealmWrapperTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 93 | 6551D14420E78A8E004DB378 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 94 | 6551D14920E78D55004DB378 /* RealmSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RealmSwift.framework; path = Carthage/Build/iOS/RealmSwift.framework; sourceTree = ""; }; 95 | 656201F621225796000E209D /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 96 | 656201F821225796000E209D /* RealmWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RealmWrapper.h; sourceTree = ""; }; 97 | 65E54904221D462800074FAD /* MultipleAddViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultipleAddViewController.swift; sourceTree = ""; }; 98 | 65E54906221D465900074FAD /* UIViewController-Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController-Extension.swift"; sourceTree = ""; }; 99 | 65E54911221D611700074FAD /* AppNavigator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppNavigator.swift; sourceTree = ""; }; 100 | 65F564B321E4ED7100C4860F /* RealmQuery.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealmQuery.swift; sourceTree = ""; }; 101 | 65F564B421E4ED7100C4860F /* RealmManageable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealmManageable.swift; sourceTree = ""; }; 102 | 65F564B521E4ED7100C4860F /* RealmProxiable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealmProxiable.swift; sourceTree = ""; }; 103 | /* End PBXFileReference section */ 104 | 105 | /* Begin PBXFrameworksBuildPhase section */ 106 | 6507D61420E798DA00A31564 /* Frameworks */ = { 107 | isa = PBXFrameworksBuildPhase; 108 | buildActionMask = 2147483647; 109 | files = ( 110 | ); 111 | runOnlyForDeploymentPostprocessing = 0; 112 | }; 113 | 6551D11720E78A12004DB378 /* Frameworks */ = { 114 | isa = PBXFrameworksBuildPhase; 115 | buildActionMask = 2147483647; 116 | files = ( 117 | 6507D60A20E7903400A31564 /* Realm.framework in Frameworks */, 118 | 6551D14A20E78D55004DB378 /* RealmSwift.framework in Frameworks */, 119 | ); 120 | runOnlyForDeploymentPostprocessing = 0; 121 | }; 122 | 6551D12120E78A12004DB378 /* Frameworks */ = { 123 | isa = PBXFrameworksBuildPhase; 124 | buildActionMask = 2147483647; 125 | files = ( 126 | 652F13B420E7EDD900C138DA /* RealmSwift.framework in Frameworks */, 127 | 652F13B320E7EDD000C138DA /* Realm.framework in Frameworks */, 128 | 6551D12520E78A12004DB378 /* RealmWrapper.framework in Frameworks */, 129 | ); 130 | runOnlyForDeploymentPostprocessing = 0; 131 | }; 132 | /* End PBXFrameworksBuildPhase section */ 133 | 134 | /* Begin PBXGroup section */ 135 | 0AC3887922EC41C3005EF115 /* Mocks */ = { 136 | isa = PBXGroup; 137 | children = ( 138 | 0AC3887B22EC41D1005EF115 /* DB */, 139 | 0AC3887A22EC41CC005EF115 /* Models */, 140 | ); 141 | path = Mocks; 142 | sourceTree = ""; 143 | }; 144 | 0AC3887A22EC41CC005EF115 /* Models */ = { 145 | isa = PBXGroup; 146 | children = ( 147 | 0AC3889622EC4918005EF115 /* MockUser.swift */, 148 | ); 149 | path = Models; 150 | sourceTree = ""; 151 | }; 152 | 0AC3887B22EC41D1005EF115 /* DB */ = { 153 | isa = PBXGroup; 154 | children = ( 155 | 0AC3887D22EC41DB005EF115 /* Managers */, 156 | 0AC3887C22EC41D6005EF115 /* Proxies */, 157 | ); 158 | path = DB; 159 | sourceTree = ""; 160 | }; 161 | 0AC3887C22EC41D6005EF115 /* Proxies */ = { 162 | isa = PBXGroup; 163 | children = ( 164 | 0AC3889C22EC4960005EF115 /* MockUserInMemoryRealmProxy.swift */, 165 | 0AC3889E22EC496B005EF115 /* MockUserRealmProxy.swift */, 166 | ); 167 | path = Proxies; 168 | sourceTree = ""; 169 | }; 170 | 0AC3887D22EC41DB005EF115 /* Managers */ = { 171 | isa = PBXGroup; 172 | children = ( 173 | 0AC3889822EC4932005EF115 /* MockInMemoryRealmManager.swift */, 174 | 0AC3889A22EC493B005EF115 /* MockUserRealmManager.swift */, 175 | ); 176 | path = Managers; 177 | sourceTree = ""; 178 | }; 179 | 652783A620E7A5AA00F067B6 /* Example */ = { 180 | isa = PBXGroup; 181 | children = ( 182 | 652783AE20E7A5AA00F067B6 /* Sources */, 183 | 652783A720E7A5AA00F067B6 /* Support */, 184 | ); 185 | path = Example; 186 | sourceTree = ""; 187 | }; 188 | 652783A720E7A5AA00F067B6 /* Support */ = { 189 | isa = PBXGroup; 190 | children = ( 191 | 652783A820E7A5AA00F067B6 /* Assets.xcassets */, 192 | 652783A920E7A5AA00F067B6 /* LaunchScreen.storyboard */, 193 | 652783AD20E7A5AA00F067B6 /* Info.plist */, 194 | ); 195 | path = Support; 196 | sourceTree = ""; 197 | }; 198 | 652783AE20E7A5AA00F067B6 /* Sources */ = { 199 | isa = PBXGroup; 200 | children = ( 201 | 652783BB20E7A5AA00F067B6 /* AppDelegate.swift */, 202 | 65E54911221D611700074FAD /* AppNavigator.swift */, 203 | 652783D320E7A64D00F067B6 /* Cells */, 204 | 65E5490B221D604500074FAD /* DB */, 205 | 652783D620E7A65D00F067B6 /* Extension */, 206 | 652783D820E7A6AF00F067B6 /* Models */, 207 | 652783D920E7A6C500F067B6 /* ViewControllers */, 208 | ); 209 | path = Sources; 210 | sourceTree = ""; 211 | }; 212 | 652783D320E7A64D00F067B6 /* Cells */ = { 213 | isa = PBXGroup; 214 | children = ( 215 | 652783EF20E7A95500F067B6 /* UserTableViewCell.swift */, 216 | ); 217 | path = Cells; 218 | sourceTree = ""; 219 | }; 220 | 652783D620E7A65D00F067B6 /* Extension */ = { 221 | isa = PBXGroup; 222 | children = ( 223 | 652783E720E7A8FA00F067B6 /* ClassName-Extension.swift */, 224 | 652783E920E7A90400F067B6 /* UITableView-Extension.swift */, 225 | 65E54906221D465900074FAD /* UIViewController-Extension.swift */, 226 | ); 227 | path = Extension; 228 | sourceTree = ""; 229 | }; 230 | 652783D820E7A6AF00F067B6 /* Models */ = { 231 | isa = PBXGroup; 232 | children = ( 233 | 652783E020E7A71600F067B6 /* User.swift */, 234 | ); 235 | path = Models; 236 | sourceTree = ""; 237 | }; 238 | 652783D920E7A6C500F067B6 /* ViewControllers */ = { 239 | isa = PBXGroup; 240 | children = ( 241 | 652783DE20E7A6FB00F067B6 /* EditViewController.swift */, 242 | 65E54904221D462800074FAD /* MultipleAddViewController.swift */, 243 | 652783DC20E7A6F400F067B6 /* SingleAddViewController.swift */, 244 | 652783DA20E7A6EB00F067B6 /* TableViewController.swift */, 245 | ); 246 | path = ViewControllers; 247 | sourceTree = ""; 248 | }; 249 | 6551D11120E78A12004DB378 = { 250 | isa = PBXGroup; 251 | children = ( 252 | 65104392210C430B00A5CF9D /* README.md */, 253 | 656201F621225796000E209D /* Info.plist */, 254 | 656201F821225796000E209D /* RealmWrapper.h */, 255 | 6551D13520E78A87004DB378 /* Sources */, 256 | 652783A620E7A5AA00F067B6 /* Example */, 257 | 6551D14220E78A8E004DB378 /* Tests */, 258 | 6551D14820E78D55004DB378 /* Frameworks */, 259 | 6551D11C20E78A12004DB378 /* Products */, 260 | ); 261 | sourceTree = ""; 262 | }; 263 | 6551D11C20E78A12004DB378 /* Products */ = { 264 | isa = PBXGroup; 265 | children = ( 266 | 6551D11B20E78A12004DB378 /* RealmWrapper.framework */, 267 | 6551D12420E78A12004DB378 /* RealmWrapperTests.xctest */, 268 | 6507D61720E798DA00A31564 /* RealmWrapperExample.app */, 269 | ); 270 | name = Products; 271 | sourceTree = ""; 272 | }; 273 | 6551D13520E78A87004DB378 /* Sources */ = { 274 | isa = PBXGroup; 275 | children = ( 276 | 65F564B221E4ED7100C4860F /* RealmWrapper */, 277 | ); 278 | path = Sources; 279 | sourceTree = ""; 280 | }; 281 | 6551D14220E78A8E004DB378 /* Tests */ = { 282 | isa = PBXGroup; 283 | children = ( 284 | 6551D14420E78A8E004DB378 /* Info.plist */, 285 | 0AC3888E22EC432A005EF115 /* RealmCRUDTests.swift */, 286 | 0AC388A022EC4A4A005EF115 /* RealmNotificationTests.swift */, 287 | 0AC388A222EC4A77005EF115 /* RealmTransactionTests.swift */, 288 | 0AC3887922EC41C3005EF115 /* Mocks */, 289 | ); 290 | path = Tests; 291 | sourceTree = ""; 292 | }; 293 | 6551D14820E78D55004DB378 /* Frameworks */ = { 294 | isa = PBXGroup; 295 | children = ( 296 | 6507D60920E7903400A31564 /* Realm.framework */, 297 | 6551D14920E78D55004DB378 /* RealmSwift.framework */, 298 | ); 299 | name = Frameworks; 300 | sourceTree = ""; 301 | }; 302 | 65E5490B221D604500074FAD /* DB */ = { 303 | isa = PBXGroup; 304 | children = ( 305 | 65E5490C221D604C00074FAD /* Managers */, 306 | 65E5490D221D605300074FAD /* Proxies */, 307 | ); 308 | path = DB; 309 | sourceTree = ""; 310 | }; 311 | 65E5490C221D604C00074FAD /* Managers */ = { 312 | isa = PBXGroup; 313 | children = ( 314 | 652783EB20E7A93000F067B6 /* InMemoryRealmManager.swift */, 315 | 652783ED20E7A93900F067B6 /* UserRealmManager.swift */, 316 | ); 317 | path = Managers; 318 | sourceTree = ""; 319 | }; 320 | 65E5490D221D605300074FAD /* Proxies */ = { 321 | isa = PBXGroup; 322 | children = ( 323 | 652783F120E7A9BC00F067B6 /* UserInMemoryRealmProxy.swift */, 324 | 652783F320E7A9CC00F067B6 /* UserRealmProxy.swift */, 325 | ); 326 | path = Proxies; 327 | sourceTree = ""; 328 | }; 329 | 65F564B221E4ED7100C4860F /* RealmWrapper */ = { 330 | isa = PBXGroup; 331 | children = ( 332 | 65F564B321E4ED7100C4860F /* RealmQuery.swift */, 333 | 65F564B421E4ED7100C4860F /* RealmManageable.swift */, 334 | 65F564B521E4ED7100C4860F /* RealmProxiable.swift */, 335 | ); 336 | path = RealmWrapper; 337 | sourceTree = ""; 338 | }; 339 | /* End PBXGroup section */ 340 | 341 | /* Begin PBXHeadersBuildPhase section */ 342 | 6551D11820E78A12004DB378 /* Headers */ = { 343 | isa = PBXHeadersBuildPhase; 344 | buildActionMask = 2147483647; 345 | files = ( 346 | 656201FD21225796000E209D /* RealmWrapper.h in Headers */, 347 | ); 348 | runOnlyForDeploymentPostprocessing = 0; 349 | }; 350 | /* End PBXHeadersBuildPhase section */ 351 | 352 | /* Begin PBXNativeTarget section */ 353 | 6507D61620E798DA00A31564 /* RealmWrapperExample */ = { 354 | isa = PBXNativeTarget; 355 | buildConfigurationList = 6507D62820E798DB00A31564 /* Build configuration list for PBXNativeTarget "RealmWrapperExample" */; 356 | buildPhases = ( 357 | 6507D61320E798DA00A31564 /* Sources */, 358 | 6507D61420E798DA00A31564 /* Frameworks */, 359 | 6507D61520E798DA00A31564 /* Resources */, 360 | 6592FCD021006FD500D8AFE9 /* [CT] Carthage Script */, 361 | ); 362 | buildRules = ( 363 | ); 364 | dependencies = ( 365 | ); 366 | name = RealmWrapperExample; 367 | productName = RealmWrapperExample; 368 | productReference = 6507D61720E798DA00A31564 /* RealmWrapperExample.app */; 369 | productType = "com.apple.product-type.application"; 370 | }; 371 | 6551D11A20E78A12004DB378 /* RealmWrapper */ = { 372 | isa = PBXNativeTarget; 373 | buildConfigurationList = 6551D12F20E78A12004DB378 /* Build configuration list for PBXNativeTarget "RealmWrapper" */; 374 | buildPhases = ( 375 | 6551D11620E78A12004DB378 /* Sources */, 376 | 6551D11720E78A12004DB378 /* Frameworks */, 377 | 6551D11820E78A12004DB378 /* Headers */, 378 | 6551D11920E78A12004DB378 /* Resources */, 379 | ); 380 | buildRules = ( 381 | ); 382 | dependencies = ( 383 | ); 384 | name = RealmWrapper; 385 | productName = RealmWrapper; 386 | productReference = 6551D11B20E78A12004DB378 /* RealmWrapper.framework */; 387 | productType = "com.apple.product-type.framework"; 388 | }; 389 | 6551D12320E78A12004DB378 /* RealmWrapperTests */ = { 390 | isa = PBXNativeTarget; 391 | buildConfigurationList = 6551D13220E78A12004DB378 /* Build configuration list for PBXNativeTarget "RealmWrapperTests" */; 392 | buildPhases = ( 393 | 6551D12020E78A12004DB378 /* Sources */, 394 | 6551D12120E78A12004DB378 /* Frameworks */, 395 | 6551D12220E78A12004DB378 /* Resources */, 396 | 6592FCD1210070DF00D8AFE9 /* [CT] Carthage Script */, 397 | ); 398 | buildRules = ( 399 | ); 400 | dependencies = ( 401 | 6551D12720E78A12004DB378 /* PBXTargetDependency */, 402 | 6538B583210EEBDE008688B9 /* PBXTargetDependency */, 403 | ); 404 | name = RealmWrapperTests; 405 | productName = RealmWrapperTests; 406 | productReference = 6551D12420E78A12004DB378 /* RealmWrapperTests.xctest */; 407 | productType = "com.apple.product-type.bundle.unit-test"; 408 | }; 409 | /* End PBXNativeTarget section */ 410 | 411 | /* Begin PBXProject section */ 412 | 6551D11220E78A12004DB378 /* Project object */ = { 413 | isa = PBXProject; 414 | attributes = { 415 | LastSwiftUpdateCheck = 0940; 416 | LastUpgradeCheck = 1000; 417 | ORGANIZATIONNAME = "k-lpmg"; 418 | TargetAttributes = { 419 | 6507D61620E798DA00A31564 = { 420 | CreatedOnToolsVersion = 9.4; 421 | }; 422 | 6551D11A20E78A12004DB378 = { 423 | CreatedOnToolsVersion = 9.4; 424 | LastSwiftMigration = 0940; 425 | }; 426 | 6551D12320E78A12004DB378 = { 427 | CreatedOnToolsVersion = 9.4; 428 | TestTargetID = 6507D61620E798DA00A31564; 429 | }; 430 | }; 431 | }; 432 | buildConfigurationList = 6551D11520E78A12004DB378 /* Build configuration list for PBXProject "RealmWrapper" */; 433 | compatibilityVersion = "Xcode 9.3"; 434 | developmentRegion = en; 435 | hasScannedForEncodings = 0; 436 | knownRegions = ( 437 | en, 438 | Base, 439 | ); 440 | mainGroup = 6551D11120E78A12004DB378; 441 | productRefGroup = 6551D11C20E78A12004DB378 /* Products */; 442 | projectDirPath = ""; 443 | projectRoot = ""; 444 | targets = ( 445 | 6551D11A20E78A12004DB378 /* RealmWrapper */, 446 | 6507D61620E798DA00A31564 /* RealmWrapperExample */, 447 | 6551D12320E78A12004DB378 /* RealmWrapperTests */, 448 | ); 449 | }; 450 | /* End PBXProject section */ 451 | 452 | /* Begin PBXResourcesBuildPhase section */ 453 | 6507D61520E798DA00A31564 /* Resources */ = { 454 | isa = PBXResourcesBuildPhase; 455 | buildActionMask = 2147483647; 456 | files = ( 457 | 652783C220E7A5AA00F067B6 /* Assets.xcassets in Resources */, 458 | 652783C320E7A5AA00F067B6 /* LaunchScreen.storyboard in Resources */, 459 | ); 460 | runOnlyForDeploymentPostprocessing = 0; 461 | }; 462 | 6551D11920E78A12004DB378 /* Resources */ = { 463 | isa = PBXResourcesBuildPhase; 464 | buildActionMask = 2147483647; 465 | files = ( 466 | 65104393210C430B00A5CF9D /* README.md in Resources */, 467 | ); 468 | runOnlyForDeploymentPostprocessing = 0; 469 | }; 470 | 6551D12220E78A12004DB378 /* Resources */ = { 471 | isa = PBXResourcesBuildPhase; 472 | buildActionMask = 2147483647; 473 | files = ( 474 | ); 475 | runOnlyForDeploymentPostprocessing = 0; 476 | }; 477 | /* End PBXResourcesBuildPhase section */ 478 | 479 | /* Begin PBXShellScriptBuildPhase section */ 480 | 6592FCD021006FD500D8AFE9 /* [CT] Carthage Script */ = { 481 | isa = PBXShellScriptBuildPhase; 482 | buildActionMask = 2147483647; 483 | files = ( 484 | ); 485 | inputPaths = ( 486 | "${SRCROOT}/Carthage/Build/iOS/Realm.framework", 487 | "${SRCROOT}/Carthage/Build/iOS/RealmSwift.framework", 488 | ); 489 | name = "[CT] Carthage Script"; 490 | outputPaths = ( 491 | ); 492 | runOnlyForDeploymentPostprocessing = 0; 493 | shellPath = /bin/sh; 494 | shellScript = "/usr/local/bin/carthage copy-frameworks\n"; 495 | }; 496 | 6592FCD1210070DF00D8AFE9 /* [CT] Carthage Script */ = { 497 | isa = PBXShellScriptBuildPhase; 498 | buildActionMask = 2147483647; 499 | files = ( 500 | ); 501 | inputPaths = ( 502 | "${SRCROOT}/Carthage/Build/iOS/Realm.framework", 503 | "${SRCROOT}/Carthage/Build/iOS/RealmSwift.framework", 504 | ); 505 | name = "[CT] Carthage Script"; 506 | outputPaths = ( 507 | ); 508 | runOnlyForDeploymentPostprocessing = 0; 509 | shellPath = /bin/sh; 510 | shellScript = "/usr/local/bin/carthage copy-frameworks"; 511 | }; 512 | /* End PBXShellScriptBuildPhase section */ 513 | 514 | /* Begin PBXSourcesBuildPhase section */ 515 | 6507D61320E798DA00A31564 /* Sources */ = { 516 | isa = PBXSourcesBuildPhase; 517 | buildActionMask = 2147483647; 518 | files = ( 519 | 652783DF20E7A6FB00F067B6 /* EditViewController.swift in Sources */, 520 | 652783F420E7A9CC00F067B6 /* UserRealmProxy.swift in Sources */, 521 | 652783EA20E7A90400F067B6 /* UITableView-Extension.swift in Sources */, 522 | 652783F220E7A9BC00F067B6 /* UserInMemoryRealmProxy.swift in Sources */, 523 | 652783F020E7A95500F067B6 /* UserTableViewCell.swift in Sources */, 524 | 652783EC20E7A93000F067B6 /* InMemoryRealmManager.swift in Sources */, 525 | 652783CE20E7A5AA00F067B6 /* AppDelegate.swift in Sources */, 526 | 652783EE20E7A93900F067B6 /* UserRealmManager.swift in Sources */, 527 | 65E54907221D465900074FAD /* UIViewController-Extension.swift in Sources */, 528 | 65E54905221D462800074FAD /* MultipleAddViewController.swift in Sources */, 529 | 652F13B620E7EE1B00C138DA /* User.swift in Sources */, 530 | 65E54912221D611700074FAD /* AppNavigator.swift in Sources */, 531 | 652783E820E7A8FA00F067B6 /* ClassName-Extension.swift in Sources */, 532 | 652783DB20E7A6EB00F067B6 /* TableViewController.swift in Sources */, 533 | 652783DD20E7A6F400F067B6 /* SingleAddViewController.swift in Sources */, 534 | ); 535 | runOnlyForDeploymentPostprocessing = 0; 536 | }; 537 | 6551D11620E78A12004DB378 /* Sources */ = { 538 | isa = PBXSourcesBuildPhase; 539 | buildActionMask = 2147483647; 540 | files = ( 541 | 65F564B721E4ED7100C4860F /* RealmManageable.swift in Sources */, 542 | 65F564B621E4ED7100C4860F /* RealmQuery.swift in Sources */, 543 | 65F564B821E4ED7100C4860F /* RealmProxiable.swift in Sources */, 544 | ); 545 | runOnlyForDeploymentPostprocessing = 0; 546 | }; 547 | 6551D12020E78A12004DB378 /* Sources */ = { 548 | isa = PBXSourcesBuildPhase; 549 | buildActionMask = 2147483647; 550 | files = ( 551 | 0AC388A322EC4A77005EF115 /* RealmTransactionTests.swift in Sources */, 552 | 0AC3889D22EC4960005EF115 /* MockUserInMemoryRealmProxy.swift in Sources */, 553 | 0AC3889922EC4932005EF115 /* MockInMemoryRealmManager.swift in Sources */, 554 | 0AC3889B22EC493B005EF115 /* MockUserRealmManager.swift in Sources */, 555 | 0AC3889722EC4918005EF115 /* MockUser.swift in Sources */, 556 | 0AC388A122EC4A4A005EF115 /* RealmNotificationTests.swift in Sources */, 557 | 0AC3889122EC432A005EF115 /* RealmCRUDTests.swift in Sources */, 558 | 0AC3889F22EC496B005EF115 /* MockUserRealmProxy.swift in Sources */, 559 | ); 560 | runOnlyForDeploymentPostprocessing = 0; 561 | }; 562 | /* End PBXSourcesBuildPhase section */ 563 | 564 | /* Begin PBXTargetDependency section */ 565 | 6538B583210EEBDE008688B9 /* PBXTargetDependency */ = { 566 | isa = PBXTargetDependency; 567 | target = 6507D61620E798DA00A31564 /* RealmWrapperExample */; 568 | targetProxy = 6538B582210EEBDE008688B9 /* PBXContainerItemProxy */; 569 | }; 570 | 6551D12720E78A12004DB378 /* PBXTargetDependency */ = { 571 | isa = PBXTargetDependency; 572 | target = 6551D11A20E78A12004DB378 /* RealmWrapper */; 573 | targetProxy = 6551D12620E78A12004DB378 /* PBXContainerItemProxy */; 574 | }; 575 | /* End PBXTargetDependency section */ 576 | 577 | /* Begin PBXVariantGroup section */ 578 | 652783A920E7A5AA00F067B6 /* LaunchScreen.storyboard */ = { 579 | isa = PBXVariantGroup; 580 | children = ( 581 | 652783AA20E7A5AA00F067B6 /* Base */, 582 | ); 583 | name = LaunchScreen.storyboard; 584 | sourceTree = ""; 585 | }; 586 | /* End PBXVariantGroup section */ 587 | 588 | /* Begin XCBuildConfiguration section */ 589 | 6507D62620E798DB00A31564 /* Debug */ = { 590 | isa = XCBuildConfiguration; 591 | buildSettings = { 592 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 593 | CODE_SIGN_IDENTITY = ""; 594 | CODE_SIGN_STYLE = Manual; 595 | DEVELOPMENT_TEAM = ""; 596 | FRAMEWORK_SEARCH_PATHS = ( 597 | "$(inherited)", 598 | "$(PROJECT_DIR)/Carthage/Build/iOS", 599 | ); 600 | INFOPLIST_FILE = "$(SRCROOT)/Example/Support/Info.plist"; 601 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 602 | LD_RUNPATH_SEARCH_PATHS = ( 603 | "$(inherited)", 604 | "@executable_path/Frameworks", 605 | ); 606 | PRODUCT_BUNDLE_IDENTIFIER = "com.k-lpmg.RealmWrapperExample"; 607 | PRODUCT_NAME = "$(TARGET_NAME)"; 608 | PROVISIONING_PROFILE_SPECIFIER = ""; 609 | TARGETED_DEVICE_FAMILY = "1,2"; 610 | }; 611 | name = Debug; 612 | }; 613 | 6507D62720E798DB00A31564 /* Release */ = { 614 | isa = XCBuildConfiguration; 615 | buildSettings = { 616 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 617 | CODE_SIGN_IDENTITY = ""; 618 | CODE_SIGN_STYLE = Manual; 619 | DEVELOPMENT_TEAM = ""; 620 | FRAMEWORK_SEARCH_PATHS = ( 621 | "$(inherited)", 622 | "$(PROJECT_DIR)/Carthage/Build/iOS", 623 | ); 624 | INFOPLIST_FILE = "$(SRCROOT)/Example/Support/Info.plist"; 625 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 626 | LD_RUNPATH_SEARCH_PATHS = ( 627 | "$(inherited)", 628 | "@executable_path/Frameworks", 629 | ); 630 | PRODUCT_BUNDLE_IDENTIFIER = "com.k-lpmg.RealmWrapperExample"; 631 | PRODUCT_NAME = "$(TARGET_NAME)"; 632 | PROVISIONING_PROFILE_SPECIFIER = ""; 633 | TARGETED_DEVICE_FAMILY = "1,2"; 634 | }; 635 | name = Release; 636 | }; 637 | 6551D12D20E78A12004DB378 /* Debug */ = { 638 | isa = XCBuildConfiguration; 639 | buildSettings = { 640 | ALWAYS_SEARCH_USER_PATHS = NO; 641 | CLANG_ANALYZER_NONNULL = YES; 642 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 643 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 644 | CLANG_CXX_LIBRARY = "libc++"; 645 | CLANG_ENABLE_MODULES = YES; 646 | CLANG_ENABLE_OBJC_ARC = YES; 647 | CLANG_ENABLE_OBJC_WEAK = YES; 648 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 649 | CLANG_WARN_BOOL_CONVERSION = YES; 650 | CLANG_WARN_COMMA = YES; 651 | CLANG_WARN_CONSTANT_CONVERSION = YES; 652 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 653 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 654 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 655 | CLANG_WARN_EMPTY_BODY = YES; 656 | CLANG_WARN_ENUM_CONVERSION = YES; 657 | CLANG_WARN_INFINITE_RECURSION = YES; 658 | CLANG_WARN_INT_CONVERSION = YES; 659 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 660 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 661 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 662 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 663 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 664 | CLANG_WARN_STRICT_PROTOTYPES = YES; 665 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 666 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 667 | CLANG_WARN_UNREACHABLE_CODE = YES; 668 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 669 | CODE_SIGN_IDENTITY = "iPhone Developer"; 670 | COPY_PHASE_STRIP = NO; 671 | CURRENT_PROJECT_VERSION = 1; 672 | DEBUG_INFORMATION_FORMAT = dwarf; 673 | ENABLE_STRICT_OBJC_MSGSEND = YES; 674 | ENABLE_TESTABILITY = YES; 675 | GCC_C_LANGUAGE_STANDARD = gnu11; 676 | GCC_DYNAMIC_NO_PIC = NO; 677 | GCC_NO_COMMON_BLOCKS = YES; 678 | GCC_OPTIMIZATION_LEVEL = 0; 679 | GCC_PREPROCESSOR_DEFINITIONS = ( 680 | "DEBUG=1", 681 | "$(inherited)", 682 | ); 683 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 684 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 685 | GCC_WARN_UNDECLARED_SELECTOR = YES; 686 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 687 | GCC_WARN_UNUSED_FUNCTION = YES; 688 | GCC_WARN_UNUSED_VARIABLE = YES; 689 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 690 | MTL_ENABLE_DEBUG_INFO = YES; 691 | ONLY_ACTIVE_ARCH = YES; 692 | SDKROOT = iphoneos; 693 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 694 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 695 | SWIFT_VERSION = 5.0; 696 | VERSIONING_SYSTEM = "apple-generic"; 697 | VERSION_INFO_PREFIX = ""; 698 | }; 699 | name = Debug; 700 | }; 701 | 6551D12E20E78A12004DB378 /* Release */ = { 702 | isa = XCBuildConfiguration; 703 | buildSettings = { 704 | ALWAYS_SEARCH_USER_PATHS = NO; 705 | CLANG_ANALYZER_NONNULL = YES; 706 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 707 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 708 | CLANG_CXX_LIBRARY = "libc++"; 709 | CLANG_ENABLE_MODULES = YES; 710 | CLANG_ENABLE_OBJC_ARC = YES; 711 | CLANG_ENABLE_OBJC_WEAK = YES; 712 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 713 | CLANG_WARN_BOOL_CONVERSION = YES; 714 | CLANG_WARN_COMMA = YES; 715 | CLANG_WARN_CONSTANT_CONVERSION = YES; 716 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 717 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 718 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 719 | CLANG_WARN_EMPTY_BODY = YES; 720 | CLANG_WARN_ENUM_CONVERSION = YES; 721 | CLANG_WARN_INFINITE_RECURSION = YES; 722 | CLANG_WARN_INT_CONVERSION = YES; 723 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 724 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 725 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 726 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 727 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 728 | CLANG_WARN_STRICT_PROTOTYPES = YES; 729 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 730 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 731 | CLANG_WARN_UNREACHABLE_CODE = YES; 732 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 733 | CODE_SIGN_IDENTITY = "iPhone Developer"; 734 | COPY_PHASE_STRIP = NO; 735 | CURRENT_PROJECT_VERSION = 1; 736 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 737 | ENABLE_NS_ASSERTIONS = NO; 738 | ENABLE_STRICT_OBJC_MSGSEND = YES; 739 | GCC_C_LANGUAGE_STANDARD = gnu11; 740 | GCC_NO_COMMON_BLOCKS = YES; 741 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 742 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 743 | GCC_WARN_UNDECLARED_SELECTOR = YES; 744 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 745 | GCC_WARN_UNUSED_FUNCTION = YES; 746 | GCC_WARN_UNUSED_VARIABLE = YES; 747 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 748 | MTL_ENABLE_DEBUG_INFO = NO; 749 | SDKROOT = iphoneos; 750 | SWIFT_COMPILATION_MODE = wholemodule; 751 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 752 | SWIFT_VERSION = 5.0; 753 | VALIDATE_PRODUCT = YES; 754 | VERSIONING_SYSTEM = "apple-generic"; 755 | VERSION_INFO_PREFIX = ""; 756 | }; 757 | name = Release; 758 | }; 759 | 6551D13020E78A12004DB378 /* Debug */ = { 760 | isa = XCBuildConfiguration; 761 | buildSettings = { 762 | CLANG_ENABLE_MODULES = YES; 763 | CODE_SIGN_IDENTITY = ""; 764 | CODE_SIGN_STYLE = Manual; 765 | DEFINES_MODULE = YES; 766 | DEVELOPMENT_TEAM = ""; 767 | DYLIB_COMPATIBILITY_VERSION = 1; 768 | DYLIB_CURRENT_VERSION = 1; 769 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 770 | FRAMEWORK_SEARCH_PATHS = ( 771 | "$(inherited)", 772 | "$(PROJECT_DIR)/Carthage/Build/iOS", 773 | ); 774 | INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; 775 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 776 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 777 | LD_RUNPATH_SEARCH_PATHS = ( 778 | "$(inherited)", 779 | "@executable_path/Frameworks", 780 | "@loader_path/Frameworks", 781 | ); 782 | PRODUCT_BUNDLE_IDENTIFIER = "com.k-lpmg.RealmWrapper"; 783 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 784 | PROVISIONING_PROFILE_SPECIFIER = ""; 785 | SKIP_INSTALL = YES; 786 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 787 | TARGETED_DEVICE_FAMILY = "1,2"; 788 | }; 789 | name = Debug; 790 | }; 791 | 6551D13120E78A12004DB378 /* Release */ = { 792 | isa = XCBuildConfiguration; 793 | buildSettings = { 794 | CLANG_ENABLE_MODULES = YES; 795 | CODE_SIGN_IDENTITY = ""; 796 | CODE_SIGN_STYLE = Manual; 797 | DEFINES_MODULE = YES; 798 | DEVELOPMENT_TEAM = ""; 799 | DYLIB_COMPATIBILITY_VERSION = 1; 800 | DYLIB_CURRENT_VERSION = 1; 801 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 802 | FRAMEWORK_SEARCH_PATHS = ( 803 | "$(inherited)", 804 | "$(PROJECT_DIR)/Carthage/Build/iOS", 805 | ); 806 | INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; 807 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 808 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 809 | LD_RUNPATH_SEARCH_PATHS = ( 810 | "$(inherited)", 811 | "@executable_path/Frameworks", 812 | "@loader_path/Frameworks", 813 | ); 814 | PRODUCT_BUNDLE_IDENTIFIER = "com.k-lpmg.RealmWrapper"; 815 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 816 | PROVISIONING_PROFILE_SPECIFIER = ""; 817 | SKIP_INSTALL = YES; 818 | TARGETED_DEVICE_FAMILY = "1,2"; 819 | }; 820 | name = Release; 821 | }; 822 | 6551D13320E78A12004DB378 /* Debug */ = { 823 | isa = XCBuildConfiguration; 824 | buildSettings = { 825 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 826 | BUNDLE_LOADER = "$(TEST_HOST)"; 827 | CODE_SIGN_IDENTITY = ""; 828 | CODE_SIGN_STYLE = Manual; 829 | DEVELOPMENT_TEAM = ""; 830 | FRAMEWORK_SEARCH_PATHS = ( 831 | "$(inherited)", 832 | "$(PROJECT_DIR)/Carthage/Build/iOS", 833 | ); 834 | INFOPLIST_FILE = Tests/Info.plist; 835 | LD_RUNPATH_SEARCH_PATHS = ( 836 | "$(inherited)", 837 | "@executable_path/Frameworks", 838 | "@loader_path/Frameworks", 839 | ); 840 | PRODUCT_BUNDLE_IDENTIFIER = "k-lpmg.RealmWrapperTests"; 841 | PRODUCT_NAME = "$(TARGET_NAME)"; 842 | PROVISIONING_PROFILE_SPECIFIER = ""; 843 | TARGETED_DEVICE_FAMILY = "1,2"; 844 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/RealmWrapperExample.app/RealmWrapperExample"; 845 | }; 846 | name = Debug; 847 | }; 848 | 6551D13420E78A12004DB378 /* Release */ = { 849 | isa = XCBuildConfiguration; 850 | buildSettings = { 851 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 852 | BUNDLE_LOADER = "$(TEST_HOST)"; 853 | CODE_SIGN_IDENTITY = ""; 854 | CODE_SIGN_STYLE = Manual; 855 | DEVELOPMENT_TEAM = ""; 856 | FRAMEWORK_SEARCH_PATHS = ( 857 | "$(inherited)", 858 | "$(PROJECT_DIR)/Carthage/Build/iOS", 859 | ); 860 | INFOPLIST_FILE = Tests/Info.plist; 861 | LD_RUNPATH_SEARCH_PATHS = ( 862 | "$(inherited)", 863 | "@executable_path/Frameworks", 864 | "@loader_path/Frameworks", 865 | ); 866 | PRODUCT_BUNDLE_IDENTIFIER = "k-lpmg.RealmWrapperTests"; 867 | PRODUCT_NAME = "$(TARGET_NAME)"; 868 | PROVISIONING_PROFILE_SPECIFIER = ""; 869 | TARGETED_DEVICE_FAMILY = "1,2"; 870 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/RealmWrapperExample.app/RealmWrapperExample"; 871 | }; 872 | name = Release; 873 | }; 874 | /* End XCBuildConfiguration section */ 875 | 876 | /* Begin XCConfigurationList section */ 877 | 6507D62820E798DB00A31564 /* Build configuration list for PBXNativeTarget "RealmWrapperExample" */ = { 878 | isa = XCConfigurationList; 879 | buildConfigurations = ( 880 | 6507D62620E798DB00A31564 /* Debug */, 881 | 6507D62720E798DB00A31564 /* Release */, 882 | ); 883 | defaultConfigurationIsVisible = 0; 884 | defaultConfigurationName = Release; 885 | }; 886 | 6551D11520E78A12004DB378 /* Build configuration list for PBXProject "RealmWrapper" */ = { 887 | isa = XCConfigurationList; 888 | buildConfigurations = ( 889 | 6551D12D20E78A12004DB378 /* Debug */, 890 | 6551D12E20E78A12004DB378 /* Release */, 891 | ); 892 | defaultConfigurationIsVisible = 0; 893 | defaultConfigurationName = Release; 894 | }; 895 | 6551D12F20E78A12004DB378 /* Build configuration list for PBXNativeTarget "RealmWrapper" */ = { 896 | isa = XCConfigurationList; 897 | buildConfigurations = ( 898 | 6551D13020E78A12004DB378 /* Debug */, 899 | 6551D13120E78A12004DB378 /* Release */, 900 | ); 901 | defaultConfigurationIsVisible = 0; 902 | defaultConfigurationName = Release; 903 | }; 904 | 6551D13220E78A12004DB378 /* Build configuration list for PBXNativeTarget "RealmWrapperTests" */ = { 905 | isa = XCConfigurationList; 906 | buildConfigurations = ( 907 | 6551D13320E78A12004DB378 /* Debug */, 908 | 6551D13420E78A12004DB378 /* Release */, 909 | ); 910 | defaultConfigurationIsVisible = 0; 911 | defaultConfigurationName = Release; 912 | }; 913 | /* End XCConfigurationList section */ 914 | }; 915 | rootObject = 6551D11220E78A12004DB378 /* Project object */; 916 | } 917 | --------------------------------------------------------------------------------