Skip to content

Commit 9fab8c1

Browse files
authored
Add swift and objective-c code snippets for MultipeerReplicator (#988)
* The code snippet file names are MultipeerReplicator.swift (Swift) and MultipeerReplicator.m (Objective-C)
1 parent c179c4b commit 9fab8c1

File tree

4 files changed

+534
-2
lines changed

4 files changed

+534
-2
lines changed

modules/objc/examples/code_snippets.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
4001A1BD2BB344790067B507 /* CouchbaseLiteVectorSearch.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 4001A1BA2BB344790067B507 /* CouchbaseLiteVectorSearch.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
1919
4001A1BE2BB344790067B507 /* CouchbaseLite.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4001A1BB2BB344790067B507 /* CouchbaseLite.xcframework */; };
2020
4001A1BF2BB344790067B507 /* CouchbaseLite.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 4001A1BB2BB344790067B507 /* CouchbaseLite.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
21+
40A3E5F82E1FC20600DF445B /* MultipeerReplicator.m in Sources */ = {isa = PBXBuildFile; fileRef = 40A3E5F72E1FC20600DF445B /* MultipeerReplicator.m */; };
2122
AE96AF0C2BF24E36000253DF /* VectorSearch.m in Sources */ = {isa = PBXBuildFile; fileRef = AE96AF0B2BF24E36000253DF /* VectorSearch.m */; };
2223
/* End PBXBuildFile section */
2324

@@ -50,6 +51,7 @@
5051
4001A1B82BB343120067B507 /* code-snippets.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "code-snippets.app"; sourceTree = BUILT_PRODUCTS_DIR; };
5152
4001A1BA2BB344790067B507 /* CouchbaseLiteVectorSearch.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = CouchbaseLiteVectorSearch.xcframework; path = Frameworks/CouchbaseLiteVectorSearch.xcframework; sourceTree = "<group>"; };
5253
4001A1BB2BB344790067B507 /* CouchbaseLite.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = CouchbaseLite.xcframework; path = Frameworks/CouchbaseLite.xcframework; sourceTree = "<group>"; };
54+
40A3E5F72E1FC20600DF445B /* MultipeerReplicator.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MultipeerReplicator.m; sourceTree = "<group>"; };
5355
AE96AF0B2BF24E36000253DF /* VectorSearch.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VectorSearch.m; sourceTree = "<group>"; };
5456
/* End PBXFileReference section */
5557

@@ -78,6 +80,7 @@
7880
1A508E6E273A3887007EABB2 /* main.m */,
7981
1A508E6F273A3887007EABB2 /* AppDelegate.m */,
8082
1A508E70273A3887007EABB2 /* SampleCodeTest.m */,
83+
40A3E5F72E1FC20600DF445B /* MultipeerReplicator.m */,
8184
AE96AF0B2BF24E36000253DF /* VectorSearch.m */,
8285
1A354CEB273B5996001A20BE /* GettingStarted.h */,
8386
);
@@ -177,6 +180,7 @@
177180
isa = PBXSourcesBuildPhase;
178181
buildActionMask = 2147483647;
179182
files = (
183+
40A3E5F82E1FC20600DF445B /* MultipeerReplicator.m in Sources */,
180184
1A508E7A273A3887007EABB2 /* main.m in Sources */,
181185
1A508E79273A3887007EABB2 /* GettingStarted.m in Sources */,
182186
1A508E7B273A3887007EABB2 /* AppDelegate.m in Sources */,
Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
//
2+
// MultipeerReplicator.m
3+
// CouchbaseLite
4+
//
5+
// Copyright © 2025 couchbase. All rights reserved.
6+
//
7+
8+
#import <Foundation/Foundation.h>
9+
#import <CouchbaseLite/CouchbaseLite.h>
10+
11+
@interface MultipeerReplicatorSnippets : NSObject
12+
13+
@end
14+
15+
@interface CustomConflictResolver : NSObject <CBLMultipeerConflictResolver>
16+
@end
17+
18+
@implementation CustomConflictResolver
19+
20+
- (nullable CBLDocument*) resolveConflict: (CBLConflict*)conflict forPeer: (CBLPeerID*)peerID {
21+
return conflict.remoteDocument;
22+
}
23+
24+
@end
25+
26+
@implementation MultipeerReplicatorSnippets {
27+
CBLDatabase *database;
28+
CBLCollection *collection1;
29+
CBLCollection *collection2;
30+
CBLCollection *collection3;
31+
}
32+
33+
- (NSArray<CBLMultipeerCollectionConfiguration *> *)collectionSimple {
34+
// tag::multipeer-collection-simple
35+
NSMutableArray<CBLMultipeerCollectionConfiguration*> *collections = [NSMutableArray array];
36+
for (CBLCollection *col in @[collection1, collection2, collection3]) {
37+
CBLMultipeerCollectionConfiguration *config =
38+
[[CBLMultipeerCollectionConfiguration alloc] initWithCollection:col];
39+
[collections addObject:config];
40+
}
41+
// end::multipeer-collection-simple
42+
return collections;
43+
}
44+
45+
- (NSArray<CBLMultipeerCollectionConfiguration *> *)collectionConfig {
46+
// Config with custom conflict resolver
47+
CBLMultipeerCollectionConfiguration *config1 =
48+
[[CBLMultipeerCollectionConfiguration alloc] initWithCollection:collection1];
49+
config1.conflictResolver = [[CustomConflictResolver alloc] init];
50+
51+
// Config with document IDs filter
52+
CBLMultipeerCollectionConfiguration *config2 =
53+
[[CBLMultipeerCollectionConfiguration alloc] initWithCollection:collection2];
54+
config2.documentIDs = @[@"doc1", @"doc2"];
55+
56+
// Config with push replication filter
57+
CBLMultipeerCollectionConfiguration *config3 =
58+
[[CBLMultipeerCollectionConfiguration alloc] initWithCollection:collection3];
59+
config3.pushFilter = ^BOOL(CBLPeerID *peerID, CBLDocument *document, CBLDocumentFlags flags) {
60+
return [document integerForKey:@"access-level"] == 2;
61+
};
62+
63+
NSArray *collections = @[config1, config2, config3];
64+
// end::multipeer-collection-config
65+
return collections;
66+
}
67+
68+
- (CBLTLSIdentity *)peerIdentity {
69+
// tag::multipeer-tlsidentity
70+
// Note: This example is simplified for demonstration and does not include error handling.
71+
NSString *persistentLabel = @"com.myapp.identity";
72+
73+
// Retrieve the TLS identity from the keychain using the persistent label.
74+
NSError *error = nil;
75+
CBLTLSIdentity *identity = [CBLTLSIdentity identityWithLabel:persistentLabel error:&error];
76+
77+
// If the identity doesn't exist or expired, create a new one.
78+
if (!identity || [identity.expiration compare:[NSDate date]] == NSOrderedAscending) {
79+
// Create an issuer identity from the private key and certificate data in DER format.
80+
NSData *privateKey = [self getIssuerPrivateKeyData];
81+
NSData *cert = [self getIssuerCertificateData];
82+
CBLTLSIdentity *issuer = [CBLTLSIdentity createIdentityWithPrivateKey:privateKey
83+
certificate:cert
84+
error:&error];
85+
86+
// Create a new identity signed with the issuer.
87+
NSDictionary *attrs = @{ kCBLCertAttrCommonName: @"MyApp" };
88+
NSDate *expiration = [[NSCalendar currentCalendar] dateByAddingUnit:NSCalendarUnitYear
89+
value:2
90+
toDate:[NSDate date]
91+
options:0];
92+
93+
identity = [CBLTLSIdentity createIdentityForKeyUsages:kCBLKeyUsagesClientAuth|kCBLKeyUsagesServerAuth
94+
attributes:attrs
95+
expiration:expiration
96+
issuer:issuer
97+
label:persistentLabel
98+
error:&error];
99+
}
100+
// end::multipeer-tlsidentity
101+
return identity;
102+
}
103+
104+
- (NSData *)getIssuerPrivateKeyData {
105+
return [[NSData alloc] initWithBase64EncodedString:@"your_base64_encoded_private_key" options:0];
106+
}
107+
108+
- (NSData *)getIssuerCertificateData {
109+
return [[NSData alloc] initWithBase64EncodedString:@"your_base64_encoded_certicate" options:0];
110+
}
111+
112+
- (id<CBLMultipeerAuthenticator>)authenticatorWithCallback {
113+
// tag::multipeer-authenticator-callback
114+
id<CBLMultipeerAuthenticator> authenticator =
115+
[[CBLMultipeerCertificateAuthenticator alloc] initWithBlock:^BOOL(CBLPeerID *peerID, NSArray *certs) {
116+
return YES;
117+
}];
118+
// end::multipeer-authenticator-callback
119+
return authenticator;
120+
}
121+
122+
- (id<CBLMultipeerAuthenticator>)authenticatorWithRootCerts {
123+
NSData *privateKey = [self getIssuerPrivateKeyData];
124+
NSData *cert = [self getIssuerCertificateData];
125+
NSError *error = nil;
126+
CBLTLSIdentity *issuer = [CBLTLSIdentity createIdentityWithPrivateKey:privateKey
127+
certificate:cert
128+
error:&error];
129+
// tag::multipeer-authenticator-rootcerts
130+
id<CBLMultipeerAuthenticator> authenticator =
131+
[[CBLMultipeerCertificateAuthenticator alloc] initWithRootCerts:issuer.certs];
132+
// end::multipeer-authenticator-rootcerts
133+
return authenticator;
134+
}
135+
136+
- (CBLMultipeerReplicatorConfiguration *)createConfig {
137+
CBLTLSIdentity *identity = [self peerIdentity];
138+
id<CBLMultipeerAuthenticator> authenticator = [self authenticatorWithRootCerts];
139+
NSArray<CBLMultipeerCollectionConfiguration *> *collections = [self collectionConfig];
140+
141+
// tag::multipeer-config
142+
CBLMultipeerReplicatorConfiguration *config =
143+
[[CBLMultipeerReplicatorConfiguration alloc] initWithPeerGroupID:@"com.myapp"
144+
identity:identity
145+
authenticator:authenticator
146+
collections:collections];
147+
// end::multipeer-config
148+
return config;
149+
}
150+
151+
- (CBLMultipeerReplicator *)createMultipeerReplicator {
152+
CBLMultipeerReplicatorConfiguration *config = [self createConfig];
153+
NSError *error = nil;
154+
// tag::multipeer-replicator
155+
CBLMultipeerReplicator *replicator = [[CBLMultipeerReplicator alloc] initWithConfig:config error:&error];
156+
// end::multipeer-replicator
157+
return replicator;
158+
}
159+
160+
- (void)startReplicator {
161+
CBLMultipeerReplicator *replicator = [self createMultipeerReplicator];
162+
// tag::multipeer-replicator-start
163+
[replicator start];
164+
// end::multipeer-replicator-start
165+
}
166+
167+
- (void)stopReplicator {
168+
CBLMultipeerReplicator *replicator = [self createMultipeerReplicator];
169+
// tag::multipeer-replicator-stop
170+
[replicator stop];
171+
// end::multipeer-replicator-stop
172+
}
173+
174+
- (void)statusListener {
175+
NSError *error = nil;
176+
CBLMultipeerReplicator *replicator = [self createMultipeerReplicator];
177+
// tag::multipeer-status-listener
178+
[replicator addStatusListenerWithQueue:nil listener:^(CBLMultipeerReplicatorStatus *status) {
179+
NSString *state = status.active ? @"active" : @"inactive";
180+
NSString *err = status.error ? status.error.localizedDescription : @"none";
181+
NSLog(@"Multipeer Replicator Status: %@, Error: %@", state, err);
182+
}];
183+
// end::multipeer-status-listener
184+
}
185+
186+
- (void)peerDiscoveryListener {
187+
CBLMultipeerReplicator *replicator = [self createMultipeerReplicator];
188+
// tag::multipeer-peer-discovery-listener
189+
[replicator addPeerDiscoveryStatusListenerWithQueue:nil listener:^(CBLPeerDiscoveryStatus *status) {
190+
NSString *online = status.online ? @"online" : @"offline";
191+
NSLog(@"Peer Discovery Status - Peer ID: %@, Status: %@", status.peerID, online);
192+
}];
193+
// end::multipeer-peer-discovery-listener
194+
}
195+
196+
- (void)peerReplicatorStatus {
197+
CBLMultipeerReplicator *replicator = [self createMultipeerReplicator];
198+
// tag::multipeer-replicator-status-listener
199+
NSArray<NSString *> *activities = @[ @"stopped", @"offline", @"connecting", @"idle", @"busy" ];
200+
[replicator addPeerReplicatorStatusListenerWithQueue:nil listener:^(CBLPeerReplicatorStatus *replStatus) {
201+
NSString *direction = replStatus.outgoing ? @"outgoing" : @"incoming";
202+
NSString *activity = activities[replStatus.status.activity];
203+
NSString *error = replStatus.status.error ? replStatus.status.error.localizedDescription : @"none";
204+
NSLog(@"Peer Replicator Status - "
205+
"Peer ID: %@, Direction: %@, Activity: %@, Error: %@",
206+
replStatus.peerID, direction, activity, error);
207+
}];
208+
// end::multipeer-replicator-status-listener
209+
}
210+
211+
- (void)peerDocumentReplication {
212+
CBLMultipeerReplicator *replicator = [self createMultipeerReplicator];
213+
// tag::multipeer-document-replication-listener
214+
[replicator addPeerDocumentReplicationListenerWithQueue:nil listener:^(CBLPeerDocumentReplication *docRepl) {
215+
NSString *direction = docRepl.isPush ? @"Push" : @"Pull";
216+
NSLog(@"Peer Document Replication - Peer ID: %@, Direction: %@", docRepl.peerID, direction);
217+
for (CBLReplicatedDocument *doc in docRepl.documents) {
218+
NSString *error = doc.error ? doc.error.localizedDescription : @"none";
219+
NSString *collection = [NSString stringWithFormat:@"%@.%@", doc.scope, doc.collection];
220+
NSLog(@" Collection: %@ Document ID: %@, Flags: %lu, Error: %@",
221+
collection, doc.id, (unsigned long)doc.flags, error);
222+
}
223+
}];
224+
// end::multipeer-document-replication-listener
225+
}
226+
227+
- (void)peerID {
228+
CBLMultipeerReplicator *replicator = [self createMultipeerReplicator];
229+
// tag::multipeer-peer-id
230+
CBLPeerID *peerID = replicator.peerID;
231+
NSLog(@"Peer ID: %@", peerID);
232+
// end::multipeer-peer-id
233+
}
234+
235+
- (void)neighborPeers {
236+
CBLMultipeerReplicator *replicator = [self createMultipeerReplicator];
237+
// tag::multipeer-neighbor-peers
238+
NSArray<CBLPeerID *> *neighborPeers = replicator.neighborPeers;
239+
NSLog(@"Neighbor Peers:");
240+
for (CBLPeerID *peerID in neighborPeers) {
241+
NSLog(@" %@", peerID);
242+
}
243+
// end::multipeer-neighbor-peers
244+
}
245+
246+
- (void)peerInfo {
247+
CBLMultipeerReplicator *replicator = [self createMultipeerReplicator];
248+
// tag::multipeer-peer-info
249+
NSArray<NSString *> *activities = @[ @"stopped", @"offline", @"connecting", @"idle", @"busy" ];
250+
251+
void (^printPeerInfo)(CBLPeerInfo *) = ^(CBLPeerInfo *info) {
252+
NSLog(@"Peer ID: %@", info.peerID);
253+
NSLog(@" Status: %@", info.online ? @"online" : @"offline");
254+
NSArray<CBLPeerID *> *neighborPeers = replicator.neighborPeers;
255+
NSLog(@" Neighbor Peers:");
256+
for (CBLPeerID *peerID in neighborPeers) {
257+
NSLog(@" %@", peerID);
258+
}
259+
260+
CBLReplicatorStatus *replStatus = info.replicatorStatus;
261+
NSString *activity = activities[(NSInteger)replStatus.activity];
262+
NSString *error = replStatus.error ? replStatus.error.localizedDescription : @"none";
263+
NSLog(@" Replicator Status: %@, Error: %@", activity, error);
264+
};
265+
266+
for (CBLPeerID *peerID in replicator.neighborPeers) {
267+
CBLPeerInfo *peerInfo = [replicator peerInfoForPeerID: peerID];
268+
if (peerInfo) {
269+
printPeerInfo(peerInfo);
270+
}
271+
}
272+
// end::multipeer-peer-info
273+
}
274+
275+
@end

modules/swift/examples/code_snippets.xcodeproj/project.pbxproj

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
4001A1B52BB33AB60067B507 /* CouchbaseLiteVectorSearch.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 4001A1B22BB33AB60067B507 /* CouchbaseLiteVectorSearch.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
1717
4001A1B62BB33AB60067B507 /* CouchbaseLiteSwift.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4001A1B32BB33AB60067B507 /* CouchbaseLiteSwift.xcframework */; };
1818
4001A1B72BB33AB60067B507 /* CouchbaseLiteSwift.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 4001A1B32BB33AB60067B507 /* CouchbaseLiteSwift.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
19+
40A3E5F02E1FA67E00DF445B /* MultipeerReplicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40A3E5EF2E1FA67E00DF445B /* MultipeerReplicator.swift */; };
1920
AE3079322DBBE9B500B4112C /* CodableCombine.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE3079312DBBE9B000B4112C /* CodableCombine.swift */; };
2021
AE5F25412CA7039A00AAB7F4 /* Getting-Started.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE5F25402CA7039A00AAB7F4 /* Getting-Started.swift */; };
2122
AEE304BF2BADBAB500C12A19 /* VectorSearch.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEE304BE2BADBAB500C12A19 /* VectorSearch.swift */; };
@@ -45,6 +46,7 @@
4546
1A354D0D273B643F001A20BE /* SampleCodeTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SampleCodeTest.swift; sourceTree = "<group>"; };
4647
4001A1B22BB33AB60067B507 /* CouchbaseLiteVectorSearch.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = CouchbaseLiteVectorSearch.xcframework; path = Frameworks/CouchbaseLiteVectorSearch.xcframework; sourceTree = "<group>"; };
4748
4001A1B32BB33AB60067B507 /* CouchbaseLiteSwift.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = CouchbaseLiteSwift.xcframework; path = Frameworks/CouchbaseLiteSwift.xcframework; sourceTree = "<group>"; };
49+
40A3E5EF2E1FA67E00DF445B /* MultipeerReplicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultipeerReplicator.swift; sourceTree = "<group>"; };
4850
AE3079312DBBE9B000B4112C /* CodableCombine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodableCombine.swift; sourceTree = "<group>"; };
4951
AE5F25402CA7039A00AAB7F4 /* Getting-Started.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Getting-Started.swift"; sourceTree = "<group>"; };
5052
AEE304BE2BADBAB500C12A19 /* VectorSearch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VectorSearch.swift; sourceTree = "<group>"; };
@@ -67,15 +69,16 @@
6769
1A354D00273B643F001A20BE /* code_snippets */ = {
6870
isa = PBXGroup;
6971
children = (
70-
AE5F25402CA7039A00AAB7F4 /* Getting-Started.swift */,
7172
1A354D02273B643F001A20BE /* Assets.xcassets */,
7273
1A354D03273B643F001A20BE /* LaunchScreen.storyboard */,
7374
1A354D05273B643F001A20BE /* Main.storyboard */,
7475
1A354D09273B643F001A20BE /* AppDelegate.swift */,
76+
AE5F25402CA7039A00AAB7F4 /* Getting-Started.swift */,
7577
1A354D0C273B643F001A20BE /* Info.plist */,
78+
AE3079312DBBE9B000B4112C /* CodableCombine.swift */,
7679
1A354D0D273B643F001A20BE /* SampleCodeTest.swift */,
80+
40A3E5EF2E1FA67E00DF445B /* MultipeerReplicator.swift */,
7781
AEE304BE2BADBAB500C12A19 /* VectorSearch.swift */,
78-
AE3079312DBBE9B000B4112C /* CodableCombine.swift */,
7982
);
8083
path = code_snippets;
8184
sourceTree = "<group>";
@@ -182,6 +185,7 @@
182185
files = (
183186
AE3079322DBBE9B500B4112C /* CodableCombine.swift in Sources */,
184187
1A354D18273B643F001A20BE /* SampleCodeTest.swift in Sources */,
188+
40A3E5F02E1FA67E00DF445B /* MultipeerReplicator.swift in Sources */,
185189
AEE304BF2BADBAB500C12A19 /* VectorSearch.swift in Sources */,
186190
AE5F25412CA7039A00AAB7F4 /* Getting-Started.swift in Sources */,
187191
1A354D14273B643F001A20BE /* AppDelegate.swift in Sources */,

0 commit comments

Comments
 (0)