diff --git a/.swift-version b/.swift-version index 9f55b2c..5186d07 100644 --- a/.swift-version +++ b/.swift-version @@ -1 +1 @@ -3.0 +4.0 diff --git a/.swiftlint.yml b/.swiftlint.yml index 8bbde1a..aa5da50 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -2,9 +2,11 @@ included: - ../Sources/ disabled_rules: + - block_based_kvo - cyclomatic_complexity - file_length - line_length + - unused_optional_binding - variable_name reporter: "xcode" diff --git a/README.md b/README.md index 3973994..a14427a 100755 --- a/README.md +++ b/README.md @@ -23,30 +23,52 @@ For the latest updates, refer to the [Releases](https://github.com/ArtSabintsev/ ### Installation Instructions +| Swift Version | Branch Name | Will Continue to Receive Updates? +| ------------- | ------------- | ------------- +| 4.0 | master | **Yes** +| 3.2 | swift3.2 | No +| 3.1 | swift3.1 | No + #### CocoaPods -For Swift 3 support: +For Swift 4 support: ```ruby pod 'Zephyr' ``` -For Swift 4 support: +For Swift 3.2 support: ```ruby -pod 'Zephyr', :git => 'https://github.com/ArtSabintsev/Zephyr.git', :branch => 'swift4' +pod 'Zephyr', :git => 'https://github.com/ArtSabintsev/Zephyr.git', :branch => 'swift3.2' +``` +For Swift 3.1 support: +```ruby +pod 'Zephyr', :git => 'https://github.com/ArtSabintsev/Zephyr.git', :branch => 'swift3.1' ``` - ### Carthage -For Swift 3 support: +For Swift 4 support: ``` swift github "ArtSabintsev/Zephyr" ``` +For Swift 3.2 support: + +``` swift +github "ArtSabintsev/Zephyr", "swift3.2" +``` + +For Swift 3.1 support: + +``` swift +github "ArtSabintsev/Zephyr", "swift3.1" +``` + ### Swift Package Manager ``` swift -.Package(url: "https://github.com/ArtSabintsev/Zephyr.git", majorVersion: 2) +.Package(url: "https://github.com/ArtSabintsev/Zephyr.git", majorVersion: 3) ``` + #### Manual 1. [Download Zephyr](http://github.com/ArtSabintsev/Zephyr/archive/master.zip) diff --git a/Sources/Zephyr.swift b/Sources/Zephyr.swift index 6c7808a..e1881f4 100644 --- a/Sources/Zephyr.swift +++ b/Sources/Zephyr.swift @@ -9,8 +9,8 @@ import Foundation /// Enumerates the Local (NSUserDefaults) and Remote (NSUNSUbiquitousKeyValueStore) data stores -fileprivate enum ZephyrDataStore { - case local // NSUserDefaults +private enum ZephyrDataStore { + case local // UserDefaults case remote // NSUbiquitousKeyValueStore } @@ -29,28 +29,28 @@ public class Zephyr: NSObject { public static var syncUbiquitousStoreKeyValueStoreOnChange = true /// The singleton for Zephyr. - fileprivate static let shared = Zephyr() + private static let shared = Zephyr() /// A shared key that stores the last synchronization date between NSUserDefaults and NSUbiquitousKeyValueStore. - fileprivate let ZephyrSyncKey = "ZephyrSyncKey" + private let ZephyrSyncKey = "ZephyrSyncKey" /// An array of keys that should be actively monitored for changes. - fileprivate var monitoredKeys = [String]() + private var monitoredKeys = [String]() /// An array of keys that are currently registered for observation. - fileprivate var registeredObservationKeys = [String]() + private var registeredObservationKeys = [String]() /// A queue used to serialize synchronization on monitored keys. - fileprivate let zephyrQueue = DispatchQueue(label: "com.zephyr.queue") + private let zephyrQueue = DispatchQueue(label: "com.zephyr.queue") /// A session-persisted variable to directly access all of the NSUserDefaults elements. - fileprivate var zephyrLocalStoreDictionary: [String: Any] { - return UserDefaults.standard.dictionaryRepresentation() + private var zephyrLocalStoreDictionary: [String: Any] { + return UserDefaults.standard.dictionaryRepresentation() } /// A session-persisted variable to directly access all of the NSUbiquitousKeyValueStore elements. - fileprivate var zephyrRemoteStoreDictionary: [String: Any] { - return NSUbiquitousKeyValueStore.default().dictionaryRepresentation + private var zephyrRemoteStoreDictionary: [String: Any] { + return NSUbiquitousKeyValueStore.default.dictionaryRepresentation } /// Zephyr's initialization method. @@ -64,7 +64,7 @@ public class Zephyr: NSObject { NotificationCenter.default.addObserver(self, selector: #selector(willEnterForeground(notification:)), name: NSNotification.Name.UIApplicationWillEnterForeground, object: nil) - NSUbiquitousKeyValueStore.default().synchronize() + NSUbiquitousKeyValueStore.default.synchronize() } /// Zephyr's de-initialization method. @@ -85,7 +85,7 @@ public class Zephyr: NSObject { /// - Parameters: /// - keys: If you pass a one or more keys, only those key will be synchronized. If no keys are passed, than all NSUserDefaults will be synchronized with NSUbiquitousKeyValueStore. public static func sync(keys: String...) { - if keys.count > 0 { + if !keys.isEmpty { sync(keys: keys) return } @@ -185,9 +185,9 @@ public class Zephyr: NSObject { } -// MARK: Helpers +// MARK: - Helpers -fileprivate extension Zephyr { +private extension Zephyr { /// Compares the last sync date between NSUbiquitousKeyValueStore and NSUserDefaults. /// /// If no data exists in NSUbiquitousKeyValueStore, then NSUbiquitousKeyValueStore will synchronize with data from NSUserDefaults. @@ -222,9 +222,9 @@ fileprivate extension Zephyr { } -// MARK: Synchronizers +// MARK: - Synchronizers -fileprivate extension Zephyr { +private extension Zephyr { /// Synchronizes specific keys to/from NSUbiquitousKeyValueStore and NSUserDefaults. /// /// - Parameters: @@ -251,7 +251,7 @@ fileprivate extension Zephyr { /// - key: If you pass a key, only that key will be updated in NSUbiquitousKeyValueStore. /// - value: The value that will be synchronized. Must be passed with a key, otherwise, nothing will happen. func syncToCloud(key: String? = nil, value: Any? = nil) { - let ubiquitousStore = NSUbiquitousKeyValueStore.default() + let ubiquitousStore = NSUbiquitousKeyValueStore.default ubiquitousStore.set(Date(), forKey: ZephyrSyncKey) // Sync all defaults to iCloud if key is nil, otherwise sync only the specific key/value pair. @@ -275,7 +275,7 @@ fileprivate extension Zephyr { ubiquitousStore.set(value, forKey: key) Zephyr.printKeySyncStatus(key: key, value: value, destination: .remote) } else { - NSUbiquitousKeyValueStore.default().removeObject(forKey: key) + NSUbiquitousKeyValueStore.default.removeObject(forKey: key) Zephyr.printKeySyncStatus(key: key, value: value, destination: .remote) } @@ -324,7 +324,7 @@ fileprivate extension Zephyr { } -// MARK: Observers +// MARK: - Observers extension Zephyr { @@ -332,7 +332,7 @@ extension Zephyr { /// /// - Parameters: /// - key: The key that should be added and monitored. - fileprivate func registerObserver(key: String) { + private func registerObserver(key: String) { if key == ZephyrSyncKey { return } @@ -351,7 +351,7 @@ extension Zephyr { /// /// - Parameters: /// - key: The key that should be removed from being monitored. - fileprivate func unregisterObserver(key: String) { + private func unregisterObserver(key: String) { if key == ZephyrSyncKey { return } @@ -366,9 +366,31 @@ extension Zephyr { Zephyr.printObservationStatus(key: key, subscribed: false) } + public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { + guard let keyPath = keyPath, let object = object, monitoredKeys.contains(keyPath) else { + return + } + + // Synchronize changes if key is monitored and if key is currently registered to respond to changes + zephyrQueue.async { + if self.registeredObservationKeys.contains(keyPath) { + if object is UserDefaults { + UserDefaults.standard.set(Date(), forKey: self.ZephyrSyncKey) + } + + self.syncSpecificKeys(keys: [keyPath], dataStore: .local) + } + } + } +} + +// MARK: - Observers (Objective-C) + +@objc extension Zephyr { + /// Observation method for UIApplicationWillEnterForegroundNotification func willEnterForeground(notification: Notification) { - NSUbiquitousKeyValueStore.default().synchronize() + NSUbiquitousKeyValueStore.default.synchronize() } /// Observation method for NSUbiquitousKeyValueStoreDidChangeExternallyNotification @@ -388,29 +410,11 @@ extension Zephyr { } } - public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { - guard let keyPath = keyPath, let object = object else { - return - } - - // Synchronize changes if key is monitored and if key is currently registered to respond to changes - if monitoredKeys.contains(keyPath) { - zephyrQueue.async { - if self.registeredObservationKeys.contains(keyPath) { - if object is UserDefaults { - UserDefaults.standard.set(Date(), forKey: self.ZephyrSyncKey) - } - - self.syncSpecificKeys(keys: [keyPath], dataStore: .local) - } - } - } - } } -// MARK: Loggers +// MARK: - Loggers -fileprivate extension Zephyr { +private extension Zephyr { /// Prints Zephyr's current sync status if /// /// - Parameters: diff --git a/Zephyr.podspec b/Zephyr.podspec index 73dc403..df392b7 100755 --- a/Zephyr.podspec +++ b/Zephyr.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "Zephyr" - s.version = "2.2.3" + s.version = "3.0.0" s.summary = "Effortlessly synchronize UserDefaults over iCloud" s.description = <<-DESC diff --git a/ZephyrExample/ZephyrExample.xcodeproj/project.pbxproj b/ZephyrExample/ZephyrExample.xcodeproj/project.pbxproj index 20c70e2..dc12552 100644 --- a/ZephyrExample/ZephyrExample.xcodeproj/project.pbxproj +++ b/ZephyrExample/ZephyrExample.xcodeproj/project.pbxproj @@ -175,18 +175,19 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0820; - LastUpgradeCheck = 0820; + LastUpgradeCheck = 0900; ORGANIZATIONNAME = "Arthur Ariel Sabintsev"; TargetAttributes = { 55EC36331E6BB0BB00726F13 = { CreatedOnToolsVersion = 8.2.1; DevelopmentTeam = HT94948NDD; - LastSwiftMigration = 0820; + LastSwiftMigration = 0900; ProvisioningStyle = Automatic; }; 8E5ACC6C1E21881B00FF1FA3 = { CreatedOnToolsVersion = 8.2.1; DevelopmentTeam = HT94948NDD; + LastSwiftMigration = 0900; ProvisioningStyle = Automatic; }; }; @@ -314,7 +315,8 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = Default; + SWIFT_VERSION = 4.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -338,7 +340,8 @@ PRODUCT_BUNDLE_IDENTIFIER = com.sabintsev.Zephyr; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = Default; + SWIFT_VERSION = 4.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -353,7 +356,9 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; @@ -361,7 +366,11 @@ CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -403,7 +412,9 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; @@ -411,7 +422,11 @@ CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -447,7 +462,8 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.sabintsev.Zephyr-Sample-App"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = Default; + SWIFT_VERSION = 4.0; }; name = Debug; }; @@ -461,7 +477,8 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.sabintsev.Zephyr-Sample-App"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = Default; + SWIFT_VERSION = 4.0; }; name = Release; }; @@ -475,6 +492,7 @@ 55EC363E1E6BB0BB00726F13 /* Release */, ); defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; }; 8E5ACC681E21881B00FF1FA3 /* Build configuration list for PBXProject "ZephyrExample" */ = { isa = XCConfigurationList; diff --git a/ZephyrExample/ZephyrExample.xcodeproj/xcshareddata/xcschemes/Zephyr.xcscheme b/ZephyrExample/ZephyrExample.xcodeproj/xcshareddata/xcschemes/Zephyr.xcscheme index 3721177..dc44a2c 100644 --- a/ZephyrExample/ZephyrExample.xcodeproj/xcshareddata/xcschemes/Zephyr.xcscheme +++ b/ZephyrExample/ZephyrExample.xcodeproj/xcshareddata/xcschemes/Zephyr.xcscheme @@ -1,6 +1,6 @@