├── BetterCoreData ├── BetterCoreData │ ├── Assets.xcassets │ │ ├── Contents.json │ │ └── AppIcon.appiconset │ │ │ ├── Icon-60@2x.png │ │ │ ├── Icon-60@3x.png │ │ │ ├── Icon-Small@2x.png │ │ │ ├── Icon-Small@3x.png │ │ │ ├── Icon-Spotlight-40@2x.png │ │ │ ├── Icon-Spotlight-40@3x.png │ │ │ └── Contents.json │ ├── ManagedObjectConvertible.swift │ ├── User.swift │ ├── UserMO+CoreDataProperties.swift │ ├── UserMO+CoreDataClass.swift │ ├── DataModel.xcdatamodeld │ │ └── DataModel.xcdatamodel │ │ │ └── contents │ ├── Info.plist │ ├── NewUserDataController.swift │ ├── UserDataController.swift │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── CoreDataStack.swift │ ├── ManagedObjectProtocol.swift │ ├── AppDelegate.swift │ ├── CoreDataWorker2.swift │ ├── CoreDataWorker1.swift │ └── ViewController.swift └── BetterCoreData.xcodeproj │ ├── project.xcworkspace │ └── contents.xcworkspacedata │ ├── xcuserdata │ └── michal.xcuserdatad │ │ └── xcschemes │ │ ├── xcschememanagement.plist │ │ └── BetterCoreData.xcscheme │ └── project.pbxproj ├── README.md └── .gitignore /BetterCoreData/BetterCoreData/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /BetterCoreData/BetterCoreData/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftingio/BetterSwifterCoreData/HEAD/BetterCoreData/BetterCoreData/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png -------------------------------------------------------------------------------- /BetterCoreData/BetterCoreData/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftingio/BetterSwifterCoreData/HEAD/BetterCoreData/BetterCoreData/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png -------------------------------------------------------------------------------- /BetterCoreData/BetterCoreData/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftingio/BetterSwifterCoreData/HEAD/BetterCoreData/BetterCoreData/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png -------------------------------------------------------------------------------- /BetterCoreData/BetterCoreData/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftingio/BetterSwifterCoreData/HEAD/BetterCoreData/BetterCoreData/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png -------------------------------------------------------------------------------- /BetterCoreData/BetterCoreData/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftingio/BetterSwifterCoreData/HEAD/BetterCoreData/BetterCoreData/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-40@2x.png -------------------------------------------------------------------------------- /BetterCoreData/BetterCoreData/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/swiftingio/BetterSwifterCoreData/HEAD/BetterCoreData/BetterCoreData/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-40@3x.png -------------------------------------------------------------------------------- /BetterCoreData/BetterCoreData.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /BetterCoreData/BetterCoreData/ManagedObjectConvertible.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ManagedObjectConvertible.swift 3 | // Created by swifting.io Team 4 | // 5 | 6 | import CoreData 7 | 8 | protocol ManagedObjectConvertible { 9 | associatedtype ManagedObject: NSManagedObject, ManagedObjectProtocol 10 | func toManagedObject(in context: NSManagedObjectContext) -> ManagedObject? 11 | } 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BetterSwifterCoreData 2 | Sample project for model structs and their conversion to Core Data entities. 3 | 4 | Refer Companion Blog: 5 | - [#25 Core Data in iOS10: NSPersistentContainer](https://swifting.io/blog/2016/09/25/25-core-data-in-ios10-nspersistentcontainer/) 6 | - [#28 Better CoreData with Swift Generics](https://swifting.io/blog/2016/09/25/25-core-data-in-ios10-nspersistentcontainer/) 7 | -------------------------------------------------------------------------------- /BetterCoreData/BetterCoreData/User.swift: -------------------------------------------------------------------------------- 1 | // 2 | // User.swift 3 | // BetterCoreData 4 | // 5 | // Created by Michal Wojtysiak on 23/11/2016. 6 | // Copyright © 2016 Michal Wojtysiak. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct User { 12 | let id: String 13 | var username: String? 14 | var name: String? 15 | var birthday: Date? 16 | } 17 | 18 | extension User { 19 | init(id: String){ 20 | self.id = id 21 | self.username = nil 22 | self.name = nil 23 | self.birthday = Date() 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /BetterCoreData/BetterCoreData.xcodeproj/xcuserdata/michal.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | BetterCoreData.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 90C7682C1DE61BD100EF2926 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /BetterCoreData/BetterCoreData/UserMO+CoreDataProperties.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserMO+CoreDataProperties.swift 3 | // BetterCoreData 4 | // 5 | // Created by Michal Wojtysiak on 23/11/2016. 6 | // Copyright © 2016 Michal Wojtysiak. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import CoreData 11 | 12 | 13 | extension UserMO { 14 | 15 | @nonobjc public class func fetchRequest() -> NSFetchRequest { 16 | return NSFetchRequest(entityName: "UserMO"); 17 | } 18 | 19 | @NSManaged public var birthday: NSDate? 20 | @NSManaged public var identifier: String 21 | @NSManaged public var name: String? 22 | @NSManaged public var username: String? 23 | 24 | } 25 | -------------------------------------------------------------------------------- /BetterCoreData/BetterCoreData/UserMO+CoreDataClass.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserMO+CoreDataClass.swift 3 | // BetterCoreData 4 | // 5 | // Created by Michal Wojtysiak on 23/11/2016. 6 | // Copyright © 2016 Michal Wojtysiak. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import CoreData 11 | 12 | 13 | public class UserMO: NSManagedObject {} 14 | 15 | extension UserMO: ManagedObjectProtocol { 16 | func toEntity() -> User? { 17 | return User(id: identifier, username: username, name: name, birthday: birthday as Date?) 18 | } 19 | } 20 | 21 | extension User: ManagedObjectConvertible { 22 | func toManagedObject(in context: NSManagedObjectContext) -> UserMO? { 23 | let user = UserMO.getOrCreateSingle(with: id, from: context) 24 | user.birthday = birthday as NSDate? 25 | user.name = name 26 | user.username = username 27 | return user 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /BetterCoreData/BetterCoreData/DataModel.xcdatamodeld/DataModel.xcdatamodel/contents: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /BetterCoreData/BetterCoreData/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 | "size" : "29x29", 15 | "idiom" : "iphone", 16 | "filename" : "Icon-Small@2x.png", 17 | "scale" : "2x" 18 | }, 19 | { 20 | "size" : "29x29", 21 | "idiom" : "iphone", 22 | "filename" : "Icon-Small@3x.png", 23 | "scale" : "3x" 24 | }, 25 | { 26 | "size" : "40x40", 27 | "idiom" : "iphone", 28 | "filename" : "Icon-Spotlight-40@2x.png", 29 | "scale" : "2x" 30 | }, 31 | { 32 | "size" : "40x40", 33 | "idiom" : "iphone", 34 | "filename" : "Icon-Spotlight-40@3x.png", 35 | "scale" : "3x" 36 | }, 37 | { 38 | "size" : "60x60", 39 | "idiom" : "iphone", 40 | "filename" : "Icon-60@2x.png", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "size" : "60x60", 45 | "idiom" : "iphone", 46 | "filename" : "Icon-60@3x.png", 47 | "scale" : "3x" 48 | } 49 | ], 50 | "info" : { 51 | "version" : 1, 52 | "author" : "xcode" 53 | } 54 | } -------------------------------------------------------------------------------- /BetterCoreData/BetterCoreData/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 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.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /BetterCoreData/BetterCoreData/NewUserDataController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserDataController.swift 3 | // BetterCoreData 4 | // 5 | // Created by Michal Wojtysiak on 25/11/2016. 6 | // Copyright © 2016 Michal Wojtysiak. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class NewUserDataController: UserDataControllerProtocol { 12 | let worker: NewCoreDataWorkerProtocol 13 | private var currentUser: User? 14 | 15 | init(worker: NewCoreDataWorkerProtocol = NewCoreDataWorker()){ 16 | self.worker = worker 17 | } 18 | 19 | func fetchUser(completion: @escaping (User?) -> Void) { 20 | worker.get{ [weak self](result: Result<[User], Error>) in 21 | switch result { 22 | case .success(let users): 23 | self?.currentUser = users.first 24 | completion(users.first) 25 | case .failure(let error): 26 | print("\(error)") 27 | completion(nil) 28 | } 29 | } 30 | } 31 | 32 | func updateUser(name: String?, username: String?){ 33 | var user: User = currentUser ?? User(id: UUID().uuidString) 34 | user.name = name 35 | user.username = username 36 | worker.upsert(entities: [user]){ (error) in 37 | guard let error = error else { return } 38 | print("\(error)") 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /BetterCoreData/BetterCoreData/UserDataController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserDataController.swift 3 | // BetterCoreData 4 | // 5 | // Created by Michal Wojtysiak on 26/11/2016. 6 | // Copyright © 2016 Michal Wojtysiak. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol UserDataControllerProtocol { 12 | func fetchUser(completion: @escaping (User?) -> Void) 13 | func updateUser(name: String?, username: String?) 14 | } 15 | 16 | class UserDataController: UserDataControllerProtocol { 17 | let worker: CoreDataWorker 18 | private var currentUser: User? 19 | 20 | init(worker: CoreDataWorker = CoreDataWorker()){ 21 | self.worker = worker 22 | } 23 | 24 | func fetchUser(completion: @escaping (User?) -> Void) { 25 | worker.get{ [weak self](result: Result<[User], Error>) in 26 | switch result { 27 | case .success(let users): 28 | self?.currentUser = users.first 29 | completion(users.first) 30 | case .failure(let error): 31 | print("\(error)") 32 | completion(nil) 33 | } 34 | } 35 | } 36 | 37 | func updateUser(name: String?, username: String?){ 38 | var user: User = currentUser ?? User(id: UUID().uuidString) 39 | user.name = name 40 | user.username = username 41 | worker.upsert(entities: [user]){ (error) in 42 | guard let error = error else { return } 43 | print("\(error)") 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xcuserstate 23 | 24 | ## Obj-C/Swift specific 25 | *.hmap 26 | *.ipa 27 | *.dSYM.zip 28 | *.dSYM 29 | 30 | ## Playgrounds 31 | timeline.xctimeline 32 | playground.xcworkspace 33 | 34 | # Swift Package Manager 35 | # 36 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 37 | # Packages/ 38 | .build/ 39 | 40 | # CocoaPods 41 | # 42 | # We recommend against adding the Pods directory to your .gitignore. However 43 | # you should judge for yourself, the pros and cons are mentioned at: 44 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 45 | # 46 | # Pods/ 47 | 48 | # Carthage 49 | # 50 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 51 | # Carthage/Checkouts 52 | 53 | Carthage/Build 54 | 55 | # fastlane 56 | # 57 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 58 | # screenshots whenever they are needed. 59 | # For more information about the recommended setup visit: 60 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 61 | 62 | fastlane/report.xml 63 | fastlane/Preview.html 64 | fastlane/screenshots 65 | fastlane/test_output 66 | -------------------------------------------------------------------------------- /BetterCoreData/BetterCoreData/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 | -------------------------------------------------------------------------------- /BetterCoreData/BetterCoreData/Base.lproj/Main.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 | -------------------------------------------------------------------------------- /BetterCoreData/BetterCoreData/CoreDataStack.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CoreDataStack.swift 3 | // BetterCoreData 4 | // 5 | // Created by Michal Wojtysiak on 23/11/2016. 6 | // Copyright © 2016 Michal Wojtysiak. All rights reserved. 7 | // 8 | 9 | import CoreData 10 | 11 | protocol CoreDataServiceProtocol:class { 12 | var errorHandler: (Error) -> Void {get set} 13 | var persistentContainer: NSPersistentContainer {get} 14 | var viewContext: NSManagedObjectContext {get} 15 | var backgroundContext: NSManagedObjectContext {get} 16 | func performBackgroundTask(_ block: @escaping (NSManagedObjectContext) -> Void) 17 | func performForegroundTask(_ block: @escaping (NSManagedObjectContext) -> Void) 18 | } 19 | 20 | final class CoreDataService: CoreDataServiceProtocol { 21 | 22 | static let shared = CoreDataService() 23 | var errorHandler: (Error) -> Void = {_ in } 24 | 25 | lazy var persistentContainer: NSPersistentContainer = { 26 | let container = NSPersistentContainer(name: "DataModel") 27 | container.loadPersistentStores(completionHandler: { [weak self](storeDescription, error) in 28 | if let error = error { 29 | NSLog("CoreData error \(error), \(String(describing: error._userInfo))") 30 | self?.errorHandler(error) 31 | } 32 | }) 33 | return container 34 | }() 35 | 36 | lazy var viewContext: NSManagedObjectContext = { 37 | let context:NSManagedObjectContext = self.persistentContainer.viewContext 38 | context.automaticallyMergesChangesFromParent = true 39 | return context 40 | }() 41 | 42 | lazy var backgroundContext: NSManagedObjectContext = { 43 | return self.persistentContainer.newBackgroundContext() 44 | }() 45 | 46 | func performForegroundTask(_ block: @escaping (NSManagedObjectContext) -> Void) { 47 | self.viewContext.perform { 48 | block(self.viewContext) 49 | } 50 | } 51 | 52 | func performBackgroundTask(_ block: @escaping (NSManagedObjectContext) -> Void) { 53 | self.persistentContainer.performBackgroundTask(block) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /BetterCoreData/BetterCoreData/ManagedObjectProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ManagedObjectProtocol.swift 3 | // Created by swifting.io Team 4 | // 5 | 6 | import CoreData 7 | 8 | protocol ManagedObjectProtocol { 9 | associatedtype Entity 10 | func toEntity() -> Entity? 11 | } 12 | 13 | extension ManagedObjectProtocol where Self: NSManagedObject { 14 | 15 | static func getOrCreateSingle(with id: String, from context: NSManagedObjectContext) -> Self { 16 | let result = single(with: id, from: context) ?? insertNew(in: context) 17 | result.setValue(id, forKey: "identifier") 18 | return result 19 | } 20 | 21 | static func single(from context: NSManagedObjectContext, with predicate: NSPredicate?, 22 | sortDescriptors: [NSSortDescriptor]?) -> Self? { 23 | return fetch(from: context, with: predicate, 24 | sortDescriptors: sortDescriptors, fetchLimit: 1)?.first 25 | } 26 | 27 | static func single(with id: String, from context: NSManagedObjectContext) -> Self? { 28 | let predicate = NSPredicate(format: "identifier == %@", id) 29 | return single(from: context, with: predicate, sortDescriptors: nil) 30 | } 31 | 32 | static func insertNew(in context: NSManagedObjectContext) -> Self { 33 | return Self(context:context) 34 | } 35 | 36 | static func fetch(from context: NSManagedObjectContext, with predicate: NSPredicate?, 37 | sortDescriptors: [NSSortDescriptor]?, fetchLimit: Int?) -> [Self]? { 38 | 39 | let fetchRequest = Self.fetchRequest() 40 | fetchRequest.sortDescriptors = sortDescriptors 41 | fetchRequest.predicate = predicate 42 | fetchRequest.returnsObjectsAsFaults = false 43 | 44 | if let fetchLimit = fetchLimit { 45 | fetchRequest.fetchLimit = fetchLimit 46 | } 47 | 48 | var result: [Self]? 49 | context.performAndWait { () -> Void in 50 | do { 51 | result = try context.fetch(fetchRequest) as? [Self] 52 | } catch { 53 | result = nil 54 | //Report Error 55 | print("CoreData fetch error \(error)") 56 | } 57 | } 58 | return result 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /BetterCoreData/BetterCoreData/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // BetterCoreData 4 | // 5 | // Created by Michal Wojtysiak on 23/11/2016. 6 | // Copyright © 2016 Michal Wojtysiak. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | } 44 | 45 | -------------------------------------------------------------------------------- /BetterCoreData/BetterCoreData/CoreDataWorker2.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CoreDataWorker2.swift 3 | // BetterCoreData 4 | // 5 | // Created by Michal Wojtysiak on 23/11/2016. 6 | // Copyright © 2016 Michal Wojtysiak. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol NewCoreDataWorkerProtocol { 12 | func get 13 | (with predicate: NSPredicate?, 14 | sortDescriptors: [NSSortDescriptor]?, 15 | fetchLimit: Int?, 16 | completion: @escaping (Result<[Entity], Error>) -> Void) 17 | func upsert 18 | (entities: [Entity], 19 | completion: @escaping (Error?) -> Void) 20 | 21 | } 22 | 23 | extension NewCoreDataWorkerProtocol { 24 | func get 25 | (with predicate: NSPredicate? = nil, 26 | sortDescriptors: [NSSortDescriptor]? = nil, 27 | fetchLimit: Int? = nil, 28 | completion: @escaping (Result<[Entity], Error>) -> Void) { 29 | get(with: predicate, 30 | sortDescriptors: sortDescriptors, 31 | fetchLimit: fetchLimit, 32 | completion: completion) 33 | } 34 | } 35 | 36 | 37 | class NewCoreDataWorker: NewCoreDataWorkerProtocol { 38 | let coreData: CoreDataServiceProtocol 39 | 40 | init(coreData: CoreDataServiceProtocol = CoreDataService.shared) { 41 | self.coreData = coreData 42 | } 43 | 44 | func get 45 | (with predicate: NSPredicate?, 46 | sortDescriptors: [NSSortDescriptor]?, 47 | fetchLimit: Int?, 48 | completion: @escaping (Result<[Entity], Error>) -> Void) { 49 | coreData.performForegroundTask { context in 50 | do { 51 | let fetchRequest = Entity.ManagedObject.fetchRequest() 52 | fetchRequest.predicate = predicate 53 | fetchRequest.sortDescriptors = sortDescriptors 54 | if let fetchLimit = fetchLimit { 55 | fetchRequest.fetchLimit = fetchLimit 56 | } 57 | let results = try context.fetch(fetchRequest) as? [Entity.ManagedObject] 58 | let items: [Entity] = results?.compactMap { $0.toEntity() as? Entity } ?? [] 59 | completion(.success(items)) 60 | } catch { 61 | let fetchError = CoreDataWorkerError.cannotFetch("Cannot fetch error: \(error))") 62 | completion(.failure(fetchError)) 63 | } 64 | } 65 | } 66 | 67 | func upsert 68 | (entities: [Entity], 69 | completion: @escaping (Error?) -> Void) { 70 | 71 | coreData.performBackgroundTask { context in 72 | _ = entities.compactMap({ (entity) -> Entity.ManagedObject? in 73 | return entity.toManagedObject(in: context) 74 | }) 75 | do { 76 | try context.save() 77 | completion(nil) 78 | } catch { 79 | completion(CoreDataWorkerError.cannotSave(error)) 80 | } 81 | } 82 | } 83 | } 84 | 85 | -------------------------------------------------------------------------------- /BetterCoreData/BetterCoreData/CoreDataWorker1.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CoreDataWorker1.swift 3 | // BetterCoreData 4 | // 5 | // Created by Michal Wojtysiak on 23/11/2016. 6 | // Copyright © 2016 Michal Wojtysiak. All rights reserved. 7 | // 8 | 9 | import CoreData 10 | 11 | protocol CoreDataWorkerProtocol { 12 | associatedtype EntityType 13 | func get(with predicate: NSPredicate?, 14 | sortDescriptors: [NSSortDescriptor]?, 15 | fetchLimit: Int?, 16 | completion: @escaping (Result<[EntityType], Error>) -> Void) 17 | func upsert(entities: [EntityType], 18 | completion: @escaping (Error?) -> Void) 19 | } 20 | 21 | extension CoreDataWorkerProtocol { 22 | func get(with predicate: NSPredicate? = nil, 23 | sortDescriptors: [NSSortDescriptor]? = nil, 24 | fetchLimit: Int? = nil, 25 | completion: @escaping (Result<[EntityType], Error>) -> Void){ 26 | get(with: predicate, 27 | sortDescriptors: sortDescriptors, 28 | fetchLimit: fetchLimit, 29 | completion: completion) 30 | } 31 | } 32 | 33 | enum CoreDataWorkerError: Error{ 34 | case cannotFetch(String) 35 | case cannotSave(Error) 36 | } 37 | 38 | class CoreDataWorker: CoreDataWorkerProtocol where 39 | ManagedEntity: NSManagedObject, 40 | ManagedEntity: ManagedObjectProtocol, 41 | Entity: ManagedObjectConvertible { 42 | 43 | let coreData: CoreDataServiceProtocol 44 | init(coreData: CoreDataServiceProtocol = CoreDataService.shared) { 45 | self.coreData = coreData 46 | } 47 | 48 | func get(with predicate: NSPredicate?, sortDescriptors: [NSSortDescriptor]?, fetchLimit: Int?, 49 | completion: @escaping (Result<[Entity], Error>) -> Void) { 50 | 51 | coreData.performForegroundTask { (context) in 52 | do { 53 | let fetchRequest = ManagedEntity.fetchRequest() 54 | fetchRequest.predicate = predicate 55 | fetchRequest.sortDescriptors = sortDescriptors 56 | if let fetchLimit = fetchLimit { 57 | fetchRequest.fetchLimit = fetchLimit 58 | } 59 | let results = try context.fetch(fetchRequest) as? [ManagedEntity] 60 | let items: [Entity] = results?.compactMap { $0.toEntity() as? Entity } ?? [] 61 | completion(.success(items)) 62 | } catch { 63 | let fetchError = CoreDataWorkerError.cannotFetch("Cannot fetch error: \(error))") 64 | completion(.failure(fetchError)) 65 | } 66 | } 67 | } 68 | 69 | func upsert(entities: [Entity], completion: @escaping (Error?) -> Void) { 70 | coreData.performBackgroundTask { (context) in 71 | _ = entities.compactMap({ (entity) -> ManagedEntity? in 72 | return entity.toManagedObject(in: context) as? ManagedEntity 73 | }) 74 | do { 75 | try context.save() 76 | completion(nil) 77 | } catch { 78 | completion(CoreDataWorkerError.cannotSave(error)) 79 | } 80 | } 81 | } 82 | 83 | // other methods: remove, ... 84 | } 85 | -------------------------------------------------------------------------------- /BetterCoreData/BetterCoreData.xcodeproj/xcuserdata/michal.xcuserdatad/xcschemes/BetterCoreData.xcscheme: -------------------------------------------------------------------------------- 1 | 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 | 68 | 69 | 70 | 71 | 75 | 76 | 77 | 78 | 79 | 80 | 86 | 88 | 94 | 95 | 96 | 97 | 99 | 100 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /BetterCoreData/BetterCoreData/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // BetterCoreData 4 | // 5 | // Created by Michal Wojtysiak on 23/11/2016. 6 | // Copyright © 2016 Michal Wojtysiak. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | let userController: UserDataControllerProtocol = NewUserDataController() 13 | // Switch between two implementations of CoreDataWorker 14 | //let userController: UserDataControllerProtocol = UserDataController() 15 | 16 | let nameTextField: UITextField = UITextField() 17 | let usernameTextField: UITextField = UITextField() 18 | let loadButton: UIButton = UIButton(type: UIButton.ButtonType.system) 19 | let updateButton: UIButton = UIButton(type: UIButton.ButtonType.system) 20 | let clearButton: UIButton = UIButton(type: UIButton.ButtonType.system) 21 | 22 | override func viewDidLoad() { 23 | super.viewDidLoad() 24 | 25 | configure(textField: nameTextField, with: "name") 26 | configure(textField: usernameTextField, with: "username") 27 | configure(button: loadButton, with: "Load", and: #selector(loadPressed)) 28 | configure(button: updateButton, with: "Update", and: #selector(updatePressed)) 29 | configure(button: clearButton, with: "Clear", and: #selector(clearPressed)) 30 | 31 | view.addSubview(nameTextField) 32 | view.addSubview(usernameTextField) 33 | view.addSubview(loadButton) 34 | view.addSubview(updateButton) 35 | view.addSubview(clearButton) 36 | 37 | setupConstraints() 38 | loadUser() 39 | } 40 | 41 | private func configure(textField: UITextField, with placeholder: String? = nil){ 42 | textField.translatesAutoresizingMaskIntoConstraints = false 43 | textField.placeholder = placeholder 44 | textField.textAlignment = .center 45 | textField.autocapitalizationType = .none 46 | textField.layer.borderWidth = 0.5 47 | textField.layer.borderColor = UIColor.lightGray.cgColor 48 | textField.layer.cornerRadius = 5.0 49 | textField.widthAnchor.constraint(equalToConstant: 200.0).isActive = true 50 | textField.heightAnchor.constraint(equalToConstant: 40.0).isActive = true 51 | } 52 | 53 | private func configure(button: UIButton, with title: String, and action: Selector){ 54 | button.translatesAutoresizingMaskIntoConstraints = false 55 | button.setTitle(title, for: .normal) 56 | button.addTarget(self, action: action, for: .touchUpInside) 57 | button.widthAnchor.constraint(equalToConstant: 60.0).isActive = true 58 | button.heightAnchor.constraint(equalToConstant: 40.0).isActive = true 59 | } 60 | 61 | private func setupConstraints(){ 62 | 63 | nameTextField.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true 64 | nameTextField.centerYAnchor.constraint(equalTo: view.topAnchor, 65 | constant: 100.0).isActive = true 66 | 67 | usernameTextField.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true 68 | usernameTextField.topAnchor.constraint(equalTo: nameTextField.bottomAnchor, 69 | constant: 20.0).isActive = true 70 | 71 | updateButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true 72 | updateButton.topAnchor.constraint(equalTo: usernameTextField.bottomAnchor, 73 | constant: 20.0).isActive = true 74 | 75 | loadButton.trailingAnchor.constraint(equalTo: updateButton.leadingAnchor, 76 | constant: -20).isActive = true 77 | loadButton.topAnchor.constraint(equalTo: updateButton.topAnchor).isActive = true 78 | 79 | clearButton.leadingAnchor.constraint(equalTo: updateButton.trailingAnchor, 80 | constant: 20).isActive = true 81 | clearButton.topAnchor.constraint(equalTo: updateButton.topAnchor).isActive = true 82 | } 83 | 84 | private func loadUser(){ 85 | userController.fetchUser { (user) in 86 | guard let user = user else { return } 87 | self.nameTextField.text = user.name 88 | self.usernameTextField.text = user.username 89 | } 90 | } 91 | 92 | @objc private func updatePressed(){ 93 | userController.updateUser(name: nameTextField.text ?? "", 94 | username: usernameTextField.text ?? "") 95 | } 96 | 97 | @objc private func loadPressed(){ 98 | loadUser() 99 | } 100 | 101 | @objc private func clearPressed(){ 102 | nameTextField.text = nil 103 | usernameTextField.text = nil 104 | } 105 | } 106 | 107 | -------------------------------------------------------------------------------- /BetterCoreData/BetterCoreData.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 9028456D1DE8EB3B0004F96E /* NewUserDataController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9028456C1DE8EB3B0004F96E /* NewUserDataController.swift */; }; 11 | 9028456F1DE971490004F96E /* UserDataController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9028456E1DE971490004F96E /* UserDataController.swift */; }; 12 | 90C768311DE61BD200EF2926 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90C768301DE61BD200EF2926 /* AppDelegate.swift */; }; 13 | 90C768331DE61BD200EF2926 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90C768321DE61BD200EF2926 /* ViewController.swift */; }; 14 | 90C768361DE61BD200EF2926 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 90C768341DE61BD200EF2926 /* Main.storyboard */; }; 15 | 90C768381DE61BD200EF2926 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 90C768371DE61BD200EF2926 /* Assets.xcassets */; }; 16 | 90C7683B1DE61BD200EF2926 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 90C768391DE61BD200EF2926 /* LaunchScreen.storyboard */; }; 17 | 90C768471DE61C5600EF2926 /* DataModel.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 90C768451DE61C5600EF2926 /* DataModel.xcdatamodeld */; }; 18 | 90C7684A1DE61D0100EF2926 /* UserMO+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90C768481DE61D0100EF2926 /* UserMO+CoreDataClass.swift */; }; 19 | 90C7684B1DE61D0100EF2926 /* UserMO+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90C768491DE61D0100EF2926 /* UserMO+CoreDataProperties.swift */; }; 20 | 90C7684D1DE61D1900EF2926 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90C7684C1DE61D1900EF2926 /* User.swift */; }; 21 | 90C7684F1DE61F5A00EF2926 /* ManagedObjectProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90C7684E1DE61F5A00EF2926 /* ManagedObjectProtocol.swift */; }; 22 | 90C768511DE61F6900EF2926 /* ManagedObjectConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90C768501DE61F6900EF2926 /* ManagedObjectConvertible.swift */; }; 23 | 90C768531DE6205300EF2926 /* CoreDataStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90C768521DE6205300EF2926 /* CoreDataStack.swift */; }; 24 | 90C7685A1DE620E600EF2926 /* CoreDataWorker1.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90C768591DE620E600EF2926 /* CoreDataWorker1.swift */; }; 25 | 90C7685C1DE620F600EF2926 /* CoreDataWorker2.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90C7685B1DE620F600EF2926 /* CoreDataWorker2.swift */; }; 26 | /* End PBXBuildFile section */ 27 | 28 | /* Begin PBXFileReference section */ 29 | 9028456C1DE8EB3B0004F96E /* NewUserDataController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NewUserDataController.swift; sourceTree = ""; }; 30 | 9028456E1DE971490004F96E /* UserDataController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserDataController.swift; sourceTree = ""; }; 31 | 90C7682D1DE61BD100EF2926 /* BetterCoreData.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BetterCoreData.app; sourceTree = BUILT_PRODUCTS_DIR; }; 32 | 90C768301DE61BD200EF2926 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 33 | 90C768321DE61BD200EF2926 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 34 | 90C768351DE61BD200EF2926 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 35 | 90C768371DE61BD200EF2926 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 36 | 90C7683A1DE61BD200EF2926 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 37 | 90C7683C1DE61BD200EF2926 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 38 | 90C768461DE61C5600EF2926 /* DataModel.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = DataModel.xcdatamodel; sourceTree = ""; }; 39 | 90C768481DE61D0100EF2926 /* UserMO+CoreDataClass.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UserMO+CoreDataClass.swift"; sourceTree = ""; }; 40 | 90C768491DE61D0100EF2926 /* UserMO+CoreDataProperties.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UserMO+CoreDataProperties.swift"; sourceTree = ""; }; 41 | 90C7684C1DE61D1900EF2926 /* User.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = ""; }; 42 | 90C7684E1DE61F5A00EF2926 /* ManagedObjectProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ManagedObjectProtocol.swift; sourceTree = ""; }; 43 | 90C768501DE61F6900EF2926 /* ManagedObjectConvertible.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ManagedObjectConvertible.swift; sourceTree = ""; }; 44 | 90C768521DE6205300EF2926 /* CoreDataStack.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreDataStack.swift; sourceTree = ""; }; 45 | 90C768591DE620E600EF2926 /* CoreDataWorker1.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreDataWorker1.swift; sourceTree = ""; }; 46 | 90C7685B1DE620F600EF2926 /* CoreDataWorker2.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreDataWorker2.swift; sourceTree = ""; }; 47 | /* End PBXFileReference section */ 48 | 49 | /* Begin PBXFrameworksBuildPhase section */ 50 | 90C7682A1DE61BD100EF2926 /* Frameworks */ = { 51 | isa = PBXFrameworksBuildPhase; 52 | buildActionMask = 2147483647; 53 | files = ( 54 | ); 55 | runOnlyForDeploymentPostprocessing = 0; 56 | }; 57 | /* End PBXFrameworksBuildPhase section */ 58 | 59 | /* Begin PBXGroup section */ 60 | 90C768241DE61BD100EF2926 = { 61 | isa = PBXGroup; 62 | children = ( 63 | 90C7682F1DE61BD200EF2926 /* BetterCoreData */, 64 | 90C7682E1DE61BD200EF2926 /* Products */, 65 | ); 66 | sourceTree = ""; 67 | }; 68 | 90C7682E1DE61BD200EF2926 /* Products */ = { 69 | isa = PBXGroup; 70 | children = ( 71 | 90C7682D1DE61BD100EF2926 /* BetterCoreData.app */, 72 | ); 73 | name = Products; 74 | sourceTree = ""; 75 | }; 76 | 90C7682F1DE61BD200EF2926 /* BetterCoreData */ = { 77 | isa = PBXGroup; 78 | children = ( 79 | 90C768301DE61BD200EF2926 /* AppDelegate.swift */, 80 | 90C768321DE61BD200EF2926 /* ViewController.swift */, 81 | 9028456C1DE8EB3B0004F96E /* NewUserDataController.swift */, 82 | 9028456E1DE971490004F96E /* UserDataController.swift */, 83 | 90C768581DE620D100EF2926 /* CoreData */, 84 | 90C768441DE61C3F00EF2926 /* Model */, 85 | 90C768431DE61C1F00EF2926 /* Storyboards */, 86 | 90C768421DE61C0E00EF2926 /* Resources */, 87 | ); 88 | path = BetterCoreData; 89 | sourceTree = ""; 90 | }; 91 | 90C768421DE61C0E00EF2926 /* Resources */ = { 92 | isa = PBXGroup; 93 | children = ( 94 | 90C7683C1DE61BD200EF2926 /* Info.plist */, 95 | 90C768371DE61BD200EF2926 /* Assets.xcassets */, 96 | ); 97 | name = Resources; 98 | sourceTree = ""; 99 | }; 100 | 90C768431DE61C1F00EF2926 /* Storyboards */ = { 101 | isa = PBXGroup; 102 | children = ( 103 | 90C768341DE61BD200EF2926 /* Main.storyboard */, 104 | 90C768391DE61BD200EF2926 /* LaunchScreen.storyboard */, 105 | ); 106 | name = Storyboards; 107 | sourceTree = ""; 108 | }; 109 | 90C768441DE61C3F00EF2926 /* Model */ = { 110 | isa = PBXGroup; 111 | children = ( 112 | 90C7684C1DE61D1900EF2926 /* User.swift */, 113 | 90C768481DE61D0100EF2926 /* UserMO+CoreDataClass.swift */, 114 | 90C768491DE61D0100EF2926 /* UserMO+CoreDataProperties.swift */, 115 | 90C768451DE61C5600EF2926 /* DataModel.xcdatamodeld */, 116 | 90C7684E1DE61F5A00EF2926 /* ManagedObjectProtocol.swift */, 117 | 90C768501DE61F6900EF2926 /* ManagedObjectConvertible.swift */, 118 | ); 119 | name = Model; 120 | sourceTree = ""; 121 | }; 122 | 90C768581DE620D100EF2926 /* CoreData */ = { 123 | isa = PBXGroup; 124 | children = ( 125 | 90C768521DE6205300EF2926 /* CoreDataStack.swift */, 126 | 90C768591DE620E600EF2926 /* CoreDataWorker1.swift */, 127 | 90C7685B1DE620F600EF2926 /* CoreDataWorker2.swift */, 128 | ); 129 | name = CoreData; 130 | sourceTree = ""; 131 | }; 132 | /* End PBXGroup section */ 133 | 134 | /* Begin PBXNativeTarget section */ 135 | 90C7682C1DE61BD100EF2926 /* BetterCoreData */ = { 136 | isa = PBXNativeTarget; 137 | buildConfigurationList = 90C7683F1DE61BD200EF2926 /* Build configuration list for PBXNativeTarget "BetterCoreData" */; 138 | buildPhases = ( 139 | 90C768291DE61BD100EF2926 /* Sources */, 140 | 90C7682A1DE61BD100EF2926 /* Frameworks */, 141 | 90C7682B1DE61BD100EF2926 /* Resources */, 142 | ); 143 | buildRules = ( 144 | ); 145 | dependencies = ( 146 | ); 147 | name = BetterCoreData; 148 | productName = BetterCoreData; 149 | productReference = 90C7682D1DE61BD100EF2926 /* BetterCoreData.app */; 150 | productType = "com.apple.product-type.application"; 151 | }; 152 | /* End PBXNativeTarget section */ 153 | 154 | /* Begin PBXProject section */ 155 | 90C768251DE61BD100EF2926 /* Project object */ = { 156 | isa = PBXProject; 157 | attributes = { 158 | LastSwiftUpdateCheck = 0810; 159 | LastUpgradeCheck = 1020; 160 | ORGANIZATIONNAME = "Michal Wojtysiak"; 161 | TargetAttributes = { 162 | 90C7682C1DE61BD100EF2926 = { 163 | CreatedOnToolsVersion = 8.1; 164 | ProvisioningStyle = Automatic; 165 | SystemCapabilities = { 166 | com.apple.DataProtection = { 167 | enabled = 0; 168 | }; 169 | }; 170 | }; 171 | }; 172 | }; 173 | buildConfigurationList = 90C768281DE61BD100EF2926 /* Build configuration list for PBXProject "BetterCoreData" */; 174 | compatibilityVersion = "Xcode 3.2"; 175 | developmentRegion = English; 176 | hasScannedForEncodings = 0; 177 | knownRegions = ( 178 | English, 179 | en, 180 | Base, 181 | ); 182 | mainGroup = 90C768241DE61BD100EF2926; 183 | productRefGroup = 90C7682E1DE61BD200EF2926 /* Products */; 184 | projectDirPath = ""; 185 | projectRoot = ""; 186 | targets = ( 187 | 90C7682C1DE61BD100EF2926 /* BetterCoreData */, 188 | ); 189 | }; 190 | /* End PBXProject section */ 191 | 192 | /* Begin PBXResourcesBuildPhase section */ 193 | 90C7682B1DE61BD100EF2926 /* Resources */ = { 194 | isa = PBXResourcesBuildPhase; 195 | buildActionMask = 2147483647; 196 | files = ( 197 | 90C7683B1DE61BD200EF2926 /* LaunchScreen.storyboard in Resources */, 198 | 90C768381DE61BD200EF2926 /* Assets.xcassets in Resources */, 199 | 90C768361DE61BD200EF2926 /* Main.storyboard in Resources */, 200 | ); 201 | runOnlyForDeploymentPostprocessing = 0; 202 | }; 203 | /* End PBXResourcesBuildPhase section */ 204 | 205 | /* Begin PBXSourcesBuildPhase section */ 206 | 90C768291DE61BD100EF2926 /* Sources */ = { 207 | isa = PBXSourcesBuildPhase; 208 | buildActionMask = 2147483647; 209 | files = ( 210 | 90C7684A1DE61D0100EF2926 /* UserMO+CoreDataClass.swift in Sources */, 211 | 90C7685C1DE620F600EF2926 /* CoreDataWorker2.swift in Sources */, 212 | 90C7684F1DE61F5A00EF2926 /* ManagedObjectProtocol.swift in Sources */, 213 | 90C768511DE61F6900EF2926 /* ManagedObjectConvertible.swift in Sources */, 214 | 9028456D1DE8EB3B0004F96E /* NewUserDataController.swift in Sources */, 215 | 90C768531DE6205300EF2926 /* CoreDataStack.swift in Sources */, 216 | 9028456F1DE971490004F96E /* UserDataController.swift in Sources */, 217 | 90C7684B1DE61D0100EF2926 /* UserMO+CoreDataProperties.swift in Sources */, 218 | 90C7685A1DE620E600EF2926 /* CoreDataWorker1.swift in Sources */, 219 | 90C7684D1DE61D1900EF2926 /* User.swift in Sources */, 220 | 90C768471DE61C5600EF2926 /* DataModel.xcdatamodeld in Sources */, 221 | 90C768331DE61BD200EF2926 /* ViewController.swift in Sources */, 222 | 90C768311DE61BD200EF2926 /* AppDelegate.swift in Sources */, 223 | ); 224 | runOnlyForDeploymentPostprocessing = 0; 225 | }; 226 | /* End PBXSourcesBuildPhase section */ 227 | 228 | /* Begin PBXVariantGroup section */ 229 | 90C768341DE61BD200EF2926 /* Main.storyboard */ = { 230 | isa = PBXVariantGroup; 231 | children = ( 232 | 90C768351DE61BD200EF2926 /* Base */, 233 | ); 234 | name = Main.storyboard; 235 | sourceTree = ""; 236 | }; 237 | 90C768391DE61BD200EF2926 /* LaunchScreen.storyboard */ = { 238 | isa = PBXVariantGroup; 239 | children = ( 240 | 90C7683A1DE61BD200EF2926 /* Base */, 241 | ); 242 | name = LaunchScreen.storyboard; 243 | sourceTree = ""; 244 | }; 245 | /* End PBXVariantGroup section */ 246 | 247 | /* Begin XCBuildConfiguration section */ 248 | 90C7683D1DE61BD200EF2926 /* Debug */ = { 249 | isa = XCBuildConfiguration; 250 | buildSettings = { 251 | ALWAYS_SEARCH_USER_PATHS = NO; 252 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 253 | CLANG_ANALYZER_NONNULL = YES; 254 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 255 | CLANG_CXX_LIBRARY = "libc++"; 256 | CLANG_ENABLE_MODULES = YES; 257 | CLANG_ENABLE_OBJC_ARC = YES; 258 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 259 | CLANG_WARN_BOOL_CONVERSION = YES; 260 | CLANG_WARN_COMMA = YES; 261 | CLANG_WARN_CONSTANT_CONVERSION = YES; 262 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 263 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 264 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 265 | CLANG_WARN_EMPTY_BODY = YES; 266 | CLANG_WARN_ENUM_CONVERSION = YES; 267 | CLANG_WARN_INFINITE_RECURSION = YES; 268 | CLANG_WARN_INT_CONVERSION = YES; 269 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 270 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 271 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 272 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 273 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 274 | CLANG_WARN_STRICT_PROTOTYPES = YES; 275 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 276 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 277 | CLANG_WARN_UNREACHABLE_CODE = YES; 278 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 279 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 280 | COPY_PHASE_STRIP = NO; 281 | DEBUG_INFORMATION_FORMAT = dwarf; 282 | ENABLE_STRICT_OBJC_MSGSEND = YES; 283 | ENABLE_TESTABILITY = YES; 284 | GCC_C_LANGUAGE_STANDARD = gnu99; 285 | GCC_DYNAMIC_NO_PIC = NO; 286 | GCC_NO_COMMON_BLOCKS = YES; 287 | GCC_OPTIMIZATION_LEVEL = 0; 288 | GCC_PREPROCESSOR_DEFINITIONS = ( 289 | "DEBUG=1", 290 | "$(inherited)", 291 | ); 292 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 293 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 294 | GCC_WARN_UNDECLARED_SELECTOR = YES; 295 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 296 | GCC_WARN_UNUSED_FUNCTION = YES; 297 | GCC_WARN_UNUSED_VARIABLE = YES; 298 | IPHONEOS_DEPLOYMENT_TARGET = 10.1; 299 | MTL_ENABLE_DEBUG_INFO = YES; 300 | ONLY_ACTIVE_ARCH = YES; 301 | SDKROOT = iphoneos; 302 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 303 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 304 | }; 305 | name = Debug; 306 | }; 307 | 90C7683E1DE61BD200EF2926 /* Release */ = { 308 | isa = XCBuildConfiguration; 309 | buildSettings = { 310 | ALWAYS_SEARCH_USER_PATHS = NO; 311 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 312 | CLANG_ANALYZER_NONNULL = YES; 313 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 314 | CLANG_CXX_LIBRARY = "libc++"; 315 | CLANG_ENABLE_MODULES = YES; 316 | CLANG_ENABLE_OBJC_ARC = YES; 317 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 318 | CLANG_WARN_BOOL_CONVERSION = YES; 319 | CLANG_WARN_COMMA = YES; 320 | CLANG_WARN_CONSTANT_CONVERSION = YES; 321 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 322 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 323 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 324 | CLANG_WARN_EMPTY_BODY = YES; 325 | CLANG_WARN_ENUM_CONVERSION = YES; 326 | CLANG_WARN_INFINITE_RECURSION = YES; 327 | CLANG_WARN_INT_CONVERSION = YES; 328 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 329 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 330 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 331 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 332 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 333 | CLANG_WARN_STRICT_PROTOTYPES = YES; 334 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 335 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 336 | CLANG_WARN_UNREACHABLE_CODE = YES; 337 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 338 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 339 | COPY_PHASE_STRIP = NO; 340 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 341 | ENABLE_NS_ASSERTIONS = NO; 342 | ENABLE_STRICT_OBJC_MSGSEND = YES; 343 | GCC_C_LANGUAGE_STANDARD = gnu99; 344 | GCC_NO_COMMON_BLOCKS = YES; 345 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 346 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 347 | GCC_WARN_UNDECLARED_SELECTOR = YES; 348 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 349 | GCC_WARN_UNUSED_FUNCTION = YES; 350 | GCC_WARN_UNUSED_VARIABLE = YES; 351 | IPHONEOS_DEPLOYMENT_TARGET = 10.1; 352 | MTL_ENABLE_DEBUG_INFO = NO; 353 | SDKROOT = iphoneos; 354 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 355 | VALIDATE_PRODUCT = YES; 356 | }; 357 | name = Release; 358 | }; 359 | 90C768401DE61BD200EF2926 /* Debug */ = { 360 | isa = XCBuildConfiguration; 361 | buildSettings = { 362 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 363 | DEVELOPMENT_TEAM = ""; 364 | INFOPLIST_FILE = BetterCoreData/Info.plist; 365 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 366 | PRODUCT_BUNDLE_IDENTIFIER = com.swiftingio.BetterCoreData; 367 | PRODUCT_NAME = "$(TARGET_NAME)"; 368 | SWIFT_VERSION = 5.0; 369 | }; 370 | name = Debug; 371 | }; 372 | 90C768411DE61BD200EF2926 /* Release */ = { 373 | isa = XCBuildConfiguration; 374 | buildSettings = { 375 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 376 | DEVELOPMENT_TEAM = ""; 377 | INFOPLIST_FILE = BetterCoreData/Info.plist; 378 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 379 | PRODUCT_BUNDLE_IDENTIFIER = com.swiftingio.BetterCoreData; 380 | PRODUCT_NAME = "$(TARGET_NAME)"; 381 | SWIFT_VERSION = 5.0; 382 | }; 383 | name = Release; 384 | }; 385 | /* End XCBuildConfiguration section */ 386 | 387 | /* Begin XCConfigurationList section */ 388 | 90C768281DE61BD100EF2926 /* Build configuration list for PBXProject "BetterCoreData" */ = { 389 | isa = XCConfigurationList; 390 | buildConfigurations = ( 391 | 90C7683D1DE61BD200EF2926 /* Debug */, 392 | 90C7683E1DE61BD200EF2926 /* Release */, 393 | ); 394 | defaultConfigurationIsVisible = 0; 395 | defaultConfigurationName = Release; 396 | }; 397 | 90C7683F1DE61BD200EF2926 /* Build configuration list for PBXNativeTarget "BetterCoreData" */ = { 398 | isa = XCConfigurationList; 399 | buildConfigurations = ( 400 | 90C768401DE61BD200EF2926 /* Debug */, 401 | 90C768411DE61BD200EF2926 /* Release */, 402 | ); 403 | defaultConfigurationIsVisible = 0; 404 | defaultConfigurationName = Release; 405 | }; 406 | /* End XCConfigurationList section */ 407 | 408 | /* Begin XCVersionGroup section */ 409 | 90C768451DE61C5600EF2926 /* DataModel.xcdatamodeld */ = { 410 | isa = XCVersionGroup; 411 | children = ( 412 | 90C768461DE61C5600EF2926 /* DataModel.xcdatamodel */, 413 | ); 414 | currentVersion = 90C768461DE61C5600EF2926 /* DataModel.xcdatamodel */; 415 | path = DataModel.xcdatamodeld; 416 | sourceTree = ""; 417 | versionGroupType = wrapper.xcdatamodel; 418 | }; 419 | /* End XCVersionGroup section */ 420 | }; 421 | rootObject = 90C768251DE61BD100EF2926 /* Project object */; 422 | } 423 | --------------------------------------------------------------------------------