You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
243 lines
8.8 KiB
243 lines
8.8 KiB
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copyright 2016 Realm Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
#import "RLMSyncManager_Private.h"
|
|
|
|
#import "RLMRealmConfiguration+Sync.h"
|
|
#import "RLMSyncConfiguration_Private.hpp"
|
|
#import "RLMSyncSession_Private.hpp"
|
|
#import "RLMSyncUser_Private.hpp"
|
|
#import "RLMSyncUtil_Private.hpp"
|
|
#import "RLMUtil.hpp"
|
|
|
|
#import "sync/sync_config.hpp"
|
|
#import "sync/sync_manager.hpp"
|
|
#import "sync/sync_session.hpp"
|
|
|
|
#if !defined(REALM_COCOA_VERSION)
|
|
#import "RLMVersion.h"
|
|
#endif
|
|
|
|
using namespace realm;
|
|
using Level = realm::util::Logger::Level;
|
|
|
|
namespace {
|
|
|
|
Level levelForSyncLogLevel(RLMSyncLogLevel logLevel) {
|
|
switch (logLevel) {
|
|
case RLMSyncLogLevelOff: return Level::off;
|
|
case RLMSyncLogLevelFatal: return Level::fatal;
|
|
case RLMSyncLogLevelError: return Level::error;
|
|
case RLMSyncLogLevelWarn: return Level::warn;
|
|
case RLMSyncLogLevelInfo: return Level::info;
|
|
case RLMSyncLogLevelDetail: return Level::detail;
|
|
case RLMSyncLogLevelDebug: return Level::debug;
|
|
case RLMSyncLogLevelTrace: return Level::trace;
|
|
case RLMSyncLogLevelAll: return Level::all;
|
|
}
|
|
REALM_UNREACHABLE(); // Unrecognized log level.
|
|
}
|
|
|
|
RLMSyncLogLevel logLevelForLevel(Level logLevel) {
|
|
switch (logLevel) {
|
|
case Level::off: return RLMSyncLogLevelOff;
|
|
case Level::fatal: return RLMSyncLogLevelFatal;
|
|
case Level::error: return RLMSyncLogLevelError;
|
|
case Level::warn: return RLMSyncLogLevelWarn;
|
|
case Level::info: return RLMSyncLogLevelInfo;
|
|
case Level::detail: return RLMSyncLogLevelDetail;
|
|
case Level::debug: return RLMSyncLogLevelDebug;
|
|
case Level::trace: return RLMSyncLogLevelTrace;
|
|
case Level::all: return RLMSyncLogLevelAll;
|
|
}
|
|
REALM_UNREACHABLE(); // Unrecognized log level.
|
|
}
|
|
|
|
struct CocoaSyncLogger : public realm::util::RootLogger {
|
|
void do_log(Level, std::string message) override {
|
|
NSLog(@"Sync: %@", RLMStringDataToNSString(message));
|
|
}
|
|
};
|
|
|
|
struct CocoaSyncLoggerFactory : public realm::SyncLoggerFactory {
|
|
std::unique_ptr<realm::util::Logger> make_logger(realm::util::Logger::Level level) override {
|
|
auto logger = std::make_unique<CocoaSyncLogger>();
|
|
logger->set_level_threshold(level);
|
|
return std::move(logger);
|
|
}
|
|
} s_syncLoggerFactory;
|
|
|
|
} // anonymous namespace
|
|
|
|
@interface RLMSyncManager ()
|
|
- (instancetype)initWithCustomRootDirectory:(nullable NSURL *)rootDirectory NS_DESIGNATED_INITIALIZER;
|
|
@end
|
|
|
|
@implementation RLMSyncManager
|
|
|
|
static RLMSyncManager *s_sharedManager = nil;
|
|
|
|
+ (instancetype)sharedManager {
|
|
static std::once_flag flag;
|
|
std::call_once(flag, [] {
|
|
try {
|
|
s_sharedManager = [[RLMSyncManager alloc] initWithCustomRootDirectory:nil];
|
|
}
|
|
catch (std::exception const& e) {
|
|
@throw RLMException(e);
|
|
}
|
|
});
|
|
return s_sharedManager;
|
|
}
|
|
|
|
- (instancetype)initWithCustomRootDirectory:(NSURL *)rootDirectory {
|
|
if (self = [super init]) {
|
|
[RLMSyncUser _setUpBindingContextFactory];
|
|
|
|
// Initialize the sync engine.
|
|
SyncManager::shared().set_logger_factory(s_syncLoggerFactory);
|
|
bool should_encrypt = !getenv("REALM_DISABLE_METADATA_ENCRYPTION") && !RLMIsRunningInPlayground();
|
|
auto mode = should_encrypt ? SyncManager::MetadataMode::Encryption : SyncManager::MetadataMode::NoEncryption;
|
|
rootDirectory = rootDirectory ?: [NSURL fileURLWithPath:RLMDefaultDirectoryForBundleIdentifier(nil)];
|
|
@autoreleasepool {
|
|
bool isSwift = !!NSClassFromString(@"RealmSwiftObjectUtil");
|
|
auto userAgent = [[NSMutableString alloc] initWithFormat:@"Realm%@/%@",
|
|
isSwift ? @"Swift" : @"ObjectiveC", REALM_COCOA_VERSION];
|
|
SyncManager::shared().configure(rootDirectory.path.UTF8String, mode, RLMStringDataWithNSString(userAgent), none, true);
|
|
SyncManager::shared().set_user_agent(RLMStringDataWithNSString(self.appID));
|
|
}
|
|
return self;
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
- (NSString *)appID {
|
|
if (!_appID) {
|
|
_appID = [[NSBundle mainBundle] bundleIdentifier] ?: @"(none)";
|
|
}
|
|
return _appID;
|
|
}
|
|
|
|
- (void)setUserAgent:(NSString *)userAgent {
|
|
SyncManager::shared().set_user_agent(RLMStringDataWithNSString(userAgent));
|
|
_userAgent = userAgent;
|
|
}
|
|
|
|
- (void)setCustomRequestHeaders:(NSDictionary<NSString *,NSString *> *)customRequestHeaders {
|
|
_customRequestHeaders = customRequestHeaders.copy;
|
|
|
|
for (auto&& user : SyncManager::shared().all_logged_in_users()) {
|
|
for (auto&& session : user->all_sessions()) {
|
|
auto config = session->config();
|
|
config.custom_http_headers.clear();;
|
|
for (NSString *key in customRequestHeaders) {
|
|
config.custom_http_headers.emplace(key.UTF8String, customRequestHeaders[key].UTF8String);
|
|
}
|
|
session->update_configuration(std::move(config));
|
|
}
|
|
}
|
|
}
|
|
|
|
#pragma mark - Passthrough properties
|
|
|
|
- (RLMSyncLogLevel)logLevel {
|
|
return logLevelForLevel(realm::SyncManager::shared().log_level());
|
|
}
|
|
|
|
- (void)setLogLevel:(RLMSyncLogLevel)logLevel {
|
|
realm::SyncManager::shared().set_log_level(levelForSyncLogLevel(logLevel));
|
|
}
|
|
|
|
#pragma mark - Private API
|
|
|
|
- (void)_fireError:(NSError *)error {
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
if (self.errorHandler) {
|
|
self.errorHandler(error, nil);
|
|
}
|
|
});
|
|
}
|
|
|
|
- (void)_fireErrorWithCode:(int)errorCode
|
|
message:(NSString *)message
|
|
isFatal:(BOOL)fatal
|
|
session:(RLMSyncSession *)session
|
|
userInfo:(NSDictionary *)userInfo
|
|
errorClass:(RLMSyncSystemErrorKind)errorClass {
|
|
NSError *error = nil;
|
|
BOOL shouldMakeError = YES;
|
|
NSDictionary *custom = nil;
|
|
// Note that certain types of errors are 'interactive'; users have several options
|
|
// as to how to proceed after the error is reported.
|
|
switch (errorClass) {
|
|
case RLMSyncSystemErrorKindClientReset: {
|
|
std::string path = [userInfo[@(realm::SyncError::c_original_file_path_key)] UTF8String];
|
|
custom = @{kRLMSyncPathOfRealmBackupCopyKey:
|
|
userInfo[@(realm::SyncError::c_recovery_file_path_key)],
|
|
kRLMSyncErrorActionTokenKey:
|
|
[[RLMSyncErrorActionToken alloc] initWithOriginalPath:std::move(path)]
|
|
};;
|
|
break;
|
|
}
|
|
case RLMSyncSystemErrorKindPermissionDenied: {
|
|
std::string path = [userInfo[@(realm::SyncError::c_original_file_path_key)] UTF8String];
|
|
custom = @{kRLMSyncErrorActionTokenKey:
|
|
[[RLMSyncErrorActionToken alloc] initWithOriginalPath:std::move(path)]
|
|
};
|
|
break;
|
|
}
|
|
case RLMSyncSystemErrorKindUser:
|
|
case RLMSyncSystemErrorKindSession:
|
|
break;
|
|
case RLMSyncSystemErrorKindConnection:
|
|
case RLMSyncSystemErrorKindClient:
|
|
case RLMSyncSystemErrorKindUnknown:
|
|
// Report the error. There's nothing the user can do about it, though.
|
|
shouldMakeError = fatal;
|
|
break;
|
|
}
|
|
error = shouldMakeError ? make_sync_error(errorClass, message, errorCode, custom) : nil;
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
if (!self.errorHandler || !error) {
|
|
return;
|
|
}
|
|
self.errorHandler(error, session);
|
|
});
|
|
}
|
|
|
|
- (NSArray<RLMSyncUser *> *)_allUsers {
|
|
NSMutableArray<RLMSyncUser *> *buffer = [NSMutableArray array];
|
|
for (auto user : SyncManager::shared().all_logged_in_users()) {
|
|
[buffer addObject:[[RLMSyncUser alloc] initWithSyncUser:std::move(user)]];
|
|
}
|
|
return buffer;
|
|
}
|
|
|
|
+ (void)resetForTesting {
|
|
SyncManager::shared().reset_for_testing();
|
|
}
|
|
|
|
- (RLMNetworkRequestOptions *)networkRequestOptions {
|
|
RLMNetworkRequestOptions *options = [[RLMNetworkRequestOptions alloc] init];
|
|
options.authorizationHeaderName = self.authorizationHeaderName;
|
|
options.customHeaders = self.customRequestHeaders;
|
|
options.pinnedCertificatePaths = self.pinnedCertificatePaths;
|
|
return options;
|
|
}
|
|
|
|
@end
|
|
|