Skip to content
This repository has been archived by the owner on Aug 29, 2024. It is now read-only.

Commit

Permalink
Swift 4 and Xcode 9 Support (#24)
Browse files Browse the repository at this point in the history
* Initial commit of Swift4 code using Xcode9b1

* Added objcMembers keyword

* Updated README

* Implemented Swift 4 Access Control Rules.

* Minor change to @objc and @objcMembers declarations

* Swiftier boolean check

* Slight changes for Xcode 9 GM support

* Fixed indentation

* Updated comments

* Updated README

* Updated cocoapods related metadata
  • Loading branch information
ArtSabintsev authored Sep 13, 2017
1 parent 5df5296 commit 350f849
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 59 deletions.
2 changes: 1 addition & 1 deletion .swift-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.0
4.0
2 changes: 2 additions & 0 deletions .swiftlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ included:
- ../Sources/

disabled_rules:
- block_based_kvo
- cyclomatic_complexity
- file_length
- line_length
- unused_optional_binding
- variable_name

reporter: "xcode"
34 changes: 28 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
90 changes: 47 additions & 43 deletions Sources/Zephyr.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand All @@ -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.
Expand All @@ -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.
Expand All @@ -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
}
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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:
Expand All @@ -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.
Expand All @@ -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)
}

Expand Down Expand Up @@ -324,15 +324,15 @@ fileprivate extension Zephyr {

}

// MARK: Observers
// MARK: - Observers

extension Zephyr {

/// Adds key-value observation after synchronization of a specific key.
///
/// - Parameters:
/// - key: The key that should be added and monitored.
fileprivate func registerObserver(key: String) {
private func registerObserver(key: String) {
if key == ZephyrSyncKey {
return
}
Expand All @@ -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
}
Expand All @@ -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
Expand All @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion Zephyr.podspec
Original file line number Diff line number Diff line change
@@ -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
Expand Down
30 changes: 24 additions & 6 deletions ZephyrExample/ZephyrExample.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
};
Expand Down Expand Up @@ -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 = "";
};
Expand All @@ -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 = "";
};
Expand All @@ -353,15 +356,21 @@
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;
CLANG_WARN_EMPTY_BODY = YES;
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;
Expand Down Expand Up @@ -403,15 +412,21 @@
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;
CLANG_WARN_EMPTY_BODY = YES;
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;
Expand Down Expand Up @@ -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;
};
Expand All @@ -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;
};
Expand All @@ -475,6 +492,7 @@
55EC363E1E6BB0BB00726F13 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
8E5ACC681E21881B00FF1FA3 /* Build configuration list for PBXProject "ZephyrExample" */ = {
isa = XCConfigurationList;
Expand Down
Loading

0 comments on commit 350f849

Please sign in to comment.