pub struct CKSyncEngine { /* private fields */ }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:
- You add pending changes to
CKSyncEngine/state. - You receive
CKSyncEngine/Event/willSendChanges(_:)in-[CKSyncEngineDelegate syncEngine:handleEvent:] - If there are pending database changes, the sync engine sends the next batch.
- If any database changes were sent, your delegate receives
CKSyncEngine/Event/sentDatabaseChanges(_:). - Repeat from step 3 until all pending database changes are sent, then move on to record zone changes in step 6.
- The sync engine asks for the next batch of record zone changes by calling
-[CKSyncEngineDelegate syncEngine:nextRecordZoneChangeBatchForContext:]. - The sync engine sends the next record zone change batch to the server.
- If any record zone changes were sent, your delegate receives
CKSyncEngine/Event/sentRecordZoneChanges(_:). - If you added any pending database changes during steps 6-8, the sync engine repeats from step 3. Otherwise, it repeats from step 6.
- 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:
- Your delegate receives
CKSyncEngine/Event/willFetchChanges(_:). - If there are new database changes to fetch, you receive batches of them in
CKSyncEngine/Event/fetchedDatabaseChanges(_:)events. - If there are new record zone changes to fetch, you will receive
CKSyncEngine/Event/willFetchRecordZoneChanges(_:)for each zone that has new changes. - The sync engine fetches record zone changes and gives you batches of them in
CKSyncEngine/Event/fetchedRecordZoneChanges(_:)events. - Your delegate receives
CKSyncEngine/Event/didFetchRecordZoneChanges(_:)for each zone that had changes to fetch. - 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/notAuthenticatedCKError/accountTemporarilyUnavailableCKError/networkFailureCKError/networkUnavailableCKError/requestRateLimitedCKError/serviceUnavailableCKError/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
impl CKSyncEngine
Sourcepub unsafe fn initWithConfiguration(
this: Allocated<Self>,
configuration: &CKSyncEngineConfiguration,
) -> Retained<Self>
Available on crate feature CKSyncEngineConfiguration only.
pub unsafe fn initWithConfiguration( this: Allocated<Self>, configuration: &CKSyncEngineConfiguration, ) -> Retained<Self>
CKSyncEngineConfiguration only.Initializes a CKSyncEngine with the given configuration.
See properties on CKSyncEngineConfiguration for more details on all the options.
pub unsafe fn init(this: Allocated<Self>) -> Retained<Self>
pub unsafe fn new() -> Retained<Self>
Sourcepub unsafe fn database(&self) -> Retained<CKDatabase>
Available on crate feature CKDatabase only.
pub unsafe fn database(&self) -> Retained<CKDatabase>
CKDatabase only.The database this sync engine will sync with.
Sourcepub unsafe fn state(&self) -> Retained<CKSyncEngineState>
Available on crate feature CKSyncEngineState only.
pub unsafe fn state(&self) -> Retained<CKSyncEngineState>
CKSyncEngineState only.A collection of state properties used to efficiently manage sync engine operation.
See CKSyncEngineState for more details.
Sourcepub unsafe fn fetchChangesWithCompletionHandler(
&self,
completion_handler: Option<&DynBlock<dyn Fn(*mut NSError)>>,
)
Available on crate feature block2 only.
pub unsafe fn fetchChangesWithCompletionHandler( &self, completion_handler: Option<&DynBlock<dyn Fn(*mut NSError)>>, )
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.
Sourcepub unsafe fn fetchChangesWithOptions_completionHandler(
&self,
options: &CKSyncEngineFetchChangesOptions,
completion_handler: Option<&DynBlock<dyn Fn(*mut NSError)>>,
)
Available on crate feature block2 only.
pub unsafe fn fetchChangesWithOptions_completionHandler( &self, options: &CKSyncEngineFetchChangesOptions, completion_handler: Option<&DynBlock<dyn Fn(*mut NSError)>>, )
block2 only.Fetches changes from the server with the specified options.
See fetchChangesWithCompletionHandler: for more information.
§Safety
completion_handler block must be sendable.
Sourcepub unsafe fn sendChangesWithCompletionHandler(
&self,
completion_handler: Option<&DynBlock<dyn Fn(*mut NSError)>>,
)
Available on crate feature block2 only.
pub unsafe fn sendChangesWithCompletionHandler( &self, completion_handler: Option<&DynBlock<dyn Fn(*mut NSError)>>, )
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.
Sourcepub unsafe fn sendChangesWithOptions_completionHandler(
&self,
options: &CKSyncEngineSendChangesOptions,
completion_handler: Option<&DynBlock<dyn Fn(*mut NSError)>>,
)
Available on crate feature block2 only.
pub unsafe fn sendChangesWithOptions_completionHandler( &self, options: &CKSyncEngineSendChangesOptions, completion_handler: Option<&DynBlock<dyn Fn(*mut NSError)>>, )
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.
Sourcepub unsafe fn cancelOperationsWithCompletionHandler(
&self,
completion_handler: Option<&DynBlock<dyn Fn()>>,
)
Available on crate feature block2 only.
pub unsafe fn cancelOperationsWithCompletionHandler( &self, completion_handler: Option<&DynBlock<dyn Fn()>>, )
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>§
Sourcepub fn doesNotRecognizeSelector(&self, sel: Sel) -> !
pub fn doesNotRecognizeSelector(&self, sel: Sel) -> !
Handle messages the object doesn’t recognize.
See Apple’s documentation for details.
Methods from Deref<Target = AnyObject>§
Sourcepub fn class(&self) -> &'static AnyClass
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());Sourcepub unsafe fn get_ivar<T>(&self, name: &str) -> &Twhere
T: Encode,
👎Deprecated: this is difficult to use correctly, use Ivar::load instead.
pub unsafe fn get_ivar<T>(&self, name: &str) -> &Twhere
T: Encode,
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.
Sourcepub fn downcast_ref<T>(&self) -> Option<&T>where
T: DowncastTarget,
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
impl AsRef<AnyObject> for CKSyncEngine
Source§impl AsRef<CKSyncEngine> for CKSyncEngine
impl AsRef<CKSyncEngine> for CKSyncEngine
Source§impl AsRef<NSObject> for CKSyncEngine
impl AsRef<NSObject> for CKSyncEngine
Source§impl Borrow<AnyObject> for CKSyncEngine
impl Borrow<AnyObject> for CKSyncEngine
Source§impl Borrow<NSObject> for CKSyncEngine
impl Borrow<NSObject> for CKSyncEngine
Source§impl ClassType for CKSyncEngine
impl ClassType for CKSyncEngine
Source§const NAME: &'static str = "CKSyncEngine"
const NAME: &'static str = "CKSyncEngine"
Source§type ThreadKind = <<CKSyncEngine as ClassType>::Super as ClassType>::ThreadKind
type ThreadKind = <<CKSyncEngine as ClassType>::Super as ClassType>::ThreadKind
Source§impl Debug for CKSyncEngine
impl Debug for CKSyncEngine
Source§impl Deref for CKSyncEngine
impl Deref for CKSyncEngine
Source§impl Hash for CKSyncEngine
impl Hash for CKSyncEngine
Source§impl Message for CKSyncEngine
impl Message for CKSyncEngine
Source§impl NSObjectProtocol for CKSyncEngine
impl NSObjectProtocol for CKSyncEngine
Source§fn isEqual(&self, other: Option<&AnyObject>) -> bool
fn isEqual(&self, other: Option<&AnyObject>) -> bool
Source§fn hash(&self) -> usize
fn hash(&self) -> usize
Source§fn isKindOfClass(&self, cls: &AnyClass) -> bool
fn isKindOfClass(&self, cls: &AnyClass) -> bool
Source§fn is_kind_of<T>(&self) -> bool
fn is_kind_of<T>(&self) -> bool
isKindOfClass directly, or cast your objects with AnyObject::downcast_ref