diff --git a/.travis.yml b/.travis.yml index 78d7597..a2b993c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ language: objective-c -osx_image: xcode10.0 +osx_image: xcode10.2 script: -- xcodebuild build test -workspace ManagedObjectAdapter.xcworkspace -scheme 'ManagedObjectAdapter iOS' -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone X,OS=12.0' +- xcodebuild build test -workspace ManagedObjectAdapter.xcworkspace -scheme 'ManagedObjectAdapter iOS' -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone X,OS=12.2' diff --git a/Example.xcodeproj/project.pbxproj b/Example.xcodeproj/project.pbxproj index bd3f2fb..40d51c2 100644 --- a/Example.xcodeproj/project.pbxproj +++ b/Example.xcodeproj/project.pbxproj @@ -191,7 +191,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0730; - LastUpgradeCheck = 0930; + LastUpgradeCheck = 1020; ORGANIZATIONNAME = Teambition; TargetAttributes = { D3283CA91D48812300E1B445 = { @@ -202,7 +202,7 @@ }; buildConfigurationList = D3283CA51D48812300E1B445 /* Build configuration list for PBXProject "Example" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, @@ -291,6 +291,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -347,6 +348,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -406,7 +408,7 @@ PRODUCT_BUNDLE_IDENTIFIER = Teambition.ManagedObjectAdapter.Example; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -423,7 +425,7 @@ PRODUCT_BUNDLE_IDENTIFIER = Teambition.ManagedObjectAdapter.Example; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; }; name = Release; }; diff --git a/Example.xcodeproj/xcuserdata/hongxin.xcuserdatad/xcschemes/Example.xcscheme b/Example.xcodeproj/xcuserdata/hongxin.xcuserdatad/xcschemes/Example.xcscheme index 8525d92..da233c2 100644 --- a/Example.xcodeproj/xcuserdata/hongxin.xcuserdatad/xcschemes/Example.xcscheme +++ b/Example.xcodeproj/xcuserdata/hongxin.xcuserdatad/xcschemes/Example.xcscheme @@ -1,6 +1,6 @@ Event? in - return Event(object) - }) else { - return - } + let events = json.compactMap { Event($0) } self.events = events tableView.reloadData() } diff --git a/Example/ExampleViewController.swift b/Example/ExampleViewController.swift index 4d166ce..8d7e006 100644 --- a/Example/ExampleViewController.swift +++ b/Example/ExampleViewController.swift @@ -40,12 +40,12 @@ class ExampleViewController: UITableViewController { } // MARK: - Helper - fileprivate func setupUI() { + private func setupUI() { navigationItem.title = "Example" tableView.tableFooterView = UIView() } - fileprivate func loadData() { + private func loadData() { guard let path = Bundle(identifier: "Teambition.ManagedObjectAdapter.Example")?.path(forResource: "organizations", ofType: "json") ?? Bundle.main.path(forResource: "organizations", ofType: "json") else { return } @@ -58,11 +58,7 @@ class ExampleViewController: UITableViewController { return } - guard let organizations = json?.compactMap({ (object) -> Organization? in - return Organization(object) - }) else { - return - } + let organizations = json.compactMap { Organization($0) } self.organizations = organizations tableView.reloadData() } diff --git a/Example/Info.plist b/Example/Info.plist index 492f5e4..a052c5a 100644 --- a/Example/Info.plist +++ b/Example/Info.plist @@ -15,11 +15,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 0.0.6 + 0.0.7 CFBundleSignature ???? CFBundleVersion - 6 + 7 LSRequiresIPhoneOS UILaunchStoryboardName diff --git a/Example/ProjectsViewController.swift b/Example/ProjectsViewController.swift index 5601146..fdb6588 100644 --- a/Example/ProjectsViewController.swift +++ b/Example/ProjectsViewController.swift @@ -38,13 +38,13 @@ class ProjectsViewController: UITableViewController { } // MARK: - Life cycle - fileprivate func setupUI() { + private func setupUI() { navigationItem.title = "Projects" tableView.tableFooterView = UIView() } // MARK: - Helper - fileprivate func loadData() { + private func loadData() { guard let path = Bundle(identifier: "Teambition.ManagedObjectAdapter.Example")?.path(forResource: "projects", ofType: "json") ?? Bundle.main.path(forResource: "projects", ofType: "json") else { return } @@ -57,11 +57,7 @@ class ProjectsViewController: UITableViewController { return } - guard let projects = json?.compactMap({ (object) -> Project? in - return Project(object) - }) else { - return - } + let projects = json.compactMap { Project($0) } self.projects = projects tableView.reloadData() } diff --git a/ManagedObjectAdapter.xcodeproj/project.pbxproj b/ManagedObjectAdapter.xcodeproj/project.pbxproj index 55a9c69..9773bc8 100644 --- a/ManagedObjectAdapter.xcodeproj/project.pbxproj +++ b/ManagedObjectAdapter.xcodeproj/project.pbxproj @@ -195,7 +195,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0730; - LastUpgradeCheck = 0930; + LastUpgradeCheck = 1020; ORGANIZATIONNAME = Teambition; TargetAttributes = { D3283C951D4880C500E1B445 = { @@ -214,10 +214,11 @@ }; buildConfigurationList = D3283C901D4880C500E1B445 /* Build configuration list for PBXProject "ManagedObjectAdapter" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, + Base, ); mainGroup = D3283C8C1D4880C500E1B445; productRefGroup = D3283C971D4880C500E1B445 /* Products */; @@ -299,6 +300,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -358,6 +360,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -424,7 +427,7 @@ SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -444,7 +447,7 @@ PRODUCT_NAME = ManagedObjectAdapter; SKIP_INSTALL = YES; SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; }; name = Release; }; @@ -456,7 +459,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = Teambition.ManagedObjectAdapterTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -468,7 +471,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = Teambition.ManagedObjectAdapterTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; }; name = Release; }; @@ -489,7 +492,7 @@ SDKROOT = watchos; SKIP_INSTALL = YES; SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 4; WATCHOS_DEPLOYMENT_TARGET = 2.0; }; @@ -512,7 +515,7 @@ SDKROOT = watchos; SKIP_INSTALL = YES; SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 4; WATCHOS_DEPLOYMENT_TARGET = 2.0; }; diff --git a/ManagedObjectAdapter.xcodeproj/xcshareddata/xcschemes/ManagedObjectAdapter iOS.xcscheme b/ManagedObjectAdapter.xcodeproj/xcshareddata/xcschemes/ManagedObjectAdapter iOS.xcscheme index bd5c3d3..089959e 100644 --- a/ManagedObjectAdapter.xcodeproj/xcshareddata/xcschemes/ManagedObjectAdapter iOS.xcscheme +++ b/ManagedObjectAdapter.xcodeproj/xcshareddata/xcschemes/ManagedObjectAdapter iOS.xcscheme @@ -1,6 +1,6 @@ Self? { +public extension ManagedObjectSerializing where Self: NSObject { + static func model(from managedObject: NSManagedObject) -> Self? { let propertyKeys = self.propertyKeys let managedObjectKeys = generateManagedObjectKeysByPropertyKey() @@ -43,7 +43,7 @@ public extension ManagedObjectSerializing { return model(from: managedObject, processedModels: processedModels) } - fileprivate static func model(from managedObject: NSManagedObject, processedModels: [NSManagedObject: Any]) -> Self? { + private static func model(from managedObject: NSManagedObject, processedModels: [NSManagedObject: Any]) -> Self? { let propertyKeys = self.propertyKeys let managedObjectKeys = generateManagedObjectKeysByPropertyKey() let valueTransformers = valueTransformersByPropertyKey() @@ -67,7 +67,7 @@ public extension ManagedObjectSerializing { } var value: Any? - perform(in: managedObject.managedObjectContext, block: { + ManagedObjectAdapter.perform(in: managedObject.managedObjectContext, block: { value = managedObject.value(forKey: managedObjectKey) }) @@ -78,7 +78,7 @@ public extension ManagedObjectSerializing { model.setValue(transformedValue, forKey: propertyKey) } else { let attribute: NSAttributeDescription = propertyDescription as! NSAttributeDescription - if attribute.attributeValueClassName == "NSNumber" && value == nil { + if attribute.attributeValueClassName == "NSNumber" && value == nil { value = NSNumber(value: 0) } model.setValue(value, forKey: propertyKey) @@ -91,10 +91,10 @@ public extension ManagedObjectSerializing { if relationshipDescription.isToMany { var models: [Any]? - perform(in: managedObject.managedObjectContext, block: { + ManagedObjectAdapter.perform(in: managedObject.managedObjectContext, block: { if let relationshipCollection = value { models = (relationshipCollection as AnyObject).objectEnumerator().allObjects.compactMap({ (object) -> Any? in - if let nestedManagedObject = object as? NSManagedObject, let nestedClass = nestedClass as? ManagedObjectSerializing.Type { + if let nestedManagedObject = object as? NSManagedObject, let nestedClass = nestedClass as? (ManagedObjectSerializing & NSObject).Type { return nestedClass.model(from: nestedManagedObject, processedModels: mutableProcessedModels) } return nil @@ -114,7 +114,7 @@ public extension ManagedObjectSerializing { model.setValue(models, forKey: propertyKey) } } else { - if let nestedManagedObject = value as? NSManagedObject, let nestedClass = nestedClass as? ManagedObjectSerializing.Type { + if let nestedManagedObject = value as? NSManagedObject, let nestedClass = nestedClass as? (ManagedObjectSerializing & NSObject).Type { let nestedObject = nestedClass.model(from: nestedManagedObject, processedModels: mutableProcessedModels) model.setValue(nestedObject, forKey: propertyKey) } @@ -128,7 +128,7 @@ public extension ManagedObjectSerializing { } @discardableResult - public func toManagedObject(in context: NSManagedObjectContext) -> NSManagedObject? { + func toManagedObject(in context: NSManagedObjectContext) -> NSManagedObject? { let entityName = type(of: self).managedObjectEntityName() let valueTransformers = type(of: self).valueTransformersByPropertyKey() let managedObjectKeys = type(of: self).generateManagedObjectKeysByPropertyKey() @@ -136,7 +136,7 @@ public extension ManagedObjectSerializing { var managedObject: NSManagedObject? if let uniquingPredicate = generateUniquingPredicate() { - perform(in: context, block: { + ManagedObjectAdapter.perform(in: context, block: { let fetchRequest = NSFetchRequest() fetchRequest.entity = NSEntityDescription.entity(forEntityName: entityName, in: context) fetchRequest.predicate = uniquingPredicate @@ -186,7 +186,7 @@ public extension ManagedObjectSerializing { let relationshipCollection = relationshipDescription.isOrdered ? NSMutableOrderedSet() : NSMutableSet() for nestedValue in (value as AnyObject).objectEnumerator().allObjects { - if let nestedObject = nestedValue as? ManagedObjectSerializing, let nestedManagedObject = nestedObject.toManagedObject(in: context) { + if let nestedObject = nestedValue as? (ManagedObjectSerializing & NSObject), let nestedManagedObject = nestedObject.toManagedObject(in: context) { switch relationshipCollection { case is NSMutableOrderedSet: (relationshipCollection as! NSMutableOrderedSet).add(nestedManagedObject) @@ -200,7 +200,7 @@ public extension ManagedObjectSerializing { managedObject?.setValue(relationshipCollection, forKey: managedObjectKey) } else { - if let nestedObject = value as? ManagedObjectSerializing, let nestedManagedObject = nestedObject.toManagedObject(in: context) { + if let nestedObject = value as? (ManagedObjectSerializing & NSObject), let nestedManagedObject = nestedObject.toManagedObject(in: context) { managedObject?.setValue(nestedManagedObject, forKey: managedObjectKey) } } @@ -213,7 +213,7 @@ public extension ManagedObjectSerializing { do { try object.validateForInsert() } catch { - perform(in: context, block: { + ManagedObjectAdapter.perform(in: context, block: { context.delete(object) managedObject = nil }) @@ -224,8 +224,8 @@ public extension ManagedObjectSerializing { } } -internal extension ManagedObjectSerializing { - fileprivate static func generateManagedObjectKeysByPropertyKey() -> [String: String] { +internal extension ManagedObjectSerializing where Self: NSObject { + private static func generateManagedObjectKeysByPropertyKey() -> [String: String] { var managedObjectKeys = [String: String]() for property in propertyKeys { managedObjectKeys.updateValue(property, forKey: property) @@ -238,7 +238,7 @@ internal extension ManagedObjectSerializing { return managedObjectKeys } - fileprivate func generateUniquingPredicate() -> NSPredicate? { + private func generateUniquingPredicate() -> NSPredicate? { let uniquingPropertyKeys = type(of: self).propertyKeysForManagedObjectUniquing() let valueTransformers = type(of: self).valueTransformersByPropertyKey() let managedObjectKeys = type(of: self).generateManagedObjectKeysByPropertyKey() diff --git a/ManagedObjectAdapter/ManagedObjectSerializing.swift b/ManagedObjectAdapter/ManagedObjectSerializing.swift index 1010487..0babc9e 100644 --- a/ManagedObjectAdapter/ManagedObjectSerializing.swift +++ b/ManagedObjectAdapter/ManagedObjectSerializing.swift @@ -9,10 +9,6 @@ import Foundation public protocol ManagedObjectSerializing { - init() - func value(forKey key: String) -> Any? - func setValue(_ value: Any?, forKey key: String) - static func managedObjectEntityName() -> String static func managedObjectKeysByPropertyKey() -> [String: String] static func valueTransformersByPropertyKey() -> [String: ValueTransformer] @@ -42,8 +38,8 @@ public extension ManagedObjectSerializing { } } -public extension ManagedObjectSerializing { - public static var propertyKeys: Set { +public extension ManagedObjectSerializing where Self: NSObject { + static var propertyKeys: Set { if let cachedKeys = objc_getAssociatedObject(self, &AssociatedKeys.cachedPropertyKeys) as? Set { return cachedKeys } @@ -52,7 +48,7 @@ public extension ManagedObjectSerializing { return keys } - public var propertyKeys: Set { + var propertyKeys: Set { var keys = [String]() var currentMirror = Mirror(reflecting: self) while true { diff --git a/ManagedObjectAdapter/Supporting Files/Info-iOS.plist b/ManagedObjectAdapter/Supporting Files/Info-iOS.plist index 2730238..587b5df 100644 --- a/ManagedObjectAdapter/Supporting Files/Info-iOS.plist +++ b/ManagedObjectAdapter/Supporting Files/Info-iOS.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 0.0.6 + 0.0.7 CFBundleSignature ???? CFBundleVersion diff --git a/ManagedObjectAdapter/Supporting Files/Info-watchOS.plist b/ManagedObjectAdapter/Supporting Files/Info-watchOS.plist index 2730238..587b5df 100644 --- a/ManagedObjectAdapter/Supporting Files/Info-watchOS.plist +++ b/ManagedObjectAdapter/Supporting Files/Info-watchOS.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 0.0.6 + 0.0.7 CFBundleSignature ???? CFBundleVersion diff --git a/ManagedObjectAdapterTests/Info.plist b/ManagedObjectAdapterTests/Info.plist index 842f19b..3cbc539 100644 --- a/ManagedObjectAdapterTests/Info.plist +++ b/ManagedObjectAdapterTests/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 0.0.6 + 0.0.7 CFBundleSignature ???? CFBundleVersion diff --git a/README.md b/README.md index c11e56c..6985f4c 100644 --- a/README.md +++ b/README.md @@ -12,14 +12,10 @@ github "teambition/ManagedObjectAdapter" ``` ### Usage -Models that you want to use ManagedObjectAdapter must conform to ```ManagedObjectSerializing``` protocol. +Models that you want to use ManagedObjectAdapter must be a subclass of ```NSObject``` and conform to ```ManagedObjectSerializing``` protocol. ```swift protocol ManagedObjectSerializing { - init() - func value(forKey key: String) -> Any? - func setValue(_ value: Any?, forKey key: String) - static func managedObjectEntityName() -> String static func managedObjectKeysByPropertyKey() -> [String: String] static func valueTransformersByPropertyKey() -> [String: ValueTransformer] @@ -50,7 +46,7 @@ class TestModel: NSObject, ManagedObjectSerializing { return ["downloadURL": "downloadUrl"] } - static func valueTransformersByPropertyKey() -> [String : ValueTransformer] { + static func valueTransformersByPropertyKey() -> [String: ValueTransformer] { return ["transformableModel": TransformableModelTransformer()] }