objc2-cloud-kit 0.3.2

Bindings to the CloudKit framework
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
//! This file has been automatically generated by `objc2`'s `header-translator`.
//! DO NOT EDIT
use core::ffi::*;
use core::ptr::NonNull;
use objc2::__framework_prelude::*;
use objc2_foundation::*;

use crate::*;

extern_class!(
    /// `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 receives``CKSyncEngine/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](https://developer.apple.com/documentation/cloudkit/cksyncengine?language=objc)
    #[unsafe(super(NSObject))]
    #[derive(Debug, PartialEq, Eq, Hash)]
    pub struct CKSyncEngine;
);

unsafe impl Send for CKSyncEngine {}

unsafe impl Sync for CKSyncEngine {}

extern_conformance!(
    unsafe impl NSObjectProtocol for CKSyncEngine {}
);

impl CKSyncEngine {
    extern_methods!(
        #[cfg(feature = "CKSyncEngineConfiguration")]
        /// Initializes a `CKSyncEngine` with the given configuration.
        /// See properties on ``CKSyncEngineConfiguration`` for more details on all the options.
        #[unsafe(method(initWithConfiguration:))]
        #[unsafe(method_family = init)]
        pub unsafe fn initWithConfiguration(
            this: Allocated<Self>,
            configuration: &CKSyncEngineConfiguration,
        ) -> Retained<Self>;

        #[unsafe(method(init))]
        #[unsafe(method_family = init)]
        pub unsafe fn init(this: Allocated<Self>) -> Retained<Self>;

        #[unsafe(method(new))]
        #[unsafe(method_family = new)]
        pub unsafe fn new() -> Retained<Self>;

        #[cfg(feature = "CKDatabase")]
        /// The database this sync engine will sync with.
        #[unsafe(method(database))]
        #[unsafe(method_family = none)]
        pub unsafe fn database(&self) -> Retained<CKDatabase>;

        #[cfg(feature = "CKSyncEngineState")]
        /// A collection of state properties used to efficiently manage sync engine operation.
        /// See ``CKSyncEngineState`` for more details.
        #[unsafe(method(state))]
        #[unsafe(method_family = none)]
        pub unsafe fn state(&self) -> Retained<CKSyncEngineState>;

        #[cfg(feature = "block2")]
        /// 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.
        #[unsafe(method(fetchChangesWithCompletionHandler:))]
        #[unsafe(method_family = none)]
        pub unsafe fn fetchChangesWithCompletionHandler(
            &self,
            completion_handler: Option<&block2::DynBlock<dyn Fn(*mut NSError)>>,
        );

        #[cfg(feature = "block2")]
        /// Fetches changes from the server with the specified options.
        /// See ``fetchChangesWithCompletionHandler:`` for more information.
        ///
        /// # Safety
        ///
        /// `completion_handler` block must be sendable.
        #[unsafe(method(fetchChangesWithOptions:completionHandler:))]
        #[unsafe(method_family = none)]
        pub unsafe fn fetchChangesWithOptions_completionHandler(
            &self,
            options: &CKSyncEngineFetchChangesOptions,
            completion_handler: Option<&block2::DynBlock<dyn Fn(*mut NSError)>>,
        );

        #[cfg(feature = "block2")]
        /// 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.
        #[unsafe(method(sendChangesWithCompletionHandler:))]
        #[unsafe(method_family = none)]
        pub unsafe fn sendChangesWithCompletionHandler(
            &self,
            completion_handler: Option<&block2::DynBlock<dyn Fn(*mut NSError)>>,
        );

        #[cfg(feature = "block2")]
        /// Sends pending changes to the server with the specified options.
        /// See discussion in ``sendChangesWithCompletionHandler:`` for more information.
        ///
        /// # Safety
        ///
        /// `completion_handler` block must be sendable.
        #[unsafe(method(sendChangesWithOptions:completionHandler:))]
        #[unsafe(method_family = none)]
        pub unsafe fn sendChangesWithOptions_completionHandler(
            &self,
            options: &CKSyncEngineSendChangesOptions,
            completion_handler: Option<&block2::DynBlock<dyn Fn(*mut NSError)>>,
        );

        #[cfg(feature = "block2")]
        /// 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.
        #[unsafe(method(cancelOperationsWithCompletionHandler:))]
        #[unsafe(method_family = none)]
        pub unsafe fn cancelOperationsWithCompletionHandler(
            &self,
            completion_handler: Option<&block2::DynBlock<dyn Fn()>>,
        );
    );
}

extern_protocol!(
    /// An interface by which `CKSyncEngine` communicates with your application.
    ///
    /// See also [Apple's documentation](https://developer.apple.com/documentation/cloudkit/cksyncenginedelegate?language=objc)
    pub unsafe trait CKSyncEngineDelegate: NSObjectProtocol {
        #[cfg(feature = "CKSyncEngineEvent")]
        /// Called when an event occurs during the sync engine's operation.
        ///
        /// This is how you receive updates about local state changes, fetched changes, sent changes, and more.
        /// See ``CKSyncEngineEventType`` and ``CKSyncEngineEvent`` for all the possible events that might be posted.
        ///
        /// ## Event ordering
        ///
        /// Events will be given to your delegate serially.
        /// You will not receive the next event until you have returned from this function for the previous event.
        #[unsafe(method(syncEngine:handleEvent:))]
        #[unsafe(method_family = none)]
        unsafe fn syncEngine_handleEvent(
            &self,
            sync_engine: &CKSyncEngine,
            event: &CKSyncEngineEvent,
        );

        #[cfg(feature = "CKSyncEngineRecordZoneChangeBatch")]
        /// Called to get the next batch of record zone changes to send to the server.
        ///
        /// The sync engine will call this function when it's about to to send changes to the server.
        /// This might happen during an automatically scheduled sync or as a result of you calling `sendChanges`.
        /// Provide the next batch of record zone changes to send by returning them from this function.
        ///
        /// Once the sync engine starts sending changes, it will continue until there are no more pending changes to send.
        /// You can return `nil` to indicate that you have no more pending changes for now.
        /// The next time the sync engine tries to sync, it will call this again to get any new pending changes.
        ///
        /// ## Sending changes for specific zones
        ///
        /// When you call ``CKSyncEngine/sendChanges(_:)`` for a specific set of zone IDs, you should make sure to only send changes for those zones.
        /// You can do this by checking the `zoneIDs` property on ``CKSyncEngineSendChangesContext/options``.
        ///
        /// For example, you might have some code like this:
        ///
        /// ```objc
        /// - (CKSyncEngineRecordZoneChangeBatch *)syncEngine:(CKSyncEngine *)syncEngine nextRecordZoneChangeBatchForContext:(CKSyncEngineSendChangesContext *)context {
        /// CKSyncEngineSendChangesScope *scope = context.options.scope;
        ///
        /// NSMutableArray
        /// <CKSyncEnginePendingRecordZoneChange
        /// *> *pendingChanges = [NSMutableArray new];
        /// for (CKSyncEnginePendingRecordZoneChange *pendingChange in syncEngine.state.pendingRecordZoneChanges) {
        /// if ([scope containsPendingRecordZoneChange:pendingChange]) {
        /// [filteredChanges addObject:pendingChange];
        /// }
        /// }
        ///
        /// CKSyncEngineRecordZoneChangeBatch *batch = [[CKSyncEngineRecordZoneChangeBatch alloc] initWithPendingChanges:pendingChangesToSave recordProvider:^CKRecord * _Nullable(CKRecordID *recordID) {
        /// return [self recordToSaveForRecordID:recordID];
        /// }];
        ///
        /// return batch;
        /// }
        /// ```
        #[unsafe(method(syncEngine:nextRecordZoneChangeBatchForContext:))]
        #[unsafe(method_family = none)]
        unsafe fn syncEngine_nextRecordZoneChangeBatchForContext(
            &self,
            sync_engine: &CKSyncEngine,
            context: &CKSyncEngineSendChangesContext,
        ) -> Option<Retained<CKSyncEngineRecordZoneChangeBatch>>;

        /// Returns a custom set of options for `CKSyncEngine` to use while fetching changes.
        ///
        /// While `CKSyncEngine` fetches changes from the server, it calls this function to determine priority and other options for fetching changes.
        /// This allows you to configure your fetches dynamically while the state changes in your app.
        ///
        /// For example, you can use this to prioritize fetching the object currently showing in the UI.
        /// You can also use this to prioritize specific zones during initial sync.
        ///
        /// By default, `CKSyncEngine` will use whatever options are in the context.
        /// You can return `context.options` if you don't want to perform any customization.
        ///
        /// This callback will be called in between each server request while fetching changes.
        /// This allows the fetching mechanism to react dynamically while your app state changes.
        ///
        /// An example implementation might look something like this:
        /// ```objc
        /// - (CKSyncEngineFetchChangesOptions *)syncEngine:(CKSyncEngine *)syncEngine nextFetchChangesOptionsForContext:(CKSyncEngineFetchChangesContext *)context {
        ///
        /// // Start with the options from the context.
        /// CKSyncEngineFetchChangesOptions *options = context.options;
        ///
        /// // By default, the sync engine will automatically fetch changes for all zones.
        /// // If you know that you only want to sync a specific set of zones, you can override that here.
        /// options.scope = [[CKSyncEngineFetchChangesScope alloc] initWithZoneIDs:
        /// @
        /// [...]];
        ///
        /// // You can prioritize specific zones to be fetched first by putting them in order.
        /// NSMutableArray
        /// <CKRecordZoneID
        /// *> *prioritizedZoneIDs = [[NSMutableArray alloc] init];
        ///
        /// // If you're showing some data in the UI, you might want to prioritize that zone first.
        /// CKRecordZoneID *onScreenZoneID = uiController.currentlyViewedItem.zoneID;
        /// if (onScreenZoneID != nil) {
        /// [prioritizedZoneIDs addObject:onScreenZoneID];
        /// }
        ///
        /// // You could also prioritize special, well-known zones if that makes sense for your app.
        /// // For example, if you have a top-level metadata zone that you'd like to sync first, you can prioritize that here.
        /// CKRecordZoneID *topLevelZoneID = [[CKRecordZoneID alloc] initWithZoneName:
        /// "
        /// MyImportantMetadata"];
        /// [prioritizedZoneIDs addObject:topLevelZoneID];
        ///
        /// options.prioritizedZoneIDs = prioritizedZoneIDs;
        /// return options
        /// }
        /// ```
        #[optional]
        #[unsafe(method(syncEngine:nextFetchChangesOptionsForContext:))]
        #[unsafe(method_family = none)]
        unsafe fn syncEngine_nextFetchChangesOptionsForContext(
            &self,
            sync_engine: &CKSyncEngine,
            context: &CKSyncEngineFetchChangesContext,
        ) -> Retained<CKSyncEngineFetchChangesOptions>;
    }
);

extern_class!(
    /// A set of options to use when fetching changes from the server.
    ///
    /// See also [Apple's documentation](https://developer.apple.com/documentation/cloudkit/cksyncenginefetchchangesoptions?language=objc)
    #[unsafe(super(NSObject))]
    #[derive(Debug, PartialEq, Eq, Hash)]
    pub struct CKSyncEngineFetchChangesOptions;
);

unsafe impl Send for CKSyncEngineFetchChangesOptions {}

unsafe impl Sync for CKSyncEngineFetchChangesOptions {}

extern_conformance!(
    unsafe impl NSCopying for CKSyncEngineFetchChangesOptions {}
);

unsafe impl CopyingHelper for CKSyncEngineFetchChangesOptions {
    type Result = Self;
}

extern_conformance!(
    unsafe impl NSObjectProtocol for CKSyncEngineFetchChangesOptions {}
);

impl CKSyncEngineFetchChangesOptions {
    extern_methods!(
        /// The scope in which to fetch changes from the server.
        #[unsafe(method(scope))]
        #[unsafe(method_family = none)]
        pub unsafe fn scope(&self) -> Retained<CKSyncEngineFetchChangesScope>;

        /// Setter for [`scope`][Self::scope].
        ///
        /// This is [copied][objc2_foundation::NSCopying::copy] when set.
        #[unsafe(method(setScope:))]
        #[unsafe(method_family = none)]
        pub unsafe fn setScope(&self, scope: &CKSyncEngineFetchChangesScope);

        #[cfg(feature = "CKOperationGroup")]
        /// The operation group to use for the underlying operations when fetching changes.
        ///
        /// You might set an operation group with a particular name in order to help you analyze telemetry in the CloudKit Console.
        /// If you don't provide an operation group, a default one will be created for you.
        #[unsafe(method(operationGroup))]
        #[unsafe(method_family = none)]
        pub unsafe fn operationGroup(&self) -> Retained<CKOperationGroup>;

        #[cfg(feature = "CKOperationGroup")]
        /// Setter for [`operationGroup`][Self::operationGroup].
        #[unsafe(method(setOperationGroup:))]
        #[unsafe(method_family = none)]
        pub unsafe fn setOperationGroup(&self, operation_group: &CKOperationGroup);

        #[cfg(feature = "CKRecordZoneID")]
        /// A list of zones that should be prioritized over others while fetching changes.
        ///
        /// `CKSyncEngine` will fetch changes for the zones in this list first before any other zones.
        /// You might use this to prioritize a specific set of zones for initial sync.
        /// You could also prioritize the object currently showing in the UI by putting it first in this list.
        ///
        /// Any zones not included in this list will be prioritized in a default manner.
        /// If a zone in this list has no changes to fetch, then that zone will be ignored.
        #[unsafe(method(prioritizedZoneIDs))]
        #[unsafe(method_family = none)]
        pub unsafe fn prioritizedZoneIDs(&self) -> Retained<NSArray<CKRecordZoneID>>;

        #[cfg(feature = "CKRecordZoneID")]
        /// Setter for [`prioritizedZoneIDs`][Self::prioritizedZoneIDs].
        ///
        /// This is [copied][objc2_foundation::NSCopying::copy] when set.
        #[unsafe(method(setPrioritizedZoneIDs:))]
        #[unsafe(method_family = none)]
        pub unsafe fn setPrioritizedZoneIDs(&self, prioritized_zone_i_ds: &NSArray<CKRecordZoneID>);

        /// Initializes a set of options with the specific scope.
        /// If no scope is provided, the default scope will include everything.
        #[unsafe(method(initWithScope:))]
        #[unsafe(method_family = init)]
        pub unsafe fn initWithScope(
            this: Allocated<Self>,
            scope: Option<&CKSyncEngineFetchChangesScope>,
        ) -> Retained<Self>;
    );
}

/// Methods declared on superclass `NSObject`.
impl CKSyncEngineFetchChangesOptions {
    extern_methods!(
        #[unsafe(method(init))]
        #[unsafe(method_family = init)]
        pub unsafe fn init(this: Allocated<Self>) -> Retained<Self>;

        #[unsafe(method(new))]
        #[unsafe(method_family = new)]
        pub unsafe fn new() -> Retained<Self>;
    );
}

extern_class!(
    /// A scope in which the sync engine will fetch changes from the server.
    ///
    /// See also [Apple's documentation](https://developer.apple.com/documentation/cloudkit/cksyncenginefetchchangesscope?language=objc)
    #[unsafe(super(NSObject))]
    #[derive(Debug, PartialEq, Eq, Hash)]
    pub struct CKSyncEngineFetchChangesScope;
);

unsafe impl Send for CKSyncEngineFetchChangesScope {}

unsafe impl Sync for CKSyncEngineFetchChangesScope {}

extern_conformance!(
    unsafe impl NSCopying for CKSyncEngineFetchChangesScope {}
);

unsafe impl CopyingHelper for CKSyncEngineFetchChangesScope {
    type Result = Self;
}

extern_conformance!(
    unsafe impl NSObjectProtocol for CKSyncEngineFetchChangesScope {}
);

impl CKSyncEngineFetchChangesScope {
    extern_methods!(
        #[cfg(feature = "CKRecordZoneID")]
        /// A specific set of zone IDs to include in the scope.
        /// For example, if you want to fetch changes for a specific set of zones, you can specify them here.
        /// If `nil`, this scope includes all zones except those in `excludedZoneIDs`.
        #[unsafe(method(zoneIDs))]
        #[unsafe(method_family = none)]
        pub unsafe fn zoneIDs(&self) -> Option<Retained<NSSet<CKRecordZoneID>>>;

        #[cfg(feature = "CKRecordZoneID")]
        /// A specific set of zone IDs to exclude from this scope.
        /// If you know that you don't want to fetch changes for a particular set of zones, you can set those zones here.
        #[unsafe(method(excludedZoneIDs))]
        #[unsafe(method_family = none)]
        pub unsafe fn excludedZoneIDs(&self) -> Retained<NSSet<CKRecordZoneID>>;

        #[cfg(feature = "CKRecordZoneID")]
        /// Creates a scope that includes only the specified set of zones.
        #[unsafe(method(initWithZoneIDs:))]
        #[unsafe(method_family = init)]
        pub unsafe fn initWithZoneIDs(
            this: Allocated<Self>,
            zone_i_ds: Option<&NSSet<CKRecordZoneID>>,
        ) -> Retained<Self>;

        #[cfg(feature = "CKRecordZoneID")]
        /// Creates a scope that includes all zones except the specified excluded zones.
        #[unsafe(method(initWithExcludedZoneIDs:))]
        #[unsafe(method_family = init)]
        pub unsafe fn initWithExcludedZoneIDs(
            this: Allocated<Self>,
            zone_i_ds: &NSSet<CKRecordZoneID>,
        ) -> Retained<Self>;

        #[cfg(feature = "CKRecordZoneID")]
        /// Returns true if the specified zone ID is included in this scope.
        #[unsafe(method(containsZoneID:))]
        #[unsafe(method_family = none)]
        pub unsafe fn containsZoneID(&self, zone_id: &CKRecordZoneID) -> bool;
    );
}

/// Methods declared on superclass `NSObject`.
impl CKSyncEngineFetchChangesScope {
    extern_methods!(
        #[unsafe(method(init))]
        #[unsafe(method_family = init)]
        pub unsafe fn init(this: Allocated<Self>) -> Retained<Self>;

        #[unsafe(method(new))]
        #[unsafe(method_family = new)]
        pub unsafe fn new() -> Retained<Self>;
    );
}

extern_class!(
    /// A set of options to use when sending changes to the server.
    ///
    /// See also [Apple's documentation](https://developer.apple.com/documentation/cloudkit/cksyncenginesendchangesoptions?language=objc)
    #[unsafe(super(NSObject))]
    #[derive(Debug, PartialEq, Eq, Hash)]
    pub struct CKSyncEngineSendChangesOptions;
);

unsafe impl Send for CKSyncEngineSendChangesOptions {}

unsafe impl Sync for CKSyncEngineSendChangesOptions {}

extern_conformance!(
    unsafe impl NSCopying for CKSyncEngineSendChangesOptions {}
);

unsafe impl CopyingHelper for CKSyncEngineSendChangesOptions {
    type Result = Self;
}

extern_conformance!(
    unsafe impl NSObjectProtocol for CKSyncEngineSendChangesOptions {}
);

impl CKSyncEngineSendChangesOptions {
    extern_methods!(
        /// The scope in which to send changes to the server.
        #[unsafe(method(scope))]
        #[unsafe(method_family = none)]
        pub unsafe fn scope(&self) -> Retained<CKSyncEngineSendChangesScope>;

        /// Setter for [`scope`][Self::scope].
        ///
        /// This is [copied][objc2_foundation::NSCopying::copy] when set.
        #[unsafe(method(setScope:))]
        #[unsafe(method_family = none)]
        pub unsafe fn setScope(&self, scope: &CKSyncEngineSendChangesScope);

        #[cfg(feature = "CKOperationGroup")]
        /// The operation group to use for the underlying operations when sending changes.
        ///
        /// You might set an operation group with a particular name in order to help you analyze telemetry in the CloudKit Console.
        /// If you don't provide an operation group, a default one will be created for you.
        #[unsafe(method(operationGroup))]
        #[unsafe(method_family = none)]
        pub unsafe fn operationGroup(&self) -> Retained<CKOperationGroup>;

        #[cfg(feature = "CKOperationGroup")]
        /// Setter for [`operationGroup`][Self::operationGroup].
        #[unsafe(method(setOperationGroup:))]
        #[unsafe(method_family = none)]
        pub unsafe fn setOperationGroup(&self, operation_group: &CKOperationGroup);

        /// Initializes a set of options with the specific scope.
        /// If no scope is provided, the default scope will include everything.
        #[unsafe(method(initWithScope:))]
        #[unsafe(method_family = init)]
        pub unsafe fn initWithScope(
            this: Allocated<Self>,
            scope: Option<&CKSyncEngineSendChangesScope>,
        ) -> Retained<Self>;
    );
}

/// Methods declared on superclass `NSObject`.
impl CKSyncEngineSendChangesOptions {
    extern_methods!(
        #[unsafe(method(init))]
        #[unsafe(method_family = init)]
        pub unsafe fn init(this: Allocated<Self>) -> Retained<Self>;

        #[unsafe(method(new))]
        #[unsafe(method_family = new)]
        pub unsafe fn new() -> Retained<Self>;
    );
}

extern_class!(
    /// A scope in which the sync engine will send changes to  the server.
    ///
    /// See also [Apple's documentation](https://developer.apple.com/documentation/cloudkit/cksyncenginesendchangesscope?language=objc)
    #[unsafe(super(NSObject))]
    #[derive(Debug, PartialEq, Eq, Hash)]
    pub struct CKSyncEngineSendChangesScope;
);

unsafe impl Send for CKSyncEngineSendChangesScope {}

unsafe impl Sync for CKSyncEngineSendChangesScope {}

extern_conformance!(
    unsafe impl NSCopying for CKSyncEngineSendChangesScope {}
);

unsafe impl CopyingHelper for CKSyncEngineSendChangesScope {
    type Result = Self;
}

extern_conformance!(
    unsafe impl NSObjectProtocol for CKSyncEngineSendChangesScope {}
);

impl CKSyncEngineSendChangesScope {
    extern_methods!(
        #[cfg(feature = "CKRecordZoneID")]
        /// The scope of zone IDs in which to send changes.
        ///
        /// If you only want to send changes for a particular set of zones, you can initialize your scope with those zone IDs.
        /// When creating the next batch of changes to send to the server, consult this and only send changes within these zones.
        /// If this and `recordIDs` are `nil`, then you should send all changes.
        #[unsafe(method(zoneIDs))]
        #[unsafe(method_family = none)]
        pub unsafe fn zoneIDs(&self) -> Option<Retained<NSSet<CKRecordZoneID>>>;

        #[cfg(feature = "CKRecordZoneID")]
        /// A specific set of zone IDs to exclude from this scope.
        /// If you know that you don't want to send changes for a particular set of zones, you can set those zones here.
        ///
        /// Note that if `zoneIDs` is set, then  `excludedZoneIDs` will always be empty.
        #[unsafe(method(excludedZoneIDs))]
        #[unsafe(method_family = none)]
        pub unsafe fn excludedZoneIDs(&self) -> Retained<NSSet<CKRecordZoneID>>;

        #[cfg(feature = "CKRecordID")]
        /// The scope of record IDs in which to send changes.
        ///
        /// If you only want to send changes for a particular set of records, you can initialize your scope with those records IDs.
        /// When creating the next batch of changes to send to the server, consult this property and only send changes for these record IDs.
        /// If this and `zoneIDs` are `nil`, then you should send all changes.
        #[unsafe(method(recordIDs))]
        #[unsafe(method_family = none)]
        pub unsafe fn recordIDs(&self) -> Option<Retained<NSSet<CKRecordID>>>;

        #[cfg(feature = "CKRecordZoneID")]
        /// Creates a scope that contains only the given zone IDs.
        /// If `zoneIDs` is nil, then this scope contains all zones.
        #[unsafe(method(initWithZoneIDs:))]
        #[unsafe(method_family = init)]
        pub unsafe fn initWithZoneIDs(
            this: Allocated<Self>,
            zone_i_ds: Option<&NSSet<CKRecordZoneID>>,
        ) -> Retained<Self>;

        #[cfg(feature = "CKRecordZoneID")]
        /// Creates a scope that contains all zones except for the given zone IDs.
        #[unsafe(method(initWithExcludedZoneIDs:))]
        #[unsafe(method_family = init)]
        pub unsafe fn initWithExcludedZoneIDs(
            this: Allocated<Self>,
            excluded_zone_i_ds: &NSSet<CKRecordZoneID>,
        ) -> Retained<Self>;

        #[cfg(feature = "CKRecordID")]
        /// Creates a scope that includes only the given record IDs.
        /// If `recordIDs` is nil, this scope contains all records.
        #[unsafe(method(initWithRecordIDs:))]
        #[unsafe(method_family = init)]
        pub unsafe fn initWithRecordIDs(
            this: Allocated<Self>,
            record_i_ds: Option<&NSSet<CKRecordID>>,
        ) -> Retained<Self>;

        #[cfg(feature = "CKRecordID")]
        /// Returns true if this scope includes the given record ID.
        #[unsafe(method(containsRecordID:))]
        #[unsafe(method_family = none)]
        pub unsafe fn containsRecordID(&self, record_id: &CKRecordID) -> bool;

        #[cfg(feature = "CKSyncEngineState")]
        /// Returns true if this scope includes the given pending change.
        #[unsafe(method(containsPendingRecordZoneChange:))]
        #[unsafe(method_family = none)]
        pub unsafe fn containsPendingRecordZoneChange(
            &self,
            pending_record_zone_change: &CKSyncEnginePendingRecordZoneChange,
        ) -> bool;
    );
}

/// Methods declared on superclass `NSObject`.
impl CKSyncEngineSendChangesScope {
    extern_methods!(
        #[unsafe(method(init))]
        #[unsafe(method_family = init)]
        pub unsafe fn init(this: Allocated<Self>) -> Retained<Self>;

        #[unsafe(method(new))]
        #[unsafe(method_family = new)]
        pub unsafe fn new() -> Retained<Self>;
    );
}

/// [Apple's documentation](https://developer.apple.com/documentation/cloudkit/cksyncenginesyncreason?language=objc)
// NS_ENUM
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct CKSyncEngineSyncReason(pub NSInteger);
impl CKSyncEngineSyncReason {
    /// This sync was scheduled automatically by the sync engine.
    #[doc(alias = "CKSyncEngineSyncReasonScheduled")]
    pub const Scheduled: Self = Self(0);
    /// This sync was requested manually by calling ``CKSyncEngine/fetchChanges(_:)`` or ``CKSyncEngine/sendChanges(_:)``.
    #[doc(alias = "CKSyncEngineSyncReasonManual")]
    pub const Manual: Self = Self(1);
}

unsafe impl Encode for CKSyncEngineSyncReason {
    const ENCODING: Encoding = NSInteger::ENCODING;
}

unsafe impl RefEncode for CKSyncEngineSyncReason {
    const ENCODING_REF: Encoding = Encoding::Pointer(&Self::ENCODING);
}

extern_class!(
    /// The context of an attempt to fetch changes from the server.
    ///
    /// The sync engine might attempt to fetch changes to the server for many reasons.
    /// For example, if you call ``CKSyncEngine/fetchChanges(_:)``, it'll try to fetch changes immediately.
    /// Or if it receives a push notification, it'll schedule a sync and fetch changes when the scheduler task runs.
    /// This object represents one of those attempts to fetch changes.
    ///
    /// See also [Apple's documentation](https://developer.apple.com/documentation/cloudkit/cksyncenginefetchchangescontext?language=objc)
    #[unsafe(super(NSObject))]
    #[derive(Debug, PartialEq, Eq, Hash)]
    pub struct CKSyncEngineFetchChangesContext;
);

unsafe impl Send for CKSyncEngineFetchChangesContext {}

unsafe impl Sync for CKSyncEngineFetchChangesContext {}

extern_conformance!(
    unsafe impl NSObjectProtocol for CKSyncEngineFetchChangesContext {}
);

impl CKSyncEngineFetchChangesContext {
    extern_methods!(
        #[unsafe(method(init))]
        #[unsafe(method_family = init)]
        pub unsafe fn init(this: Allocated<Self>) -> Retained<Self>;

        #[unsafe(method(new))]
        #[unsafe(method_family = new)]
        pub unsafe fn new() -> Retained<Self>;

        /// The reason why the sync engine is attempting to fetch changes.
        #[unsafe(method(reason))]
        #[unsafe(method_family = none)]
        pub unsafe fn reason(&self) -> CKSyncEngineSyncReason;

        /// The options being used for this attempt to fetch changes.
        #[unsafe(method(options))]
        #[unsafe(method_family = none)]
        pub unsafe fn options(&self) -> Retained<CKSyncEngineFetchChangesOptions>;
    );
}

extern_class!(
    /// The context of an attempt to send changes to the server.
    ///
    /// The sync engine might attempt to send changes to the server for many reasons.
    /// For example, if you call ``CKSyncEngine/sendChanges(_:)``, it'll try to send changes immediately.
    /// Or if you add pending changes to the state, it'll schedule a sync and send changes when the scheduler task runs.
    /// This object represents one of those attempts to send changes.
    ///
    /// See also [Apple's documentation](https://developer.apple.com/documentation/cloudkit/cksyncenginesendchangescontext?language=objc)
    #[unsafe(super(NSObject))]
    #[derive(Debug, PartialEq, Eq, Hash)]
    pub struct CKSyncEngineSendChangesContext;
);

unsafe impl Send for CKSyncEngineSendChangesContext {}

unsafe impl Sync for CKSyncEngineSendChangesContext {}

extern_conformance!(
    unsafe impl NSObjectProtocol for CKSyncEngineSendChangesContext {}
);

impl CKSyncEngineSendChangesContext {
    extern_methods!(
        #[unsafe(method(init))]
        #[unsafe(method_family = init)]
        pub unsafe fn init(this: Allocated<Self>) -> Retained<Self>;

        #[unsafe(method(new))]
        #[unsafe(method_family = new)]
        pub unsafe fn new() -> Retained<Self>;

        /// The reason why the sync engine is attempting to send changes.
        #[unsafe(method(reason))]
        #[unsafe(method_family = none)]
        pub unsafe fn reason(&self) -> CKSyncEngineSyncReason;

        /// The options being used for this attempt to send changes.
        #[unsafe(method(options))]
        #[unsafe(method_family = none)]
        pub unsafe fn options(&self) -> Retained<CKSyncEngineSendChangesOptions>;
    );
}