Search code examples
swiftrealmrealm-mobile-platform

Converting local Realms to synced Realms: 'customSchema' is inaccessible


I read about the Converting Local Realms to Synced Realms section in docs and I found this nice recipe for Objective-C but I can't implement it on an app fully implemented in Swift.

var syncConfig = Realm.Configuration()
syncConfig.syncConfiguration = SyncConfiguration(user: user, realmURL: server.appendingPathComponent("/~/app1"))
syncConfig.customSchema = localRealm.schema
~~~~~~~~~~~~~~~~~~~~~~~
^ 'customSchema' is inaccessible due to 'private' protection level

I even added import Realm.Private but didn't solve the problem.

Should I explicitly use Objective-C for this operation?


Solution

  • With no better option, I decided to use Objective-C in my Swift project. So, I added a SWIFT_OBJC_BRIDGING_HEADER (Xcode does that automatically when you add an Objective-C file) and created a RealmConverter object:

    RealmConverter.h

    #import <Foundation/Foundation.h>
    
    @import Realm;
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface RealmConverter : NSObject
    
    - (void)convertLocalToSyncRealm:(NSURL *)server local:(NSURL *)local username:(NSString *)username password:(NSString *)password completion:(void (^)(NSError * _Nullable))completion;
    
    @end
    
    NS_ASSUME_NONNULL_END
    

    RealmConverter.m

    #import "RealmConverter.h"
    
    @import Realm.Dynamic;
    @import Realm.Private;
    
    @implementation RealmConverter
    
    - (void)convertLocalToSyncRealm:(NSURL *)server local:(NSURL *)local username:(NSString *)username password:(NSString *)password completion:(void (^)(NSError * _Nullable))completion {
        RLMRealmConfiguration *configuration = [[RLMRealmConfiguration alloc] init];
        configuration.fileURL = local;
        configuration.dynamic = true;
        configuration.readOnly = YES;
    
        RLMRealm *localRealm = [RLMRealm realmWithConfiguration:configuration error:nil];
    
        RLMSyncCredentials *credentials = [RLMSyncCredentials credentialsWithUsername:username password:password register:YES];
        [RLMSyncUser logInWithCredentials:credentials authServerURL:server onCompletion:^(RLMSyncUser *syncUser, NSError *error) {
            if (error) {
                completion(error);
                return;
            }
    
            dispatch_async(dispatch_get_main_queue(), ^{
                RLMRealmConfiguration *syncConfig = [[RLMRealmConfiguration alloc] init];
                syncConfig.syncConfiguration = [[RLMSyncConfiguration alloc] initWithUser:syncUser realmURL:[NSURL URLWithString:[NSString stringWithFormat:@"realm://%@:%@/~/<redacted>", server.host, server.port]]];
                syncConfig.customSchema = [localRealm.schema copy];
    
                RLMRealm *syncRealm = [RLMRealm realmWithConfiguration:syncConfig error:nil];
                syncRealm.schema = syncConfig.customSchema;
    
                NSError *error = nil;
                [syncRealm transactionWithBlock:^{
                    NSArray *objectSchema = syncConfig.customSchema.objectSchema;
                    for (RLMObjectSchema *schema in objectSchema) {
                        RLMResults *allObjects = [localRealm allObjects:schema.className];
                        for (RLMObject *object in allObjects) {
                            RLMCreateObjectInRealmWithValue(syncRealm, schema.className, object, true);
                        }
                    }
                    completion(nil);
                } error:&error];
    
                if (error) {
                    completion(error);
                }
            });
        }];
    }
    
    @end
    

    Then add #import "RealmConverter.h" to your bridging header and then use it in your Swift code like:

    RealmConverter().convertLocal(toSyncRealm: URL(string: "http://localhost:9080")!, local: Realm.Configuration.defaultConfiguration.fileURL!, username: "[email protected]", password: "12345678") { error in
        print("Done:", error ?? "nil")
    }