GithubHelp home page GithubHelp logo

sample-cloudkit-privatedb-sync's Introduction

CloudKit Samples: Private Sync with Subscriptions and Push

Goals

This project demonstrates using CloudKit Database Subscriptions and push notifications to keep two separate instances of an app in sync. Ideally it is run on both a simulator and a real device, and content changes made on the simulator are received and reflected on the device via CloudKit Subscriptions, similar to the functionality of the Notes or Photos apps.

Prerequisites

  • A Mac with Xcode 13 (or later) installed is required to build and test this project.
  • An iOS device which will receive CloudKit change notifications is required to install and run the app on.
  • An active Apple Developer Program membership is needed to create a CloudKit container and sign the app to run on a device.

Note: Simulators cannot register for remote push notifications. Running this sample on a device is required to receive CKSubscription push notifications and observe syncing functionality.

Setup Instructions

  1. Ensure you are logged into your developer account in Xcode with an active membership.
  2. In the “Signing & Capabilities” tab of the PrivateSync target, ensure your team is selected in the Signing section, and there is a valid container selected under the “iCloud” section.
  3. Ensure that both the simulator you wish to use and the device you will run the app on are logged into the same iCloud account.

Using Your Own iCloud Container

  • Create a new iCloud container through Xcode’s “Signing & Capabilities” tab of the PrivateSync app target.
  • Update the containerIdentifier property in Config.swift with your new iCloud container identifier.

How it Works

  • On first launch, the app creates a custom zone on the Private Database named “Contacts”, and subscribes to all record changes on that zone.
  • When running on a device, the app also registers with APNs (Apple Push Notification service), which is the mechanism for receiving information about changes through the aforementioned subscription.
  • After this initialization process, the app fetches the latest changes from the server, using a change token representing the last time changes were fetched and processed if available. On first launch, no local token is available, so all records are returned, and the token returned from this operation is saved.
  • The app’s main UI displays a list of Contacts. When the user adds a new Contact through the UI, a new record is created and saved to the database, and if successful, also saves this to a local store. This will trigger the UI to update and include the new Contact on the main list view.
  • Creating a new record triggers a notification to other devices which are registered for push notifications with the app through the CKRecordZoneSubscription created on first launch.
  • Devices receiving this notification will react by fetching the latest changes on the zone using the last known change token, and receive only the set of records that have changed since that change token was received. The records are updated locally and the UI now reflects the latest database state once again.

Example Flow

  1. Run the app on a device. Latest changes are fetched and a change token is stored.
  2. Repeat the above on a simulator, and add a new contact through the UI.
  3. The device receives a background push notification flagging that a change has occurred.
  4. The device fetches the changes, passing along the change token received in step 1. Only the new contact added in step 2 is returned and processed, and now shows on the UI.

Things To Learn

  • Creating a custom CloudKit Record Zone.
  • Creating a CloudKit Subscription that listens to database changes and sends a content-available push notification on change events.
  • Registering for push notifications with a SwiftUI-compatible UIApplicationDelegate class.
  • Receiving and handling a CKNotification.
  • Using a cached CKServerChangeToken to fetch only record changes and deletions since the last sync.
  • Adding, removing, and merging remote changes into a local cache, and reflecting those changes live in a UI.

Note on Swift Concurrency

This project uses Swift concurrency APIs. A prior completionHandler-based implementation has been tagged pre-async.

Further Reading

sample-cloudkit-privatedb-sync's People

Contributors

foup-crypt avatar sjrmanning avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

sample-cloudkit-privatedb-sync's Issues

Should local cache be saved first then save server change token?

/// Save our new change token representing this point in time.
saveChangeToken(changes.changeToken)
/// Write updated local cache to disk.
await saveLocalCache()

Hi there, I noticed this sample app saves the server change token before local cache were being saved.
Doesn't this mean if the app exits before local cache saved to the UserDefaults, the app will only fetch changes from the newest token the next time it starts and ignoring all previous changes?

Notification comes early than changes are applied (Contact creation)

Hi, thank you for the repo I got a lot of information
I would open just discussion bot not issue about the topic but don't see the option for discussion
I test the example on iPad and iPhone

When I add Contact the notification on other devices comes early then Contact was created in the cloud data base, so fetchLatestChanges does not fetch new dataset with created record
When I delete there's no such effect everything is fine

The only solution currently to add delay(Task.sleep) before
try await PrivateSyncApp.vm.fetchLatestChanges()
then everything is fine

Do you have the same effect ?

Enhancing Smartphone OS with Pixel Art Filter for Privacy & AI Countermeasures

Dear Android and iOS Development Teams,

I hope this message finds you well. As a passionate user of your respective operating systems, I have a suggestion that I believe could significantly enhance both the user experience and privacy of smartphone users. I propose the inclusion of a built-in pixel art filter, similar to the functionality provided by tools like pixa.pics, to counteract the ease with which artificial intelligence can generate deepfakes.

Pixel art has unique emergent properties that are not present in the typical information body used to feed AI algorithms. By incorporating a pixel art filter that scales with the camera's resolution, the images captured by smartphones would not only be visually appealing, but also provide an additional layer of protection against potential misuse by AI systems. As many smartphone cameras currently generate images that can be easily exploited by AI, offering an alternative that is both aesthetically pleasing and more secure would be a valuable addition to your operating systems.

The incorporation of a pixel art filter would reduce the risk of deepfake creation, as AI systems would struggle to convincingly replicate the intricacies of pixel art. This would, in turn, provide users with greater privacy and make it more challenging for bad actors to misuse their photos or videos. Furthermore, this feature could become a unique selling point for Android and iOS devices, setting them apart from competitors and reinforcing your commitment to user privacy and safety.

I understand that integrating such a feature requires time and resources, but I truly believe the benefits it would bring to users far outweigh the costs. I would be delighted to discuss this further and provide any additional information or support you may require. Thank you for considering my suggestion, and I look forward to hearing your thoughts on this matter.

Sincerely,

A. Matias

This app does not work

Error message: "Error saving new contact: <CKError 0x600003c877b0: "Bad Container" (5/1014); "Couldn't get container configuration from the server for container

The app compiles and runs but it does not seem to save any contacts

I'm a bit stuck here. I've done the following:

  1. I downloaded the project.
  2. I verified that I was signed-in to my active developer account in Xcode.
  3. Xcode version is: 14.3.1 (14E300c).
  4. I configured the signing team, app bundle of my own and iCloud container (the container id is the same as the app id but with the iCloud prefix).
  5. I configured the iCloud container id in the Config.swift file.
  6. I ran the project on two devices, both showed "Did register for remote notifications" in the console.
  7. Both devices are running the latest public version of iOS and iPadOS (iOS 16.5.1 (20F75) and iPadOS 16.5.1 (20F75)) and both are signed-in with the same Apple ID Account.

When I run the app on the devices, it initially shows and empty contacts screen, when I try to add a new contact it just dismisses the screen and no contact is added and nothing happens on the other device.

Also, no message is shown on the console when trying to add a contact in the other device or the current one.

I'm attaching a screen recording of this running on an iPad.

RPReplay_Final1689618220.mp4

Any ideas of what I might be missing?

Thanks in advance.

Add basic error handling

It might help those trying for the sample for the first time to discover errors in their CloudKit container configuration if the error "Couldn't get container configuration from the server for container containerID" was output to the console. I was able to see by adding the code below but it originates in line 65 of ViewModel.swift let changes = try await database.recordZoneChanges

            .onAppear {
                Task {
                    do {
                        try await vm.initialize() // replaces try? await vm.initialize()
                        try await vm.fetchLatestChanges()
                    }
                    catch {
                        print(error.localizedDescription)
                    }
                }
            }

I had the same problem as #6 and had to do some debugging to fix it, it was because I changed the bundle ID. Perhaps if the usual sample code disambiguator routine for an automatic unique bundle ID then it might prevent requiring to edit the bundle ID at all?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.