From 6f77b0e56dd7cca9528ea01affa6efc0af1e713a Mon Sep 17 00:00:00 2001 From: JP Wright Date: Fri, 3 Feb 2017 14:31:05 +0100 Subject: [PATCH] Migrate to Swift-3 --- .swift-version | 2 +- .travis.yml | 2 +- Code/ContentfulSynchronizer.swift | 100 ++++++++++-------- Code/CoreDataStore.swift | 61 +++++------ Code/DataCache.swift | 50 ++++----- Code/Operators.swift | 12 +-- Code/PersistenceStore.swift | 4 +- .../project.pbxproj | 18 +++- .../xcschemes/ContentfulPersistence.xcscheme | 2 +- ContentfulPersistenceSwift.podspec | 4 +- Podfile | 14 +-- Podfile.lock | 48 ++++----- Tests/BasicTests.swift | 1 - Tests/ContentfulPersistenceTests.swift | 23 ++-- Tests/CoreDataTests.swift | 13 ++- Tests/Generated/Post+CoreDataProperties.swift | 2 +- .../SyncInfo+CoreDataProperties.swift | 2 +- Tests/TestBase.swift | 23 ++-- 18 files changed, 192 insertions(+), 189 deletions(-) diff --git a/.swift-version b/.swift-version index c52f96a..8ef0f50 100644 --- a/.swift-version +++ b/.swift-version @@ -1,2 +1,2 @@ -2.3 +3.0 diff --git a/.travis.yml b/.travis.yml index fff40d0..6001517 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: objective-c -osx_image: xcode7.3 +osx_image: xcode8.1 cache: - cocoapods before_install: diff --git a/Code/ContentfulSynchronizer.swift b/Code/ContentfulSynchronizer.swift index 13ecb12..8f52fa2 100644 --- a/Code/ContentfulSynchronizer.swift +++ b/Code/ContentfulSynchronizer.swift @@ -9,26 +9,26 @@ import Contentful import Interstellar -func predicateForIdentifier(identifier: String) -> NSPredicate { +func predicate(for identifier: String) -> NSPredicate { return NSPredicate(format: "identifier == %@", identifier) } /// Provides the ability to sync content from Contentful to a persistence store. public class ContentfulSynchronizer: SyncSpaceDelegate { - private let client: Client - private let matching: [String: AnyObject] - private let store: PersistenceStore + fileprivate let client: Client + fileprivate let matching: [String: AnyObject] + fileprivate let store: PersistenceStore - private var mappingForEntries = [String: [String: String]]() - private var mappingForAssets: [String: String]! + fileprivate var mappingForEntries = [String: [String: String]]() + fileprivate var mappingForAssets: [String: String]! - private var typeForAssets: Asset.Type! + fileprivate var typeForAssets: Asset.Type! // Dictionary mapping contentTypeId's to Types - private var typeForEntries = [String: Resource.Type]() - private var typeForSpaces: Space.Type! + fileprivate var typeForEntries = [String: Resource.Type]() + fileprivate var typeForSpaces: Space.Type! // Dictionary mapping Entry identifier's to a dictionary with fieldName to related entry id's. - private var relationshipsToResolve = [String: [String: Any]]() + fileprivate var relationshipsToResolve = [String: [String: Any]]() var syncToken: String? { return fetchSpace().syncToken @@ -63,7 +63,7 @@ public class ContentfulSynchronizer: SyncSpaceDelegate { - parameter type: The type Entries should be mapped to (needs to implement the `Resource` protocol) - parameter propertyMapping: Optional mapping between Contentful fields and object properties */ - public func map(contentTypeId contentTypeId: String, to type: Resource.Type, propertyMapping: [String:String]? = nil) { + public func map(contentTypeId: String, to type: Resource.Type, propertyMapping: [String:String]? = nil) { mappingForEntries[contentTypeId] = propertyMapping typeForEntries[contentTypeId] = type } @@ -104,7 +104,7 @@ public class ContentfulSynchronizer: SyncSpaceDelegate { - parameter completion: A completion handler which is called after completing the sync process. */ - public func sync(completion: (Bool) -> ()) { + public func sync(_ completion: @escaping (Bool) -> ()) { assert(typeForAssets != nil, "Define a type for Assets using mapAssets(to:)") assert(typeForEntries.first?.1 != nil, "Define a type for Entries using map(contentTypeId:to:)") assert(typeForSpaces != nil, "Define a type for Spaces using mapSpaces(to:)") @@ -114,20 +114,22 @@ public class ContentfulSynchronizer: SyncSpaceDelegate { let syncCompletion: (Result) -> () = { result in switch result { - case .Success(let syncSpace): + case .success(let syncSpace): // Fetch the current space var space = self.fetchSpace() space.syncToken = syncSpace.syncToken + assert(space.syncToken != nil) + // Delegate callback will createEntries when necessary. - if let initial = initial where initial == true { + if let initial = initial, initial == true { for asset in syncSpace.assets { - self.createAsset(asset) + self.create(asset: asset) } for entry in syncSpace.entries { - self.createEntry(entry) + self.create(entry: entry) } } @@ -135,7 +137,7 @@ public class ContentfulSynchronizer: SyncSpaceDelegate { _ = try? self.store.save() completion(true) - case .Error(let error): + case .error(let error): NSLog("Error: \(error)") completion(false) } @@ -144,7 +146,7 @@ public class ContentfulSynchronizer: SyncSpaceDelegate { if let syncToken = syncToken { initial = false let syncSpace = SyncSpace(client: client, syncToken: syncToken, delegate: self) - syncSpace.sync(matching, completion: syncCompletion) + syncSpace.sync(matching: matching, completion: syncCompletion) } else { initial = true client.initialSync(completion: syncCompletion) @@ -156,16 +158,16 @@ public class ContentfulSynchronizer: SyncSpaceDelegate { // MARK: - Helpers // Attempts to fetch the object from the the persistent store, if it exists, - private func create(identifier: String, fields: [String: Any], type: Resource.Type, mapping: [String: String]) { + fileprivate func create(_ identifier: String, fields: [String: Any], type: Resource.Type, mapping: [String: String]) { assert(mapping.count > 0, "Empty mapping for \(type)") - let fetched: [Resource]? = try? store.fetchAll(type, predicate: predicateForIdentifier(identifier)) + let fetched: [Resource]? = try? store.fetchAll(type: type, predicate: predicate(for: identifier)) let persisted: Resource if let fetched = fetched?.first { persisted = fetched } else { - persisted = try! store.create(type) + persisted = try! store.create(type: type) persisted.identifier = identifier } @@ -174,62 +176,68 @@ public class ContentfulSynchronizer: SyncSpaceDelegate { } } - private func deriveMapping(fields: [String], type: Resource.Type, prefix: String = "") -> [String: String] { + fileprivate func deriveMapping(_ fields: [String], type: Resource.Type, prefix: String = "") -> [String: String] { var mapping = [String: String]() - let properties = (try! store.propertiesFor(type: type)).filter { propertyName in + let properties = (try! store.properties(for: type)).filter { propertyName in fields.contains(propertyName) } properties.forEach { mapping["\(prefix)\($0)"] = $0 } return mapping } - private func fetchSpace() -> Space { - // FIXME: the predicate could be a bit safer and actually use the space identifier. - let result: [Space]? = try? self.store.fetchAll(self.typeForSpaces, predicate: NSPredicate(value: true)) + fileprivate func fetchSpace() -> Space { + let createNewPersistentSpace: () -> (Space) = { + return try! self.store.create(type: self.typeForSpaces) + } - guard let space = result?.first else { - return try! self.store.create(self.typeForSpaces) + guard let fetchedResults = try? self.store.fetchAll(type: self.typeForSpaces, predicate: NSPredicate(value: true)) as [Space] else { + return createNewPersistentSpace() } - assert(result?.count == 1) + assert(fetchedResults.count <= 1) + + guard let space = fetchedResults.first else { + return createNewPersistentSpace() + } + return space } - private func map(fields: [String: Any], to: NSObject, mapping: [String: String]) { + fileprivate func map(_ fields: [String: Any], to: NSObject, mapping: [String: String]) { for (mapKey, mapValue) in mapping { var fieldValue = valueFor(fields, keyPath: mapKey) - if let string = fieldValue as? String where string.hasPrefix("//") && mapValue == "url" { + if let string = fieldValue as? String, string.hasPrefix("//") && mapValue == "url" { fieldValue = "https:\(string)" } // handle symbol arrays if let array = fieldValue as? NSArray { - fieldValue = NSKeyedArchiver.archivedDataWithRootObject(array) + fieldValue = NSKeyedArchiver.archivedData(withRootObject: array) } to.setValue(fieldValue as? NSObject, forKeyPath: mapValue) } } - private func resolveRelationships() { + fileprivate func resolveRelationships() { let entryTypes = typeForEntries.map { contentTypeId, type in return type } let cache = DataCache(persistenceStore: store, assetType: typeForAssets, entryTypes: entryTypes) for (entryId, field) in relationshipsToResolve { - if let entry = cache.entryForIdentifier(entryId) as? NSObject { + if let entry = cache.entry(for: entryId) as? NSObject { for (fieldName, relatedEntryId) in field { if let identifier = relatedEntryId as? String { - entry.setValue(cache.itemForIdentifier(identifier), forKey: fieldName) + entry.setValue(cache.item(for: identifier), forKey: fieldName) } if let identifiers = relatedEntryId as? [String] { let targets = identifiers.flatMap { id in - return cache.itemForIdentifier(id) + return cache.item(for: id) } entry.setValue(NSOrderedSet(array: targets), forKey: fieldName) } @@ -245,7 +253,7 @@ public class ContentfulSynchronizer: SyncSpaceDelegate { - parameter asset: The newly created Asset */ - public func createAsset(asset: Contentful.Asset) { + public func create(asset: Contentful.Asset) { if mappingForAssets == nil { mappingForAssets = deriveMapping(Array(asset.fields.keys), type: typeForAssets) @@ -259,7 +267,7 @@ public class ContentfulSynchronizer: SyncSpaceDelegate { create(asset.identifier, fields: asset.fields, type: typeForAssets, mapping: mappingForAssets) } - private func getIdentifier(target: Any) -> String? { + fileprivate func getIdentifier(_ target: Any) -> String? { if let target = target as? Contentful.Asset { return target.identifier } @@ -283,11 +291,11 @@ public class ContentfulSynchronizer: SyncSpaceDelegate { - parameter entry: The newly created Entry */ - public func createEntry(entry: Entry) { + public func create(entry: Entry) { let contentTypeId = ((entry.sys["contentType"] as? [String: AnyObject])?["sys"] as? [String: AnyObject])?["id"] as? String - if let contentTypeId = contentTypeId, type = typeForEntries[contentTypeId] { + if let contentTypeId = contentTypeId, let type = typeForEntries[contentTypeId] { var mapping = mappingForEntries[contentTypeId] if mapping == nil { mapping = deriveMapping(Array(entry.fields.keys), type: type) @@ -299,7 +307,7 @@ public class ContentfulSynchronizer: SyncSpaceDelegate { var relationships = [String: Any]() // Get fieldNames which are links/relationships/references to other types. - if let relationshipNames = try? store.relationshipsFor(type: type) { + if let relationshipNames = try? store.relationships(for: type) { for relationshipName in relationshipNames { @@ -328,8 +336,8 @@ public class ContentfulSynchronizer: SyncSpaceDelegate { - parameter assetId: The ID of the deleted Asset */ - public func deleteAsset(assetId: String) { - _ = try? store.delete(typeForAssets, predicate: predicateForIdentifier(assetId)) + public func delete(assetWithId: String) { + _ = try? store.delete(type: typeForAssets, predicate: predicate(for: assetWithId)) } /** @@ -337,11 +345,11 @@ public class ContentfulSynchronizer: SyncSpaceDelegate { - parameter entryId: The ID of the deleted Entry */ - public func deleteEntry(entryId: String) { - let predicate = predicateForIdentifier(entryId) + public func delete(entryWithId: String) { + let predicate = ContentfulPersistence.predicate(for: entryWithId) typeForEntries.forEach { - _ = try? self.store.delete($0.1, predicate: predicate) + _ = try? self.store.delete(type: $0.1, predicate: predicate) } } } diff --git a/Code/CoreDataStore.swift b/Code/CoreDataStore.swift index 7779016..497989e 100644 --- a/Code/CoreDataStore.swift +++ b/Code/CoreDataStore.swift @@ -8,13 +8,13 @@ import CoreData -enum Errors: ErrorType { - case InvalidType(type: Any.Type) +enum Errors: Error { + case invalidType(type: Any.Type) } /// Implementation fo the `PersistenceStore` protocol using CoreData public class CoreDataStore : PersistenceStore { - private let context: NSManagedObjectContext + fileprivate let context: NSManagedObjectContext /** Initialize a new CoreData persistence store @@ -27,14 +27,14 @@ public class CoreDataStore : PersistenceStore { self.context = context } - public func fetchRequest(type: Any.Type, predicate: NSPredicate) throws -> NSFetchRequest { - if let `class` = type as? AnyClass { - let request = NSFetchRequest(entityName: NSStringFromClass(`class`)) - request.predicate = predicate - return request + public func fetchRequest(for type: Any.Type, predicate: NSPredicate) throws -> NSFetchRequest { + guard let `class` = type as? AnyClass else { + throw Errors.invalidType(type: type) } - throw Errors.InvalidType(type: type) + let request = NSFetchRequest(entityName: String(describing: `class`)) + request.predicate = predicate + return request } // MARK: @@ -49,19 +49,16 @@ public class CoreDataStore : PersistenceStore { - returns: A newly created object of the given type */ public func create(type: Any.Type) throws -> T { - var type = type - - if let `class` = type as? AnyClass { - let object = NSEntityDescription.insertNewObjectForEntityForName(NSStringFromClass(`class`), inManagedObjectContext: context) + guard let `class` = type as? AnyClass else { + throw Errors.invalidType(type: type) + } + let object = NSEntityDescription.insertNewObject(forEntityName: String(describing: `class`), into: context) - if let object = object as? T { - return object - } else { - type = object.dynamicType - } + guard let managedObject = object as? T else { + throw Errors.invalidType(type: type(of: object)) } - throw Errors.InvalidType(type: type) + return managedObject } /** @@ -73,9 +70,9 @@ public class CoreDataStore : PersistenceStore { - throws: If an invalid type was specified */ public func delete(type: Any.Type, predicate: NSPredicate) throws { - let objects: [NSManagedObject] = try fetchAll(type, predicate: predicate) - objects.forEach { - self.context.deleteObject($0) + let managedObjects: [NSManagedObject] = try fetchAll(type: type, predicate: predicate) + managedObjects.forEach { + self.context.delete($0) } } @@ -90,8 +87,8 @@ public class CoreDataStore : PersistenceStore { - returns: An array of matching objects */ public func fetchAll(type: Any.Type, predicate: NSPredicate) throws -> [T] { - let request = try fetchRequest(type, predicate: predicate) - return try context.executeFetchRequest(request).flatMap { $0 as? T } + let request = try fetchRequest(for: type, predicate: predicate) + return try context.fetch(request).flatMap { $0 as? T } } /** @@ -105,9 +102,9 @@ public class CoreDataStore : PersistenceStore { - returns: An array of property names representing system native types. */ - public func propertiesFor(type type: Any.Type) throws -> [String] { - let description = try entityDescriptionFor(type: type) - return try description.propertiesByName.map { $0.0 } - relationshipsFor(type: type) + public func properties(for type: Any.Type) throws -> [String] { + let description = try entityDescription(for: type) + return try description.propertiesByName.map { $0.0 } - relationships(for: type) } /** @@ -119,8 +116,8 @@ public class CoreDataStore : PersistenceStore { - returns: An array of property names representing related entities. */ - public func relationshipsFor(type type: Any.Type) throws -> [String] { - let description = try entityDescriptionFor(type: type) + public func relationships(for type: Any.Type) throws -> [String] { + let description = try entityDescription(for: type) return description.relationshipsByName.map { $0.0 } } @@ -135,11 +132,11 @@ public class CoreDataStore : PersistenceStore { // MARK: - Helper methods - private func entityDescriptionFor(type type: Any.Type) throws -> NSEntityDescription { - if let `class` = type as? AnyClass, description = NSEntityDescription.entityForName(NSStringFromClass(`class`), inManagedObjectContext: context) { + fileprivate func entityDescription(for type: Any.Type) throws -> NSEntityDescription { + if let `class` = type as? AnyClass, let description = NSEntityDescription.entity(forEntityName: String(describing: `class`), in: context) { return description } - throw Errors.InvalidType(type: type) + throw Errors.invalidType(type: type) } } diff --git a/Code/DataCache.swift b/Code/DataCache.swift index 86c9fef..718606e 100644 --- a/Code/DataCache.swift +++ b/Code/DataCache.swift @@ -11,15 +11,15 @@ import Foundation protocol DataCacheProtocol { init(persistenceStore: PersistenceStore, assetType: Asset.Type, entryTypes: [Resource.Type]) - func entryForIdentifier(identifier: String) -> Resource? - func itemForIdentifier(identifier: String) -> NSObject? + func entry(for identifier: String) -> Resource? + func item (for identifier: String) -> NSObject? } /// Does not actually cache anything, but directly uses the persistence store instead class NoDataCache: DataCacheProtocol { - private let assetType: Asset.Type - private let entryTypes: [Resource.Type] - private let store: PersistenceStore + fileprivate let assetType: Asset.Type + fileprivate let entryTypes: [Resource.Type] + fileprivate let store: PersistenceStore required init(persistenceStore: PersistenceStore, assetType: Asset.Type, entryTypes: [Resource.Type]) { self.assetType = assetType @@ -27,11 +27,11 @@ class NoDataCache: DataCacheProtocol { self.store = persistenceStore } - private func itemsOf(types: [Resource.Type], identifier: String) -> Resource? { - let predicate = predicateForIdentifier(identifier) + fileprivate func itemsOf(_ types: [Resource.Type], identifier: String) -> Resource? { + let predicate = ContentfulPersistence.predicate(for: identifier) let items: [Resource] = types.flatMap { - if let result = try? store.fetchAll($0, predicate: predicate) as [Resource] { + if let result = try? store.fetchAll(type: $0, predicate: predicate) as [Resource] { return result.first } return nil @@ -40,53 +40,53 @@ class NoDataCache: DataCacheProtocol { return items.first } - func entryForIdentifier(identifier: String) -> Resource? { + func entry(for identifier: String) -> Resource? { return itemsOf(entryTypes, identifier: identifier) } - func itemForIdentifier(identifier: String) -> NSObject? { + func item(for identifier: String) -> NSObject? { return itemsOf([assetType] + entryTypes, identifier: identifier) as? NSObject } } /// Implemented using `NSCache` class DataCache: DataCacheProtocol { - private let assetCache = NSCache() - private let entryCache = NSCache() + fileprivate let assetCache = NSCache() + fileprivate let entryCache = NSCache() required init(persistenceStore: PersistenceStore, assetType: Asset.Type, entryTypes: [Resource.Type]) { let truePredicate = NSPredicate(value: true) - let assets: [Asset]? = try? persistenceStore.fetchAll(assetType, predicate: truePredicate) - assets?.forEach { self.dynamicType.cacheResource(in: assetCache, resource: $0) } + let assets: [Asset]? = try? persistenceStore.fetchAll(type: assetType, predicate: truePredicate) + assets?.forEach { type(of: self).cacheResource(in: assetCache, resource: $0) } entryTypes.forEach { - let entries: [Resource]? = try? persistenceStore.fetchAll($0, predicate: truePredicate) - entries?.forEach { self.dynamicType.cacheResource(in: entryCache, resource: $0) } + let entries: [Resource]? = try? persistenceStore.fetchAll(type: $0, predicate: truePredicate) + entries?.forEach { type(of: self).cacheResource(in: entryCache, resource: $0) } } } - private func assetForIdentifier(identifier: String) -> Asset? { - return assetCache.objectForKey(identifier) as? Asset + fileprivate func assetForIdentifier(_ identifier: String) -> Asset? { + return assetCache.object(forKey: identifier as AnyObject) as? Asset } - func entryForIdentifier(identifier: String) -> Resource? { - return entryCache.objectForKey(identifier) as? Resource + func entry(for identifier: String) -> Resource? { + return entryCache.object(forKey: identifier as AnyObject) as? Resource } - func itemForIdentifier(identifier: String) -> NSObject? { + func item(for identifier: String) -> NSObject? { var target = self.assetForIdentifier(identifier) as? NSObject if target == nil { - target = self.entryForIdentifier(identifier) as? NSObject + target = self.entry(for: identifier) as? NSObject } return target } - private static func cacheResource(in cache: NSCache, resource: Resource) { - if let id = resource.identifier, resource = resource as? AnyObject { - cache.setObject(resource, forKey: id) + fileprivate static func cacheResource(in cache: NSCache, resource: Resource) { + if let id = resource.identifier { + cache.setObject(resource as AnyObject, forKey: id as AnyObject) } } } diff --git a/Code/Operators.swift b/Code/Operators.swift index 61de959..b11f8d3 100644 --- a/Code/Operators.swift +++ b/Code/Operators.swift @@ -6,17 +6,17 @@ // Copyright © 2016 Contentful GmbH. All rights reserved. // -func += (inout left: Dictionary, right: Dictionary) { +func += (left: inout Dictionary, right: Dictionary) { for (k, v) in right { left.updateValue(v, forKey: k) } } -func - (left: [T], right: [T]) -> [T] { +func - (left: [T], right: [T]) -> [T] where T: Equatable { return left.filter { !right.contains($0) } } -func valueFor(dictionary: [String: T], keyPath: String) -> T? { +func valueFor(_ dictionary: [String: T], keyPath: String) -> T? { let components = keyPath.split(".") switch components.count { @@ -28,7 +28,7 @@ func valueFor(dictionary: [String: T], keyPath: String) -> T? { break } - let newKeyPath = components.dropFirst().joinWithSeparator(".") + let newKeyPath = components.dropFirst().joined(separator: ".") let value = dictionary[components[0]] if let dictionary = value as? [String: Any] { @@ -43,7 +43,7 @@ func valueFor(dictionary: [String: T], keyPath: String) -> T? { } extension String { - func split(separator: Character) -> [String] { - return self.characters.split(separator).map { String.init($0) } + func split(_ separator: Character) -> [String] { + return self.characters.split(separator: separator).map { String.init($0) } } } diff --git a/Code/PersistenceStore.swift b/Code/PersistenceStore.swift index 2edfe4b..a7fc17a 100644 --- a/Code/PersistenceStore.swift +++ b/Code/PersistenceStore.swift @@ -54,7 +54,7 @@ public protocol PersistenceStore { - returns: An array of property names */ - func propertiesFor(type type: Any.Type) throws -> [String] + func properties(for type: Any.Type) throws -> [String] /** Returns an array of names of properties for any relationship the given type stores persistently. @@ -65,7 +65,7 @@ public protocol PersistenceStore { - returns: An array of property names */ - func relationshipsFor(type type: Any.Type) throws -> [String] + func relationships(for type: Any.Type) throws -> [String] /** Performs the actual save to the persistence store. diff --git a/ContentfulPersistence.xcodeproj/project.pbxproj b/ContentfulPersistence.xcodeproj/project.pbxproj index 3245196..e3fc07b 100644 --- a/ContentfulPersistence.xcodeproj/project.pbxproj +++ b/ContentfulPersistence.xcodeproj/project.pbxproj @@ -248,14 +248,16 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0730; - LastUpgradeCheck = 0800; + LastUpgradeCheck = 0820; ORGANIZATIONNAME = "Contentful GmbH"; TargetAttributes = { A1A9FBE21CABE8EA00430734 = { CreatedOnToolsVersion = 7.3; + LastSwiftMigration = 0820; }; A1A9FBEC1CABE8EA00430734 = { CreatedOnToolsVersion = 7.3; + LastSwiftMigration = 0820; }; }; }; @@ -448,8 +450,10 @@ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -497,8 +501,10 @@ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -532,6 +538,7 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; APPLICATION_EXTENSION_API_ONLY = NO; CLANG_ENABLE_MODULES = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -543,7 +550,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 2.3; + SWIFT_VERSION = 3.0; }; name = Debug; }; @@ -554,6 +561,7 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; APPLICATION_EXTENSION_API_ONLY = NO; CLANG_ENABLE_MODULES = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -565,7 +573,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - SWIFT_VERSION = 2.3; + SWIFT_VERSION = 3.0; }; name = Release; }; @@ -578,7 +586,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.contentful.ContentfulPersistenceTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 2.3; + SWIFT_VERSION = 3.0; }; name = Debug; }; @@ -592,7 +600,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.contentful.ContentfulPersistenceTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - SWIFT_VERSION = 2.3; + SWIFT_VERSION = 3.0; }; name = Release; }; diff --git a/ContentfulPersistence.xcodeproj/xcshareddata/xcschemes/ContentfulPersistence.xcscheme b/ContentfulPersistence.xcodeproj/xcshareddata/xcschemes/ContentfulPersistence.xcscheme index 749535f..55fa057 100644 --- a/ContentfulPersistence.xcodeproj/xcshareddata/xcschemes/ContentfulPersistence.xcscheme +++ b/ContentfulPersistence.xcodeproj/xcshareddata/xcschemes/ContentfulPersistence.xcscheme @@ -1,6 +1,6 @@ 0.2.3' + s.dependency 'Contentful', '~> 0.3.1' end diff --git a/Podfile b/Podfile index 41efe74..44bb9c3 100644 --- a/Podfile +++ b/Podfile @@ -9,18 +9,8 @@ podspec :path => 'ContentfulPersistenceSwift.podspec' target 'ContentfulPersistenceTests' do inherit! :search_paths - pod 'CatchingFire' - pod 'Nimble', '~> 4.1.0' - pod 'Quick', '~> 0.9.3' - end -end - - -post_install do |installer| - installer.pods_project.targets.each do |target| - target.build_configurations.each do |config| - config.build_settings['SWIFT_VERSION'] = '2.3' - end + pod 'Nimble', '~> 5.1.0' + pod 'Quick', '~> 1.0.0' end end diff --git a/Podfile.lock b/Podfile.lock index e391ae0..d66396c 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,35 +1,29 @@ PODS: - - CatchingFire (0.2.0) - - Contentful (0.2.3): - - Decodable (~> 0.4.2) - - Interstellar (~> 1.4.0) - - "\U0001F555 (= 0.0.1)" - - Decodable (0.4.3) - - Interstellar (1.4.0): - - Interstellar/Core (= 1.4.0) - - Interstellar/Warpdrive (= 1.4.0) - - Interstellar/Core (1.4.0) - - Interstellar/Warpdrive (1.4.0): + - Contentful (0.3.1): + - Decodable (~> 0.5) + - Interstellar (~> 2.0.0) + - Decodable (0.5) + - Interstellar (2.0.0): + - Interstellar/Core (= 2.0.0) + - Interstellar/Warpdrive (= 2.0.0) + - Interstellar/Core (2.0.0) + - Interstellar/Warpdrive (2.0.0): - Interstellar/Core - - Nimble (4.1.0) - - Quick (0.9.3) - - "\U0001F555 (0.0.1)" + - Nimble (5.1.1) + - Quick (1.0.0) DEPENDENCIES: - - CatchingFire - - Contentful (~> 0.2.3) - - Nimble (~> 4.1.0) - - Quick (~> 0.9.3) + - Contentful (~> 0.3.1) + - Nimble (~> 5.1.0) + - Quick (~> 1.0.0) SPEC CHECKSUMS: - CatchingFire: 935ecc35fc5864f12ef1c033a97e9a9affbf6056 - Contentful: 9876b7b1b69a5298a2d4ccb98c11e5a56a57a60d - Decodable: ab6c7d02a5df949d3ab16b2696528a3241e37c76 - Interstellar: 40fc666872436d6c1b3524c59437ebdb5dec1c6a - Nimble: 97a0a4cae5124c117115634b2d055d8c97d0af19 - Quick: 13a2a2b19a5d8e3ed4fd0c36ee46597fd77ebf71 - "\U0001F555": 2a41c1f9b76698cb697b8ec9a1db1087f5621187 + Contentful: 2b3ffedfdf360e97a49f722148526c85d4f40e60 + Decodable: 7c90acc68d357b71d0d6efc120faf9c53c644b7e + Interstellar: 63a760ff3a3657f791a4523de4b89727c51edec3 + Nimble: 415e3aa3267e7bc2c96b05fa814ddea7bb686a29 + Quick: 8024e4a47e6cc03a9d5245ef0948264fc6d27cff -PODFILE CHECKSUM: f3b7c7d64d427d1bc223e1466eaa0a6a2ff8bdae +PODFILE CHECKSUM: 929fae544067f9876a1b9d4fb9cdecce542f1c07 -COCOAPODS: 1.1.1 +COCOAPODS: 1.2.0 diff --git a/Tests/BasicTests.swift b/Tests/BasicTests.swift index b8eec26..8d84928 100644 --- a/Tests/BasicTests.swift +++ b/Tests/BasicTests.swift @@ -6,7 +6,6 @@ // Copyright © 2016 Contentful GmbH. All rights reserved. // -import CatchingFire import Nimble import Quick diff --git a/Tests/ContentfulPersistenceTests.swift b/Tests/ContentfulPersistenceTests.swift index 272043a..fe5b1cc 100644 --- a/Tests/ContentfulPersistenceTests.swift +++ b/Tests/ContentfulPersistenceTests.swift @@ -9,7 +9,6 @@ @testable import ContentfulPersistence import Contentful -import CatchingFire import Nimble import Quick @@ -43,16 +42,18 @@ class ContentfulPersistenceTests: ContentfulPersistenceTestBase { return sync }() - func postTests(expectations: TestFunc) { + func postTests(expectations: @escaping TestFunc) { waitUntil(timeout: 10) { done in self.sync.sync() { expect($0).to(beTrue()) - AssertNoThrow { - let posts: [Post] = try self.store.fetchAll(Post.self, predicate: NSPredicate(value: true)) + do { + let posts: [Post] = try self.store.fetchAll(type: Post.self, predicate: NSPredicate(value: true)) expect(posts.count).to(equal(2)) try expectations(done) + } catch { + XCTAssert(false, "Fetching posts should not throw an error") } } } @@ -72,14 +73,16 @@ class ContentfulPersistenceTests: ContentfulPersistenceTestBase { self.sync.sync() { expect($0).to(beTrue()) - AssertNoThrow { - let assets: [Asset] = try self.store.fetchAll(Asset.self, predicate: NSPredicate(value: true)) + do { + let assets: [Asset] = try self.store.fetchAll(type: Asset.self, predicate: NSPredicate(value: true)) expect(assets.count).to(equal(6)) - let alice: Asset? = try self.store.fetchAll(Asset.self, predicate: self.assetPredicate).first + let alice: Asset? = try self.store.fetchAll(type: Asset.self, predicate: self.assetPredicate).first expect(alice).toNot(beNil()) expect(alice?.title).to(equal("Alice in Wonderland")) expect(alice?.url).to(equal("https://images.contentful.com/dqpnpm0n4e75/bXvdSYHB3Guy2uUmuEco8/608761ef6c0ef23815b410d5629208f9/alice-in-wonderland.gif")) + } catch { + XCTAssert(false, "Fetching asset(s) should not throw an error") } done() @@ -88,7 +91,7 @@ class ContentfulPersistenceTests: ContentfulPersistenceTestBase { it("can store Entries") { self.postTests { done in - let post: Post? = try self.store.fetchAll(Post.self, predicate: self.postPredicate).first + let post: Post? = try self.store.fetchAll(type: Post.self, predicate: self.postPredicate).first expect(post).toNot(beNil()) expect(post?.title).to(equal("Down the Rabbit Hole")) done() @@ -97,7 +100,7 @@ class ContentfulPersistenceTests: ContentfulPersistenceTestBase { it("can map Contentful Asset links to CoreData relationships") { self.postTests { done in - let post: Post? = try self.store.fetchAll(Post.self, predicate: self.postPredicate).first + let post: Post? = try self.store.fetchAll(type: Post.self, predicate: self.postPredicate).first expect(post).toNot(beNil()) expect(post?.featuredImage).toNot(beNil()) @@ -110,7 +113,7 @@ class ContentfulPersistenceTests: ContentfulPersistenceTestBase { it("can map Contentful Entry links to CoreData relationships") { self.postTests { done in - let post: Post? = try self.store.fetchAll(Post.self, predicate: self.postPredicate).first + let post: Post? = try self.store.fetchAll(type: Post.self, predicate: self.postPredicate).first expect(post).toNot(beNil()) expect(post?.author).toNot(beNil()) diff --git a/Tests/CoreDataTests.swift b/Tests/CoreDataTests.swift index b695965..89e5cbe 100644 --- a/Tests/CoreDataTests.swift +++ b/Tests/CoreDataTests.swift @@ -8,7 +8,6 @@ @testable import ContentfulPersistence import Contentful -import CatchingFire import Nimble import Quick @@ -26,20 +25,24 @@ class CoreDataTests: ContentfulPersistenceTestBase { it("can determine properties of a type") { let store = CoreDataStore(context: self.managedObjectContext) - AssertNoThrow { - let relationships = try store.propertiesFor(type: Category.self) + do { + let relationships = try store.properties(for: Category.self) expect(relationships).to(equal(["title", "identifier"])) + } catch { + XCTAssert(false, "Storing properties for Categories should not throw an error") } } it("can determine relationships of a type") { let store = CoreDataStore(context: self.managedObjectContext) - AssertNoThrow { - let relationships = try store.relationshipsFor(type: Post.self) + do { + let relationships = try store.relationships(for: Post.self) expect(relationships).to(equal(["author", "category", "featuredImage"])) + } catch { + XCTAssert(false, "Storing relationships for Posts should not throw an error") } } } diff --git a/Tests/Generated/Post+CoreDataProperties.swift b/Tests/Generated/Post+CoreDataProperties.swift index 3e7ec3a..9827b7c 100644 --- a/Tests/Generated/Post+CoreDataProperties.swift +++ b/Tests/Generated/Post+CoreDataProperties.swift @@ -20,7 +20,7 @@ extension Post { //@NSManaged var date: NSDate? @NSManaged var identifier: String? @NSManaged var slug: String? - @NSManaged var tags: NSData? + @NSManaged var tags: Data? @NSManaged var title: String? @NSManaged var author: NSOrderedSet? @NSManaged var category: NSOrderedSet? diff --git a/Tests/Generated/SyncInfo+CoreDataProperties.swift b/Tests/Generated/SyncInfo+CoreDataProperties.swift index 8046662..aea4294 100644 --- a/Tests/Generated/SyncInfo+CoreDataProperties.swift +++ b/Tests/Generated/SyncInfo+CoreDataProperties.swift @@ -14,7 +14,7 @@ import CoreData extension SyncInfo { - @NSManaged var lastSyncTimestamp: NSDate? + @NSManaged var lastSyncTimestamp: Date? @NSManaged var syncToken: String? } diff --git a/Tests/TestBase.swift b/Tests/TestBase.swift index 9896daa..31eee51 100644 --- a/Tests/TestBase.swift +++ b/Tests/TestBase.swift @@ -6,33 +6,34 @@ // Copyright © 2016 Contentful GmbH. All rights reserved. // -import CatchingFire import CoreData import Nimble import Quick class ContentfulPersistenceTestBase: QuickSpec { - let storeURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).last?.URLByAppendingPathComponent("Test.sqlite") + let storeURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last?.appendingPathComponent("Test.sqlite") lazy var managedObjectContext: NSManagedObjectContext = { - let modelURL = NSBundle(forClass: self.dynamicType).URLForResource("Test", withExtension: "momd") - let mom = NSManagedObjectModel(contentsOfURL: modelURL!) + let modelURL = Bundle(for: type(of: self)).url(forResource: "Test", withExtension: "momd") + let mom = NSManagedObjectModel(contentsOf: modelURL!) expect(mom).toNot(beNil()) let psc = NSPersistentStoreCoordinator(managedObjectModel: mom!) - AssertNoThrow { - _ = try? NSFileManager.defaultManager().removeItemAtURL(self.storeURL!) + do { + _ = try? FileManager.default.removeItem(at: self.storeURL!) - let path = self.storeURL!.path! - _ = try? NSFileManager.defaultManager().removeItemAtPath("\(path)-shm") - _ = try? NSFileManager.defaultManager().removeItemAtPath("\(path)-wal") + let path = self.storeURL!.path + _ = try? FileManager.default.removeItem(atPath: "\(path)-shm") + _ = try? FileManager.default.removeItem(atPath: "\(path)-wal") - var store = try psc.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: self.storeURL!, options: nil) + var store = try psc.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: self.storeURL!, options: nil) expect(store).toNot(beNil()) + } catch { + XCTAssert(false, "Recreating the persistent store SQL files should not throw an error") } - var managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType) + var managedObjectContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType) managedObjectContext.persistentStoreCoordinator = psc return managedObjectContext }()