Browse Source

Update DB - fix split view controller - onlyoffice useragent + fix

marinofaggiana 5 years ago
parent
commit
d16225d79e
59 changed files with 1618 additions and 558 deletions
  1. 2 2
      Cartfile
  2. 3 3
      Cartfile.resolved
  3. 0 1
      Carthage/Checkouts/CocoaLumberjack/.travis.yml
  4. 14 0
      Carthage/Checkouts/CocoaLumberjack/CHANGELOG.md
  5. 1 1
      Carthage/Checkouts/CocoaLumberjack/CocoaLumberjack.podspec
  6. 289 83
      Carthage/Checkouts/realm-cocoa/.jenkins.yml
  7. 86 0
      Carthage/Checkouts/realm-cocoa/CHANGELOG.md
  8. 2 0
      Carthage/Checkouts/realm-cocoa/Configuration/Base.xcconfig
  9. 4 1
      Carthage/Checkouts/realm-cocoa/Configuration/Realm/Realm.xcconfig
  10. 4 1
      Carthage/Checkouts/realm-cocoa/Configuration/TestHost.xcconfig
  11. 4 3
      Carthage/Checkouts/realm-cocoa/Jenkinsfile.releasability
  12. 119 7
      Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/RLMObjectServerTests.mm
  13. 0 25
      Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/RLMSyncManager+ObjectServerTests.h
  14. 0 44
      Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/RLMSyncManager+ObjectServerTests.m
  15. 4 3
      Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/RLMSyncTestCase.h
  16. 25 15
      Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/RLMSyncTestCase.mm
  17. 42 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/SwiftObjectServerTests.swift
  18. 2 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/SwiftSyncTestCase.swift
  19. 44 15
      Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/test-ros-server.js
  20. 2 2
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/dependencies.list
  21. 6 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/CMakeLists.txt
  22. 1 2
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/realm.cpp
  23. 24 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/util/test_file.cpp
  24. 6 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/util/test_file.hpp
  25. 4 0
      Carthage/Checkouts/realm-cocoa/Realm/RLMConstants.h
  26. 11 5
      Carthage/Checkouts/realm-cocoa/Realm/RLMNetworkClient.mm
  27. 5 1
      Carthage/Checkouts/realm-cocoa/Realm/RLMObjectBase.mm
  28. 1 1
      Carthage/Checkouts/realm-cocoa/Realm/RLMPlatform.h.in
  29. 55 22
      Carthage/Checkouts/realm-cocoa/Realm/RLMRealm.h
  30. 38 0
      Carthage/Checkouts/realm-cocoa/Realm/RLMRealm.mm
  31. 9 0
      Carthage/Checkouts/realm-cocoa/Realm/RLMSyncConfiguration.h
  32. 79 48
      Carthage/Checkouts/realm-cocoa/Realm/RLMSyncConfiguration.mm
  33. 2 3
      Carthage/Checkouts/realm-cocoa/Realm/RLMSyncConfiguration_Private.hpp
  34. 94 4
      Carthage/Checkouts/realm-cocoa/Realm/RLMSyncManager.h
  35. 120 68
      Carthage/Checkouts/realm-cocoa/Realm/RLMSyncManager.mm
  36. 0 7
      Carthage/Checkouts/realm-cocoa/Realm/RLMSyncManager_Private.h
  37. 39 39
      Carthage/Checkouts/realm-cocoa/Realm/RLMSyncSessionRefreshHandle.mm
  38. 2 3
      Carthage/Checkouts/realm-cocoa/Realm/RLMSyncUser.mm
  39. 18 3
      Carthage/Checkouts/realm-cocoa/Realm/RLMUtil.mm
  40. 2 2
      Carthage/Checkouts/realm-cocoa/Realm/Realm-Info.plist
  41. 4 0
      Carthage/Checkouts/realm-cocoa/Realm/Tests/InterprocessTests.m
  42. 76 9
      Carthage/Checkouts/realm-cocoa/Realm/Tests/RealmTests.mm
  43. 1 1
      Carthage/Checkouts/realm-cocoa/Realm/Tests/SchemaTests.mm
  44. 4 0
      Carthage/Checkouts/realm-cocoa/Realm/Tests/Swift/SwiftSchemaTests.swift
  45. 4 4
      Carthage/Checkouts/realm-cocoa/Realm/Tests/TestHost/main.m
  46. 38 1
      Carthage/Checkouts/realm-cocoa/RealmSwift/Realm.swift
  47. 90 4
      Carthage/Checkouts/realm-cocoa/RealmSwift/Tests/ObjectTests.swift
  48. 1 1
      Carthage/Checkouts/realm-cocoa/RealmSwift/Tests/PerformanceTests.swift
  49. 9 0
      Carthage/Checkouts/realm-cocoa/RealmSwift/Tests/RealmTests.swift
  50. 42 4
      Carthage/Checkouts/realm-cocoa/build.sh
  51. 3 3
      Carthage/Checkouts/realm-cocoa/dependencies.list
  52. 4 3
      Carthage/Checkouts/realm-cocoa/scripts/generate-rlmplatform.sh
  53. 1 1
      Carthage/Checkouts/realm-cocoa/scripts/package_examples.rb
  54. 131 95
      iOSClient/AppDelegate.m
  55. 7 1
      iOSClient/Main/CCDetail.m
  56. 25 6
      iOSClient/Main/Create cloud/NCCreateFormUploadDocuments.swift
  57. 2 2
      iOSClient/RichWorkspace/NCRichWorkspaceCommon.swift
  58. 10 0
      iOSClient/Utility/NCUtility.swift
  59. 3 9
      iOSClient/Viewer/NCViewerNextcloudText.swift

+ 2 - 2
Cartfile

@@ -1,4 +1,4 @@
-github "nextcloud/ios-communication-library" "v0.51"
+github "nextcloud/ios-communication-library" "develop"
 github "tilltue/TLPhotoPicker" "2.0.7"
 github "kishikawakatsumi/UICKeyChainStore" "v2.1.2"
 github "danielsaidi/Sheeeeeeeeet" "3.0.9"
@@ -6,7 +6,7 @@ github "MortimerGoro/MGSwipeTableCell" "1.6.8"
 github "dzenbot/DZNEmptyDataSet" "v1.8.1"
 github "ChangbaDevs/KTVHTTPCache" "2.0.1"
 github "jdg/MBProgressHUD" "1.1.0"
-github "realm/realm-cocoa" "v4.1.1"
+github "realm/realm-cocoa" "v4.3.1"
 github "SVGKit/SVGKit" "3.x"
 github "WeTransfer/WeScan" "1.2.0"
 github "malcommac/SwiftRichString"

+ 3 - 3
Cartfile.resolved

@@ -1,7 +1,7 @@
 github "Alamofire/Alamofire" "5.0.0-rc.2"
 github "AssistoLab/DropDown" "v2.3.13"
 github "ChangbaDevs/KTVHTTPCache" "2.0.1"
-github "CocoaLumberjack/CocoaLumberjack" "3.6.0"
+github "CocoaLumberjack/CocoaLumberjack" "3.6.1"
 github "MortimerGoro/MGSwipeTableCell" "1.6.8"
 github "SVGKit/SVGKit" "8560ef56bd61e34d5cd8d9739954f9765e3ac794"
 github "SwiftyJSON/SwiftyJSON" "5.0.0"
@@ -18,8 +18,8 @@ github "krzyzanowskim/OpenSSL" "1.0.218"
 github "malcommac/SwiftRichString" "3.7.1"
 github "marinofaggiana/AFNetworking" "2967678c3e0e98c9b8d7e06222ad12d1f49c26f2"
 github "marinofaggiana/FastScroll" "81967c2309d29bc2c330d422da612160a30bade8"
-github "nextcloud/ios-communication-library" "v0.51"
-github "realm/realm-cocoa" "v4.1.1"
+github "nextcloud/ios-communication-library" "c41e5cb352ad574283ff9e66d387c09702f08125"
+github "realm/realm-cocoa" "v4.3.1"
 github "rechsteiner/Parchment" "v1.7.0"
 github "scenee/FloatingPanel" "v1.7.2"
 github "tilltue/TLPhotoPicker" "2.0.7"

+ 0 - 1
Carthage/Checkouts/CocoaLumberjack/.travis.yml

@@ -58,7 +58,6 @@ script:
     - xcodebuild clean build -project Integration/Integration.xcodeproj -scheme 'watchOSSwiftIntegration' -configuration Release -sdk watchsimulator -destination 'platform=iOS Simulator,name=iPhone 11 Pro' | xcpretty -c
 
     - echo "Run the tests"
-    - xcodebuild test -skip-testing:'iOS Tests/DDFileLoggerPerformanceTests' -project Tests/Tests.xcodeproj -scheme 'iOS Tests' -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 7,OS=10.3.1' GCC_GENERATE_TEST_COVERAGE_FILES=YES | xcpretty -c
     - xcodebuild test -skip-testing:'iOS Tests/DDFileLoggerPerformanceTests' -project Tests/Tests.xcodeproj -scheme 'iOS Tests' -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 11 Pro,OS=latest' GCC_GENERATE_TEST_COVERAGE_FILES=YES | xcpretty -c
     - xcodebuild test -skip-testing:'OS X Tests/DDFileLoggerPerformanceTests' -project Tests/Tests.xcodeproj -scheme 'OS X Tests' -sdk macosx GCC_GENERATE_TEST_COVERAGE_FILES=YES | xcpretty -c
 

+ 14 - 0
Carthage/Checkouts/CocoaLumberjack/CHANGELOG.md

@@ -1,3 +1,17 @@
+## [3.6.1 - Xcode 11.3.1 on Jan 25th, 2020](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/3.6.1)
+
+### Public
+- Improve error handling during log file creation in DDFileLogger & DDLogFileManager (#1103 / #1111)
+- Improve nullability annotations in public headers (#1111 / #1112 / #1119)
+- Added support for thread QOS in DDLogMessage class (#1124)
+
+### Internal
+- Fix rolling timer being rescheduled rapidly due to leeway (#1106 / #1107)
+- Fix -didArchiveLogFile: returning the file name instead of the file path (#1078)
+- Fix setxattr() function usage (#1118)
+- Fix NSDateFormatter thread safety (#1121)
+- Fix -lt_dataForMessage: duplicated code (#1122)
+
 ## [3.6.0 - Xcode 11 on October 2nd, 2019](https://github.com/CocoaLumberjack/CocoaLumberjack/releases/tag/3.6.0)
 
 ### Public

+ 1 - 1
Carthage/Checkouts/CocoaLumberjack/CocoaLumberjack.podspec

@@ -1,7 +1,7 @@
 Pod::Spec.new do |s|
 
   s.name     = 'CocoaLumberjack'
-  s.version  = '3.6.0'
+  s.version  = '3.6.1'
   s.license  = 'BSD'
   s.summary  = 'A fast & simple, yet powerful & flexible logging framework for Mac and iOS.'
   s.homepage = 'https://github.com/CocoaLumberjack/CocoaLumberjack'

+ 289 - 83
Carthage/Checkouts/realm-cocoa/.jenkins.yml

@@ -6,10 +6,10 @@
 xcode_version: 
  - 10.0
  - 10.1
- - 10.2.1
  - 10.3
  - 11.1
- - 11.2
+ - 11.2.1
+ - 11.3
 target: 
  - docs
  - swiftlint
@@ -20,9 +20,11 @@ target:
  - ios-dynamic
  - watchos
  - tvos
- - ios-swift
  - osx-swift
+ - ios-swift
  - tvos-swift
+ - catalyst
+ - catalyst-swift
  - cocoapods-osx
  - cocoapods-ios
  - cocoapods-ios-dynamic
@@ -52,31 +54,31 @@ exclude:
     target: docs
     configuration: Release
 
-  - xcode_version: 10.2.1
+  - xcode_version: 10.3
     target: docs
     configuration: Debug
 
-  - xcode_version: 10.2.1
+  - xcode_version: 10.3
     target: docs
     configuration: Release
 
-  - xcode_version: 10.3
+  - xcode_version: 11.1
     target: docs
     configuration: Debug
 
-  - xcode_version: 10.3
+  - xcode_version: 11.1
     target: docs
     configuration: Release
 
-  - xcode_version: 11.1
+  - xcode_version: 11.2.1
     target: docs
     configuration: Debug
 
-  - xcode_version: 11.1
+  - xcode_version: 11.2.1
     target: docs
     configuration: Release
 
-  - xcode_version: 11.2
+  - xcode_version: 11.3
     target: docs
     configuration: Debug
 
@@ -96,31 +98,31 @@ exclude:
     target: swiftlint
     configuration: Release
 
-  - xcode_version: 10.2.1
+  - xcode_version: 10.3
     target: swiftlint
     configuration: Debug
 
-  - xcode_version: 10.2.1
+  - xcode_version: 10.3
     target: swiftlint
     configuration: Release
 
-  - xcode_version: 10.3
+  - xcode_version: 11.1
     target: swiftlint
     configuration: Debug
 
-  - xcode_version: 10.3
+  - xcode_version: 11.1
     target: swiftlint
     configuration: Release
 
-  - xcode_version: 11.1
+  - xcode_version: 11.2.1
     target: swiftlint
     configuration: Debug
 
-  - xcode_version: 11.1
+  - xcode_version: 11.2.1
     target: swiftlint
     configuration: Release
 
-  - xcode_version: 11.2
+  - xcode_version: 11.3
     target: swiftlint
     configuration: Debug
 
@@ -136,31 +138,31 @@ exclude:
     target: osx-encryption
     configuration: Release
 
-  - xcode_version: 10.2.1
+  - xcode_version: 10.3
     target: osx-encryption
     configuration: Debug
 
-  - xcode_version: 10.2.1
+  - xcode_version: 10.3
     target: osx-encryption
     configuration: Release
 
-  - xcode_version: 10.3
+  - xcode_version: 11.1
     target: osx-encryption
     configuration: Debug
 
-  - xcode_version: 10.3
+  - xcode_version: 11.1
     target: osx-encryption
     configuration: Release
 
-  - xcode_version: 11.1
+  - xcode_version: 11.2.1
     target: osx-encryption
     configuration: Debug
 
-  - xcode_version: 11.1
+  - xcode_version: 11.2.1
     target: osx-encryption
     configuration: Release
 
-  - xcode_version: 11.2
+  - xcode_version: 11.3
     target: osx-encryption
     configuration: Debug
 
@@ -176,31 +178,31 @@ exclude:
     target: osx-object-server
     configuration: Release
 
-  - xcode_version: 10.2.1
+  - xcode_version: 10.3
     target: osx-object-server
     configuration: Debug
 
-  - xcode_version: 10.2.1
+  - xcode_version: 10.3
     target: osx-object-server
     configuration: Release
 
-  - xcode_version: 10.3
+  - xcode_version: 11.1
     target: osx-object-server
     configuration: Debug
 
-  - xcode_version: 10.3
+  - xcode_version: 11.1
     target: osx-object-server
     configuration: Release
 
-  - xcode_version: 11.1
+  - xcode_version: 11.2.1
     target: osx-object-server
     configuration: Debug
 
-  - xcode_version: 11.1
+  - xcode_version: 11.2.1
     target: osx-object-server
     configuration: Release
 
-  - xcode_version: 11.2
+  - xcode_version: 11.3
     target: osx-object-server
     configuration: Debug
 
@@ -212,15 +214,35 @@ exclude:
     target: ios-static
     configuration: Debug
 
-  - xcode_version: 10.2.1
+  - xcode_version: 10.1
+    target: ios-static
+    configuration: Release
+
+  - xcode_version: 10.3
     target: ios-static
     configuration: Debug
 
   - xcode_version: 10.3
+    target: ios-static
+    configuration: Release
+
+  - xcode_version: 11.1
     target: ios-static
     configuration: Debug
 
   - xcode_version: 11.1
+    target: ios-static
+    configuration: Release
+
+  - xcode_version: 11.2.1
+    target: ios-static
+    configuration: Debug
+
+  - xcode_version: 11.2.1
+    target: ios-static
+    configuration: Release
+
+  - xcode_version: 11.3
     target: ios-static
     configuration: Debug
 
@@ -232,15 +254,35 @@ exclude:
     target: ios-dynamic
     configuration: Debug
 
-  - xcode_version: 10.2.1
+  - xcode_version: 10.1
+    target: ios-dynamic
+    configuration: Release
+
+  - xcode_version: 10.3
     target: ios-dynamic
     configuration: Debug
 
   - xcode_version: 10.3
+    target: ios-dynamic
+    configuration: Release
+
+  - xcode_version: 11.1
     target: ios-dynamic
     configuration: Debug
 
   - xcode_version: 11.1
+    target: ios-dynamic
+    configuration: Release
+
+  - xcode_version: 11.2.1
+    target: ios-dynamic
+    configuration: Debug
+
+  - xcode_version: 11.2.1
+    target: ios-dynamic
+    configuration: Release
+
+  - xcode_version: 11.3
     target: ios-dynamic
     configuration: Debug
 
@@ -252,15 +294,35 @@ exclude:
     target: watchos
     configuration: Debug
 
-  - xcode_version: 10.2.1
+  - xcode_version: 10.1
+    target: watchos
+    configuration: Release
+
+  - xcode_version: 10.3
     target: watchos
     configuration: Debug
 
   - xcode_version: 10.3
+    target: watchos
+    configuration: Release
+
+  - xcode_version: 11.1
     target: watchos
     configuration: Debug
 
   - xcode_version: 11.1
+    target: watchos
+    configuration: Release
+
+  - xcode_version: 11.2.1
+    target: watchos
+    configuration: Debug
+
+  - xcode_version: 11.2.1
+    target: watchos
+    configuration: Release
+
+  - xcode_version: 11.3
     target: watchos
     configuration: Debug
 
@@ -272,15 +334,35 @@ exclude:
     target: tvos
     configuration: Debug
 
-  - xcode_version: 10.2.1
+  - xcode_version: 10.1
+    target: tvos
+    configuration: Release
+
+  - xcode_version: 10.3
     target: tvos
     configuration: Debug
 
   - xcode_version: 10.3
+    target: tvos
+    configuration: Release
+
+  - xcode_version: 11.1
     target: tvos
     configuration: Debug
 
   - xcode_version: 11.1
+    target: tvos
+    configuration: Release
+
+  - xcode_version: 11.2.1
+    target: tvos
+    configuration: Debug
+
+  - xcode_version: 11.2.1
+    target: tvos
+    configuration: Release
+
+  - xcode_version: 11.3
     target: tvos
     configuration: Debug
 
@@ -292,67 +374,147 @@ exclude:
     target: ios-swift
     configuration: Debug
 
-  - xcode_version: 10.2.1
+  - xcode_version: 10.1
+    target: ios-swift
+    configuration: Release
+
+  - xcode_version: 10.3
     target: ios-swift
     configuration: Debug
 
   - xcode_version: 10.3
+    target: ios-swift
+    configuration: Release
+
+  - xcode_version: 11.1
     target: ios-swift
     configuration: Debug
 
   - xcode_version: 11.1
+    target: ios-swift
+    configuration: Release
+
+  - xcode_version: 11.2.1
+    target: ios-swift
+    configuration: Debug
+
+  - xcode_version: 11.2.1
+    target: ios-swift
+    configuration: Release
+
+  - xcode_version: 11.3
     target: ios-swift
     configuration: Debug
 
   - xcode_version: 10.0
-    target: osx-swift
+    target: tvos-swift
     configuration: Debug
 
   - xcode_version: 10.1
-    target: osx-swift
+    target: tvos-swift
     configuration: Debug
 
-  - xcode_version: 10.2.1
-    target: osx-swift
-    configuration: Debug
+  - xcode_version: 10.1
+    target: tvos-swift
+    configuration: Release
 
   - xcode_version: 10.3
-    target: osx-swift
+    target: tvos-swift
     configuration: Debug
 
+  - xcode_version: 10.3
+    target: tvos-swift
+    configuration: Release
+
   - xcode_version: 11.1
-    target: osx-swift
+    target: tvos-swift
     configuration: Debug
 
-  - xcode_version: 10.0
+  - xcode_version: 11.1
     target: tvos-swift
-    configuration: Debug
+    configuration: Release
 
-  - xcode_version: 10.1
+  - xcode_version: 11.2.1
     target: tvos-swift
     configuration: Debug
 
-  - xcode_version: 10.2.1
+  - xcode_version: 11.2.1
+    target: tvos-swift
+    configuration: Release
+
+  - xcode_version: 11.3
     target: tvos-swift
     configuration: Debug
 
+  - xcode_version: 10.0
+    target: catalyst
+    configuration: Debug
+
+  - xcode_version: 10.0
+    target: catalyst
+    configuration: Release
+
+  - xcode_version: 10.1
+    target: catalyst
+    configuration: Debug
+
+  - xcode_version: 10.1
+    target: catalyst
+    configuration: Release
+
   - xcode_version: 10.3
-    target: tvos-swift
+    target: catalyst
     configuration: Debug
 
+  - xcode_version: 10.3
+    target: catalyst
+    configuration: Release
+
   - xcode_version: 11.1
-    target: tvos-swift
+    target: catalyst
+    configuration: Debug
+
+  - xcode_version: 11.2.1
+    target: catalyst
     configuration: Debug
 
   - xcode_version: 10.0
-    target: cocoapods-osx
+    target: catalyst-swift
+    configuration: Debug
+
+  - xcode_version: 10.0
+    target: catalyst-swift
+    configuration: Release
+
+  - xcode_version: 10.1
+    target: catalyst-swift
     configuration: Debug
 
   - xcode_version: 10.1
+    target: catalyst-swift
+    configuration: Release
+
+  - xcode_version: 10.3
+    target: catalyst-swift
+    configuration: Debug
+
+  - xcode_version: 10.3
+    target: catalyst-swift
+    configuration: Release
+
+  - xcode_version: 11.1
+    target: catalyst-swift
+    configuration: Debug
+
+  - xcode_version: 11.2.1
+    target: catalyst-swift
+    configuration: Debug
+
+  - xcode_version: 10.0
     target: cocoapods-osx
     configuration: Debug
 
-  - xcode_version: 10.2.1
+  - xcode_version: 10.1
     target: cocoapods-osx
     configuration: Debug
 
@@ -364,7 +526,11 @@ exclude:
     target: cocoapods-osx
     configuration: Debug
 
-  - xcode_version: 11.2
+  - xcode_version: 11.2.1
+    target: cocoapods-osx
+    configuration: Debug
+
+  - xcode_version: 11.3
     target: cocoapods-osx
     configuration: Debug
 
@@ -376,19 +542,35 @@ exclude:
     target: cocoapods-ios
     configuration: Debug
 
-  - xcode_version: 10.2.1
+  - xcode_version: 10.1
+    target: cocoapods-ios
+    configuration: Release
+
+  - xcode_version: 10.3
     target: cocoapods-ios
     configuration: Debug
 
   - xcode_version: 10.3
+    target: cocoapods-ios
+    configuration: Release
+
+  - xcode_version: 11.1
     target: cocoapods-ios
     configuration: Debug
 
   - xcode_version: 11.1
+    target: cocoapods-ios
+    configuration: Release
+
+  - xcode_version: 11.2.1
     target: cocoapods-ios
     configuration: Debug
 
-  - xcode_version: 11.2
+  - xcode_version: 11.2.1
+    target: cocoapods-ios
+    configuration: Release
+
+  - xcode_version: 11.3
     target: cocoapods-ios
     configuration: Debug
 
@@ -400,19 +582,35 @@ exclude:
     target: cocoapods-ios-dynamic
     configuration: Debug
 
-  - xcode_version: 10.2.1
+  - xcode_version: 10.1
+    target: cocoapods-ios-dynamic
+    configuration: Release
+
+  - xcode_version: 10.3
     target: cocoapods-ios-dynamic
     configuration: Debug
 
   - xcode_version: 10.3
+    target: cocoapods-ios-dynamic
+    configuration: Release
+
+  - xcode_version: 11.1
     target: cocoapods-ios-dynamic
     configuration: Debug
 
   - xcode_version: 11.1
+    target: cocoapods-ios-dynamic
+    configuration: Release
+
+  - xcode_version: 11.2.1
     target: cocoapods-ios-dynamic
     configuration: Debug
 
-  - xcode_version: 11.2
+  - xcode_version: 11.2.1
+    target: cocoapods-ios-dynamic
+    configuration: Release
+
+  - xcode_version: 11.3
     target: cocoapods-ios-dynamic
     configuration: Debug
 
@@ -424,19 +622,35 @@ exclude:
     target: cocoapods-watchos
     configuration: Debug
 
-  - xcode_version: 10.2.1
+  - xcode_version: 10.1
+    target: cocoapods-watchos
+    configuration: Release
+
+  - xcode_version: 10.3
     target: cocoapods-watchos
     configuration: Debug
 
   - xcode_version: 10.3
+    target: cocoapods-watchos
+    configuration: Release
+
+  - xcode_version: 11.1
     target: cocoapods-watchos
     configuration: Debug
 
   - xcode_version: 11.1
+    target: cocoapods-watchos
+    configuration: Release
+
+  - xcode_version: 11.2.1
     target: cocoapods-watchos
     configuration: Debug
 
-  - xcode_version: 11.2
+  - xcode_version: 11.2.1
+    target: cocoapods-watchos
+    configuration: Release
+
+  - xcode_version: 11.3
     target: cocoapods-watchos
     configuration: Debug
 
@@ -456,27 +670,19 @@ exclude:
     target: swiftpm
     configuration: Release
 
-  - xcode_version: 10.2.1
+  - xcode_version: 10.3
     target: swiftpm
     configuration: Debug
 
-  - xcode_version: 10.2.1
-    target: swiftpm
-    configuration: Release
-
   - xcode_version: 10.3
     target: swiftpm
-    configuration: Debug
+    configuration: Release
 
   - xcode_version: 11.1
     target: swiftpm
     configuration: Debug
 
-  - xcode_version: 11.1
-    target: swiftpm
-    configuration: Release
-
-  - xcode_version: 11.2
+  - xcode_version: 11.2.1
     target: swiftpm
     configuration: Debug
 
@@ -496,31 +702,31 @@ exclude:
     target: swiftpm-address
     configuration: Release
 
-  - xcode_version: 10.2.1
+  - xcode_version: 10.3
     target: swiftpm-address
     configuration: Debug
 
-  - xcode_version: 10.2.1
+  - xcode_version: 10.3
     target: swiftpm-address
     configuration: Release
 
-  - xcode_version: 10.3
+  - xcode_version: 11.1
     target: swiftpm-address
     configuration: Debug
 
-  - xcode_version: 10.3
+  - xcode_version: 11.1
     target: swiftpm-address
     configuration: Release
 
-  - xcode_version: 11.1
+  - xcode_version: 11.2.1
     target: swiftpm-address
     configuration: Debug
 
-  - xcode_version: 11.1
+  - xcode_version: 11.2.1
     target: swiftpm-address
     configuration: Release
 
-  - xcode_version: 11.2
+  - xcode_version: 11.3
     target: swiftpm-address
     configuration: Debug
 
@@ -540,30 +746,30 @@ exclude:
     target: swiftpm-thread
     configuration: Release
 
-  - xcode_version: 10.2.1
+  - xcode_version: 10.3
     target: swiftpm-thread
     configuration: Debug
 
-  - xcode_version: 10.2.1
+  - xcode_version: 10.3
     target: swiftpm-thread
     configuration: Release
 
-  - xcode_version: 10.3
+  - xcode_version: 11.1
     target: swiftpm-thread
     configuration: Debug
 
-  - xcode_version: 10.3
+  - xcode_version: 11.1
     target: swiftpm-thread
     configuration: Release
 
-  - xcode_version: 11.1
+  - xcode_version: 11.2.1
     target: swiftpm-thread
     configuration: Debug
 
-  - xcode_version: 11.1
+  - xcode_version: 11.2.1
     target: swiftpm-thread
     configuration: Release
 
-  - xcode_version: 11.2
+  - xcode_version: 11.3
     target: swiftpm-thread
     configuration: Debug

+ 86 - 0
Carthage/Checkouts/realm-cocoa/CHANGELOG.md

@@ -1,3 +1,89 @@
+4.3.1 Release notes (2020-01-16)
+=============================================================
+
+### Enhancements
+
+* Reduce the encrypted page reclaimer's impact on battery life when encryption
+  is used. ([Core #3461](https://github.com/realm/realm-core/pull/3461)).
+
+### Fixed
+
+* macOS binaries were built with the incorrect deployment target (10.14 rather
+  than 10.9), resulting in linker warnings. ([#6299](https://github.com/realm/realm-cocoa/issues/6299), since 3.18.0).
+* An internal datastructure for List properties could be double-deleted if the
+  last reference was released from a thread other than the one which the List
+  was created on at the wrong time. This would typically manifest as
+  "pthread_mutex_destroy() failed", but could also result in other kinds of
+  crashes. ([#6333](https://github.com/realm/realm-cocoa/issues/6333)).
+* Sorting on float or double properties containing NaN values had inconsistent
+  results and would sometimes crash due to out-of-bounds memory accesses.
+  ([#6357](https://github.com/realm/realm-cocoa/issues/6357)).
+* A NOT query on a `List<Object>` which happened to have the objects in a
+  different order than the underlying table would sometimes include the object
+  immediately before an object which matches the query. ([#6289](https://github.com/realm/realm-cocoa/issues/6289), since 0.90.0).
+
+### Compatibility
+
+* File format: Generates Realms with format v9 (Reads and upgrades all previous formats)
+* Realm Object Server: 3.21.0 or later.
+* Carthage release for Swift is built with Xcode 11.3.
+
+### Internal
+
+* Upgraded realm-core from 5.23.6 to 5.23.8.
+* Upgraded realm-sync from 4.9.0 to 4.9.4.
+
+4.3.0 Release notes (2019-12-19)
+=============================================================
+
+### Enhancements
+
+* Add the ability to set a custom logger function on `RLMSyncManager` which is
+  called instead of the default NSLog-based logger.
+* Expose configuration options for the various types of sync connection
+  timeouts and heartbeat intervals on `RLMSyncManager`.
+* Add an option to have `Realm.asyncOpen()` report an error if the connection
+  times out rather than swallowing the error and attempting to reconnect until
+  it succeeds.
+
+### Fixed
+
+* Fix a crash when using value(forKey:) on a LinkingObjects property (including
+  when doing so indirectly, such as by querying on that property).
+  ([#6366](https://github.com/realm/realm-cocoa/issues/6366), since 4.0.0).
+* Fix a rare crash in `ClientHistoryImpl::integrate_server_changesets()` which
+  would only happen in Debug builds (since v3.0.0).
+
+### Compatibility
+
+* File format: Generates Realms with format v9 (Reads and upgrades all previous formats)
+* Realm Object Server: 3.21.0 or later.
+* Carthage release for Swift is built with Xcode 11.3.
+
+### Internal
+
+* Upgraded realm-sync from 4.8.2 to 4.9.0.
+
+4.2.0 Release notes (2019-12-16)
+=============================================================
+
+### Enhancements
+
+* Add `-[RLMRealm fileExistsForConfiguration:]`/`Realm.fileExists(for:)`,
+  which checks if a local Realm file exists for the given configuration.
+* Add `-[RLMRealm deleteFilesForConfiguration:]`/`Realm.deleteFiles(for:)`
+  to delete the Realm file and all auxiliary files for the given configuration.
+
+### Fixed
+
+* None.
+
+### Compatibility
+
+* File format: Generates Realms with format v9 (Reads and upgrades all previous formats)
+* Realm Object Server: 3.21.0 or later.
+* Carthage release for Swift is built with Xcode 11.3.
+
 4.1.1 Release notes (2019-11-18)
 =============================================================
 

+ 2 - 0
Carthage/Checkouts/realm-cocoa/Configuration/Base.xcconfig

@@ -61,3 +61,5 @@ WATCHOS_DEPLOYMENT_TARGET = 2.0;
 TVOS_DEPLOYMENT_TARGET = 9.0;
 
 SWIFT_VERSION = 4.0;
+TARGETED_DEVICE_FAMILY = 1,2,3,4;
+SDKROOT = $(REALM_SDKROOT);

+ 4 - 1
Carthage/Checkouts/realm-cocoa/Configuration/Realm/Realm.xcconfig

@@ -28,7 +28,10 @@ LD_RUNPATH_SEARCH_PATHS[sdk=iphone*] = $(inherited) @executable_path/Frameworks
 LD_RUNPATH_SEARCH_PATHS[sdk=watch*] = $(inherited) @executable_path/Frameworks @loader_path/Frameworks;
 LD_RUNPATH_SEARCH_PATHS[sdk=appletv*] = $(inherited) @executable_path/Frameworks @loader_path/Frameworks;
 
-REALM_PLATFORM_SUFFIX = $(PLATFORM_NAME);
+REALM_PLATFORM_SUFFIX_ = $(PLATFORM_NAME); // Xcode 10 does not define SDK_VARIANT
+REALM_PLATFORM_SUFFIX_macos = macosx;
+REALM_PLATFORM_SUFFIX_iosmac = maccatalyst;
+REALM_PLATFORM_SUFFIX = $(REALM_PLATFORM_SUFFIX_$(SDK_VARIANT));
 OTHER_LDFLAGS[sdk=macosx*] = -lrealm-$(REALM_PLATFORM_SUFFIX)$(REALM_LIBRARY_SUFFIX) $(REALM_CATALYST_FLAGS);
 OTHER_LIBTOOLFLAGS[sdk=macosx*] = -lrealm-$(REALM_PLATFORM_SUFFIX)$(REALM_LIBRARY_SUFFIX) $(REALM_CATALYST_FLAGS);
 OTHER_LDFLAGS[sdk=iphone*] = -lrealm-ios$(REALM_LIBRARY_SUFFIX);

+ 4 - 1
Carthage/Checkouts/realm-cocoa/Configuration/TestHost.xcconfig

@@ -11,9 +11,12 @@ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
 
 PRODUCT_BUNDLE_IDENTIFIER = io.realm.TestHost;
 
+REALM_UI_FRAMEWORK_ = Cocoa;
+REALM_UI_FRAMEWORK_uikit = UIKit;
+
 OTHER_LDFLAGS[sdk=iphone*] = -framework UIKit;
 OTHER_LDFLAGS[sdk=appletv*] = -framework UIKit;
-OTHER_LDFLAGS[sdk=macosx*] = -framework Cocoa;
+OTHER_LDFLAGS[sdk=macosx*] = -framework $(REALM_UI_FRAMEWORK_$(RESOURCES_UI_FRAMEWORK_FAMILY));
 
 PRINCIPAL_CLASS[sdk=iphone*] = UIApplication;
 PRINCIPAL_CLASS[sdk=appletv*] = UIApplication;

+ 4 - 3
Carthage/Checkouts/realm-cocoa/Jenkinsfile.releasability

@@ -1,10 +1,10 @@
-xcodeVersions = ['10.0', '10.1', '10.2.1', '10.3', '11.1', '11.2']
+xcodeVersions = ['10.0', '10.1', '10.3', '11.1', '11.2.1', '11.3']
 platforms = ['osx', 'ios', 'watchos', 'tvos', 'catalyst']
 carthagePlatforms = ['osx', 'ios', 'watchos', 'tvos']
 platformNames = ['osx': 'macOS', 'ios': 'iOS', 'watchos': 'watchOS', 'tvos': 'tvOS', 'catalyst': 'Catalyst']
-carthageXcodeVersion = '11.2'
+carthageXcodeVersion = '11.3'
 objcXcodeVersion = '10.1'
-docsSwiftVersion = '5.1'
+docsSwiftVersion = '5.1.2'
 
 def installationTest(platform, test, language) {
   return {
@@ -65,6 +65,7 @@ def doBuild() {
           unstash 'source'
           sh """
           export REALM_SWIFT_VERSION=${docsSwiftVersion}
+          export PATH='/Users/realm/.rbenv/bin:/Users/realm/.rbenv/shims:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Users/realm/.gems/bin'
           ./scripts/reset-simulators.sh
           ./build.sh docs
           cd docs

+ 119 - 7
Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/RLMObjectServerTests.mm

@@ -219,10 +219,11 @@
     __block XCTestExpectation *ex;
     [RLMSyncSessionRefreshHandle calculateFireDateUsingTestLogic:YES
                                         blockOnRefreshCompletion:^(BOOL success) {
-                                            XCTAssertTrue(success);
-                                            refreshCount++;
-                                            [ex fulfill];
-                                        }];
+        XCTAssertTrue(success);
+        if (refreshCount++ == 3) { // arbitrary choice; refreshes every second
+            [ex fulfill];
+        }
+    }];
     // Open the Realm.
     NSURL *url = REALM_URL();
     RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:NSStringFromSelector(_cmd)
@@ -232,7 +233,91 @@
     ex = [self expectationWithDescription:@"Timer fired"];
     [self waitForExpectationsWithTimeout:10 handler:nil];
     XCTAssertTrue(errorCount == 0);
-    XCTAssertTrue(refreshCount > 0);
+    XCTAssertTrue(refreshCount >= 4);
+}
+
+- (void)testLoginConnectionTimeoutFromManager {
+    // First create the user, talking directly to ROS
+    NSString *userName = NSStringFromSelector(_cmd);
+    @autoreleasepool {
+        RLMSyncCredentials *credentials = [RLMObjectServerTests basicCredentialsWithName:userName register:YES];
+        RLMSyncUser *user = [self logInUserForCredentials:credentials server:[NSURL URLWithString:@"http://127.0.0.1:9080"]];
+        [user logOut];
+    }
+
+    RLMSyncTimeoutOptions *timeoutOptions = [RLMSyncTimeoutOptions new];
+
+    // 9082 is a proxy which delays responding to requests
+    NSURL *authURL = [NSURL URLWithString:@"http://127.0.0.1:9082"];
+
+    // Login attempt should time out
+    timeoutOptions.connectTimeout = 1000.0;
+    RLMSyncManager.sharedManager.timeoutOptions = timeoutOptions;
+
+    RLMSyncCredentials *credentials = [RLMObjectServerTests basicCredentialsWithName:userName register:NO];
+    XCTestExpectation *ex = [self expectationWithDescription:@"Login should time out"];
+    [RLMSyncUser logInWithCredentials:credentials authServerURL:authURL
+                         onCompletion:^(RLMSyncUser *user, NSError *error) {
+        XCTAssertNil(user);
+        XCTAssertNotNil(error);
+        XCTAssertEqualObjects(error.domain, NSURLErrorDomain);
+        XCTAssertEqual(error.code, NSURLErrorTimedOut);
+        [ex fulfill];
+    }];
+    [self waitForExpectationsWithTimeout:2.0 handler:nil];
+
+    // Login attempt should succeeed
+    timeoutOptions.connectTimeout = 3000.0;
+    RLMSyncManager.sharedManager.timeoutOptions = timeoutOptions;
+
+    ex = [self expectationWithDescription:@"Login should succeed"];
+    [RLMSyncUser logInWithCredentials:credentials authServerURL:authURL
+                         onCompletion:^(RLMSyncUser *user, NSError *error) {
+        [user logOut];
+        XCTAssertNotNil(user);
+        XCTAssertNil(error);
+        [ex fulfill];
+    }];
+    [self waitForExpectationsWithTimeout:3.0 handler:nil];
+}
+
+- (void)testLoginConnectionTimeoutDirect {
+    // First create the user, talking directly to ROS
+    NSString *userName = NSStringFromSelector(_cmd);
+    @autoreleasepool {
+        RLMSyncCredentials *credentials = [RLMObjectServerTests basicCredentialsWithName:userName register:YES];
+        RLMSyncUser *user = [self logInUserForCredentials:credentials server:[NSURL URLWithString:@"http://127.0.0.1:9080"]];
+        [user logOut];
+    }
+
+    // 9082 is a proxy which delays responding to requests
+    NSURL *authURL = [NSURL URLWithString:@"http://127.0.0.1:9082"];
+
+    // Login attempt should time out
+    RLMSyncCredentials *credentials = [RLMObjectServerTests basicCredentialsWithName:userName register:NO];
+    XCTestExpectation *ex = [self expectationWithDescription:@"Login should time out"];
+    [RLMSyncUser logInWithCredentials:credentials authServerURL:authURL
+                              timeout:1.0 callbackQueue:dispatch_get_main_queue()
+                         onCompletion:^(RLMSyncUser *user, NSError *error) {
+        XCTAssertNil(user);
+        XCTAssertNotNil(error);
+        XCTAssertEqualObjects(error.domain, NSURLErrorDomain);
+        XCTAssertEqual(error.code, NSURLErrorTimedOut);
+        [ex fulfill];
+    }];
+    [self waitForExpectationsWithTimeout:2.0 handler:nil];
+
+    // Login attempt should succeeed
+    ex = [self expectationWithDescription:@"Login should succeed"];
+    [RLMSyncUser logInWithCredentials:credentials authServerURL:authURL
+                              timeout:3.0 callbackQueue:dispatch_get_main_queue()
+                         onCompletion:^(RLMSyncUser *user, NSError *error) {
+        [user logOut];
+        XCTAssertNotNil(user);
+        XCTAssertNil(error);
+        [ex fulfill];
+    }];
+    [self waitForExpectationsWithTimeout:3.0 handler:nil];
 }
 
 #pragma mark - Users
@@ -1662,6 +1747,34 @@ static const NSInteger NUMBER_OF_BIG_OBJECTS = 2;
     [self waitForExpectationsWithTimeout:2.0 handler:nil];
 }
 
+- (void)testAsyncOpenConnectionTimeout {
+    NSString *userName = NSStringFromSelector(_cmd);
+    // 9083 is a proxy which delays responding to requests
+    NSURL *authURL = [NSURL URLWithString:@"http://127.0.0.1:9083"];
+    RLMSyncUser *user = [self logInUserForCredentials:[RLMObjectServerTests basicCredentialsWithName:userName register:YES]
+                                               server:authURL];
+    RLMRealmConfiguration *c = [user configuration];
+    RLMSyncConfiguration *syncConfig = c.syncConfiguration;
+    syncConfig.cancelAsyncOpenOnNonFatalErrors = true;
+    c.syncConfiguration = syncConfig;
+
+    RLMSyncTimeoutOptions *timeoutOptions = [RLMSyncTimeoutOptions new];
+    timeoutOptions.connectTimeout = 1000.0;
+    RLMSyncManager.sharedManager.timeoutOptions = timeoutOptions;
+
+    XCTestExpectation *ex = [self expectationWithDescription:@"async open"];
+    [RLMRealm asyncOpenWithConfiguration:c
+                           callbackQueue:dispatch_get_main_queue()
+                                callback:^(RLMRealm *realm, NSError *error) {
+        XCTAssertNotNil(error);
+        XCTAssertEqual(error.code, ETIMEDOUT);
+        XCTAssertEqual(error.domain, NSPOSIXErrorDomain);
+        XCTAssertNil(realm);
+        [ex fulfill];
+    }];
+    [self waitForExpectationsWithTimeout:10.0 handler:nil];
+}
+
 #pragma mark - Compact on Launch
 
 - (void)testCompactOnLaunch {
@@ -2150,8 +2263,6 @@ static NSURL *certificateURL(NSString *filename) {
 }
 
 - (void)verifyOpenFails:(RLMRealmConfiguration *)config {
-    [self openRealmWithConfiguration:config];
-
     XCTestExpectation *expectation = [self expectationWithDescription:@"wait for error"];
     RLMSyncManager.sharedManager.errorHandler = ^(NSError *error, __unused RLMSyncSession *session) {
         XCTAssertTrue([error.domain isEqualToString:RLMSyncErrorDomain]);
@@ -2159,6 +2270,7 @@ static NSURL *certificateURL(NSString *filename) {
         [expectation fulfill];
     };
 
+    [self openRealmWithConfiguration:config];
     [self waitForExpectationsWithTimeout:20.0 handler:nil];
 }
 

+ 0 - 25
Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/RLMSyncManager+ObjectServerTests.h

@@ -1,25 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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.h"
-
-@interface RLMSyncManager (ObjectServerTests)
-
-- (void)prepareForDestruction;
-
-@end

+ 0 - 44
Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/RLMSyncManager+ObjectServerTests.m

@@ -1,44 +0,0 @@
-////////////////////////////////////////////////////////////////////////////
-//
-// 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 "RLMSyncManager+ObjectServerTests.h"
-#import "RLMSyncTestCase.h"
-#import "RLMTestUtils.h"
-
-@interface RLMSyncManager ()
-- (NSArray<RLMSyncUser *> *)_allUsers;
-@end
-
-@implementation RLMSyncManager (ObjectServerTests)
-
-+ (void)load {
-    RLMSwapOutClassMethod(self, @selector(sharedManager),  @selector(ost_sharedManager));
-}
-
-+ (instancetype)ost_sharedManager {
-    return [RLMSyncTestCase managerForCurrentTest];
-}
-
-- (void)prepareForDestruction {
-    // Log out all the logged-in users.
-    [[self _allUsers] makeObjectsPerformSelector:@selector(logOut)];
-    [RLMSyncManager resetForTesting];
-}
-
-@end

+ 4 - 3
Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/RLMSyncTestCase.h

@@ -24,7 +24,7 @@ typedef void(^RLMSyncBasicErrorReportingBlock)(NSError * _Nullable);
 NS_ASSUME_NONNULL_BEGIN
 
 @interface RLMSyncManager ()
-- (void)setSessionCompletionNotifier:(RLMSyncBasicErrorReportingBlock)sessionCompletionNotifier;
+- (void)setSessionCompletionNotifier:(nullable RLMSyncBasicErrorReportingBlock)sessionCompletionNotifier;
 @end
 
 @interface SyncObject : RLMObject
@@ -38,8 +38,6 @@ NS_ASSUME_NONNULL_BEGIN
 
 @interface RLMSyncTestCase : RLMMultiProcessTestCase
 
-+ (RLMSyncManager *)managerForCurrentTest;
-
 + (NSURL *)authServerURL;
 + (NSURL *)secureAuthServerURL;
 
@@ -125,6 +123,9 @@ NS_ASSUME_NONNULL_BEGIN
 /// Manually set the refresh token for a user. Used for testing invalid token conditions.
 - (void)manuallySetRefreshTokenForUser:(RLMSyncUser *)user value:(NSString *)tokenValue;
 
+- (void)setupSyncManager;
+- (void)resetSyncManager;
+
 @end
 
 NS_ASSUME_NONNULL_END

+ 25 - 15
Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/RLMSyncTestCase.mm

@@ -24,7 +24,7 @@
 #import "RLMRealm_Dynamic.h"
 #import "RLMRealm_Private.hpp"
 #import "RLMRealmConfiguration_Private.h"
-#import "RLMSyncManager+ObjectServerTests.h"
+#import "RLMSyncManager_Private.h"
 #import "RLMSyncSessionRefreshHandle+ObjectServerTests.h"
 #import "RLMSyncConfiguration_Private.h"
 #import "RLMUtil.hpp"
@@ -53,7 +53,8 @@ static NSString *nodePath() {
 
 @interface RLMSyncManager ()
 + (void)_setCustomBundleID:(NSString *)customBundleID;
-- (instancetype)initWithCustomRootDirectory:(NSURL *)rootDirectory;
+- (void)configureWithRootDirectory:(NSURL *)rootDirectory;
+- (NSArray<RLMSyncUser *> *)_allUsers;
 @end
 
 @interface RLMSyncTestCase ()
@@ -90,7 +91,6 @@ static NSString *nodePath() {
 @end
 
 static NSTask *s_task;
-static RLMSyncManager *s_managerForTest;
 
 static NSURL *syncDirectoryForChildProcess() {
     NSString *path = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES)[0];
@@ -240,10 +240,6 @@ static NSURL *syncDirectoryForChildProcess() {
 
 @implementation RLMSyncTestCase
 
-+ (RLMSyncManager *)managerForCurrentTest {
-    return s_managerForTest;
-}
-
 #pragma mark - Helper methods
 
 - (BOOL)isPartial {
@@ -323,6 +319,7 @@ static NSURL *syncDirectoryForChildProcess() {
     // Wait for login to succeed or fail.
     XCTAssert(dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeout * NSEC_PER_SEC))) == 0,
               @"Timed out while trying to asynchronously open Realm for URL: %@", url);
+    RLMSyncManager.sharedManager.sessionCompletionNotifier = nil;
     return realm;
 }
 
@@ -512,6 +509,19 @@ static NSURL *syncDirectoryForChildProcess() {
 - (void)setUp {
     [super setUp];
     self.continueAfterFailure = NO;
+
+    REALM_ASSERT(RLMSyncManager.sharedManager._allUsers.count == 0);
+    [RLMSyncManager resetForTesting];
+
+    [self setupSyncManager];
+}
+
+- (void)tearDown {
+    [self resetSyncManager];
+    [super tearDown];
+}
+
+- (void)setupSyncManager {
     NSURL *clientDataRoot;
     if (self.isParent) {
         [RealmObjectServer.sharedServer launch];
@@ -520,21 +530,21 @@ static NSURL *syncDirectoryForChildProcess() {
     else {
         clientDataRoot = syncDirectoryForChildProcess();
     }
+
     NSError *error;
     [NSFileManager.defaultManager removeItemAtURL:clientDataRoot error:&error];
     [NSFileManager.defaultManager createDirectoryAtURL:clientDataRoot
                            withIntermediateDirectories:YES attributes:nil error:&error];
-    s_managerForTest = [[RLMSyncManager alloc] initWithCustomRootDirectory:clientDataRoot];
-    [RLMSyncManager sharedManager].logLevel = RLMSyncLogLevelOff;
-    [RLMSyncManager sharedManager].userAgent = self.name;
+    RLMSyncManager *syncManager = RLMSyncManager.sharedManager;
+    [syncManager configureWithRootDirectory:clientDataRoot];
+    syncManager.logLevel = RLMSyncLogLevelOff;
+    syncManager.userAgent = self.name;
 }
 
-- (void)tearDown {
-    [s_managerForTest prepareForDestruction];
-    s_managerForTest = nil;
+- (void)resetSyncManager {
+    [RLMSyncManager.sharedManager._allUsers makeObjectsPerformSelector:@selector(logOut)];
+    [RLMSyncManager resetForTesting];
     [RLMSyncSessionRefreshHandle calculateFireDateUsingTestLogic:NO blockOnRefreshCompletion:nil];
-
-    [super tearDown];
 }
 
 @end

+ 42 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/SwiftObjectServerTests.swift

@@ -354,6 +354,48 @@ class SwiftObjectServerTests: SwiftSyncTestCase {
         waitForExpectations(timeout: 10.0, handler: nil)
     }
 
+    func testAsyncOpenTimeout() {
+        let syncTimeoutOptions = SyncTimeoutOptions()
+        syncTimeoutOptions.connectTimeout = 3000
+        SyncManager.shared.timeoutOptions = syncTimeoutOptions
+
+        // The server proxy adds a 2 second delay, so a 3 second timeout should succeed
+        autoreleasepool {
+            let user = try! synchronouslyLogInUser(for: basicCredentials(register: true), server: slowConnectAuthURL)
+            let config = user.configuration(cancelAsyncOpenOnNonFatalErrors: true)
+            let ex = expectation(description: "async open")
+            Realm.asyncOpen(configuration: config) { _, error in
+                XCTAssertNil(error)
+                ex.fulfill()
+            }
+            waitForExpectations(timeout: 10.0, handler: nil)
+            user.logOut()
+        }
+
+        self.resetSyncManager()
+        self.setupSyncManager()
+
+        // and a 1 second timeout should fail
+        autoreleasepool {
+            let user = try! synchronouslyLogInUser(for: basicCredentials(register: true), server: slowConnectAuthURL)
+            let config = user.configuration(cancelAsyncOpenOnNonFatalErrors: true)
+
+            syncTimeoutOptions.connectTimeout = 1000
+            SyncManager.shared.timeoutOptions = syncTimeoutOptions
+
+            let ex = expectation(description: "async open")
+            Realm.asyncOpen(configuration: config) { _, error in
+                XCTAssertNotNil(error)
+                if let error = error as NSError? {
+                    XCTAssertEqual(error.code, Int(ETIMEDOUT))
+                    XCTAssertEqual(error.domain, NSPOSIXErrorDomain)
+                }
+                ex.fulfill()
+            }
+            waitForExpectations(timeout: 4.0, handler: nil)
+        }
+    }
+
     // MARK: - Administration
 
     func testRetrieveUserInfo() {

+ 2 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/SwiftSyncTestCase.swift

@@ -73,6 +73,7 @@ class SwiftSyncTestCase: RLMSyncTestCase {
     var task: Process?
 
     let authURL: URL = URL(string: "http://127.0.0.1:9080")!
+    let slowConnectAuthURL: URL = URL(string: "http://127.0.0.1:9083")!
     let realmURL: URL = URL(string: "realm://127.0.0.1:9080/~/testBasicSync")!
 
     /// For testing, make a unique Realm URL of the form "realm://127.0.0.1:9080/~/X",
@@ -110,6 +111,7 @@ class SwiftSyncTestCase: RLMSyncTestCase {
         SyncManager.shared.setSessionCompletionNotifier(basicBlock)
         let realm = try Realm(configuration: configuration)
         let result = semaphore.wait(timeout: .now() + DispatchTimeInterval.seconds(20))
+        SyncManager.shared.setSessionCompletionNotifier(nil)
         XCTAssertEqual(result, .success)
         return realm
     }

+ 44 - 15
Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/test-ros-server.js

@@ -48,29 +48,17 @@ class PasswordEmailHandler {
     }
 }
 
-// A simple proxy server that runs in front of ROS and validates custom headers
-class HeaderValidationProxy {
+class Proxy {
     constructor(listenPort, targetPort) {
         this.proxy = httpProxy.createProxyServer({target: `http://127.0.0.1:${targetPort}`, ws: true});
         this.proxy.on('error', e => {
             console.log('proxy error', e);
         });
         this.server = http.createServer((req, res) => {
-            if (this.validate(req)) {
-                this.proxy.web(req, res);
-            }
-            else {
-                res.writeHead(400);
-                res.end('Missing X-Allow-Connection header');
-            }
+            this.web(req, res);
         });
         this.server.on('upgrade', (req, socket, head) => {
-            if (this.validate(req)) {
-                this.proxy.ws(req, socket, head);
-            }
-            else {
-                socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
-            }
+            this.ws(req, socket, head);
         });
         this.server.listen(listenPort);
     }
@@ -80,11 +68,50 @@ class HeaderValidationProxy {
         this.proxy.close();
     }
 
+    web(req, res) {
+        this.proxy.web(req, res);
+    }
+
+    ws(req, socket, head) {
+        this.proxy.ws(req, socket, head);
+    }
+}
+
+// A simple proxy server that runs in front of ROS and validates custom headers
+class HeaderValidationProxy extends Proxy {
+    web(req, res) {
+        if (this.validate(req)) {
+            this.proxy.web(req, res);
+        }
+        else {
+            res.writeHead(400);
+            res.end('Missing X-Allow-Connection header');
+        }
+    }
+    ws(req, socket, head) {
+        if (this.validate(req)) {
+            this.proxy.ws(req, socket, head);
+        }
+        else {
+            socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
+        }
+    }
     validate(req) {
         return !!req.headers['x-allow-connection'];
     }
 }
 
+// A proxy which sits in front of ROS and takes a long time to establish connections
+class SlowConnectingWebProxy extends Proxy {
+    web(req, res) {
+        setTimeout(() => this.proxy.web(req, res), 2000);
+    }
+}
+class SlowConnectingWsProxy extends Proxy {
+    ws(req, socket, head) {
+        setTimeout(() => this.proxy.ws(req, socket, head), 2000);
+    }
+}
 
 const server = new ROS.BasicServer();
 server.start({
@@ -125,3 +152,5 @@ server.start({
     console.error(`Error starting Realm Object Server: ${err.message}`)
 });
 new HeaderValidationProxy(9081, 9080);
+new SlowConnectingWebProxy(9082, 9080);
+new SlowConnectingWsProxy(9083, 9080);

+ 2 - 2
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/dependencies.list

@@ -1,4 +1,4 @@
-REALM_CORE_VERSION=5.23.5
-REALM_SYNC_VERSION=4.7.8
+REALM_CORE_VERSION=5.23.6
+REALM_SYNC_VERSION=4.9.0
 ANDROID_OPENSSL_VERSION=1.0.2k
 REALM_CORE_PACKAGING=2

+ 6 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/CMakeLists.txt

@@ -52,6 +52,12 @@ endif()
 add_executable(tests ${SOURCES} ${HEADERS})
 target_compile_definitions(tests PRIVATE ${PLATFORM_DEFINES})
 
+if(VSCODE_TEST_RUNNER)
+    # Increase the Catch2 virtual console width so that the Visual Studio Code
+    # Test Explorer extension can parse long test names
+    target_compile_definitions(tests PRIVATE -DCATCH_CONFIG_CONSOLE_WIDTH=300)
+endif()
+
 if(REALM_ENABLE_SYNC)
     # It's necessary to explicitly link to realm-sync here to control the order in which libraries are
     # linked to avoid link errors when using GNU ld.

+ 1 - 2
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/realm.cpp

@@ -461,8 +461,7 @@ TEST_CASE("Get Realm using Async Open", "[asyncOpen]") {
     if (!util::EventLoop::has_implementation())
         return;
 
-    auto cleanup = util::make_scope_exit([=]() noexcept { SyncManager::shared().reset_for_testing(); });
-    SyncManager::shared().configure(tmp_dir(), SyncManager::MetadataMode::NoEncryption);
+    TestSyncManager init_sync_manager;
 
     SyncServer server;
     SyncTestFile config(server, "default");

+ 24 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/util/test_file.cpp

@@ -18,6 +18,8 @@
 
 #include "util/test_file.hpp"
 
+#include "test_utils.hpp"
+
 #include "impl/realm_coordinator.hpp"
 
 #if REALM_ENABLE_SYNC
@@ -219,6 +221,28 @@ void wait_for_download(Realm& realm)
     wait_for_session(realm, &SyncSession::wait_for_download_completion);
 }
 
+TestSyncManager::TestSyncManager(std::string const& base_path, SyncManager::MetadataMode mode)
+{
+    configure(base_path, mode);
+}
+
+TestSyncManager::~TestSyncManager()
+{
+    SyncManager::shared().reset_for_testing();
+}
+
+void TestSyncManager::configure(std::string const& base_path, SyncManager::MetadataMode mode)
+{
+    SyncClientConfig config;
+    config.base_file_path = base_path.empty() ? tmp_dir() : base_path;
+    config.metadata_mode = mode;
+#if TEST_ENABLE_SYNC_LOGGING
+    config.log_level = util::Logger::Level::all;
+#else
+    config.log_level = util::Logger::Level::off;
+#endif
+    SyncManager::shared().configure(config);
+}
 
 #endif // REALM_ENABLE_SYNC
 

+ 6 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/util/test_file.hpp

@@ -133,6 +133,12 @@ struct SyncTestFile : TestFile {
                  std::string user_name="test");
 };
 
+struct TestSyncManager {
+    TestSyncManager(std::string const& base_path="", realm::SyncManager::MetadataMode = realm::SyncManager::MetadataMode::NoEncryption);
+    ~TestSyncManager();
+    static void configure(std::string const& base_path, realm::SyncManager::MetadataMode);
+};
+
 void wait_for_upload(realm::Realm& realm);
 void wait_for_download(realm::Realm& realm);
 

+ 4 - 0
Carthage/Checkouts/realm-cocoa/Realm/RLMConstants.h

@@ -161,6 +161,10 @@ typedef RLM_ERROR_ENUM(NSInteger, RLMError, RLMErrorDomain) {
      If you wish to migrate any data from the backup Realm, you can open it using the provided Realm configuration.
      */
     RLMErrorIncompatibleSyncedFile = 11,
+    /**
+     Denotates an error where an operation was requested which cannot be performed on an open file.
+     */
+    RLMErrorAlreadyOpen = 12,
 };
 
 #pragma mark - Constants

+ 11 - 5
Carthage/Checkouts/realm-cocoa/Realm/RLMNetworkClient.mm

@@ -42,8 +42,6 @@ static NSRange rangeForErrorType(RLMServerHTTPErrorCodeType type) {
     return NSMakeRange(type*100, kHTTPCodeRange);
 }
 
-static std::atomic<NSTimeInterval> g_defaultTimeout{60.0};
-
 #pragma mark Network client
 
 @interface RLMSessionDelegate <NSURLSessionDelegate> : NSObject
@@ -71,7 +69,7 @@ static std::atomic<NSTimeInterval> g_defaultTimeout{60.0};
 + (void)sendRequestToServer:(NSURL *)serverURL
                        JSON:(NSDictionary *)jsonDictionary
                  completion:(void (^)(NSError *))completionBlock {
-    [self sendRequestToServer:serverURL JSON:jsonDictionary timeout:g_defaultTimeout.load()
+    [self sendRequestToServer:serverURL JSON:jsonDictionary timeout:0
                    completion:^(NSError *error, NSDictionary *) {
         completionBlock(error);
     }];
@@ -80,6 +78,14 @@ static std::atomic<NSTimeInterval> g_defaultTimeout{60.0};
                        JSON:(NSDictionary *)jsonDictionary
                     timeout:(NSTimeInterval)timeout
                  completion:(void (^)(NSError *, NSDictionary *))completionBlock {
+    // If the timeout isn't set then use the timeout set on the sync manager,
+    // or 60 seconds if it isn't set there either.
+    RLMSyncManager *syncManager = RLMSyncManager.sharedManager;
+    if (timeout < 1)
+        timeout = syncManager.timeoutOptions.connectTimeout / 1000.0;
+    if (timeout < 1)
+        timeout = 60.0;
+
     // Create the request
     NSError *localError = nil;
     NSURL *requestURL = [self urlForAuthServer:serverURL payload:jsonDictionary];
@@ -92,8 +98,8 @@ static std::atomic<NSTimeInterval> g_defaultTimeout{60.0};
             return;
         }
     }
-    request.timeoutInterval = MAX(timeout, 10);
-    RLMNetworkRequestOptions *options = RLMSyncManager.sharedManager.networkRequestOptions;
+    request.timeoutInterval = timeout;
+    RLMNetworkRequestOptions *options = syncManager.networkRequestOptions;
     NSDictionary<NSString *, NSString *> *headers = [self httpHeadersForPayload:jsonDictionary options:options];
     for (NSString *key in headers) {
         [request addValue:headers[key] forHTTPHeaderField:key];

+ 5 - 1
Carthage/Checkouts/realm-cocoa/Realm/RLMObjectBase.mm

@@ -180,7 +180,11 @@ id RLMCreateManagedAccessor(Class cls, RLMClassInfo *info) {
 
 // Generic Swift properties can't be dynamic, so KVO doesn't work for them by default
 - (id)valueForUndefinedKey:(NSString *)key {
-    if (Ivar ivar = _objectSchema[key].swiftIvar) {
+    RLMProperty *prop = _objectSchema[key];
+    if (Class accessor = prop.swiftAccessor) {
+        return [accessor get:(char *)(__bridge void *)self + ivar_getOffset(prop.swiftIvar)];
+    }
+    if (Ivar ivar = prop.swiftIvar) {
         return RLMCoerceToNil(object_getIvar(self, ivar));
     }
     return [super valueForUndefinedKey:key];

+ 1 - 1
Carthage/Checkouts/realm-cocoa/Realm/RLMPlatform.h.in

@@ -16,7 +16,7 @@
 //
 ////////////////////////////////////////////////////////////////////////////
 
-#ifdef REALM_BUILDING_FOR_MACOSX
+#ifdef REALM_BUILDING_FOR_MACOS
 #if !__is_target_os(macosx)
 #error Attempting to use Realm''s macOS framework in a non-macOS target.
 #endif

+ 55 - 22
Carthage/Checkouts/realm-cocoa/Realm/RLMRealm.h

@@ -149,6 +149,61 @@ NS_ASSUME_NONNULL_BEGIN
  */
 @property (nonatomic, readonly) BOOL isEmpty;
 
+#pragma mark - File Management
+
+/**
+ Writes a compacted and optionally encrypted copy of the Realm to the given local URL.
+
+ The destination file cannot already exist.
+
+ Note that if this method is called from within a write transaction, the
+ *current* data is written, not the data from the point when the previous write
+ transaction was committed.
+
+ @param fileURL Local URL to save the Realm to.
+ @param key     Optional 64-byte encryption key to encrypt the new file with.
+ @param error   If an error occurs, upon return contains an `NSError` object
+ that describes the problem. If you are not interested in
+ possible errors, pass in `NULL`.
+
+ @return `YES` if the Realm was successfully written to disk, `NO` if an error occurred.
+ */
+- (BOOL)writeCopyToURL:(NSURL *)fileURL encryptionKey:(nullable NSData *)key error:(NSError **)error;
+
+/**
+ Checks if the Realm file for the given configuration exists locally on disk.
+
+ For non-synchronized, non-in-memory Realms, this is equivalent to
+ `-[NSFileManager.defaultManager fileExistsAtPath:config.path]`. For
+ synchronized Realms, it takes care of computing the actual path on disk based
+ on the server, virtual path, and user as is done when opening the Realm.
+
+ @param config A Realm configuration to check the existence of.
+ @return YES if the Realm file for the given configuration exists on disk, NO otherwise.
+ */
++ (BOOL)fileExistsForConfiguration:(RLMRealmConfiguration *)config;
+
+/**
+ Deletes the local Realm file and associated temporary files for the given configuration.
+
+ This deletes the ".realm", ".note" and ".management" files which would be
+ created by opening the Realm with the given configuration. It does not delete
+ the ".lock" file (which contains no persisted data and is recreated from
+ scratch every time the Realm file is opened).
+
+ The Realm must not be currently open on any thread or in another process. If
+ it is, this will return NO and report the error RLMErrorAlreadyOpen. Attempting to open
+ the Realm on another thread while the deletion is happening will block (and
+ then create a new Realm and open that afterwards).
+
+ If the Realm already does not exist this will return `NO` and report the error NSFileNoSuchFileError;
+
+ @param config A Realm configuration identifying the Realm to be deleted.
+ @return YES if any files were deleted, NO otherwise.
+ */
++ (BOOL)deleteFilesForConfiguration:(RLMRealmConfiguration *)config error:(NSError **)error
+ __attribute__((swift_error(nonnull_error)));
+
 #pragma mark - Notifications
 
 /**
@@ -189,9 +244,6 @@ typedef void (^RLMNotificationBlock)(RLMNotification notification, RLMRealm *rea
  */
 - (RLMNotificationToken *)addNotificationBlock:(RLMNotificationBlock)block __attribute__((warn_unused_result));
 
-#pragma mark - Transactions
-
-
 #pragma mark - Writing to a Realm
 
 /**
@@ -425,25 +477,6 @@ typedef void (^RLMNotificationBlock)(RLMNotification notification, RLMRealm *rea
  */
 @property (nonatomic) BOOL autorefresh;
 
-/**
- Writes a compacted and optionally encrypted copy of the Realm to the given local URL.
-
- The destination file cannot already exist.
-
- Note that if this method is called from within a write transaction, the
- *current* data is written, not the data from the point when the previous write
- transaction was committed.
-
- @param fileURL Local URL to save the Realm to.
- @param key     Optional 64-byte encryption key to encrypt the new file with.
- @param error   If an error occurs, upon return contains an `NSError` object
-                that describes the problem. If you are not interested in
-                possible errors, pass in `NULL`.
-
- @return `YES` if the Realm was successfully written to disk, `NO` if an error occurred.
-*/
-- (BOOL)writeCopyToURL:(NSURL *)fileURL encryptionKey:(nullable NSData *)key error:(NSError **)error;
-
 /**
  Invalidates all `RLMObject`s, `RLMResults`, `RLMLinkingObjects`, and `RLMArray`s managed by the Realm.
 

+ 38 - 0
Carthage/Checkouts/realm-cocoa/Realm/RLMRealm.mm

@@ -928,6 +928,44 @@ REALM_NOINLINE static void translateSharedGroupOpenException(RLMRealmConfigurati
     return NO;
 }
 
++ (BOOL)fileExistsForConfiguration:(RLMRealmConfiguration *)config {
+    return [NSFileManager.defaultManager fileExistsAtPath:config.pathOnDisk];
+}
+
++ (BOOL)deleteFilesForConfiguration:(RLMRealmConfiguration *)config error:(NSError **)error {
+    auto& path = config.config.path;
+    bool anyDeleted = false;
+    NSError *localError;
+    bool didCall = SharedGroup::call_with_lock(path, [&](auto const& path) {
+        NSURL *url = [NSURL fileURLWithPath:@(path.c_str())];
+        NSFileManager *fm = NSFileManager.defaultManager;
+
+        anyDeleted = [fm removeItemAtURL:url error:&localError];
+        if (localError && localError.code != NSFileNoSuchFileError) {
+            return;
+        }
+
+        [fm removeItemAtURL:[url URLByAppendingPathExtension:@"management"] error:&localError];
+        if (localError && localError.code != NSFileNoSuchFileError) {
+            return;
+        }
+
+        [fm removeItemAtURL:[url URLByAppendingPathExtension:@"note"] error:&localError];
+    });
+    if (error && localError && localError.code != NSFileNoSuchFileError) {
+        *error = localError;
+    }
+    else if (!didCall) {
+        if (error) {
+            NSString *msg = [NSString stringWithFormat:@"Realm file at path %s cannot be deleted because it is currently opened.", path.c_str()];
+            *error = [NSError errorWithDomain:RLMErrorDomain
+                                         code:RLMErrorAlreadyOpen
+                                     userInfo:@{NSLocalizedDescriptionKey: msg}];
+        }
+    }
+    return anyDeleted;
+}
+
 #if REALM_ENABLE_SYNC
 using Privilege = realm::ComputedPrivileges;
 static bool hasPrivilege(realm::ComputedPrivileges actual, realm::ComputedPrivileges expected) {

+ 9 - 0
Carthage/Checkouts/realm-cocoa/Realm/RLMSyncConfiguration.h

@@ -85,6 +85,15 @@ NS_ASSUME_NONNULL_BEGIN
 */
 @property (nonatomic, nullable, copy) NSString *urlPrefix;
 
+/**
+ Whether nonfatal connection errors should cancel async opens.
+
+ By default, if a nonfatal connection error such as a connection timing out occurs, any currently pending asyncOpen operations will ignore the error and continue to retry until it succeeds. If this is set to true, the open will instead fail and report the error.
+
+  FIXME: This should probably be true by default in the next major version.
+ */
+@property (nonatomic) bool cancelAsyncOpenOnNonFatalErrors;
+
 /// :nodoc:
 - (instancetype)initWithUser:(RLMSyncUser *)user realmURL:(NSURL *)url __attribute__((unavailable("Use [RLMSyncUser configurationWithURL:] instead")));
 

+ 79 - 48
Carthage/Checkouts/realm-cocoa/Realm/RLMSyncConfiguration.mm

@@ -68,8 +68,7 @@ BOOL isValidRealmURL(NSURL *url) {
                     realmURL:(NSURL *)url
                customFileURL:(nullable NSURL *)customFileURL
                    isPartial:(BOOL)isPartial
-                  stopPolicy:(RLMSyncStopPolicy)stopPolicy
-                errorHandler:(std::function<realm::SyncSessionErrorHandler>)errorHandler;
+                  stopPolicy:(RLMSyncStopPolicy)stopPolicy;
 @end
 
 @implementation RLMSyncConfiguration
@@ -106,6 +105,10 @@ BOOL isValidRealmURL(NSURL *url) {
     _config->is_partial = (bool)isPartial;
 }
 
+- (BOOL)isPartial {
+    return (BOOL)_config->is_partial;
+}
+
 - (NSURL *)pinnedCertificateURL {
     if (auto& path = _config->ssl_trust_certificate_path) {
         return [NSURL fileURLWithPath:RLMStringDataToNSString(*path)];
@@ -127,11 +130,6 @@ BOOL isValidRealmURL(NSURL *url) {
     }
 }
 
-
-- (BOOL)isPartial {
-    return (BOOL)_config->is_partial;
-}
-
 - (void)setFullSynchronization:(BOOL)fullSynchronization {
     _config->is_partial = !(bool)fullSynchronization;
 }
@@ -140,7 +138,7 @@ BOOL isValidRealmURL(NSURL *url) {
     return !(BOOL)_config->is_partial;
 }
 
-- (realm::SyncConfig)rawConfiguration {
+- (realm::SyncConfig&)rawConfiguration {
     return *_config;
 }
 
@@ -171,6 +169,14 @@ BOOL isValidRealmURL(NSURL *url) {
     }
 }
 
+- (bool)cancelAsyncOpenOnNonFatalErrors {
+    return _config->cancel_waits_on_nonfatal_error;
+}
+
+- (void)setCancelAsyncOpenOnNonFatalErrors:(bool)cancelAsyncOpenOnNonFatalErrors {
+    _config->cancel_waits_on_nonfatal_error = cancelAsyncOpenOnNonFatalErrors;
+}
+
 - (NSURL *)realmURL {
     NSString *rawStringURL = @(_config->reference_realm_url.c_str());
     return [NSURL URLWithString:rawStringURL];
@@ -181,8 +187,7 @@ BOOL isValidRealmURL(NSURL *url) {
                      realmURL:url
                 customFileURL:nil
                     isPartial:NO
-                   stopPolicy:RLMSyncStopPolicyAfterChangesUploaded
-                 errorHandler:nullptr];
+                   stopPolicy:RLMSyncStopPolicyAfterChangesUploaded];
 }
 
 - (instancetype)initWithUser:(RLMSyncUser *)user
@@ -196,62 +201,89 @@ BOOL isValidRealmURL(NSURL *url) {
                             realmURL:url
                        customFileURL:nil
                            isPartial:isPartial
-                          stopPolicy:stopPolicy
-                        errorHandler:nullptr];
+                          stopPolicy:stopPolicy];
     config.urlPrefix = urlPrefix;
     config.enableSSLValidation = enableSSLValidation;
     config.pinnedCertificateURL = certificatePath;
     return config;
 }
 
+static void bindHandler(std::string const&, SyncConfig const& config, std::shared_ptr<SyncSession> session) {
+    const std::shared_ptr<SyncUser>& user = config.user;
+    NSURL *realmURL = [NSURL URLWithString:@(config.realm_url().c_str())];
+    NSString *path = [realmURL path];
+    REALM_ASSERT(realmURL && path);
+    auto handle = [[RLMSyncSessionRefreshHandle alloc] initWithRealmURL:realmURL
+                                                                   user:user
+                                                                session:std::move(session)
+                                                        completionBlock:RLMSyncManager.sharedManager.sessionCompletionNotifier];
+    context_for(user).register_refresh_handle([path UTF8String], handle);
+}
+
+static void errorHandler(std::shared_ptr<SyncSession> errored_session, SyncError error) {
+    NSString *recoveryPath;
+    RLMSyncErrorActionToken *token;
+    for (auto& pair : error.user_info) {
+        if (pair.first == realm::SyncError::c_original_file_path_key) {
+            token = [[RLMSyncErrorActionToken alloc] initWithOriginalPath:pair.second];
+        }
+        else if (pair.first == realm::SyncError::c_recovery_file_path_key) {
+            recoveryPath = @(pair.second.c_str());
+        }
+    }
+
+    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.
+    auto errorClass = errorKindForSyncError(error);
+    switch (errorClass) {
+        case RLMSyncSystemErrorKindClientReset: {
+            custom = @{kRLMSyncPathOfRealmBackupCopyKey: recoveryPath, kRLMSyncErrorActionTokenKey: token};
+            break;
+        }
+        case RLMSyncSystemErrorKindPermissionDenied: {
+            custom = @{kRLMSyncErrorActionTokenKey: token};
+            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 = error.is_fatal;
+            break;
+    }
+    auto errorHandler = RLMSyncManager.sharedManager.errorHandler;
+    if (!shouldMakeError || !errorHandler) {
+        return;
+    }
+    NSError *nsError = make_sync_error(errorClass, @(error.message.c_str()), error.error_code.value(), custom);
+    RLMSyncSession *session = [[RLMSyncSession alloc] initWithSyncSession:errored_session];
+    dispatch_async(dispatch_get_main_queue(), ^{
+        errorHandler(nsError, session);
+    });
+};
+
 - (instancetype)initWithUser:(RLMSyncUser *)user
                     realmURL:(NSURL *)url
                customFileURL:(nullable NSURL *)customFileURL
                    isPartial:(BOOL)isPartial
-                  stopPolicy:(RLMSyncStopPolicy)stopPolicy
-                errorHandler:(std::function<realm::SyncSessionErrorHandler>)errorHandler {
+                  stopPolicy:(RLMSyncStopPolicy)stopPolicy {
     if (self = [super init]) {
         if (!isValidRealmURL(url)) {
             @throw RLMException(@"The provided URL (%@) was not a valid Realm URL.", [url absoluteString]);
         }
-        auto bindHandler = [=](auto&,
-                               const SyncConfig& config,
-                               const std::shared_ptr<SyncSession>& session) {
-            const std::shared_ptr<SyncUser>& user = config.user;
-            NSURL *realmURL = [NSURL URLWithString:@(config.realm_url().c_str())];
-            NSString *path = [realmURL path];
-            REALM_ASSERT(realmURL && path);
-            RLMSyncSessionRefreshHandle *handle = [[RLMSyncSessionRefreshHandle alloc] initWithRealmURL:realmURL
-                                                                                                   user:user
-                                                                                                session:std::move(session)
-                                                                                        completionBlock:[RLMSyncManager sharedManager].sessionCompletionNotifier];
-            context_for(user).register_refresh_handle([path UTF8String], handle);
-        };
-        if (!errorHandler) {
-            errorHandler = [=](std::shared_ptr<SyncSession> errored_session,
-                               SyncError error) {
-                RLMSyncSession *session = [[RLMSyncSession alloc] initWithSyncSession:errored_session];
-                NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithCapacity:error.user_info.size()];
-                for (auto& pair : error.user_info) {
-                    userInfo[@(pair.first.c_str())] = @(pair.second.c_str());
-                }
-                // FIXME: how should the binding respond if the `is_fatal` bool is true?
-                [[RLMSyncManager sharedManager] _fireErrorWithCode:error.error_code.value()
-                                                           message:@(error.message.c_str())
-                                                           isFatal:error.is_fatal
-                                                           session:session
-                                                          userInfo:userInfo
-                                                        errorClass:errorKindForSyncError(error)];
-            };
-        }
 
         _config = std::make_unique<SyncConfig>(SyncConfig{
             [user _syncUser],
             [[url absoluteString] UTF8String]
         });
         _config->stop_policy = translateStopPolicy(stopPolicy);
-        _config->bind_session_handler = std::move(bindHandler);
-        _config->error_handler = std::move(errorHandler);
+        _config->bind_session_handler = bindHandler;
+        _config->error_handler = errorHandler;
         _config->is_partial = isPartial;
         _config->client_resync_mode = realm::ClientResyncMode::Manual;
 
@@ -282,8 +314,7 @@ BOOL isValidRealmURL(NSURL *url) {
                                                                          realmURL:user.defaultRealmURL
                                                                     customFileURL:nil
                                                                         isPartial:YES
-                                                                       stopPolicy:RLMSyncStopPolicyAfterChangesUploaded
-                                                                     errorHandler:nullptr];
+                                                                       stopPolicy:RLMSyncStopPolicyAfterChangesUploaded];
     RLMRealmConfiguration *config = [[RLMRealmConfiguration alloc] init];
     config.syncConfiguration = syncConfig;
     return config;

+ 2 - 3
Carthage/Checkouts/realm-cocoa/Realm/RLMSyncConfiguration_Private.hpp

@@ -36,12 +36,11 @@ NS_ASSUME_NONNULL_BEGIN
                     realmURL:(NSURL *)url
                customFileURL:(nullable NSURL *)customFileURL
                    isPartial:(BOOL)isPartial
-                  stopPolicy:(RLMSyncStopPolicy)stopPolicy
-                errorHandler:(std::function<realm::SyncSessionErrorHandler>)errorHandler;
+                  stopPolicy:(RLMSyncStopPolicy)stopPolicy;
 
 - (instancetype)initWithRawConfig:(realm::SyncConfig)config;
 
-- (realm::SyncConfig)rawConfiguration;
+- (realm::SyncConfig&)rawConfiguration;
 
 @end
 

+ 94 - 4
Carthage/Checkouts/realm-cocoa/Realm/RLMSyncManager.h

@@ -20,7 +20,9 @@
 
 #import "RLMSyncUtil.h"
 
-@class RLMSyncSession;
+@class RLMSyncSession, RLMSyncTimeoutOptions;
+
+NS_ASSUME_NONNULL_BEGIN
 
 /// An enum representing different levels of sync-related logging that can be configured.
 typedef RLM_CLOSED_ENUM(NSUInteger, RLMSyncLogLevel) {
@@ -50,7 +52,11 @@ typedef RLM_CLOSED_ENUM(NSUInteger, RLMSyncLogLevel) {
     RLMSyncLogLevelAll
 };
 
-NS_ASSUME_NONNULL_BEGIN
+/// A log callback function which can be set on RLMSyncManager.
+///
+/// The log function may be called from multiple threads simultaneously, and is
+/// responsible for performing its own synchronization if any is required.
+typedef void (*RLMSyncLogFunction)(RLMSyncLogLevel level, NSString *message);
 
 /// A block type representing a block which can be used to report a sync-related error to the application. If the error
 /// pertains to a specific session, that session will also be passed into the block.
@@ -95,13 +101,24 @@ typedef void(^RLMSyncErrorReportingBlock)(NSError *, RLMSyncSession * _Nullable)
  The logging threshold which newly opened synced Realms will use. Defaults to
  `RLMSyncLogLevelInfo`.
 
- Logging strings are output to Apple System Logger.
+ By default logging strings are output to Apple System Logger. Set `logger` to
+ perform custom logging logic instead.
 
  @warning This property must be set before any synced Realms are opened. Setting it after
           opening any synced Realm will do nothing.
  */
 @property (nonatomic) RLMSyncLogLevel logLevel;
 
+/**
+ The function which will be invoked whenever the sync client has a log message.
+
+ If nil, log strings are output to Apple System Logger instead.
+
+ @warning This property must be set before any synced Realms are opened. Setting
+ it after opening any synced Realm will do nothing.
+ */
+@property (nonatomic, nullable) RLMSyncLogFunction logger;
+
 /**
  The name of the HTTP header to send authorization data in when making requests to a Realm Object Server which has
  been configured to expect a custom authorization header.
@@ -141,6 +158,16 @@ typedef void(^RLMSyncErrorReportingBlock)(NSError *, RLMSyncSession * _Nullable)
  */
 @property (nullable, nonatomic, copy) NSDictionary<NSString *, NSURL *> *pinnedCertificatePaths;
 
+/**
+ Options for the assorted types of connection timeouts for sync connections.
+
+ If nil default values for all timeouts are used instead.
+
+ @warning This property must be set before any synced Realms are opened. Setting
+ it after opening any synced Realm will do nothing.
+ */
+@property (nullable, nonatomic, copy) RLMSyncTimeoutOptions *timeoutOptions;
+
 /// The sole instance of the singleton.
 + (instancetype)sharedManager NS_REFINED_FOR_SWIFT;
 
@@ -150,6 +177,69 @@ typedef void(^RLMSyncErrorReportingBlock)(NSError *, RLMSyncSession * _Nullable)
 /// :nodoc:
 + (instancetype)new __attribute__((unavailable("RLMSyncManager cannot be created directly")));
 
-NS_ASSUME_NONNULL_END
+@end
 
+/**
+  Options for configuring timeouts and intervals in the sync client.
+ */
+@interface RLMSyncTimeoutOptions : NSObject
+/// The maximum number of milliseconds to allow for a connection to
+/// become fully established. This includes the time to resolve the
+/// network address, the TCP connect operation, the SSL handshake, and
+/// the WebSocket handshake.
+///
+/// Defaults to 2 minutes.
+@property (nonatomic) NSUInteger connectTimeout;
+
+/// The number of milliseconds to keep a connection open after all
+/// sessions have been abandoned.
+///
+/// After all synchronized Realms have been closed for a given server, the
+/// connection is kept open until the linger time has expire to avoid the
+/// overhead of reestablishing the connection when Realms are being closed and
+/// reopened.
+///
+/// Defaults to 30 seconds.
+@property (nonatomic) NSUInteger connectionLingerTime;
+
+/// The number of milliseconds between each heartbeat ping message.
+///
+/// The client periodically sends ping messages to the server to check if the
+/// connection is still alive. Shorter periods make connection state change
+/// notifications more responsive at the cost of battery life (as the antenna
+/// will have to wake up more often).
+///
+/// Defaults to 1 minute.
+@property (nonatomic) NSUInteger pingKeepalivePeriod;
+
+/// How long in milliseconds to wait for a reponse to a heartbeat ping before
+/// concluding that the connection has dropped.
+///
+/// Shorter values will make connection state change notifications more
+/// responsive as it will only change to `disconnected` after this much time has
+/// elapsed, but overly short values may result in spurious disconnection
+/// notifications when the server is simply taking a long time to respond.
+///
+/// Defaults to 2 minutes.
+@property (nonatomic) NSUInteger pongKeepaliveTimeout;
+
+/// The maximum amount of time, in milliseconds, since the loss of a
+/// prior connection, for a new connection to be considered a "fast
+/// reconnect".
+///
+/// When a client first connects to the server, it defers uploading any local
+/// changes until it has downloaded all changesets from the server. This
+/// typically reduces the total amount of merging that has to be done, and is
+/// particularly beneficial the first time that a specific client ever connects
+/// to the server.
+///
+/// When an existing client disconnects and then reconnects within the "fact
+/// reconnect" time this is skipped and any local changes are uploaded
+/// immediately without waiting for downloads, just as if the client was online
+/// the whole time.
+///
+/// Defaults to 1 minute.
+@property (nonatomic) NSUInteger fastReconnectLimit;
 @end
+
+NS_ASSUME_NONNULL_END

+ 120 - 68
Carthage/Checkouts/realm-cocoa/Realm/RLMSyncManager.mm

@@ -68,6 +68,8 @@ RLMSyncLogLevel logLevelForLevel(Level logLevel) {
     REALM_UNREACHABLE();    // Unrecognized log level.
 }
 
+#pragma mark - Loggers
+
 struct CocoaSyncLogger : public realm::util::RootLogger {
     void do_log(Level, std::string message) override {
         NSLog(@"Sync: %@", RLMStringDataToNSString(message));
@@ -82,21 +84,53 @@ struct CocoaSyncLoggerFactory : public realm::SyncLoggerFactory {
     }
 } s_syncLoggerFactory;
 
+struct CallbackLogger : public realm::util::RootLogger {
+    RLMSyncLogFunction logFn;
+    void do_log(Level level, std::string message) override {
+        @autoreleasepool {
+            logFn(logLevelForLevel(level), RLMStringDataToNSString(message));
+        }
+    }
+};
+struct CallbackLoggerFactory : public realm::SyncLoggerFactory {
+    RLMSyncLogFunction logFn;
+    std::unique_ptr<realm::util::Logger> make_logger(realm::util::Logger::Level level) override {
+        auto logger = std::make_unique<CallbackLogger>();
+        logger->logFn = logFn;
+        logger->set_level_threshold(level);
+        return std::move(logger); // not a redundant move because it's a different type
+    }
+
+    CallbackLoggerFactory(RLMSyncLogFunction logFn) : logFn(logFn) { }
+};
+
 } // anonymous namespace
 
-@interface RLMSyncManager ()
-- (instancetype)initWithCustomRootDirectory:(nullable NSURL *)rootDirectory NS_DESIGNATED_INITIALIZER;
+#pragma mark - RLMSyncManager
+
+@interface RLMSyncTimeoutOptions () {
+    @public
+    realm::SyncClientTimeouts _options;
+}
 @end
 
-@implementation RLMSyncManager
+@implementation RLMSyncManager {
+    std::unique_ptr<CallbackLoggerFactory> _loggerFactory;
+}
 
 static RLMSyncManager *s_sharedManager = nil;
 
+- (instancetype)initPrivate {
+    return self = [super init];
+}
+
 + (instancetype)sharedManager {
     static std::once_flag flag;
     std::call_once(flag, [] {
         try {
-            s_sharedManager = [[RLMSyncManager alloc] initWithCustomRootDirectory:nil];
+            [RLMSyncUser _setUpBindingContextFactory];
+            s_sharedManager = [[RLMSyncManager alloc] initPrivate];
+            [s_sharedManager configureWithRootDirectory:nil];
         }
         catch (std::exception const& e) {
             @throw RLMException(e);
@@ -105,25 +139,23 @@ static RLMSyncManager *s_sharedManager = nil;
     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;
+- (void)configureWithRootDirectory:(NSURL *)rootDirectory {
+    SyncClientConfig config;
+    bool should_encrypt = !getenv("REALM_DISABLE_METADATA_ENCRYPTION") && !RLMIsRunningInPlayground();
+    config.logger_factory = &s_syncLoggerFactory;
+    config.metadata_mode = should_encrypt ? SyncManager::MetadataMode::Encryption
+                                          : SyncManager::MetadataMode::NoEncryption;
+    @autoreleasepool {
         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;
+        config.base_file_path = rootDirectory.path.UTF8String;
+
+        bool isSwift = !!NSClassFromString(@"RealmSwiftObjectUtil");
+        config.user_agent_binding_info =
+            util::format("Realm%1/%2", isSwift ? "Swift" : "ObjectiveC",
+                         RLMStringDataWithNSString(REALM_COCOA_VERSION));
+        config.user_agent_application_info = RLMStringDataWithNSString(self.appID);
     }
-    return nil;
+    SyncManager::shared().configure(config);
 }
 
 - (NSString *)appID {
@@ -153,6 +185,23 @@ static RLMSyncManager *s_sharedManager = nil;
     }
 }
 
+- (void)setLogger:(RLMSyncLogFunction)logFn {
+    _logger = logFn;
+    if (_logger) {
+        _loggerFactory = std::make_unique<CallbackLoggerFactory>(logFn);
+        SyncManager::shared().set_logger_factory(*_loggerFactory);
+    }
+    else {
+        _loggerFactory = nullptr;
+        SyncManager::shared().set_logger_factory(s_syncLoggerFactory);
+    }
+}
+
+- (void)setTimeoutOptions:(RLMSyncTimeoutOptions *)timeoutOptions {
+    _timeoutOptions = timeoutOptions;
+    SyncManager::shared().set_timeouts(timeoutOptions->_options);
+}
+
 #pragma mark - Passthrough properties
 
 - (RLMSyncLogLevel)logLevel {
@@ -173,53 +222,6 @@ static RLMSyncManager *s_sharedManager = 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()) {
@@ -229,6 +231,16 @@ static RLMSyncManager *s_sharedManager = nil;
 }
 
 + (void)resetForTesting {
+    RLMSyncManager *manager = self.sharedManager;
+    manager->_errorHandler = nil;
+    manager->_appID = nil;
+    manager->_userAgent = nil;
+    manager->_logger = nil;
+    manager->_authorizationHeaderName = nil;
+    manager->_customRequestHeaders = nil;
+    manager->_pinnedCertificatePaths = nil;
+    manager->_timeoutOptions = nil;
+
     SyncManager::shared().reset_for_testing();
 }
 
@@ -241,3 +253,43 @@ static RLMSyncManager *s_sharedManager = nil;
 }
 
 @end
+
+#pragma mark - RLMSyncTimeoutOptions
+
+@implementation RLMSyncTimeoutOptions
+- (NSUInteger)connectTimeout {
+    return _options.connect_timeout;
+}
+- (void)setConnectTimeout:(NSUInteger)connectTimeout {
+    _options.connect_timeout = connectTimeout;
+}
+
+- (NSUInteger)connectLingerTime {
+    return _options.connection_linger_time;
+}
+- (void)setConnectionLingerTime:(NSUInteger)connectionLingerTime {
+    _options.connection_linger_time = connectionLingerTime;
+}
+
+- (NSUInteger)pingKeepalivePeriod {
+    return _options.ping_keepalive_period;
+}
+- (void)setPingKeepalivePeriod:(NSUInteger)pingKeepalivePeriod {
+    _options.ping_keepalive_period = pingKeepalivePeriod;
+}
+
+- (NSUInteger)pongKeepaliveTimeout {
+    return _options.pong_keepalive_timeout;
+}
+- (void)setPongKeepaliveTimeout:(NSUInteger)pongKeepaliveTimeout {
+    _options.pong_keepalive_timeout = pongKeepaliveTimeout;
+}
+
+- (NSUInteger)fastReconnectLimit {
+    return _options.fast_reconnect_limit;
+}
+- (void)setFastReconnectLimit:(NSUInteger)fastReconnectLimit {
+    _options.fast_reconnect_limit = fastReconnectLimit;
+}
+
+@end

+ 0 - 7
Carthage/Checkouts/realm-cocoa/Realm/RLMSyncManager_Private.h

@@ -34,13 +34,6 @@ NS_ASSUME_NONNULL_BEGIN
 
 - (void)_fireError:(NSError *)error;
 
-- (void)_fireErrorWithCode:(int)errorCode
-                   message:(NSString *)message
-                   isFatal:(BOOL)fatal
-                   session:(RLMSyncSession *)session
-                  userInfo:(NSDictionary *)userInfo
-                errorClass:(RLMSyncSystemErrorKind)errorClass;
-
 - (NSArray<RLMSyncUser *> *)_allUsers;
 
 + (void)resetForTesting;

+ 39 - 39
Carthage/Checkouts/realm-cocoa/Realm/RLMSyncSessionRefreshHandle.mm

@@ -58,7 +58,7 @@ static const NSTimeInterval RLMRefreshBuffer = 10;
     std::shared_ptr<SyncSession> _strongSession;
 }
 
-@property (nonatomic) NSTimer *timer;
+@property (nonatomic) dispatch_source_t timer;
 
 @property (nonatomic) NSURL *realmURL;
 @property (nonatomic) NSURL *authServerURL;
@@ -86,19 +86,26 @@ static const NSTimeInterval RLMRefreshBuffer = 10;
         _session = _strongSession;
         _user = user;
         // Immediately fire off the network request.
-        [self _timerFired:nil];
+        [self _timerFired];
         return self;
     }
     return nil;
 }
 
 - (void)dealloc {
-    [self.timer invalidate];
+    [self cancelTimer];
 }
 
 - (void)invalidate {
     _strongSession = nullptr;
-    [self.timer invalidate];
+    [self cancelTimer];
+}
+
+- (void)cancelTimer {
+    if (self.timer) {
+        dispatch_source_cancel(self.timer);
+        self.timer = nil;
+    }
 }
 
 + (NSDate *)fireDateForTokenExpirationDate:(NSDate *)date nowDate:(NSDate *)nowDate {
@@ -108,43 +115,30 @@ static const NSTimeInterval RLMRefreshBuffer = 10;
 }
 
 - (void)scheduleRefreshTimer:(NSDate *)dateWhenTokenExpires {
-    // Schedule the timer on the main queue.
-    // It's very likely that this method will be run on a side thread, for example
-    // on the thread that runs `NSURLSession`'s completion blocks. We can't be
-    // guaranteed that there's an existing runloop on those threads, and we don't want
-    // to create and start a new one if one doesn't already exist.
-    dispatch_async(dispatch_get_main_queue(), ^{
-        [self.timer invalidate];
-        NSDate *fireDate = [RLMSyncSessionRefreshHandle fireDateForTokenExpirationDate:dateWhenTokenExpires
-                                                                               nowDate:[NSDate date]];
-        if (!fireDate) {
-            unregisterRefreshHandle(_user, _path);
-            return;
-        }
-        self.timer = [[NSTimer alloc] initWithFireDate:fireDate
-                                              interval:0
-                                                target:self
-                                              selector:@selector(_timerFired:)
-                                              userInfo:nil
-                                               repeats:NO];
-        [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
-    });
-}
+    [self cancelTimer];
 
-/// Handler for network requests whose responses successfully parse into an auth response model.
-- (BOOL)_handleSuccessfulRequest:(RLMAuthResponseModel *)model {
-    std::shared_ptr<SyncSession> session = _session.lock();
-    if (!session) {
-        // The session is dead or in a fatal error state.
+    NSDate *fireDate = [RLMSyncSessionRefreshHandle fireDateForTokenExpirationDate:dateWhenTokenExpires
+                                                                           nowDate:[NSDate date]];
+    if (!fireDate) {
         unregisterRefreshHandle(_user, _path);
-        [self invalidate];
-        return NO;
+        return;
     }
 
+    NSTimeInterval timeToExpiration = [fireDate timeIntervalSinceNow];
+    self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
+    dispatch_source_set_timer(self.timer, dispatch_time(DISPATCH_TIME_NOW, timeToExpiration * NSEC_PER_SEC),
+                              /* interval */ DISPATCH_TIME_FOREVER,
+                              /* leeway */ NSEC_PER_SEC * (timeToExpiration / 10));
+    dispatch_source_set_event_handler(self.timer, ^{ [self _timerFired]; });
+    dispatch_resume(self.timer);
+}
+
+/// Handler for network requests whose responses successfully parse into an auth response model.
+- (BOOL)_handleSuccessfulRequest:(RLMAuthResponseModel *)model session:(SyncSession&)session {
     // Realm Cloud will give us a url prefix in the auth response that we need
     // to pass onto objectstore to have it connect to the proper sync worker
     if (model.urlPrefix) {
-        session->set_url_prefix(model.urlPrefix.UTF8String);
+        session.set_url_prefix(model.urlPrefix.UTF8String);
     }
 
     // Calculate the resolved path.
@@ -159,7 +153,7 @@ static const NSTimeInterval RLMRefreshBuffer = 10;
         @throw RLMException(@"Resolved path returned from the server was invalid (%@).", resolvedPath);
     }
     // Pass the token and resolved path to the underlying sync subsystem.
-    session->refresh_access_token([model.accessToken.token UTF8String], {resolvedURLString.UTF8String});
+    session.refresh_access_token([model.accessToken.token UTF8String], {resolvedURLString.UTF8String});
 
     // Schedule a refresh. If we're successful we must already have `bind()`ed the session
     // initially, so we can null out the strong pointer.
@@ -225,16 +219,23 @@ static const NSTimeInterval RLMRefreshBuffer = 10;
 
 /// Callback handler for network requests.
 - (BOOL)_onRefreshCompletionWithError:(NSError *)error json:(NSDictionary *)json {
+    std::shared_ptr<SyncSession> session = _session.lock();
+    if (!session) {
+        // The session is dead or in a fatal error state.
+        unregisterRefreshHandle(_user, _path);
+        [self invalidate];
+        return NO;
+    }
+
     if (json && !error) {
         RLMAuthResponseModel *model = [[RLMAuthResponseModel alloc] initWithDictionary:json
                                                                     requireAccessToken:YES
                                                                    requireRefreshToken:NO];
         if (model) {
-            return [self _handleSuccessfulRequest:model];
+            return [self _handleSuccessfulRequest:model session:*session];
         }
         // Otherwise, malformed JSON
         unregisterRefreshHandle(_user, _path);
-        [self.timer invalidate];
         NSError *error = make_sync_error(make_auth_error_bad_response(json));
         if (self.completionBlock) {
             self.completionBlock(error);
@@ -247,14 +248,13 @@ static const NSTimeInterval RLMRefreshBuffer = 10;
     return NO;
 }
 
-- (void)_timerFired:(__unused NSTimer *)timer {
+- (void)_timerFired {
     RLMServerToken refreshToken = nil;
     if (auto user = _user.lock()) {
         refreshToken = @(user->refresh_token().c_str());
     }
     if (!refreshToken) {
         unregisterRefreshHandle(_user, _path);
-        [self.timer invalidate];
         return;
     }
 

+ 2 - 3
Carthage/Checkouts/realm-cocoa/Realm/RLMSyncUser.mm

@@ -131,7 +131,7 @@ void CocoaSyncUserContext::set_error_handler(RLMUserErrorReportingBlock block)
                 onCompletion:(RLMUserCompletionBlock)completion {
     [self logInWithCredentials:credential
                  authServerURL:authServerURL
-                       timeout:30
+                       timeout:0 // use timeout from RLMSyncManager
                  callbackQueue:dispatch_get_main_queue()
                   onCompletion:completion];
 }
@@ -231,8 +231,7 @@ void CocoaSyncUserContext::set_error_handler(RLMUserErrorReportingBlock block)
                                                         realmURL:url ?: self.defaultRealmURL
                                                    customFileURL:nil
                                                        isPartial:!fullSynchronization
-                                                      stopPolicy:RLMSyncStopPolicyAfterChangesUploaded
-                                                    errorHandler:nullptr];
+                                                      stopPolicy:RLMSyncStopPolicyAfterChangesUploaded];
     syncConfig.urlPrefix = urlPrefix;
     syncConfig.enableSSLValidation = enableSSLValidation;
     syncConfig.pinnedCertificateURL = RLMSyncManager.sharedManager.pinnedCertificatePaths[syncConfig.realmURL.host];

+ 18 - 3
Carthage/Checkouts/realm-cocoa/Realm/RLMUtil.mm

@@ -29,6 +29,10 @@
 
 #import "shared_realm.hpp"
 
+#if REALM_ENABLE_SYNC
+#import "RLMSyncUtil.h"
+#endif
+
 #import <realm/mixed.hpp>
 #import <realm/table_view.hpp>
 
@@ -328,12 +332,23 @@ NSError *RLMMakeError(RLMError code, const realm::RealmFileException& exception)
 }
 
 NSError *RLMMakeError(std::system_error const& exception) {
+    int code = exception.code().value();
     BOOL isGenericCategoryError = (exception.code().category() == std::generic_category());
     NSString *category = @(exception.code().category().name());
     NSString *errorDomain = isGenericCategoryError ? NSPOSIXErrorDomain : RLMUnknownSystemErrorDomain;
+#if REALM_ENABLE_SYNC
+    if (exception.code().category() == realm::sync::client_error_category()) {
+        if (exception.code().value() == static_cast<int>(realm::sync::Client::Error::connect_timeout)) {
+            errorDomain = NSPOSIXErrorDomain;
+            code = ETIMEDOUT;
+        }
+        else {
+            errorDomain = RLMSyncErrorDomain;
+        }
+    }
+#endif
 
-    return [NSError errorWithDomain:errorDomain
-                               code:exception.code().value()
+    return [NSError errorWithDomain:errorDomain code:code
                            userInfo:@{NSLocalizedDescriptionKey: @(exception.what()),
                                       @"Error Code": @(exception.code().value()),
                                       @"Category": category}];
@@ -403,7 +418,7 @@ NSString *RLMDefaultDirectoryForBundleIdentifier(NSString *bundleIdentifier) {
     (void)bundleIdentifier;
     // tvOS prohibits writing to the Documents directory, so we use the Library/Caches directory instead.
     return NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0];
-#elif TARGET_OS_IPHONE
+#elif TARGET_OS_IPHONE && !TARGET_OS_MACCATALYST
     (void)bundleIdentifier;
     // On iOS the Documents directory isn't user-visible, so put files there
     return NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];

+ 2 - 2
Carthage/Checkouts/realm-cocoa/Realm/Realm-Info.plist

@@ -17,11 +17,11 @@
 	<key>CFBundlePackageType</key>
 	<string>FMWK</string>
 	<key>CFBundleShortVersionString</key>
-	<string>4.1.1</string>
+	<string>4.3.1</string>
 	<key>CFBundleSignature</key>
 	<string>????</string>
 	<key>CFBundleVersion</key>
-	<string>4.1.1</string>
+	<string>4.3.1</string>
 	<key>NSHumanReadableCopyright</key>
 	<string>Copyright © 2014 Realm. All rights reserved.</string>
 	<key>NSPrincipalClass</key>

+ 4 - 0
Carthage/Checkouts/realm-cocoa/Realm/Tests/InterprocessTests.m

@@ -20,6 +20,8 @@
 
 #import "RLMConstants.h"
 
+#if !TARGET_OS_MACCATALYST
+
 @interface InterprocessTest : RLMMultiProcessTestCase
 @end
 
@@ -412,3 +414,5 @@
 }
 
 @end
+
+#endif

+ 76 - 9
Carthage/Checkouts/realm-cocoa/Realm/Tests/RealmTests.mm

@@ -1925,15 +1925,82 @@
         @autoreleasepool { [RLMRealm defaultRealm]; }
     }];
 
-//    NSURL *fileURL = RLMRealmConfiguration.defaultConfiguration.fileURL;
-//    for (NSString *pathExtension in @[@"management", @"lock", @"note"]) {
-//        NSNumber *attribute = nil;
-//        NSError *error = nil;
-//        BOOL success = [[fileURL URLByAppendingPathExtension:pathExtension] getResourceValue:&attribute forKey:NSURLIsExcludedFromBackupKey error:&error];
-//        XCTAssertTrue(success);
-//        XCTAssertNil(error);
-//        XCTAssertTrue(attribute.boolValue);
-//    }
+    NSURL *fileURL = RLMRealmConfiguration.defaultConfiguration.fileURL;
+#if !TARGET_OS_TV
+    for (NSString *pathExtension in @[@"management", @"lock", @"note"]) {
+#else
+    for (NSString *pathExtension in @[@"management", @"lock"]) {
+#endif
+        NSNumber *attribute = nil;
+        NSError *error = nil;
+        BOOL success = [[fileURL URLByAppendingPathExtension:pathExtension] getResourceValue:&attribute forKey:NSURLIsExcludedFromBackupKey error:&error];
+        XCTAssertTrue(success);
+        XCTAssertNil(error);
+        XCTAssertTrue(attribute.boolValue);
+    }
+}
+
+- (void)testRealmExists {
+    RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
+    XCTAssertFalse([RLMRealm fileExistsForConfiguration:config]);
+    @autoreleasepool { [RLMRealm realmWithConfiguration:config error:nil]; }
+    XCTAssertTrue([RLMRealm fileExistsForConfiguration:config]);
+    [RLMRealm deleteFilesForConfiguration:config error:nil];
+    XCTAssertFalse([RLMRealm fileExistsForConfiguration:config]);
+}
+
+- (void)testDeleteNonexistentRealmFile {
+    NSError *error;
+    XCTAssertFalse([RLMRealm deleteFilesForConfiguration:RLMRealmConfiguration.defaultConfiguration error:&error]);
+    XCTAssertNil(error);
+}
+
+- (void)testDeleteClosedRealmFile {
+    RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
+    @autoreleasepool { [RLMRealm realmWithConfiguration:config error:nil]; }
+
+    NSError *error;
+    XCTAssertTrue([RLMRealm deleteFilesForConfiguration:config error:&error]);
+    XCTAssertNil(error);
+
+    NSFileManager *fm = NSFileManager.defaultManager;
+    XCTAssertTrue([fm fileExistsAtPath:[config.fileURL.path stringByAppendingPathExtension:@"lock"]]);
+    XCTAssertFalse([fm fileExistsAtPath:[config.fileURL.path stringByAppendingPathExtension:@"management"]]);
+#if !TARGET_OS_TV
+    XCTAssertFalse([fm fileExistsAtPath:[config.fileURL.path stringByAppendingPathExtension:@"note"]]);
+#endif
+}
+
+- (void)testDeleteRealmFileWithMissingManagementFiles {
+    RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
+    [NSFileManager.defaultManager createFileAtPath:config.fileURL.path contents:nil attributes:nil];
+
+    NSError *error;
+    XCTAssertTrue([RLMRealm deleteFilesForConfiguration:config error:&error]);
+    XCTAssertNil(error);
+}
+
+- (void)testDeleteRealmFileWithReadOnlyManagementFiles {
+    RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
+    NSFileManager *fm = NSFileManager.defaultManager;
+    [fm createFileAtPath:config.fileURL.path contents:nil attributes:nil];
+    NSString *notificationPipe = [config.fileURL.path stringByAppendingPathExtension:@"note"];
+    [fm createFileAtPath:notificationPipe contents:nil attributes:@{NSFileImmutable: @YES}];
+
+    NSError *error;
+    XCTAssertTrue([RLMRealm deleteFilesForConfiguration:config error:&error]);
+    XCTAssertEqual(error.code, NSFileWriteNoPermissionError);
+    [fm setAttributes:@{NSFileImmutable: @NO} ofItemAtPath:notificationPipe error:nil];
+}
+
+- (void)testDeleteOpenRealmFile {
+    RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
+    __attribute__((objc_precise_lifetime)) RLMRealm *realm = [RLMRealm realmWithConfiguration:config error:nil];
+
+    NSError *error;
+    XCTAssertFalse([RLMRealm deleteFilesForConfiguration:config error:&error]);
+    XCTAssertEqual(error.code, RLMErrorAlreadyOpen);
+    XCTAssertTrue([NSFileManager.defaultManager fileExistsAtPath:config.fileURL.path]);
 }
 
 @end

+ 1 - 1
Carthage/Checkouts/realm-cocoa/Realm/Tests/SchemaTests.mm

@@ -742,7 +742,7 @@ RLM_ARRAY_TYPE(NotARealClass)
 }
 
 // Can't spawn child processes on iOS
-#if !TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR
+#if !TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR && !TARGET_OS_MACCATALYST
 - (void)testPartialSharedSchemaInit {
     if (self.isParent) {
         RLMRunChildAndWait();

+ 4 - 0
Carthage/Checkouts/realm-cocoa/Realm/Tests/Swift/SwiftSchemaTests.swift

@@ -21,6 +21,8 @@ import Realm
 import Realm.Private
 import RealmTestSupport
 
+#if os(macOS)
+
 class InitLinkedToClass: RLMObject {
     @objc dynamic var value: SwiftRLMIntObject! = SwiftRLMIntObject(value: [0])
 }
@@ -212,3 +214,5 @@ class SwiftRLMSchemaTests: RLMMultiProcessTestCase {
                                        "RLMArray\\<invalid class\\>")
     }
 }
+
+#endif

+ 4 - 4
Carthage/Checkouts/realm-cocoa/Realm/Tests/TestHost/main.m

@@ -14,20 +14,20 @@
 int main(int argc, const char *argv[]) {
 }
 
-#elif TARGET_OS_IPHONE || TARGET_OS_TV
+#elif TARGET_OS_IPHONE || TARGET_OS_TV || TARGET_OS_MACCATALYST
 
 #import <UIKit/UIKit.h>
 
-@interface AppDelegate : UIResponder <UIApplicationDelegate>
+@interface RLMAppDelegate : UIResponder <UIApplicationDelegate>
 @property (strong, nonatomic) UIWindow *window;
 @end
 
-@implementation AppDelegate
+@implementation RLMAppDelegate
 @end
 
 int main(int argc, char *argv[]) {
     @autoreleasepool {
-        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
+        return UIApplicationMain(argc, argv, NSStringFromClass([UIApplication class]), NSStringFromClass([RLMAppDelegate class]));
     }
 }
 

+ 38 - 1
Carthage/Checkouts/realm-cocoa/RealmSwift/Realm.swift

@@ -764,7 +764,7 @@ public struct Realm {
         rlmRealm.invalidate()
     }
 
-    // MARK: Writing a Copy
+    // MARK: File Management
 
     /**
      Writes a compacted and optionally encrypted copy of the Realm to the given local URL.
@@ -783,6 +783,43 @@ public struct Realm {
         try rlmRealm.writeCopy(to: fileURL, encryptionKey: encryptionKey)
     }
 
+    /**
+     Checks if the Realm file for the given configuration exists locally on disk.
+
+     For non-synchronized, non-in-memory Realms, this is equivalent to
+     `FileManager.default.fileExists(atPath:)`. For synchronized Realms, it
+     takes care of computing the actual path on disk based on the server,
+     virtual path, and user as is done when opening the Realm.
+
+     @param config A Realm configuration to check the existence of.
+     @return true if the Realm file for the given configuration exists on disk, false otherwise.
+     */
+    public static func fileExists(for config: Configuration) -> Bool {
+        return RLMRealm.fileExists(for: config.rlmConfiguration)
+    }
+
+    /**
+     Deletes the local Realm file and associated temporary files for the given configuration.
+
+     This deletes the ".realm", ".note" and ".management" files which would be
+     created by opening the Realm with the given configuration. It does not
+     delete the ".lock" file (which contains no persisted data and is recreated
+     from scratch every time the Realm file is opened).
+
+     The Realm must not be currently open on any thread or in another process.
+     If it is, this will throw the error .alreadyOpen. Attempting to open the
+     Realm on another thread while the deletion is happening will block, and
+     then create a new Realm and open that afterwards.
+
+     If the Realm already does not exist this will return `false`.
+
+     @param config A Realm configuration identifying the Realm to be deleted.
+     @return true if any files were deleted, false otherwise.
+     */
+    public static func deleteFiles(for config: Configuration) throws -> Bool {
+        return try RLMRealm.deleteFiles(for: config.rlmConfiguration)
+    }
+
     // MARK: Internal
 
     internal var rlmRealm: RLMRealm

+ 90 - 4
Carthage/Checkouts/realm-cocoa/RealmSwift/Tests/ObjectTests.swift

@@ -248,7 +248,7 @@ class ObjectTests: TestCase {
 
             let expected = object.value(forKey: "binaryCol") as! Data
             let actual = "a".data(using: String.Encoding.utf8)!
-            XCTAssertTrue(expected == actual)
+            XCTAssertEqual(expected, actual)
 
             XCTAssertEqual(object.value(forKey: "dateCol") as! Date?, Date(timeIntervalSince1970: 1))
             XCTAssertEqual((object.value(forKey: "objectCol")! as! SwiftBoolObject).boolCol, false)
@@ -256,9 +256,95 @@ class ObjectTests: TestCase {
         }
 
         test(SwiftObject())
-        try! Realm().write {
-            let persistedObject = try! Realm().create(SwiftObject.self, value: [:])
-            test(persistedObject)
+        let realm = try! Realm()
+        try! realm.write {
+            test(realm.create(SwiftObject.self, value: [:]))
+            let addedObj = SwiftObject()
+            realm.add(addedObj)
+            test(addedObj)
+        }
+    }
+
+    func testValueForKeyOptionals() {
+        let test: (SwiftOptionalObject) -> Void = { object in
+            XCTAssertNil(object.value(forKey: "optNSStringCol"))
+            XCTAssertNil(object.value(forKey: "optStringCol"))
+            XCTAssertNil(object.value(forKey: "optBinaryCol"))
+            XCTAssertNil(object.value(forKey: "optDateCol"))
+            XCTAssertNil(object.value(forKey: "optIntCol"))
+            XCTAssertNil(object.value(forKey: "optInt8Col"))
+            XCTAssertNil(object.value(forKey: "optInt16Col"))
+            XCTAssertNil(object.value(forKey: "optInt32Col"))
+            XCTAssertNil(object.value(forKey: "optInt64Col"))
+            XCTAssertNil(object.value(forKey: "optFloatCol"))
+            XCTAssertNil(object.value(forKey: "optDoubleCol"))
+            XCTAssertNil(object.value(forKey: "optBoolCol"))
+            XCTAssertNil(object.value(forKey: "optEnumCol"))
+        }
+
+        test(SwiftOptionalObject())
+        let realm = try! Realm()
+        try! realm.write {
+            test(realm.create(SwiftOptionalObject.self, value: [:]))
+            let addedObj = SwiftOptionalObject()
+            realm.add(addedObj)
+            test(addedObj)
+        }
+    }
+
+    func testValueForKeyList() {
+        let test: (SwiftListObject) -> Void = { object in
+            XCTAssertNil((object.value(forKey: "int") as! List<Int>).first)
+            XCTAssertNil((object.value(forKey: "int8") as! List<Int8>).first)
+            XCTAssertNil((object.value(forKey: "int16") as! List<Int16>).first)
+            XCTAssertNil((object.value(forKey: "int32") as! List<Int32>).first)
+            XCTAssertNil((object.value(forKey: "int64") as! List<Int64>).first)
+            XCTAssertNil((object.value(forKey: "float") as! List<Float>).first)
+            XCTAssertNil((object.value(forKey: "double") as! List<Double>).first)
+            XCTAssertNil((object.value(forKey: "string") as! List<String>).first)
+            XCTAssertNil((object.value(forKey: "data") as! List<Data>).first)
+            XCTAssertNil((object.value(forKey: "date") as! List<Date>).first)
+
+            // The `as Any?` casts below are only to silence the warning about it
+            // happening implicitly and are not functionally required
+            XCTAssertNil((object.value(forKey: "intOpt") as! List<Int?>).first as Any?)
+            XCTAssertNil((object.value(forKey: "int8Opt") as! List<Int8?>).first as Any?)
+            XCTAssertNil((object.value(forKey: "int16Opt") as! List<Int16?>).first as Any?)
+            XCTAssertNil((object.value(forKey: "int32Opt") as! List<Int32?>).first as Any?)
+            XCTAssertNil((object.value(forKey: "int64Opt") as! List<Int64?>).first as Any?)
+            XCTAssertNil((object.value(forKey: "floatOpt") as! List<Float?>).first as Any?)
+            XCTAssertNil((object.value(forKey: "doubleOpt") as! List<Double?>).first as Any?)
+            XCTAssertNil((object.value(forKey: "stringOpt") as! List<String?>).first as Any?)
+            XCTAssertNil((object.value(forKey: "dataOpt") as! List<Data?>).first as Any?)
+            XCTAssertNil((object.value(forKey: "dateOpt") as! List<Date?>).first as Any?)
+        }
+
+        test(SwiftListObject())
+        let realm = try! Realm()
+        try! realm.write {
+            test(realm.create(SwiftListObject.self, value: [:]))
+            let addedObj = SwiftListObject()
+            realm.add(addedObj)
+            test(addedObj)
+        }
+    }
+
+    func testValueForKeyLinkingObjects() {
+        let test: (SwiftDogObject) -> Void = { object in
+            let owners = object.value(forKey: "owners") as! LinkingObjects<SwiftOwnerObject>
+            if object.realm != nil {
+                XCTAssertEqual(owners.first!.name, "owner name")
+            }
+        }
+
+        let dog = SwiftDogObject()
+        let owner = SwiftOwnerObject(value: ["owner name", dog])
+        test(dog)
+        let realm = try! Realm()
+        try! realm.write {
+            test(realm.create(SwiftOwnerObject.self, value: owner).dog!)
+            realm.add(owner)
+            test(dog)
         }
     }
 

+ 1 - 1
Carthage/Checkouts/realm-cocoa/RealmSwift/Tests/PerformanceTests.swift

@@ -48,7 +48,7 @@ class SwiftPerformanceTests: TestCase {
     }
 #else
     override class func defaultTestSuite() -> XCTestSuite {
-#if !DEBUG && os(iOS)
+#if !DEBUG && os(iOS) && !TARGET_OS_MACCATALYST
         if isRunningOnDevice {
             return super.defaultTestSuite()
         }

+ 9 - 0
Carthage/Checkouts/realm-cocoa/RealmSwift/Tests/RealmTests.swift

@@ -845,4 +845,13 @@ class RealmTests: TestCase {
             XCTFail("Failed to brigde RLMError to Realm.Error")
         }
     }
+
+    func testExists() {
+        let config = Realm.Configuration()
+        XCTAssertFalse(Realm.fileExists(for: config))
+        autoreleasepool { _ = try! Realm(configuration: config) }
+        XCTAssertTrue(Realm.fileExists(for: config))
+        XCTAssertTrue(try! Realm.deleteFiles(for: config))
+        XCTAssertFalse(Realm.fileExists(for: config))
+    }
 }

+ 42 - 4
Carthage/Checkouts/realm-cocoa/build.sh

@@ -70,6 +70,8 @@ command:
   test-tvos-devices:    tests ObjC & Swift tvOS frameworks on all attached tvOS devices
   test-osx:             tests macOS framework
   test-osx-swift:       tests RealmSwift macOS framework
+  test-catalyst:        tests Mac Catalyst framework
+  test-catalyst-swift:  tests RealmSwift Mac Catalyst framework
   test-swiftpm:         tests ObjC and Swift macOS frameworks via SwiftPM
   verify:               verifies docs, osx, osx-swift, ios-static, ios-dynamic, ios-swift, ios-device in both Debug and Release configurations, swiftlint
   verify-osx-object-server:  downloads the Realm Object Server and runs the Objective-C and Swift integration tests
@@ -569,7 +571,10 @@ case "$COMMAND" in
             exit 1
         fi
 
-        xc "-scheme Realm -configuration $CONFIGURATION REALM_CATALYST_FLAGS='-target x86_64-apple-ios13.0-macabi' REALM_PLATFORM_SUFFIX='maccatalyst'"
+        xc "-scheme Realm -configuration $CONFIGURATION \
+            REALM_CATALYST_FLAGS='-target x86_64-apple-ios13.0-macabi' \
+            REALM_PLATFORM_SUFFIX='maccatalyst' \
+            IS_MACCATALYST=YES"
         clean_retrieve "build/DerivedData/Realm/Build/Products/$CONFIGURATION/Realm.framework" "build/catalyst" "Realm.framework"
         ;;
 
@@ -585,7 +590,8 @@ case "$COMMAND" in
             REALM_CATALYST_FLAGS='-target x86_64-apple-ios13.0-macabi' \
             REALM_PLATFORM_SUFFIX='maccatalyst' \
             SWIFT_DEPLOYMENT_TARGET='13.0-macabi' \
-            SWIFT_PLATFORM_TARGET_PREFIX='ios'"
+            SWIFT_PLATFORM_TARGET_PREFIX='ios' \
+            IS_MACCATALYST=YES"
         destination="build/catalyst/swift-$REALM_XCODE_VERSION"
         clean_retrieve "build/DerivedData/Realm/Build/Products/$CONFIGURATION/RealmSwift.framework" "$destination" "RealmSwift.framework"
         rm -rf "$destination/Realm.framework"
@@ -755,6 +761,10 @@ case "$COMMAND" in
         sh build.sh test-tvos-devices || failed=1
         sh build.sh test-osx || failed=1
         sh build.sh test-osx-swift || failed=1
+        if (( $(xcode_version_major) >= 11 )); then
+            sh build.sh test-catalyst || failed=1
+            sh build.sh test-catalyst-swift || failed=1
+        fi
         exit $failed
         ;;
 
@@ -846,6 +856,20 @@ case "$COMMAND" in
         exit 0
         ;;
 
+    "test-catalyst")
+        export REALM_SDKROOT=iphoneos
+        xc "-scheme Realm -configuration $CONFIGURATION -destination 'platform=macOS,variant=Mac Catalyst' CODE_SIGN_IDENTITY='' build-for-testing"
+        xc "-scheme Realm -configuration $CONFIGURATION -destination 'platform=macOS,variant=Mac Catalyst' CODE_SIGN_IDENTITY='' test"
+        exit 0
+        ;;
+
+    "test-catalyst-swift")
+        export REALM_SDKROOT=iphoneos
+        xc "-scheme RealmSwift -configuration $CONFIGURATION -destination 'platform=macOS,variant=Mac Catalyst' CODE_SIGN_IDENTITY='' build-for-testing"
+        xc "-scheme RealmSwift -configuration $CONFIGURATION -destination 'platform=macOS,variant=Mac Catalyst' CODE_SIGN_IDENTITY='' test"
+        exit 0
+        ;;
+
     ######################################
     # Full verification
     ######################################
@@ -871,6 +895,10 @@ case "$COMMAND" in
         sh build.sh verify-swiftlint
         sh build.sh verify-swiftpm
         sh build.sh verify-osx-object-server
+        if (( $(xcode_version_major) >= 11 )); then
+            sh build.sh verify-catalyst
+            sh build.sh verify-catalyst-swift
+        fi
         ;;
 
     "verify-cocoapods")
@@ -1017,6 +1045,16 @@ case "$COMMAND" in
         exit 0
         ;;
 
+    "verify-catalyst")
+        sh build.sh test-catalyst
+        exit 0
+        ;;
+
+    "verify-catalyst-swift")
+        sh build.sh test-catalyst-swift
+        exit 0
+        ;;
+
     ######################################
     # Docs
     ######################################
@@ -1256,7 +1294,7 @@ EOM
                 sh build.sh prelaunch-simulator
             fi
 
-            source $(brew --prefix nvm)/nvm.sh
+            source $(brew --prefix nvm)/nvm.sh --no-use
             export REALM_NODE_PATH="$(nvm which 8)"
 
             # Reset CoreSimulator.log
@@ -1512,7 +1550,7 @@ x.y.z Release notes (yyyy-MM-dd)
 * File format: Generates Realms with format v9 (Reads and upgrades all previous formats)
 * Realm Object Server: 3.21.0 or later.
 * APIs are backwards compatible with all previous releases in the 4.x.y series.
-* Carthage release for Swift is built with Xcode 11.2.
+* Carthage release for Swift is built with Xcode 11.3.
 
 ### Internal
 Upgraded realm-core from ? to ?

+ 3 - 3
Carthage/Checkouts/realm-cocoa/dependencies.list

@@ -1,4 +1,4 @@
-VERSION=4.1.1
-REALM_CORE_VERSION=5.23.6
-REALM_SYNC_VERSION=4.8.2
+VERSION=4.3.1
+REALM_CORE_VERSION=5.23.8
+REALM_SYNC_VERSION=4.9.4
 REALM_OBJECT_SERVER_VERSION=3.23.1

+ 4 - 3
Carthage/Checkouts/realm-cocoa/scripts/generate-rlmplatform.sh

@@ -6,11 +6,12 @@ SOURCE_FILE="${SRCROOT}/Realm/RLMPlatform.h.in"
 DESTINATION_FILE="${TARGET_BUILD_DIR}/${PUBLIC_HEADERS_FOLDER_PATH}/RLMPlatform.h"
 TEMPORARY_FILE="${TARGET_TEMP_DIR}/RLMPlatform.h"
 
-if [ -n "${REALM_PLATFORM_SUFFIX}" ]; then
-  PLATFORM_NAME="${REALM_PLATFORM_SUFFIX}"
+PLATFORM_SUFFIX="$SWIFT_PLATFORM_TARGET_PREFIX"
+if [ "$IS_MACCATALYST" = "YES" ]; then
+  PLATFORM_SUFFIX=maccatalyst
 fi
 
-unifdef -B -DREALM_BUILDING_FOR_$(echo ${PLATFORM_NAME} | tr "[:lower:]" "[:upper:]") < "${SOURCE_FILE}" | sed -e "s/''/'/" > "${TEMPORARY_FILE}"
+unifdef -B -DREALM_BUILDING_FOR_$(echo ${PLATFORM_SUFFIX} | tr "[:lower:]" "[:upper:]") < "${SOURCE_FILE}" | sed -e "s/''/'/" > "${TEMPORARY_FILE}"
 
 if ! cmp -s "${TEMPORARY_FILE}" "${DESTINATION_FILE}"; then
   echo "Updating ${DESTINATION_FILE}"

+ 1 - 1
Carthage/Checkouts/realm-cocoa/scripts/package_examples.rb

@@ -41,7 +41,7 @@ base_examples = [
   "examples/tvos/swift",
 ]
 
-xcode_versions = %w(10.0 10.1 10.2.1 10.3 11.0)
+xcode_versions = %w(10.0 10.1 10.3 11.1 11.2.1 11.3)
 
 # Remove reference to Realm.xcodeproj from all example workspaces.
 base_examples.each do |example|

+ 131 - 95
iOSClient/AppDelegate.m

@@ -684,23 +684,30 @@
         dispatch_async(dispatch_get_main_queue(), ^{
 
             UISplitViewController *splitViewController = (UISplitViewController *)self.window.rootViewController;
-            UINavigationController *navigationControllerMaster = (UINavigationController *)splitViewController.viewControllers.firstObject;
-            UITabBarController *tabBarController = (UITabBarController *)navigationControllerMaster.topViewController;
-            
-            if (splitViewController.isCollapsed) {
-                            
-                [navigationControllerMaster popToRootViewControllerAnimated:false];
+            if ([splitViewController isKindOfClass:[UISplitViewController class]]) {
                 UINavigationController *navigationControllerMaster = (UINavigationController *)splitViewController.viewControllers.firstObject;
-                UITabBarController *tabBarController = (UITabBarController *)navigationControllerMaster.topViewController;
-                
-                if ([tabBarController isKindOfClass:[UITabBarController class]]) {
-                    [tabBarController setSelectedIndex: k_tabBarApplicationIndexMedia];
-                } 
-                
-            } else {
-            
-                if ([tabBarController isKindOfClass:[UITabBarController class]]) {
-                    [tabBarController setSelectedIndex: k_tabBarApplicationIndexMedia];
+                if ([navigationControllerMaster isKindOfClass:[UINavigationController class]]) {
+                    UITabBarController *tabBarController = (UITabBarController *)navigationControllerMaster.topViewController;
+                     if ([tabBarController isKindOfClass:[UITabBarController class]]) {
+                         
+                         if (splitViewController.isCollapsed) {
+                                         
+                             [navigationControllerMaster popToRootViewControllerAnimated:false];
+                             UINavigationController *navigationControllerMaster = (UINavigationController *)splitViewController.viewControllers.firstObject;
+                             if ([navigationControllerMaster isKindOfClass:[UINavigationController class]]) {
+                                 UITabBarController *tabBarController = (UITabBarController *)navigationControllerMaster.topViewController;
+                                 if ([tabBarController isKindOfClass:[UITabBarController class]]) {
+                                     [tabBarController setSelectedIndex: k_tabBarApplicationIndexMedia];
+                                 }
+                             }
+                        
+                         } else {
+                         
+                             if ([tabBarController isKindOfClass:[UITabBarController class]]) {
+                                 [tabBarController setSelectedIndex: k_tabBarApplicationIndexMedia];
+                             }
+                         }
+                     }
                 }
             }
         });
@@ -732,17 +739,19 @@
             [UIApplication sharedApplication].applicationIconBadgeNumber = total;
             
             UISplitViewController *splitViewController = (UISplitViewController *)self.window.rootViewController;
-            UINavigationController *navigationController = (UINavigationController *)[splitViewController.viewControllers firstObject];
-            UITabBarController *tabBarController = (UITabBarController *)navigationController.topViewController;
-
-            if ([tabBarController isKindOfClass:[UITabBarController class]]) {
-                
-                UITabBarItem *tabBarItem = [tabBarController.tabBar.items objectAtIndex:0];
-                    
-                if (total > 0) {
-                    [tabBarItem setBadgeValue:[NSString stringWithFormat:@"%li", (unsigned long)total]];
-                } else {
-                    [tabBarItem setBadgeValue:nil];
+            if ([splitViewController isKindOfClass:[UISplitViewController class]]) {
+                UINavigationController *navigationController = (UINavigationController *)[splitViewController.viewControllers firstObject];
+                if ([navigationController isKindOfClass:[UINavigationController class]]) {
+                    UITabBarController *tabBarController = (UITabBarController *)navigationController.topViewController;
+                    if ([tabBarController isKindOfClass:[UITabBarController class]]) {
+                        UITabBarItem *tabBarItem = [tabBarController.tabBar.items objectAtIndex:0];
+                            
+                        if (total > 0) {
+                            [tabBarItem setBadgeValue:[NSString stringWithFormat:@"%li", (unsigned long)total]];
+                        } else {
+                            [tabBarItem setBadgeValue:nil];
+                        }
+                    }
                 }
             }
         });
@@ -854,21 +863,27 @@
 
 - (NSString *)getTabBarControllerActiveServerUrl
 {
-    UISplitViewController *splitViewController = (UISplitViewController *)self.window.rootViewController;
-    UINavigationController *masterNavigationController = [splitViewController.viewControllers firstObject];
-    UITabBarController *tabBarController = [masterNavigationController.viewControllers firstObject];
-
     NSString *serverUrl = [CCUtility getHomeServerUrlActiveUrl:self.activeUrl];
-    NSInteger index = tabBarController.selectedIndex;
-    
-    // select active serverUrl
-    if (index == k_tabBarApplicationIndexFile) {
-        serverUrl = self.activeMain.serverUrl;
-    } else if (index == k_tabBarApplicationIndexFavorite) {
-        if (self.activeFavorites.serverUrl)
-            serverUrl = self.activeFavorites.serverUrl;
-    } else if (index == k_tabBarApplicationIndexMedia) {
-        serverUrl = [[NCManageDatabase sharedInstance] getAccountAutoUploadPath:self.activeUrl];
+
+    UISplitViewController *splitViewController = (UISplitViewController *)self.window.rootViewController;
+    if ([splitViewController isKindOfClass:[UISplitViewController class]]) {
+        UINavigationController *masterNavigationController = [splitViewController.viewControllers firstObject];
+        if ([masterNavigationController isKindOfClass:[UINavigationController class]]) {
+            UITabBarController *tabBarController = [masterNavigationController.viewControllers firstObject];
+            if ([tabBarController isKindOfClass:[UITabBarController class]]) {
+                NSInteger index = tabBarController.selectedIndex;
+                   
+                // select active serverUrl
+                if (index == k_tabBarApplicationIndexFile) {
+                    serverUrl = self.activeMain.serverUrl;
+                } else if (index == k_tabBarApplicationIndexFavorite) {
+                    if (self.activeFavorites.serverUrl)
+                        serverUrl = self.activeFavorites.serverUrl;
+                } else if (index == k_tabBarApplicationIndexMedia) {
+                    serverUrl = [[NCManageDatabase sharedInstance] getAccountAutoUploadPath:self.activeUrl];
+                }
+            }
+        }
     }
     
     return serverUrl;
@@ -946,12 +961,18 @@
     
     //tabBar button Plus
     UISplitViewController *splitViewController = (UISplitViewController *)self.window.rootViewController;
-    UINavigationController *navigationController = (UINavigationController *)[splitViewController.viewControllers firstObject];
-    UITabBarController *tabBarController = (UITabBarController *)navigationController.topViewController;
-    UIButton *button = [tabBarController.view viewWithTag:99];
-    UIImage *buttonImage = [CCGraphics changeThemingColorImage:[UIImage imageNamed:@"tabBarPlus"] multiplier:3 color:NCBrandColor.sharedInstance.brandElement];
-    [button setBackgroundImage:buttonImage forState:UIControlStateNormal];
-    [button setBackgroundImage:buttonImage forState:UIControlStateHighlighted];
+    if ([splitViewController isKindOfClass:[UISplitViewController class]]) {
+        UINavigationController *navigationController = (UINavigationController *)[splitViewController.viewControllers firstObject];
+        if ([navigationController isKindOfClass:[UINavigationController class]]) {
+            UITabBarController *tabBarController = (UITabBarController *)navigationController.topViewController;
+            if ([tabBarController isKindOfClass:[UITabBarController class]]) {
+                UIButton *button = [tabBarController.view viewWithTag:99];
+                UIImage *buttonImage = [CCGraphics changeThemingColorImage:[UIImage imageNamed:@"tabBarPlus"] multiplier:3 color:NCBrandColor.sharedInstance.brandElement];
+                [button setBackgroundImage:buttonImage forState:UIControlStateNormal];
+                [button setBackgroundImage:buttonImage forState:UIControlStateHighlighted];
+            }
+        }
+    }
                 
     // TableView
     if (tableView) {
@@ -1512,54 +1533,66 @@
                         if (matchedAccount) {
                             
                             UISplitViewController *splitViewController = (UISplitViewController *)self.window.rootViewController;
-                            UINavigationController *navigationControllerMaster = (UINavigationController *)splitViewController.viewControllers.firstObject;
-                            UITabBarController *tabBarController = (UITabBarController *)navigationControllerMaster.topViewController;
-                            
-                            if (splitViewController.isCollapsed) {
-                                           
-                                [navigationControllerMaster popToRootViewControllerAnimated:false];
+                            if ([splitViewController isKindOfClass:[UISplitViewController class]]) {
                                 UINavigationController *navigationControllerMaster = (UINavigationController *)splitViewController.viewControllers.firstObject;
-                                UITabBarController *tabBarController = (UITabBarController *)navigationControllerMaster.topViewController;
-                               
-                                if ([tabBarController isKindOfClass:[UITabBarController class]]) {
-                                    [tabBarController setSelectedIndex: k_tabBarApplicationIndexFile];
-                                }
-                               
-                            } else {
-                           
-                                if ([tabBarController isKindOfClass:[UITabBarController class]]) {
-                                    [tabBarController setSelectedIndex: k_tabBarApplicationIndexFile];
-                                }
-                            }
-                            
-                            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^(void) {
-                                
-                                [self.activeMain.navigationController popToRootViewControllerAnimated:NO];
-                                
-                                dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^(void) {
-                                    
-                                    NSString *fileNamePath = [NSString stringWithFormat:@"%@%@/%@", matchedAccount.url, k_webDAV, path];
-                                    
-                                    if ([path containsString:@"/"]) {
+                                if ([navigationControllerMaster isKindOfClass:[UINavigationController class]]) {
+                                    UITabBarController *tabBarController = (UITabBarController *)navigationControllerMaster.topViewController;
+                                    if ([tabBarController isKindOfClass:[UITabBarController class]]) {
+                                        
+                                        if (splitViewController.isCollapsed) {
+                                                        
+                                            [navigationControllerMaster popToRootViewControllerAnimated:false];
+                                            UINavigationController *navigationControllerMaster = (UINavigationController *)splitViewController.viewControllers.firstObject;
+                                            if ([navigationControllerMaster isKindOfClass:[UINavigationController class]]) {
+                                                UITabBarController *tabBarController = (UITabBarController *)navigationControllerMaster.topViewController;
+                                                if ([tabBarController isKindOfClass:[UITabBarController class]]) {
+                                                    if ([tabBarController isKindOfClass:[UITabBarController class]]) {
+                                                        [tabBarController setSelectedIndex: k_tabBarApplicationIndexFile];
+                                                    }
+                                                }
+                                            }
+                                            
+                                        } else {
                                         
-                                        // Push
-                                        NSString *directoryName = [[path stringByDeletingLastPathComponent] lastPathComponent];
-                                        NSString *serverUrl = [CCUtility deletingLastPathComponentFromServerUrl:[NSString stringWithFormat:@"%@%@/%@", matchedAccount.url, k_webDAV, [path stringByDeletingLastPathComponent]]];
-                                        tableMetadata *metadata = [CCUtility createMetadataWithAccount:matchedAccount.account date:[NSDate date] directory:NO ocId:[[NSUUID UUID] UUIDString] serverUrl:serverUrl fileName:directoryName etag:@"" size:0 status:k_metadataStatusNormal url:@"" contentType:@""];
+                                            if ([tabBarController isKindOfClass:[UITabBarController class]]) {
+                                                [tabBarController setSelectedIndex: k_tabBarApplicationIndexFile];
+                                            }
+                                        }
                                         
-                                        [self.activeMain performSegueDirectoryWithControlPasscode:true metadata:metadata blinkFileNamePath:fileNamePath];
+                                        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^(void) {
+                                            
+                                            [self.activeMain.navigationController popToRootViewControllerAnimated:NO];
+                                            
+                                            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^(void) {
+                                                
+                                                NSString *fileNamePath = [NSString stringWithFormat:@"%@%@/%@", matchedAccount.url, k_webDAV, path];
+                                                
+                                                if ([path containsString:@"/"]) {
+                                                    
+                                                    // Push
+                                                    NSString *directoryName = [[path stringByDeletingLastPathComponent] lastPathComponent];
+                                                    NSString *serverUrl = [CCUtility deletingLastPathComponentFromServerUrl:[NSString stringWithFormat:@"%@%@/%@", matchedAccount.url, k_webDAV, [path stringByDeletingLastPathComponent]]];
+                                                    tableMetadata *metadata = [CCUtility createMetadataWithAccount:matchedAccount.account date:[NSDate date] directory:NO ocId:[[NSUUID UUID] UUIDString] serverUrl:serverUrl fileName:directoryName etag:@"" size:0 status:k_metadataStatusNormal url:@"" contentType:@""];
+                                                    
+                                                    [self.activeMain performSegueDirectoryWithControlPasscode:true metadata:metadata blinkFileNamePath:fileNamePath];
+                                                    
+                                                } else {
+                                                    
+                                                    // Reload folder
+                                                    NSString *serverUrl = [NSString stringWithFormat:@"%@%@", matchedAccount.url, k_webDAV];
+                                                    
+                                                    self.activeMain.blinkFileNamePath = fileNamePath;
+                                                    [self.activeMain readFolder:serverUrl];
+                                                }
+                                            });
+                                        });
                                         
-                                    } else {
                                         
-                                        // Reload folder
-                                        NSString *serverUrl = [NSString stringWithFormat:@"%@%@", matchedAccount.url, k_webDAV];
                                         
-                                        self.activeMain.blinkFileNamePath = fileNamePath;
-                                        [self.activeMain readFolder:serverUrl];
                                     }
-                                });
-                            });
-              
+                                }
+                            }
+                       
                         } else {
                             
                             NSString *domain = [[NSURL URLWithString:link] host];
@@ -1594,13 +1627,16 @@
         if (error == nil) {
             
             UISplitViewController *splitViewController = (UISplitViewController *)self.window.rootViewController;
-            UINavigationController *navigationController = (UINavigationController *)splitViewController.viewControllers.firstObject;
-            
-            UIViewController *uploadNavigationViewController = [[UIStoryboard storyboardWithName:@"CCUploadFromOtherUpp" bundle:nil] instantiateViewControllerWithIdentifier:@"CCUploadNavigationViewController"];
-            
-            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, timer * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
-                [navigationController presentViewController:uploadNavigationViewController animated:YES completion:nil];
-            });
+            if ([splitViewController isKindOfClass:[UISplitViewController class]]) {
+                UINavigationController *navigationControllerMaster = (UINavigationController *)splitViewController.viewControllers.firstObject;
+                if ([navigationControllerMaster isKindOfClass:[UINavigationController class]]) {
+                    UIViewController *uploadNavigationViewController = [[UIStoryboard storyboardWithName:@"CCUploadFromOtherUpp" bundle:nil] instantiateViewControllerWithIdentifier:@"CCUploadNavigationViewController"];
+                    
+                    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, timer * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
+                        [navigationControllerMaster presentViewController:uploadNavigationViewController animated:YES completion:nil];
+                    });
+                }
+            }
         }
     }
     

+ 7 - 1
iOSClient/Main/CCDetail.m

@@ -227,8 +227,14 @@
                     
                     [[NCUtility sharedInstance] startActivityIndicatorWithView:self.view bottom:0];
                     
+                    NSString *customUserAgent = nil;
                     NSString *fileNamePath = [CCUtility returnFileNamePathFromFileName:self.metadataDetail.fileName serverUrl:self.metadataDetail.serverUrl activeUrl:appDelegate.activeUrl];
-                    [[NCCommunication sharedInstance] NCTextOpenFileWithUrlString:appDelegate.activeUrl fileNamePath:fileNamePath editor:editor account:self.metadataDetail.account completionHandler:^(NSString *account, NSString *url, NSInteger errorCode, NSString *errorMessage) {
+
+                    if ([editor isEqualToString:k_editor_onlyoffice]) {
+                        customUserAgent = [[NCUtility sharedInstance] getCustomUserAgentOnlyOffice];
+                    }
+                    
+                    [[NCCommunication sharedInstance] NCTextOpenFileWithUrlString:appDelegate.activeUrl fileNamePath:fileNamePath editor:editor customUserAgent:customUserAgent account:self.metadataDetail.account completionHandler:^(NSString *account, NSString *url, NSInteger errorCode, NSString *errorMessage) {
                         
                         if (errorCode == 0 && [account isEqualToString:appDelegate.activeAccount]) {
                             

+ 25 - 6
iOSClient/Main/Create cloud/NCCreateFormUploadDocuments.swift

@@ -268,15 +268,30 @@ class NCCreateFormUploadDocuments: XLFormViewController, NCSelectDelegate, UICol
         }
             
         if self.editorId == k_editor_text || self.editorId == k_editor_onlyoffice {
-                                    
-            NCCommunication.sharedInstance.NCTextCreateFile(urlString: appDelegate.activeUrl, fileNamePath: fileName, editorId: editorId, creatorId: creatorId, templateId: selectTemplate.identifier, account: self.appDelegate.activeAccount) { (account, url, errorCode, errorMessage) in
+             
+            var customUserAgent: String?
+            
+            if self.editorId == k_editor_onlyoffice {
+                customUserAgent = NCUtility.sharedInstance.getCustomUserAgentOnlyOffice()
+            }
+            
+            NCCommunication.sharedInstance.NCTextCreateFile(urlString: appDelegate.activeUrl, fileNamePath: fileName, editorId: editorId, creatorId: creatorId, templateId: selectTemplate.identifier, customUserAgent: customUserAgent, account: self.appDelegate.activeAccount) { (account, url, errorCode, errorMessage) in
                 
                 if errorCode == 0 && account == self.appDelegate.activeAccount {
                     
                     if url != nil && url!.count > 0 {
                         
+                        var contentType = "text/markdown"
+                        if let directEditingCreators = NCManageDatabase.sharedInstance.getDirectEditingCreators(account: self.appDelegate.activeAccount) {
+                            for directEditingCreator in directEditingCreators {
+                                if directEditingCreator.ext == self.fileNameExtension {
+                                    contentType = directEditingCreator.mimetype
+                                }
+                            }
+                        }
+                        
                         self.dismiss(animated: true, completion: {
-                            let metadata = CCUtility.createMetadata(withAccount: self.appDelegate.activeAccount, date: Date(), directory: false, ocId: CCUtility.createRandomString(12), serverUrl: self.serverUrl, fileName: (fileNameForm as! NSString).deletingPathExtension + "." + self.fileNameExtension, etag: "", size: 0, status: Double(k_metadataStatusNormal), url:url, contentType: "text/markdown")
+                            let metadata = CCUtility.createMetadata(withAccount: self.appDelegate.activeAccount, date: Date(), directory: false, ocId: CCUtility.createRandomString(12), serverUrl: self.serverUrl, fileName: (fileNameForm as! NSString).deletingPathExtension + "." + self.fileNameExtension, etag: "", size: 0, status: Double(k_metadataStatusNormal), url:url, contentType: contentType)
                             
                             self.appDelegate.activeMain.shouldPerformSegue(metadata, selector: "")
                         })
@@ -329,10 +344,14 @@ class NCCreateFormUploadDocuments: XLFormViewController, NCSelectDelegate, UICol
         
         if self.editorId == k_editor_text || self.editorId == k_editor_onlyoffice {
             
-            // default
-            fileNameExtension = "md"
+            fileNameExtension = "md"            
+            var customUserAgent: String?
+                       
+            if self.editorId == k_editor_onlyoffice {
+                customUserAgent = NCUtility.sharedInstance.getCustomUserAgentOnlyOffice()
+            }
             
-            NCCommunication.sharedInstance.NCTextGetListOfTemplates(urlString: appDelegate.activeUrl, account: appDelegate.activeAccount) { (account, templates, errorCode, errorMessage) in
+            NCCommunication.sharedInstance.NCTextGetListOfTemplates(urlString: appDelegate.activeUrl, customUserAgent: customUserAgent, account: appDelegate.activeAccount) { (account, templates, errorCode, errorMessage) in
                 
                 self.indicator.stopAnimating()
                 

+ 2 - 2
iOSClient/RichWorkspace/NCRichWorkspaceCommon.swift

@@ -39,7 +39,7 @@ import NCCommunication
         NCUtility.sharedInstance.startActivityIndicator(view: viewController.view, bottom: 0)
         
         let fileNamePath = CCUtility.returnFileNamePath(fromFileName: k_fileNameRichWorkspace, serverUrl: serverUrl, activeUrl: appDelegate.activeUrl)!
-        NCCommunication.sharedInstance.NCTextCreateFile(urlString: appDelegate.activeUrl, fileNamePath: fileNamePath, editorId: "text", creatorId: "" ,templateId: "", account: appDelegate.activeAccount) { (account, url, errorCode, errorMessage) in
+        NCCommunication.sharedInstance.NCTextCreateFile(urlString: appDelegate.activeUrl, fileNamePath: fileNamePath, editorId: "text", creatorId: "" ,templateId: "", customUserAgent: nil, account: appDelegate.activeAccount) { (account, url, errorCode, errorMessage) in
             
             NCUtility.sharedInstance.stopActivityIndicator()
             
@@ -74,7 +74,7 @@ import NCCommunication
                 NCUtility.sharedInstance.startActivityIndicator(view: viewController.view, bottom: 0)
                 
                 let fileNamePath = CCUtility.returnFileNamePath(fromFileName: metadata.fileName, serverUrl: metadata.serverUrl, activeUrl: appDelegate.activeUrl)!
-                NCCommunication.sharedInstance.NCTextOpenFile(urlString: appDelegate.activeUrl, fileNamePath: fileNamePath, editor: "text", account: appDelegate.activeAccount) { (account, url, errorCode, errorMessage) in
+                NCCommunication.sharedInstance.NCTextOpenFile(urlString: appDelegate.activeUrl, fileNamePath: fileNamePath, editor: "text", customUserAgent: nil, account: appDelegate.activeAccount) { (account, url, errorCode, errorMessage) in
                     
                     NCUtility.sharedInstance.stopActivityIndicator()
                     

+ 10 - 0
iOSClient/Utility/NCUtility.swift

@@ -572,5 +572,15 @@ class NCUtility: NSObject {
         }
         return true
     }
+    
+    @objc func getCustomUserAgentOnlyOffice() -> String {
+        
+        let appVersion = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString")!
+        if UIDevice.current.userInterfaceIdiom == .pad {
+            return "Mozilla/5.0 (iPad) Nextcloud-iOS/\(appVersion)"
+        }else{
+            return "Mozilla/5.0 (iPhone) Mobile Nextcloud-iOS/\(appVersion)"
+        }
+    }
 }
 

+ 3 - 9
iOSClient/Viewer/NCViewerNextcloudText.swift

@@ -49,7 +49,7 @@ class NCViewerNextcloudText: WKWebView, WKNavigationDelegate, WKScriptMessageHan
         
         self.detail = detail
         self.metadata = metadata
-        self.editor = editor;
+        self.editor = editor
         
         NotificationCenter.default.addObserver(self, selector: #selector(keyboardDidShow), name: UIResponder.keyboardDidShowNotification, object: nil)
         NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
@@ -58,15 +58,9 @@ class NCViewerNextcloudText: WKWebView, WKNavigationDelegate, WKScriptMessageHan
         request.addValue("true", forHTTPHeaderField: "OCS-APIRequest")
         let language = NSLocale.preferredLanguages[0] as String
         request.addValue(language, forHTTPHeaderField: "Accept-Language")
-        
-        let appVersion = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString")!
-        
+                
         if editor == k_editor_onlyoffice {
-            if UIDevice.current.userInterfaceIdiom == .pad {
-                customUserAgent = "Mozilla/5.0 (iPad) Nextcloud-iOS/\(appVersion)"
-            }else{
-                customUserAgent = "Mozilla/5.0 (iPhone) Mobile Nextcloud-iOS/\(appVersion)"
-            }
+            customUserAgent = NCUtility.sharedInstance.getCustomUserAgentOnlyOffice()
         } else {
             customUserAgent = CCUtility.getUserAgent()
         }