CKSyncEngine

Struct CKSyncEngine 

Source
pub struct CKSyncEngine { /* private fields */ }
Available on crate feature CKSyncEngine only.
Expand description

CKSyncEngine encapsulates the logic of syncing data with a CloudKit database.

Syncing with CloudKit involves many moving pieces. Apps need to schedule syncs, create and batch operations, subscribe to database changes, listen for push notifications, store sync state, handle a multitude of errors, and more. CKSyncEngine is designed to encapsulate this logic in a higher-level API.

§Start Your Sync Engine

Generally, you should initialize your CKSyncEngine soon after your process launches. The sync engine will perform work in the background on your behalf, and it needs to be initialized so that it can properly listen for push notifications and handle scheduled sync tasks.

When initializing your sync engine, you need to provide an object conforming to the CKSyncEngineDelegate protocol. This protocol is the main method of communication between the sync engine and your app. You also need to provide your last known version of the CKSyncEngine/State/Serialization. See CKSyncEngine/State and CKSyncEngine/Event/StateUpdate for more details on the sync engine state.

Note that before using CKSyncEngine in your app, you need to add the CloudKit and remote notification capabilities.

§Sending Changes to the Server

In order to send changes to the server, you first need to tell the sync engine you have pending changes to send. You can do this by adding pending changes to the sync engine’s CKSyncEngine/state property.

When you add pending changes to the state, the sync engine will schedule a task to sync. When the sync task runs, the sync engine will start sending changes to the server. The sync engine will automatically send database changes from CKSyncEngine/State/pendingDatabaseChanges, but you need to provide the record zone changes yourself. In order to send record zone changes, you need to return them from -[CKSyncEngineDelegate syncEngine:nextRecordZoneChangeBatchForContext:].

When the sync engine finishes sending a batch of changes to the server, your CKSyncEngineDelegate will receive CKSyncEngine/Event/sentDatabaseChanges(_:) and CKSyncEngine/Event/sentRecordZoneChanges(_:) events. These events will notify you of the success or failure of the changes you tried to send.

At a high level, sending changes to the server happens with the following order of operations:

  1. You add pending changes to CKSyncEngine/state.
  2. You receive CKSyncEngine/Event/willSendChanges(_:) in -[CKSyncEngineDelegate syncEngine:handleEvent:]
  3. If there are pending database changes, the sync engine sends the next batch.
  4. If any database changes were sent, your delegate receivesCKSyncEngine/Event/sentDatabaseChanges(_:).
  5. Repeat from step 3 until all pending database changes are sent, then move on to record zone changes in step 6.
  6. The sync engine asks for the next batch of record zone changes by calling -[CKSyncEngineDelegate syncEngine:nextRecordZoneChangeBatchForContext:].
  7. The sync engine sends the next record zone change batch to the server.
  8. If any record zone changes were sent, your delegate receives CKSyncEngine/Event/sentRecordZoneChanges(_:).
  9. If you added any pending database changes during steps 6-8, the sync engine repeats from step 3. Otherwise, it repeats from step 6.
  10. When all pending changes are sent, your delegate receives CKSyncEngine/Event/didSendChanges(_:).

§Fetching Changes from the Server

The sync engine will automatically listen for remote notifications, and it will fetch changes from the server when necessary. Generally, you’ll receive events in this order:

  1. Your delegate receives CKSyncEngine/Event/willFetchChanges(_:).
  2. If there are new database changes to fetch, you receive batches of them in CKSyncEngine/Event/fetchedDatabaseChanges(_:) events.
  3. If there are new record zone changes to fetch, you will receive CKSyncEngine/Event/willFetchRecordZoneChanges(_:) for each zone that has new changes.
  4. The sync engine fetches record zone changes and gives you batches of them in CKSyncEngine/Event/fetchedRecordZoneChanges(_:) events.
  5. Your delegate receives CKSyncEngine/Event/didFetchRecordZoneChanges(_:) for each zone that had changes to fetch.
  6. Your delegate receives CKSyncEngine/Event/didFetchChanges(_:), indicating that sync engine has finished fetching changes.

§Sync Scheduling

§Automatic sync

By default, the sync engine will automatically schedule sync tasks on your behalf. If the user is signed in, the device has a network connection, and the system is generally in a good state, these scheduled syncs will happen relatively quickly. However, if the device has no network, is low on power, or is otherwise under a heavy load, these automatic syncs might be delayed. Similarly, if the user isn’t signed in to an account, the sync engine won’t perform any sync tasks at all.

§Manual sync

Generally, you should rely on this automatic sync behavior, but there may be some cases where you want to manually trigger a sync. For example, if you have a pull-to-refresh UI, you can call CKSyncEngine/fetchChanges(_:) to tell the sync engine to fetch immediately. Or if you want to provide some sort of “backup now” button, you can call CKSyncEngine/sendChanges(_:) to send to the server immediately.

§Testing

These manual sync functions might also be useful during automated testing. When writing automated tests, you can turn off automatic sync via CKSyncEngine/Configuration/automaticallySync. Then, you’ll have complete control over the ordering of sync events. This allows you to interject behavior in the sync flow and simulate specific sequences of events.

§Error Handling

There are some transient errors that the sync engine will handle automatically behind the scenes. The sync engine will retry the operations for these transient errors automatically when it makes sense to do so. Specifically, the sync engine will handle the following errors on your behalf:

  • CKError/notAuthenticated
  • CKError/accountTemporarilyUnavailable
  • CKError/networkFailure
  • CKError/networkUnavailable
  • CKError/requestRateLimited
  • CKError/serviceUnavailable
  • CKError/zoneBusy

When the sync engine encounters one of these errors, it will wait for the system to be in a good state and try again. For example, if the server sends back a .requestRateLimited error, the sync engine will respect this throttle and try again after the retry-after time.

CKSyncEngine will not handle errors that require application-specific logic. For example, if you try to save a record and get a CKError/serverRecordChanged, you need to handle that error yourself. There are plenty of errors that the sync engine cannot handle on your behalf, see CKError for a list of all the possible errors.

§Accounts

CKSyncEngine monitors for account status, and it will only sync if there’s an account signed in. Because of this, you can initialize your CKSyncEngine at any time, regardless of account status. If there is no account, or if the user disabled sync in settings, the sync engine will stay dormant in the background. Once an account is available, the sync engine will start syncing automatically.

It will also listen for when the user signs in or out of their account. When it notices an account change, it will send an CKSyncEngine/Event/accountChange(_:) to your delegate. It’s your responsibility to react appropriately to this change and update your local persistence.

See also Apple’s documentation

Implementations§

Source§

impl CKSyncEngine

Source

pub unsafe fn initWithConfiguration( this: Allocated<Self>, configuration: &CKSyncEngineConfiguration, ) -> Retained<Self>

Available on crate feature CKSyncEngineConfiguration only.

Initializes a CKSyncEngine with the given configuration. See properties on CKSyncEngineConfiguration for more details on all the options.

Source

pub unsafe fn init(this: Allocated<Self>) -> Retained<Self>

Source

pub unsafe fn new() -> Retained<Self>

Source

pub unsafe fn database(&self) -> Retained<CKDatabase>

Available on crate feature CKDatabase only.

The database this sync engine will sync with.

Source

pub unsafe fn state(&self) -> Retained<CKSyncEngineState>

Available on crate feature CKSyncEngineState only.

A collection of state properties used to efficiently manage sync engine operation. See CKSyncEngineState for more details.

Source

pub unsafe fn fetchChangesWithCompletionHandler( &self, completion_handler: Option<&DynBlock<dyn Fn(*mut NSError)>>, )

Available on crate feature block2 only.

Fetches changes from the server immediately, bypassing the system scheduler.

By default, the sync engine will automatically fetch changes from the server for you, and you should not have to call this function. However, you can call this if for some reason you need to ensure all changes have been fetched from the server before proceeding. For example, you might use this in your tests to simulate specific sync scenarios.

Fetching changes from the server might result in some events being posted to your delegate via handleEvent. For example, you might receive a CKSyncEngineWillFetchChangesEvent or CKSyncEngineDidFetchChangesEvent. This will not complete until all the relevant events have been handled by your delegate.

§Safety

completion_handler block must be sendable.

Source

pub unsafe fn fetchChangesWithOptions_completionHandler( &self, options: &CKSyncEngineFetchChangesOptions, completion_handler: Option<&DynBlock<dyn Fn(*mut NSError)>>, )

Available on crate feature block2 only.

Fetches changes from the server with the specified options. See fetchChangesWithCompletionHandler: for more information.

§Safety

completion_handler block must be sendable.

Source

pub unsafe fn sendChangesWithCompletionHandler( &self, completion_handler: Option<&DynBlock<dyn Fn(*mut NSError)>>, )

Available on crate feature block2 only.

Sends any pending changes to the server immediately, bypassing the system scheduler.

By default, the sync engine will automatically send changes to the server for you, and you should not have to call this function. However, you can call this if for some reason you need to ensure all changes have been sent to the server before proceeding. For example, you might consider using this in your tests to simulate specific sync scenarios.

Sending changes to the server might result in some events being posted to your delegate via handleEvent. For example, you might receive a CKSyncEngineWillSendChangesEvent or CKSyncEngineDidSendChangesEvent. This function will not return until all the relevant events have been handled by your delegate.

§Safety

completion_handler block must be sendable.

Source

pub unsafe fn sendChangesWithOptions_completionHandler( &self, options: &CKSyncEngineSendChangesOptions, completion_handler: Option<&DynBlock<dyn Fn(*mut NSError)>>, )

Available on crate feature block2 only.

Sends pending changes to the server with the specified options. See discussion in sendChangesWithCompletionHandler: for more information.

§Safety

completion_handler block must be sendable.

Source

pub unsafe fn cancelOperationsWithCompletionHandler( &self, completion_handler: Option<&DynBlock<dyn Fn()>>, )

Available on crate feature block2 only.

Cancels any currently executing or pending sync operations.

Note that cancellation does not happen synchronously, and it’s possible some in-flight operations will succeed.

§Safety

completion_handler block must be sendable.

Methods from Deref<Target = NSObject>§

Source

pub fn doesNotRecognizeSelector(&self, sel: Sel) -> !

Handle messages the object doesn’t recognize.

See Apple’s documentation for details.

Methods from Deref<Target = AnyObject>§

Source

pub fn class(&self) -> &'static AnyClass

Dynamically find the class of this object.

§Panics

May panic if the object is invalid (which may be the case for objects returned from unavailable init/new methods).

§Example

Check that an instance of NSObject has the precise class NSObject.

use objc2::ClassType;
use objc2::runtime::NSObject;

let obj = NSObject::new();
assert_eq!(obj.class(), NSObject::class());
Source

pub unsafe fn get_ivar<T>(&self, name: &str) -> &T
where T: Encode,

👎Deprecated: this is difficult to use correctly, use Ivar::load instead.

Use Ivar::load instead.

§Safety

The object must have an instance variable with the given name, and it must be of type T.

See Ivar::load_ptr for details surrounding this.

Source

pub fn downcast_ref<T>(&self) -> Option<&T>
where T: DowncastTarget,

Attempt to downcast the object to a class of type T.

This is the reference-variant. Use Retained::downcast if you want to convert a retained object to another type.

§Mutable classes

Some classes have immutable and mutable variants, such as NSString and NSMutableString.

When some Objective-C API signature says it gives you an immutable class, it generally expects you to not mutate that, even though it may technically be mutable “under the hood”.

So using this method to convert a NSString to a NSMutableString, while not unsound, is generally frowned upon unless you created the string yourself, or the API explicitly documents the string to be mutable.

See Apple’s documentation on mutability and on isKindOfClass: for more details.

§Generic classes

Objective-C generics are called “lightweight generics”, and that’s because they aren’t exposed in the runtime. This makes it impossible to safely downcast to generic collections, so this is disallowed by this method.

You can, however, safely downcast to generic collections where all the type-parameters are AnyObject.

§Panics

This works internally by calling isKindOfClass:. That means that the object must have the instance method of that name, and an exception will be thrown (if CoreFoundation is linked) or the process will abort if that is not the case. In the vast majority of cases, you don’t need to worry about this, since both root objects NSObject and NSProxy implement this method.

§Examples

Cast an NSString back and forth from NSObject.

use objc2::rc::Retained;
use objc2_foundation::{NSObject, NSString};

let obj: Retained<NSObject> = NSString::new().into_super();
let string = obj.downcast_ref::<NSString>().unwrap();
// Or with `downcast`, if we do not need the object afterwards
let string = obj.downcast::<NSString>().unwrap();

Try (and fail) to cast an NSObject to an NSString.

use objc2_foundation::{NSObject, NSString};

let obj = NSObject::new();
assert!(obj.downcast_ref::<NSString>().is_none());

Try to cast to an array of strings.

use objc2_foundation::{NSArray, NSObject, NSString};

let arr = NSArray::from_retained_slice(&[NSObject::new()]);
// This is invalid and doesn't type check.
let arr = arr.downcast_ref::<NSArray<NSString>>();

This fails to compile, since it would require enumerating over the array to ensure that each element is of the desired type, which is a performance pitfall.

Downcast when processing each element instead.

use objc2_foundation::{NSArray, NSObject, NSString};

let arr = NSArray::from_retained_slice(&[NSObject::new()]);

for elem in arr {
    if let Some(data) = elem.downcast_ref::<NSString>() {
        // handle `data`
    }
}

Trait Implementations§

Source§

impl AsRef<AnyObject> for CKSyncEngine

Source§

fn as_ref(&self) -> &AnyObject

Converts this type into a shared reference of the (usually inferred) input type.
Source§

impl AsRef<CKSyncEngine> for CKSyncEngine

Source§

fn as_ref(&self) -> &Self

Converts this type into a shared reference of the (usually inferred) input type.
Source§

impl AsRef<NSObject> for CKSyncEngine

Source§

fn as_ref(&self) -> &NSObject

Converts this type into a shared reference of the (usually inferred) input type.
Source§

impl Borrow<AnyObject> for CKSyncEngine

Source§

fn borrow(&self) -> &AnyObject

Immutably borrows from an owned value. Read more
Source§

impl Borrow<NSObject> for CKSyncEngine

Source§

fn borrow(&self) -> &NSObject

Immutably borrows from an owned value. Read more
Source§

impl ClassType for CKSyncEngine

Source§

const NAME: &'static str = "CKSyncEngine"

The name of the Objective-C class that this type represents. Read more
Source§

type Super = NSObject

The superclass of this class. Read more
Source§

type ThreadKind = <<CKSyncEngine as ClassType>::Super as ClassType>::ThreadKind

Whether the type can be used from any thread, or from only the main thread. Read more
Source§

fn class() -> &'static AnyClass

Get a reference to the Objective-C class that this type represents. Read more
Source§

fn as_super(&self) -> &Self::Super

Get an immutable reference to the superclass.
Source§

impl Debug for CKSyncEngine

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Deref for CKSyncEngine

Source§

type Target = NSObject

The resulting type after dereferencing.
Source§

fn deref(&self) -> &Self::Target

Dereferences the value.
Source§

impl Hash for CKSyncEngine

Source§

fn hash<H: Hasher>(&self, state: &mut H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where H: Hasher, Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl Message for CKSyncEngine

Source§

fn retain(&self) -> Retained<Self>
where Self: Sized,

Increment the reference count of the receiver. Read more
Source§

impl NSObjectProtocol for CKSyncEngine

Source§

fn isEqual(&self, other: Option<&AnyObject>) -> bool
where Self: Sized + Message,

Check whether the object is equal to an arbitrary other object. Read more
Source§

fn hash(&self) -> usize
where Self: Sized + Message,

An integer that can be used as a table address in a hash table structure. Read more
Source§

fn isKindOfClass(&self, cls: &AnyClass) -> bool
where Self: Sized + Message,

Check if the object is an instance of the class, or one of its subclasses. Read more
Source§

fn is_kind_of<T>(&self) -> bool
where T: ClassType, Self: Sized + Message,

👎Deprecated: use isKindOfClass directly, or cast your objects with AnyObject::downcast_ref
Check if the object is an instance of the class type, or one of its subclasses. Read more
Source§

fn isMemberOfClass(&self, cls: &AnyClass) -> bool
where Self: Sized + Message,

Check if the object is an instance of a specific class, without checking subclasses. Read more
Source§

fn respondsToSelector(&self, aSelector: Sel) -> bool
where Self: Sized + Message,

Check whether the object implements or inherits a method with the given selector. Read more
Source§

fn conformsToProtocol(&self, aProtocol: &AnyProtocol) -> bool
where Self: Sized + Message,

Check whether the object conforms to a given protocol. Read more
Source§

fn description(&self) -> Retained<NSObject>
where Self: Sized + Message,

A textual representation of the object. Read more
Source§

fn debugDescription(&self) -> Retained<NSObject>
where Self: Sized + Message,

A textual representation of the object to use when debugging. Read more
Source§

fn isProxy(&self) -> bool
where Self: Sized + Message,

Check whether the receiver is a subclass of the NSProxy root class instead of the usual NSObject. Read more
Source§

fn retainCount(&self) -> usize
where Self: Sized + Message,

The reference count of the object. Read more
Source§

impl PartialEq for CKSyncEngine

Source§

fn eq(&self, other: &Self) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl RefEncode for CKSyncEngine

Source§

const ENCODING_REF: Encoding = <NSObject as ::objc2::RefEncode>::ENCODING_REF

The Objective-C type-encoding for a reference of this type. Read more
Source§

impl DowncastTarget for CKSyncEngine

Source§

impl Eq for CKSyncEngine

Source§

impl Send for CKSyncEngine

Source§

impl Sync for CKSyncEngine

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<'a, T> AnyThread for T
where T: ClassType<ThreadKind = dyn AnyThread + 'a> + ?Sized,

Source§

fn alloc() -> Allocated<Self>
where Self: Sized + ClassType,

Allocate a new instance of the class. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<P, T> Receiver for P
where P: Deref<Target = T> + ?Sized, T: ?Sized,

Source§

type Target = T

🔬This is a nightly-only experimental API. (arbitrary_self_types)
The target type on which the method may be called.
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> AutoreleaseSafe for T
where T: ?Sized,