Effortlessly synchronize UserDefaults over iCloud.

Overview

Zephyr 🌬️

Effortlessly sync UserDefaults over iCloud

Swift Support Platform CocoaPods SwiftPM Compatible


About

Zephyr synchronizes specific keys and/or all of your UserDefaults over iCloud using NSUbiquitousKeyValueStore.

Zephyr has built in monitoring, allowing it to sync specific keys in the background as they change.

For the latest updates, refer to the Releases tab.

Features

  • CocoaPods and SwiftPM Support
  • Syncs specific UserDefaults keys or all of your UserDefaults.
  • Background monitoring and synchronization between UserDefaults and NSUbiquitousKeyValueStore
  • Detailed Logging

Installation Instructions

Swift Version Branch Name Will Continue to Receive Updates?
5.1+ master Yes
5.0 swift5.0 No
4.2 swift4.2 No
4.1 swift4.1 No
3.2 swift3.2 No
3.1 swift3.1 No

CocoaPods

pod 'Zephyr' # Swift 5.1+
pod 'Zephyr', :git => 'https://github.com/ArtSabintsev/Zephyr.git', :branch => 'swift5.0' # Swift 5.0
pod 'Zephyr', :git => 'https://github.com/ArtSabintsev/Zephyr.git', :branch => 'swift4.2' # Swift 4.2
pod 'Zephyr', :git => 'https://github.com/ArtSabintsev/Zephyr.git', :branch => 'swift4.1' # Swift 4.1
pod 'Zephyr', :git => 'https://github.com/ArtSabintsev/Zephyr.git', :branch => 'swift3.2' # Swift 3.2
pod 'Zephyr', :git => 'https://github.com/ArtSabintsev/Zephyr.git', :branch => 'swift3.1' # Swift 3.1

Swift Package Manager

.Package(url: "https://github.com/ArtSabintsev/Zephyr.git", majorVersion: 3)

Manual

  1. Download Zephyr
  2. Copy Zephyr.swift into your project.

Setup

Turn on iCloud Sync in Xcode

In Xcode, open your app's project/workspace file:

  • Click on your Project
  • Click on one of your Targets
  • Click on Capabilities
  • Turn on iCloud syncing
  • Under Services, make sure to check Key-value storage
  • Repeat for all Targets (if necessary)

How to turn on iCloud Key Value Store Syncing

Integrate Zephyr into your App

Before performing each sync, Zephyr automatically checks to see if the data in UserDefaults or NSUbiquitousKeyValueStore is newer. To make sure there's no overwriting going on in a fresh installation of your app on a new device that's connected to the same iCloud account, make sure that your UserDefaults are registered BEFORE calling any of the Zephyr methods. One way to easily achieve this is by using the UserDefaults Register API.

Sync all UserDefaults

Zephyr.sync()

Sync a specific key or keys (Variadic Option)

Zephyr.sync(keys: "MyFirstKey", "MySecondKey", ...)

Sync a specific key or keys (Array Option)

Zephyr.sync(keys: ["MyFirstKey", "MySecondKey"])

Add/Remove Keys for Background Monitoring (Variadic Option)

Zephyr.addKeysToBeMonitored(keys: "MyFirstKey", "MySecondKey", ...)
Zephyr.removeKeysFromBeingMonitored(keys: "MyFirstKey", "MySecondKey", ...)

Add/Remove Keys for Background Monitoring (Array Option)

Zephyr.addKeysToBeMonitored(keys: ["MyFirstKey", "MySecondKey"])
Zephyr.removeKeysFromBeingMonitored(keys: ["MyFirstKey", "MySecondKey"])

Toggle Automatic Calling of NSUbiquitousKeyValueStore's Synchronization method

Zephyr.syncUbiquitousKeyValueStoreOnChange = true // Default
Zephyr.syncUbiquitousKeyValueStoreOnChange = false // Turns off instantaneous synchronization

Debug Logging

Zephyr.debugEnabled = true // Must be called before sync(_:)
Zephyr.sync()

Sample App

Please ignore the Sample App as I did not add any demo code in the Sample App. It's only in this repo to add support for Carthage.

Created and maintained by

Arthur Ariel Sabintsev

Comments
  • Background sync not triggered

    Background sync not triggered

    I'm currently trying to get this set up to allow me to sync an array between devices.

    In my AppDelegate I add

    Zephyr.debugEnabled = true
    Zephyr.addKeysToBeMonitored(keys: "defaultToSync")
    Zephyr.sync(keys: "defaultToSync")
    

    Which when launching the second device will pull in what value has been set.

    I then change the value using:

    UserDefaults().set(value, forKey: "defaultToSync")
    Zephyr.sync(keys: "defaultToSync")
    

    My second device never seems to pick up that things have changed, the device making the change does say it has synced TO iCloud.

    My understanding is that the key being monitored should at some point see the change?

    Any help is appreciated.

    opened by evanrobertson 11
  • Allow custom UserDefaults object, bug fixed

    Allow custom UserDefaults object, bug fixed

    Added an optional parameter to Zephyr.sync(), taking a custom UserDefaults object and using that in place of always using the container-wide UserDefaults.standard.

    Fixed bug resulting in UserDefaults.standard being used despite passing in a custom UserDefaults object to the variable-arguments sync() function (when no keys were passed in)

    opened by albertwujj 10
  • using Zephyr with a suiteName

    using Zephyr with a suiteName

    I am just getting started with Zephyr, I want to use it sync my UserDefaults between iPad and iOS version of my app. I have set it up, and it doesn't seem to find the values across devices. Both devices are logged into the same iCloud account, and in debug mode I see that Zephyr is syncing - but each devices seems to find a unique instance. (this is in the simulator on both devices). Is this addressed via a UserDefaults SuiteName?

    Question 
    opened by TheApApp 9
  • Swift 3 Support

    Swift 3 Support

    This PR will remain open until Xcode 8.0 GM is released.

    Initial conversion to Swift 3 was done in Xcode 8.0b2.

    All work for Swift 3 compatibility will be done in this branch.

    opened by ArtSabintsev 9
  • Attempted fix for initial load issue.

    Attempted fix for initial load issue.

    All parts of the guard statement have been split into let statements and two separate if statements.

    If localStoredData == nil we still want to synchronize.

    See issue #48.

    Potential Bug Enhancement 
    opened by aedificator-nl 8
  • follow up override issue

    follow up override issue

    Sorry for my late response. answering your question here: https://github.com/ArtSabintsev/Zephyr/issues/45#event-2843850328

    first in: func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?)

    I register defaults, where:

    let emptyData = Data()
    UserDefaults.standard.register(defaults: [subscription.identifier: emptyData])
    Zephyr.addKeysToBeMonitored(keys: [subscription.identifier])
    

    later after checking the status of the user subscription I store the subscription in UserDefaults to later sync:

    let encoder = PropertyListEncoder()
    let statusData = try! encoder.encode(status)                
    UserDefaults.standard.set(statusData, forKey: subscription.identifier)
    Zephyr.sync(keys: [subscription.identifier])
    

    And here is where the override issue happens. I managed to fix it in my phone by removing the key from iCloud with NSUbiquitousKeyValueStore.default.removeObject(forKey:) But trying with a different phone happen again.

    If you could give me some hint here I would really appreciated

    Question 
    opened by luicm 8
  • zephyrRemoteStoreDictionary[ZephyrSyncKey] reports incorrect time

    zephyrRemoteStoreDictionary[ZephyrSyncKey] reports incorrect time

    Initially, when I reported this issue, I thought it was due to a synchronization timing / data race issue, but I have since found otherwise.

    It appears that the date stored in zephyrRemoteStoreDictionary[ZephyrSyncKey] often supplies the incorrect time, thus causing data lost for the local store. Although I have yet to pinpoint the cause of the issue, this key fairly consistently (2 out of every 3 calls) reports a time that is ahead of the local time. Unfortunately, this means that when updating a default in the local store that is newer than the default stored remotely, the older remote value trumps the new local value.

    I will continue looking into the issue and see if I can find out any additional information. Hopefully we can get this fixed!

    Thank you for this project - saved me a good chunk of time, and it's beautifully written! 🍻

    opened by Sam-Spencer 8
  • Use of unresolved identifier 'DispatchQueueAttributes'

    Use of unresolved identifier 'DispatchQueueAttributes'

    Hey Arthur!

    Just loaded this up in Xcode 8 beta 4 and I'm getting this error on line 75.

    Use of unresolved identifier 'DispatchQueueAttributes'

    private let zephyrQueue = DispatchQueue(label: "com.zephyr.queue", attributes: DispatchQueueAttributes.serial);

    It looks like this initializer is only available in iOS 10+.

    opened by jstheoriginal 8
  • [OSX] UIApplicationWillEnterForegroundNotification is iOS only

    [OSX] UIApplicationWillEnterForegroundNotification is iOS only

    screenshot 2015-11-24 20 25 00

    Is it iOS thing only, viz. doc: https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplication_Class/#//apple_ref/c/data/UIApplicationWillEnterForegroundNotification

    opened by vlafiser 8
  • Issue when changing multiple keys in macOS Monterey

    Issue when changing multiple keys in macOS Monterey

    First off: Thank you for this library. This is exactly what I was looking for.

    There seems to be a sync issue with macOS Monterey (tested version: 12.0.1 (21A558))

    It seems that Apple changed the behavior on how it posts the NSUbiquitousKeyValueStore.didChangeExternallyNotification notification.

    Formerly, notification.userInfo?[NSUbiquitousKeyValueStoreChangedKeysKey] contained ALL changed keys (including ZephyrSyncKey). In my tests, this is no longer the case. Instead, the notification gets posted multiple times, with different values. Here is what happens in my scenario:

    We have Client A and Client B. Lets say A changes 2 keys. What happens is:

    Zephyr:

    • Receives NSUbiquitousKeyValueStore.didChangeExternallyNotification notification for key 1, stores remote value to local user defaults.
    • Zeyphr sets local sync date
    • Receives NSUbiquitousKeyValueStore.didChangeExternallyNotification notification for key 2, throws it away, because local sync date is newer
    • Receives NSUbiquitousKeyValueStore.didChangeExternallyNotification notification for key ZephyrSyncKey

    https://github.com/ArtSabintsev/Zephyr/blob/d86fe8ccc70b599ffd9adde55ada042910d045a1/Sources/Zephyr.swift#L456-L459

    In this scenario, the second value was thrown out and both apps are no longer in sync.

    I can't tell if this is a (beta) bug or if this is the new, expected behavior. I can't think of a good way to fix this, as we would now need to store the sync date of each property - or somehow batch all those updates coming in.

    Potential Bug 
    opened by paxos 7
  • Make this universally compile with current versions of Swift and Xcode

    Make this universally compile with current versions of Swift and Xcode

    This fixes a couple of issues that prevent the package from compiling with current versions of Swift and Xcode:

    • commit 6357a65:
      • updates Package.swift format to 4.0 as required by Swift 5 (and recommended for Swift 4)
    • commit 9ba6be1:
      • tracks the .UIApplicationWillEnterForeground notification name change (Swift 4.2 onwards)
    • commit eae57a6:
      • protects UIKit specifics with #if os() so Zephyr now compiles for macOS as well
    opened by rhx 7
Releases(3.7.1)
  • 3.7.1(Oct 10, 2022)

  • 3.6.3(Mar 2, 2022)

  • 3.6.2(Nov 7, 2021)

    • #55: Fixed a race condition that was accidentally overlooked when zephyrQueue was introduced. Huge thanks to @Tunous for pointing out the issue and submitting the fix.
    Source code(tar.gz)
    Source code(zip)
  • 3.6.1(Nov 22, 2020)

    This is a version bump that only affects folks using SPM. This change results in less metadata files being brought into an installation now that the architecture of this repo has changed.

    Source code(tar.gz)
    Source code(zip)
  • 3.6.0(Jun 15, 2020)

    Synchronization is Hard

    Thanks to the diligence of @aedificator-nl in (#47 & #48), a long standing bug was fixed that would early-exit an initial synchronization on the first attempted synchronization on a secondary device sharing the same iCloud account. In other words, syncing wouldn't occur right away until after the second device was had a subsequent synchronization attempt performed. I think this bug has been in the library for a long time, if not since the beginning, so I applaud @aedificator-nl for taking the time and diligence to find the nefarious line of code and working through the solution.

    Source code(tar.gz)
    Source code(zip)
  • 3.5.1(Jun 13, 2020)

  • 3.5.0(Sep 15, 2019)

  • 3.4.2(Aug 30, 2019)

    On working on a personal application of mine, I noticed that synchronization on a second/subsequent device did not work on that device's first launch of an application. After further investigation, I noticed that the syncFromCloud method did not actually post a notification on completion. This has now been fixed.

    Source code(tar.gz)
    Source code(zip)
  • 3.4.1(Aug 8, 2019)

    I fixed the Package.swift so that it is now compatible with Xcode 11 and the latest Swift Package Manifest file (// swift-tools-version:4.0).

    Source code(tar.gz)
    Source code(zip)
  • 3.4.0(Apr 19, 2019)

  • 3.3.0(Mar 31, 2019)

    As of Zephyr v3.3.0, all future changes on master will be done in Xcode 10.2 using Swift 5.0.

    For those using Swift v4.2, please check out the swift4.2 branch or version 3.2.0 - both point to the same commit hash.

    Source code(tar.gz)
    Source code(zip)
  • 3.2.0(Sep 17, 2018)

    As of Zephyr v3.2.0, all future changes on master will be done in Xcode 10 using Swift 4.2.

    For those using Swift v4.1.0, please check out the swift4.1 branch or version 3.1.2 - both point to the same commit hash.

    Source code(tar.gz)
    Source code(zip)
  • 3.1.2(Apr 23, 2018)

  • 3.1.1(Apr 23, 2018)

  • 3.1.0(Mar 29, 2018)

  • 3.0.2(Mar 15, 2018)

    • Updated Podspec file.
    • Removed .swift-version file, as it's been deprecated.
    • Removed watchOS support since there are a few insurmountable issue (and because it seems no one was using or requesting this support).
    Source code(tar.gz)
    Source code(zip)
  • 3.0.1(Oct 19, 2017)

    • Fixes #25. When converting to the codebase to Swift 4, the @objcMembers keyword was missing, so the KVO method was never being hit. This has been addressed.
    Source code(tar.gz)
    Source code(zip)
  • 3.0.0(Sep 13, 2017)

    Zephyr v3.0.0 adds support for Swift 4 / iOS 11 / Xcode 9. Only minor changes were made under-the-hood.

    Moving forward, only support for the Swift 4 codebase (on master) will be maintained.

    | Swift Version | Branch Name | Will Continue to Receive Updates? | ------------- | ------------- | ------------- | 4.0 | master | Yes | 3.2 | swift3.2 | No | 3.1 | swift3.1 | No

    Source code(tar.gz)
    Source code(zip)
  • 2.2.3(Mar 5, 2017)

  • 2.2.2(Mar 5, 2017)

  • 2.2.1(Mar 5, 2017)

  • 2.2.0(Mar 4, 2017)

    • Comments are now in the Apple preferred style.
    • syncUbiquitousKeyValueStoreOnChange replaces syncUbiquitousStoreKeyValueStoreOnChange.
      • This is only a name change. The Functionality remains the same.
    • Replaced Sample App with ZephyrExample.

    Note: These changes are for the master (Swift 3) branch only.

    Source code(tar.gz)
    Source code(zip)
  • 2.0.0(Sep 14, 2016)

  • 1.5.1(Apr 12, 2016)

  • 1.5.0(Mar 22, 2016)

  • 1.4.5(Feb 8, 2016)

  • 1.4.4(Feb 8, 2016)

  • 1.4.3(Dec 18, 2015)

  • 1.4.2(Dec 11, 2015)

    • Zephyr now synchronously processes incoming and outgoing changes to your monitoredKeys on a shared serial queue. (e.g., FIFO).
    • Bugfix to deinit method.
    • Stylistic changes

    Thanks to @vgritsenko for guidance in releasing this version.

    Source code(tar.gz)
    Source code(zip)
  • 1.4.1(Dec 1, 2015)

Owner
Arthur Ariel Sabintsev
Senior Engineering Manager
Arthur Ariel Sabintsev
Synco - Synco uses Firebase's Realtime Database to synchronize data across multiple devices, in real time

Synco Synco uses Firebase's Realtime Database to synchronize a color across mult

Alessio 0 Feb 7, 2022
CloudKit, Apple’s remote data storage service, provides a possibility to store app data using users’ iCloud accounts as a back-end storage service.

CloudKit, Apple’s remote data storage service, provides a possibility to store app data using users’ iCloud accounts as a back-end storage service. He

Yalantis 252 Nov 4, 2022
CodableCloudKit allows you to easily save and retrieve Codable objects to iCloud Database (CloudKit)

CodableCloudKit CodableCloudKit allows you to easily save and retrieve Codable objects to iCloud Database (CloudKit) Features ℹ️ Add CodableCloudKit f

Laurent Grondin 65 Oct 23, 2022
Prephirences is a Swift library that provides useful protocols and convenience methods to manage application preferences, configurations and app-state. UserDefaults

Prephirences - Preϕrences Prephirences is a Swift library that provides useful protocols and convenience methods to manage application preferences, co

Eric Marchand 557 Nov 22, 2022
Simple, Strongly Typed UserDefaults for iOS, macOS and tvOS

简体中文 DefaultsKit leverages Swift 4's powerful Codable capabilities to provide a Simple and Strongly Typed wrapper on top of UserDefaults. It uses less

Nuno Dias 1.4k Dec 26, 2022
Modern interface to UserDefaults + Codable support

Default Modern interface to UserDefaults + Codable support What is Default? Default is a library that extends what UserDefaults can do by providing ex

Nicholas Maccharoli 475 Dec 20, 2022
Why not use UserDefaults to store Codable objects 😉

tl;dr You love Swift's Codable protocol and use it everywhere, who doesn't! Here is an easy and very light way to store and retrieve -reasonable amoun

Omar Albeik 452 Oct 17, 2022
Swifty and modern UserDefaults

Defaults Swifty and modern UserDefaults Store key-value pairs persistently across launches of your app. It uses NSUserDefaults underneath but exposes

Sindre Sorhus 1.3k Dec 31, 2022
🔍 Browse and edit UserDefaults on your app

UserDefaults-Browser Browse and edit UserDefaults on your app. (SwiftUI or UIKit) Browse Edit (as JSON) Edit (Date) Export Note: We recommend to use S

Yusuke Hosonuma 25 Nov 3, 2022
⚙️ A tiny property wrapper for UserDefaults. Only 60 lines of code.

⚙️ A tiny property wrapper for UserDefaults. Only 60 lines of code. import Persistent extension UserDefaults { // Optional property @Per

Mezhevikin Alexey 6 Sep 28, 2022
🛶Shallows is a generic abstraction layer over lightweight data storage and persistence.

Shallows Shallows is a generic abstraction layer over lightweight data storage and persistence. It provides a Storage<Key, Value> type, instances of w

Oleg Dreyman 620 Dec 3, 2022
SQLite.swift - A type-safe, Swift-language layer over SQLite3.

SQLite.swift provides compile-time confidence in SQL statement syntax and intent.

Stephen Celis 8.7k Jan 3, 2023
A Swift wrapper for system shell over posix_spawn with search path and env support.

AuxiliaryExecute A Swift wrapper for system shell over posix_spawn with search path and env support. Usage import AuxiliaryExecute AuxiliaryExecute.l

Lakr Aream 11 Sep 13, 2022
Zephyr synchronizes specific keys and/or all of your UserDefaults over iCloud using NSUbiquitousKeyValueStore.

Zephyr ??️ Effortlessly sync UserDefaults over iCloud About Zephyr synchronizes specific keys and/or all of your UserDefaults over iCloud using NSUbiq

Arthur Ariel Sabintsev 845 Jan 6, 2023
SecureDefaults is a wrapper over UserDefaults/NSUserDefaults with an extra AES-256 encryption layer

SecureDefaults for iOS, macOS Requirements • Usage • Installation • Contributing • Acknowledgments • Contributing • Author • License SecureDefaults is

Victor Peschenkov 216 Dec 22, 2022
A lightweight wrapper over UserDefaults/NSUserDefaults with an additional layer of AES-256 encryption

SecureDefaults for iOS, macOS Requirements • Usage • Installation • Contributing • Acknowledgments • Contributing • Author • License SecureDefaults is

Victor Peschenkov 216 Dec 22, 2022
Securely synchronize any CareKit 2.1+ based app to a Parse Server Cloud. Compatible with parse-hipaa.

ParseCareKit Use at your own risk. There is no promise that this is HIPAA compliant and we are not responsible for any mishandling of your data This f

Network Reconnaissance Lab 31 Nov 24, 2022
The simplest abstraction to synchronize local data with remote source. For iOS, wirtten in swift.

Purpose The simplest abstraction to synchronize local data with remote source. For iOS, written in swift. Overview Many applications uses remote serve

Siarhei Ladzeika 7 Mar 17, 2022
Synco - Synco uses Firebase's Realtime Database to synchronize data across multiple devices, in real time

Synco Synco uses Firebase's Realtime Database to synchronize a color across mult

Alessio 0 Feb 7, 2022
CloudCore is a framework that manages syncing between iCloud (CloudKit) and Core Data written on native Swift.

CloudCore CloudCore is a framework that manages syncing between iCloud (CloudKit) and Core Data written on native Swift. Features Leveraging NSPersist

deeje cooley 123 Dec 31, 2022