Marino Faggiana 6 years ago
parent
commit
563f90c60e
37 changed files with 827 additions and 209 deletions
  1. 1 1
      Cartfile.resolved
  2. 26 0
      Carthage/Checkouts/realm-cocoa/CHANGELOG.md
  3. 0 1
      Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/RLMObjectServerTests.mm
  4. 12 2
      Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/RLMSyncTestCase.mm
  5. 2 2
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/dependencies.list
  6. 1 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/collection_notifications.hpp
  7. 17 13
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/object_accessor_impl.hpp
  8. 71 8
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/list.hpp
  9. 4 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object.cpp
  10. 15 4
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object.hpp
  11. 54 33
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object_accessor.hpp
  12. 4 11
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object_store.cpp
  13. 8 2
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/results.cpp
  14. 40 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/results.hpp
  15. 10 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/shared_realm.cpp
  16. 2 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/shared_realm.hpp
  17. 217 5
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/object.cpp
  18. 176 0
      Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/results.cpp
  19. 3 3
      Carthage/Checkouts/realm-cocoa/Realm/RLMAccessor.hpp
  20. 15 15
      Carthage/Checkouts/realm-cocoa/Realm/RLMAccessor.mm
  21. 1 1
      Carthage/Checkouts/realm-cocoa/Realm/RLMObjectStore.mm
  22. 42 15
      Carthage/Checkouts/realm-cocoa/Realm/RLMRealm+Sync.mm
  23. 4 4
      Carthage/Checkouts/realm-cocoa/Realm/RLMRealm.mm
  24. 1 1
      Carthage/Checkouts/realm-cocoa/Realm/RLMSyncManager.mm
  25. 1 2
      Carthage/Checkouts/realm-cocoa/Realm/RLMSyncSession.mm
  26. 0 6
      Carthage/Checkouts/realm-cocoa/Realm/RLMSyncSubscription.mm
  27. 2 2
      Carthage/Checkouts/realm-cocoa/Realm/Realm-Info.plist
  28. 3 3
      Carthage/Checkouts/realm-cocoa/dependencies.list
  29. 4 0
      Nextcloud.xcodeproj/project.pbxproj
  30. 1 1
      iOSClient/Brand/File_Provider_Extension.plist
  31. 1 1
      iOSClient/Brand/Notification_Service_Extension.plist
  32. 1 1
      iOSClient/Brand/Share.plist
  33. 1 1
      iOSClient/Brand/iOSClient.plist
  34. 25 25
      iOSClient/Main/Create cloud/NCCreateFormUploadScanDocument.swift
  35. 2 2
      iOSClient/Main/Main.storyboard
  36. 60 0
      iOSClient/Utility/NCAvatar.swift
  37. 0 44
      iOSClient/Utility/NCUtility.swift

+ 1 - 1
Cartfile.resolved

@@ -9,6 +9,6 @@ github "ealeksandrov/EAIntroView" "2.12.0"
 github "ealeksandrov/EARestrictedScrollView" "1.1.0"
 github "jdg/MBProgressHUD" "1.1.0"
 github "kishikawakatsumi/UICKeyChainStore" "v2.1.2"
-github "realm/realm-cocoa" "v3.11.1"
+github "realm/realm-cocoa" "v3.11.2"
 github "sgr-ksmt/PDFGenerator" "2.1"
 github "tilltue/TLPhotoPicker" "1.7.7"

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

@@ -1,3 +1,29 @@
+3.11.2 Release notes (2018-11-15)
+=============================================================
+
+### Enhancements
+
+* Improve the performance of the merge algorithm used for integrating remote
+  changes from the server. In particular, changesets involving many objects
+  which all link to a single object should be greatly improved.
+
+### Fixed
+
+* Fix a memory leak when removing notification blocks from collections.
+  PR: [#702](https://github.com/realm/realm-object-store/pull/702), since 1.1.0.
+* Fix re-sorting or distincting an already-sorted Results using values from
+  linked objects. Previously the unsorted order was used to read the values
+  from the linked objects.
+  PR [#3102](https://github.com/realm/realm-core/pull/3102), since 3.1.0.
+* Fix a set of bugs which could lead to bad changeset assertions when using
+  sync. The assertions would look something like the following:
+  `[realm-core-5.10.0] Assertion failed: ndx < size() with (ndx, size()) =  [742, 742]`.
+
+### Compatibility
+
+* File format: Generates Realms with format v9 (Reads and upgrades all previous formats)
+* Realm Object Server: 3.11.0 or later.
+
 3.11.1 Release notes (2018-10-19)
 =============================================================
 

+ 0 - 1
Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/RLMObjectServerTests.mm

@@ -1873,7 +1873,6 @@
     XCTestExpectation *expectation = [self expectationWithDescription:@"HTTP login"];
     [RLMSyncUser logInWithCredentials:creds authServerURL:url
                          onCompletion:^(RLMSyncUser *user, NSError *error) {
-                             NSLog(@"onCompletion %@ %@", user, error);
                              callback(user, error);
                              [expectation fulfill];
                          }];

+ 12 - 2
Carthage/Checkouts/realm-cocoa/Realm/ObjectServerTests/RLMSyncTestCase.mm

@@ -131,9 +131,19 @@ static NSURL *syncDirectoryForChildProcess() {
     // Clean up any old state from the server
     [[NSTask launchedTaskWithLaunchPath:@"/usr/bin/pkill"
                               arguments:@[@"-f", @"node.*test-ros-server.js"]] waitUntilExit];
-    [NSFileManager.defaultManager removeItemAtURL:self.serverDataRoot error:nil];
+    NSError *error;
+    [NSFileManager.defaultManager removeItemAtURL:self.serverDataRoot error:&error];
+    if (error && error.code != NSFileNoSuchFileError) {
+        NSLog(@"Failed to delete old test state: %@", error);
+        abort();
+    }
+    error = nil;
     [NSFileManager.defaultManager createDirectoryAtURL:self.serverDataRoot
-                           withIntermediateDirectories:YES attributes:nil error:nil];
+                           withIntermediateDirectories:YES attributes:nil error:&error];
+    if (error) {
+        NSLog(@"Failed to create scratch directory: %@", error);
+        abort();
+    }
 
     // Install ROS if it isn't already present
     [self downloadObjectServer];

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

@@ -1,4 +1,4 @@
-REALM_CORE_VERSION=5.8.0
-REALM_SYNC_VERSION=3.9.3
+REALM_CORE_VERSION=5.12.0
+REALM_SYNC_VERSION=3.12.10
 ANDROID_OPENSSL_VERSION=1.0.2k
 REALM_CORE_PACKAGING=2

+ 1 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/collection_notifications.hpp

@@ -126,6 +126,7 @@ public:
 
 private:
     struct Base {
+        virtual ~Base() {}
         virtual void before(CollectionChangeSet const&)=0;
         virtual void after(CollectionChangeSet const&)=0;
         virtual void error(std::exception_ptr)=0;

+ 17 - 13
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/impl/object_accessor_impl.hpp

@@ -55,11 +55,11 @@ public:
     // property and its index within the ObjectScehma's persisted_properties
     // array.
     util::Optional<util::Any> value_for_property(util::Any& dict,
-                                                 std::string const& prop_name,
+                                                 const Property& prop,
                                                  size_t /* property_index */) const
     {
         auto const& v = any_cast<AnyDict&>(dict);
-        auto it = v.find(prop_name);
+        auto it = v.find(prop.name);
         return it == v.end() ? util::none : util::make_optional(it->second);
     }
 
@@ -70,7 +70,7 @@ public:
     // This implementation does not support default values; see the default
     // value tests for an example of one which does.
     util::Optional<util::Any>
-    default_value_for_property(ObjectSchema const&, std::string const&) const
+    default_value_for_property(ObjectSchema const&, Property const&) const
     {
         return util::none;
     }
@@ -122,8 +122,12 @@ public:
     // true then `unbox()` should create a new object in the context's Realm
     // using the provided value. If `update` is true then upsert semantics
     // should be used for this.
+    // If `update_only_diff` is true, only properties that are different from
+    // already existing properties should be updated. If `create` and `update_only_diff`
+    // is true, `current_row` may hold a reference to the object that should
+    // be compared against.
     template<typename T>
-    T unbox(util::Any& v, bool /*create*/= false, bool /*update*/= false) const { return any_cast<T>(v); }
+    T unbox(util::Any& v, bool /*create*/= false, bool /*update*/= false, bool /*update_only_diff*/ = false, size_t /*current_row*/ = realm::npos) const { return any_cast<T>(v); }
 
     bool is_null(util::Any const& v) const noexcept { return !v.has_value(); }
     util::Any null_value() const noexcept { return {}; }
@@ -155,7 +159,7 @@ inline util::Any CppContext::box(RowExpr row) const
 }
 
 template<>
-inline StringData CppContext::unbox(util::Any& v, bool, bool) const
+inline StringData CppContext::unbox(util::Any& v, bool, bool, bool, size_t) const
 {
     if (!v.has_value())
         return StringData();
@@ -164,7 +168,7 @@ inline StringData CppContext::unbox(util::Any& v, bool, bool) const
 }
 
 template<>
-inline BinaryData CppContext::unbox(util::Any& v, bool, bool) const
+inline BinaryData CppContext::unbox(util::Any& v, bool, bool, bool, size_t) const
 {
     if (!v.has_value())
         return BinaryData();
@@ -173,7 +177,7 @@ inline BinaryData CppContext::unbox(util::Any& v, bool, bool) const
 }
 
 template<>
-inline RowExpr CppContext::unbox(util::Any& v, bool create, bool update) const
+inline RowExpr CppContext::unbox(util::Any& v, bool create, bool update, bool update_only_diff, size_t current_row) const
 {
     if (auto object = any_cast<Object>(&v))
         return object->row();
@@ -183,35 +187,35 @@ inline RowExpr CppContext::unbox(util::Any& v, bool create, bool update) const
         return RowExpr();
 
     REALM_ASSERT(object_schema);
-    return Object::create(const_cast<CppContext&>(*this), realm, *object_schema, v, update).row();
+    return Object::create(const_cast<CppContext&>(*this), realm, *object_schema, v, update, update_only_diff, current_row).row();
 }
 
 template<>
-inline util::Optional<bool> CppContext::unbox(util::Any& v, bool, bool) const
+inline util::Optional<bool> CppContext::unbox(util::Any& v, bool, bool, bool, size_t) const
 {
     return v.has_value() ? util::make_optional(unbox<bool>(v)) : util::none;
 }
 
 template<>
-inline util::Optional<int64_t> CppContext::unbox(util::Any& v, bool, bool) const
+inline util::Optional<int64_t> CppContext::unbox(util::Any& v, bool, bool, bool, size_t) const
 {
     return v.has_value() ? util::make_optional(unbox<int64_t>(v)) : util::none;
 }
 
 template<>
-inline util::Optional<double> CppContext::unbox(util::Any& v, bool, bool) const
+inline util::Optional<double> CppContext::unbox(util::Any& v, bool, bool, bool, size_t) const
 {
     return v.has_value() ? util::make_optional(unbox<double>(v)) : util::none;
 }
 
 template<>
-inline util::Optional<float> CppContext::unbox(util::Any& v, bool, bool) const
+inline util::Optional<float> CppContext::unbox(util::Any& v, bool, bool, bool, size_t) const
 {
     return v.has_value() ? util::make_optional(unbox<float>(v)) : util::none;
 }
 
 template<>
-inline Mixed CppContext::unbox(util::Any&, bool, bool) const
+inline Mixed CppContext::unbox(util::Any&, bool, bool, bool, size_t) const
 {
     throw std::logic_error("'Any' type is unsupported");
 }

+ 71 - 8
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/list.hpp

@@ -26,6 +26,7 @@
 #include <realm/link_view_fwd.hpp>
 #include <realm/row.hpp>
 #include <realm/table_ref.hpp>
+#include <realm/util/any.hpp>
 
 #include <functional>
 #include <memory>
@@ -132,7 +133,7 @@ public:
 
     // Replace the values in this list with the values from an enumerable object
     template<typename T, typename Context>
-    void assign(Context&, T&& value, bool update=false);
+    void assign(Context&, T&& value, bool update=false, bool update_only_diff = false);
 
     // The List object has been invalidated (due to the Realm being invalidated,
     // or the containing object being deleted)
@@ -163,6 +164,9 @@ private:
     template<typename Fn>
     auto dispatch(Fn&&) const;
 
+    template<typename T, typename Context>
+    void set_if_different(Context&, size_t row_ndx, T&& value, bool update=false);
+
     size_t to_table_ndx(size_t row) const noexcept;
 
     friend struct std::hash<List>;
@@ -204,19 +208,78 @@ void List::set(Context& ctx, size_t row_ndx, T&& value, bool update)
     dispatch([&](auto t) { this->set(row_ndx, ctx.template unbox<std::decay_t<decltype(*t)>>(value, true, update)); });
 }
 
+namespace _impl {
+template <class T>
+inline size_t help_get_current_row(const T&)
+{
+    return size_t(-1);
+}
+
+template <>
+inline size_t help_get_current_row(const RowExpr& v)
+{
+    return v.get_index();
+}
+
+template <class T>
+inline bool help_compare_values(const T& v1, const T& v2)
+{
+    return v1 != v2;
+}
+template <>
+inline bool help_compare_values(const RowExpr& v1, const RowExpr& v2)
+{
+    return v1.get_table() != v2.get_table() || v1.get_index() != v2.get_index();
+}
+}
+
 template<typename T, typename Context>
-void List::assign(Context& ctx, T&& values, bool update)
+void List::set_if_different(Context& ctx, size_t row_ndx, T&& value, bool update)
+{
+    dispatch([&](auto t) {
+        using U = std::decay_t<decltype(*t)>;
+        auto old_value =  this->get<U>(row_ndx);
+        auto new_value = ctx.template unbox<U>(value, true, update, true, _impl::help_get_current_row(old_value));
+        if (_impl::help_compare_values(old_value, new_value))
+            this->set(row_ndx, new_value);
+    });
+}
+
+
+template<typename T, typename Context>
+void List::assign(Context& ctx, T&& values, bool update, bool update_only_diff)
 {
     if (ctx.is_same_list(*this, values))
         return;
 
-    remove_all();
-    if (ctx.is_null(values))
+    if (ctx.is_null(values)) {
+        remove_all();
         return;
-
-    ctx.enumerate_list(values, [&](auto&& element) {
-        this->add(ctx, element, update);
-    });
+    }
+
+    if (update_only_diff) {
+        size_t sz = size();
+        size_t index = 0;
+        ctx.enumerate_list(values, [&](auto&& element) {
+            if (index < sz) {
+                this->set_if_different(ctx, index, element, update);
+            }
+            else {
+                this->add(ctx, element, update);
+            }
+            index++;
+        });
+        while (index < sz) {
+            remove(index);
+            sz--;
+        }
+    }
+    else {
+        remove_all();
+        ctx.enumerate_list(values, [&](auto&& element) {
+            this->add(ctx, element, update);
+        });
+    }
 }
 } // namespace realm
 

+ 4 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object.cpp

@@ -49,6 +49,10 @@ ReadOnlyPropertyException::ReadOnlyPropertyException(const std::string& object_t
 : std::logic_error(util::format("Cannot modify read-only property '%1.%2'", object_type, property_name))
 , object_type(object_type), property_name(property_name) {}
 
+ModifyPrimaryKeyException::ModifyPrimaryKeyException(const std::string& object_type, const std::string& property_name)
+        : std::logic_error(util::format("Cannot modify primary key after creation: '%1.%2'", object_type, property_name))
+        , object_type(object_type), property_name(property_name) {}
+
 Object::Object(SharedRealm r, ObjectSchema const& s, RowExpr const& o)
 : m_realm(std::move(r)), m_object_schema(&s), m_row(o) { }
 

+ 15 - 4
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object.hpp

@@ -60,7 +60,7 @@ public:
     // the binding's native data types to the core data types. See CppContext
     // for a reference implementation of such a context.
     //
-    // The actual definitions of these tempated functions is in object_accessor.hpp
+    // The actual definitions of these templated functions is in object_accessor.hpp
 
     // property getter/setter
     template<typename ValueType, typename ContextType>
@@ -74,12 +74,14 @@ public:
     template<typename ValueType, typename ContextType>
     static Object create(ContextType& ctx, std::shared_ptr<Realm> const& realm,
                          const ObjectSchema &object_schema, ValueType value,
-                         bool try_update = false, Row* = nullptr);
+                         bool try_update = false, bool update_only_diff = false,
+                         size_t current_row = size_t(-1), Row* = nullptr);
 
     template<typename ValueType, typename ContextType>
     static Object create(ContextType& ctx, std::shared_ptr<Realm> const& realm,
                          StringData object_type, ValueType value,
-                         bool try_update = false, Row* = nullptr);
+                         bool try_update = false, bool update_only_diff = false,
+                         size_t current_row = size_t(-1), Row* = nullptr);
 
     template<typename ValueType, typename ContextType>
     static Object get_for_primary_key(ContextType& ctx,
@@ -94,6 +96,8 @@ public:
                                       ValueType primary_value);
 
 private:
+    friend class Results;
+
     std::shared_ptr<Realm> m_realm;
     const ObjectSchema *m_object_schema;
     Row m_row;
@@ -102,7 +106,7 @@ private:
 
     template<typename ValueType, typename ContextType>
     void set_property_value_impl(ContextType& ctx, const Property &property,
-                                 ValueType value, bool try_update, bool is_default=false);
+                                 ValueType value, bool try_update, bool update_only_diff, bool is_default);
     template<typename ValueType, typename ContextType>
     ValueType get_property_value_impl(ContextType& ctx, const Property &property);
 
@@ -141,6 +145,13 @@ struct ReadOnlyPropertyException : public std::logic_error {
     const std::string object_type;
     const std::string property_name;
 };
+
+struct ModifyPrimaryKeyException : public std::logic_error {
+    ModifyPrimaryKeyException(const std::string& object_type, const std::string& property_name);
+    const std::string object_type;
+    const std::string property_name;
+};
+
 } // namespace realm
 
 #endif // REALM_OS_OBJECT_HPP

+ 54 - 33
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object_accessor.hpp

@@ -51,9 +51,9 @@ void Object::set_property_value(ContextType& ctx, StringData prop_name, ValueTyp
     // add a new primary key to a type (or change the property type), but it
     // is otherwise considered the immutable identity of the row
     if (property.is_primary && !m_realm->is_in_migration())
-        throw std::logic_error("Cannot modify primary key after creation");
+        throw ModifyPrimaryKeyException(m_object_schema->name, property.name);
 
-    set_property_value_impl(ctx, property, value, try_update);
+    set_property_value_impl(ctx, property, value, try_update, false, false);
 }
 
 template <typename ValueType, typename ContextType>
@@ -62,9 +62,20 @@ ValueType Object::get_property_value(ContextType& ctx, StringData prop_name)
     return get_property_value_impl<ValueType>(ctx, property_for_name(prop_name));
 }
 
+namespace {
+template <class T, typename ValueType, typename ContextType>
+inline void do_update_value(ContextType& ctx, Table& table, ValueType& value, size_t col, size_t row, bool update_only_diff, bool is_default)
+{
+    auto new_val = ctx.template unbox<T>(value);
+    if (!update_only_diff || table.get<T>(col, row) != new_val) {
+        table.set(col, row, new_val, is_default);
+    }
+}
+}
+
 template <typename ValueType, typename ContextType>
 void Object::set_property_value_impl(ContextType& ctx, const Property &property,
-                                     ValueType value, bool try_update, bool is_default)
+                                     ValueType value, bool try_update, bool update_only_diff, bool is_default)
 {
     ctx.will_change(*this, property);
 
@@ -72,12 +83,14 @@ void Object::set_property_value_impl(ContextType& ctx, const Property &property,
     size_t col = property.table_column;
     size_t row = m_row.get_index();
     if (is_nullable(property.type) && ctx.is_null(value)) {
-        if (property.type == PropertyType::Object) {
-            if (!is_default)
-                table.nullify_link(col, row);
-        }
-        else {
-            table.set_null(col, row, is_default);
+        if (!update_only_diff || !table.is_null(col, row)) {
+            if (property.type == PropertyType::Object) {
+                if (!is_default)
+                    table.nullify_link(col, row);
+            }
+            else {
+                table.set_null(col, row, is_default);
+            }
         }
 
         ctx.did_change();
@@ -89,8 +102,8 @@ void Object::set_property_value_impl(ContextType& ctx, const Property &property,
             throw ReadOnlyPropertyException(m_object_schema->name, property.name);
 
         ContextType child_ctx(ctx, property);
-        List list(m_realm, *m_row.get_table(), col, m_row.get_index());
-        list.assign(child_ctx, value, try_update);
+        List list(m_realm, table, col, m_row.get_index());
+        list.assign(child_ctx, value, try_update, update_only_diff);
         ctx.did_change();
         return;
     }
@@ -98,33 +111,36 @@ void Object::set_property_value_impl(ContextType& ctx, const Property &property,
     switch (property.type & ~PropertyType::Nullable) {
         case PropertyType::Object: {
             ContextType child_ctx(ctx, property);
-            auto link = child_ctx.template unbox<RowExpr>(value, true, try_update);
-            table.set_link(col, row, link.get_index(), is_default);
+            auto curr_link = table.get_link(col,row);
+            auto link = child_ctx.template unbox<RowExpr>(value, true, try_update, update_only_diff, curr_link);
+            if (!update_only_diff || curr_link != link.get_index()) {
+                table.set_link(col, row, link.get_index(), is_default);
+            }
             break;
         }
         case PropertyType::Bool:
-            table.set(col, row, ctx.template unbox<bool>(value), is_default);
+            do_update_value<bool>(ctx, table, value, col, row, update_only_diff, is_default);
             break;
         case PropertyType::Int:
-            table.set(col, row, ctx.template unbox<int64_t>(value), is_default);
+            do_update_value<int64_t>(ctx, table, value, col, row, update_only_diff, is_default);
             break;
         case PropertyType::Float:
-            table.set(col, row, ctx.template unbox<float>(value), is_default);
+            do_update_value<float>(ctx, table, value, col, row, update_only_diff, is_default);
             break;
         case PropertyType::Double:
-            table.set(col, row, ctx.template unbox<double>(value), is_default);
+            do_update_value<double>(ctx, table, value, col, row, update_only_diff, is_default);
             break;
         case PropertyType::String:
-            table.set(col, row, ctx.template unbox<StringData>(value), is_default);
+            do_update_value<StringData>(ctx, table, value, col, row, update_only_diff, is_default);
             break;
         case PropertyType::Data:
-            table.set(col, row, ctx.template unbox<BinaryData>(value), is_default);
+            do_update_value<BinaryData>(ctx, table, value, col, row, update_only_diff, is_default);
             break;
-        case PropertyType::Any:
-            throw std::logic_error("not supported");
         case PropertyType::Date:
-            table.set(col, row, ctx.template unbox<Timestamp>(value), is_default);
+            do_update_value<Timestamp>(ctx, table, value, col, row, update_only_diff, is_default);
             break;
+        case PropertyType::Any:
+            throw std::logic_error("not supported");
         default:
             REALM_COMPILER_HINT_UNREACHABLE();
     }
@@ -170,17 +186,17 @@ ValueType Object::get_property_value_impl(ContextType& ctx, const Property &prop
 template<typename ValueType, typename ContextType>
 Object Object::create(ContextType& ctx, std::shared_ptr<Realm> const& realm,
                       StringData object_type, ValueType value,
-                      bool try_update, Row* out_row)
+                      bool try_update, bool update_only_diff, size_t current_row, Row* out_row)
 {
     auto object_schema = realm->schema().find(object_type);
     REALM_ASSERT(object_schema != realm->schema().end());
-    return create(ctx, realm, *object_schema, value, try_update, out_row);
+    return create(ctx, realm, *object_schema, value, try_update, update_only_diff, current_row, out_row);
 }
 
 template<typename ValueType, typename ContextType>
 Object Object::create(ContextType& ctx, std::shared_ptr<Realm> const& realm,
                       ObjectSchema const& object_schema, ValueType value,
-                      bool try_update, Row* out_row)
+                      bool try_update, bool update_only_diff, size_t current_row, Row* out_row)
 {
     realm->verify_in_write();
 
@@ -194,10 +210,10 @@ Object Object::create(ContextType& ctx, std::shared_ptr<Realm> const& realm,
     bool skip_primary = true;
     if (auto primary_prop = object_schema.primary_key_property()) {
         // search for existing object based on primary key type
-        auto primary_value = ctx.value_for_property(value, primary_prop->name,
+        auto primary_value = ctx.value_for_property(value, *primary_prop,
                                                     primary_prop - &object_schema.persisted_properties[0]);
         if (!primary_value)
-            primary_value = ctx.default_value_for_property(object_schema, primary_prop->name);
+            primary_value = ctx.default_value_for_property(object_schema, *primary_prop);
         if (!primary_value) {
             if (!is_nullable(primary_prop->type))
                 throw MissingPropertyValueException(object_schema.name, primary_prop->name);
@@ -247,12 +263,17 @@ Object Object::create(ContextType& ctx, std::shared_ptr<Realm> const& realm,
         }
     }
     else {
+        if (update_only_diff && current_row != realm::not_found) {
+            row_index = current_row;
+        }
+        else {
 #if REALM_ENABLE_SYNC
-        row_index = sync::create_object(realm->read_group(), *table);
+            row_index = sync::create_object(realm->read_group(), *table);
 #else
-        row_index = table->add_empty_row();
+            row_index = table->add_empty_row();
 #endif // REALM_ENABLE_SYNC
-        created = true;
+            created = true;
+        }
     }
 
     // populate
@@ -264,13 +285,13 @@ Object Object::create(ContextType& ctx, std::shared_ptr<Realm> const& realm,
         if (skip_primary && prop.is_primary)
             continue;
 
-        auto v = ctx.value_for_property(value, prop.name, i);
+        auto v = ctx.value_for_property(value, prop, i);
         if (!created && !v)
             continue;
 
         bool is_default = false;
         if (!v) {
-            v = ctx.default_value_for_property(object_schema, prop.name);
+            v = ctx.default_value_for_property(object_schema, prop);
             is_default = true;
         }
         if ((!v || ctx.is_null(*v)) && !is_nullable(prop.type) && !is_array(prop.type)) {
@@ -278,7 +299,7 @@ Object Object::create(ContextType& ctx, std::shared_ptr<Realm> const& realm,
                 throw MissingPropertyValueException(object_schema.name, prop.name);
         }
         if (v)
-            object.set_property_value_impl(ctx, prop, *v, try_update, is_default);
+            object.set_property_value_impl(ctx, prop, *v, try_update, update_only_diff, is_default);
     }
 #if REALM_ENABLE_SYNC
     if (realm->is_partial() && object_schema.name == "__User") {

+ 4 - 11
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/object_store.cpp

@@ -57,7 +57,7 @@ const size_t c_zeroRowIndex = 0;
 
 const char c_object_table_prefix[] = "class_";
 
-void create_metadata_tables(Group& group, bool partial_realm) {
+void create_metadata_tables(Group& group) {
     // The tables 'pk' and 'metadata' are treated specially by Sync. The 'pk' table
     // is populated by `sync::create_table` and friends, while the 'metadata' table
     // is simply ignored.
@@ -76,14 +76,6 @@ void create_metadata_tables(Group& group, bool partial_realm) {
         pk_table->insert_column(c_primaryKeyPropertyNameColumnIndex, type_String, c_primaryKeyPropertyNameColumnName);
     }
     pk_table->add_search_index(c_primaryKeyObjectClassColumnIndex);
-
-#if REALM_ENABLE_SYNC
-    // Only add __ResultSets if Realm is a partial Realm
-    if (partial_realm)
-        _impl::initialize_schema(group);
-#else
-    (void)partial_realm;
-#endif
 }
 
 void set_schema_version(Group& group, uint64_t version) {
@@ -258,7 +250,7 @@ void validate_primary_column_uniqueness(Group const& group)
 } // anonymous namespace
 
 void ObjectStore::set_schema_version(Group& group, uint64_t version) {
-    ::create_metadata_tables(group, false);
+    ::create_metadata_tables(group);
     ::set_schema_version(group, version);
 }
 
@@ -719,6 +711,7 @@ static void create_default_permissions(Group& group, std::vector<SchemaChange> c
     static_cast<void>(changes);
     static_cast<void>(sync_user_id);
 #else
+    _impl::initialize_schema(group);
     sync::set_up_basic_permissions(group, true);
 
     // Ensure that this user exists so that local privileges checks work immediately
@@ -786,7 +779,7 @@ void ObjectStore::apply_schema_changes(Group& group, uint64_t schema_version,
                                        util::Optional<std::string> sync_user_id,
                                        std::function<void()> migration_function)
 {
-    create_metadata_tables(group, sync_user_id != util::none);
+    create_metadata_tables(group);
 
     if (mode == SchemaMode::Additive) {
         bool target_schema_is_newer = (schema_version < target_schema_version

+ 8 - 2
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/results.cpp

@@ -766,7 +766,6 @@ void Results::Internal::set_table_view(Results& results, TableView &&tv)
     REALM_ASSERT(results.m_table_view.is_in_sync());
     REALM_ASSERT(results.m_table_view.is_attached());
 }
-
 #define REALM_RESULTS_TYPE(T) \
     template T Results::get<T>(size_t); \
     template util::Optional<T> Results::first<T>(); \
@@ -813,8 +812,15 @@ Results::UnsupportedColumnTypeException::UnsupportedColumnTypeException(size_t c
 {
 }
 
+Results::InvalidPropertyException::InvalidPropertyException(const std::string& object_type, const std::string& property_name)
+: std::logic_error(util::format("Property '%1.%2' does not exist", object_type, property_name))
+, object_type(object_type), property_name(property_name)
+{
+}
+
 Results::UnimplementedOperationException::UnimplementedOperationException(const char* msg)
 : std::logic_error(msg)
-{ }
+{
+}
 
 } // namespace realm

+ 40 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/results.hpp

@@ -21,7 +21,11 @@
 
 #include "collection_notifications.hpp"
 #include "impl/collection_notifier.hpp"
+#include "list.hpp"
+#include "object.hpp"
+#include "object_schema.hpp"
 #include "property.hpp"
+#include "shared_realm.hpp"
 
 #include <realm/table_view.hpp>
 #include <realm/util/optional.hpp>
@@ -189,6 +193,13 @@ public:
         UnsupportedColumnTypeException(size_t column, const Table* table, const char* operation);
     };
 
+    // The property request does not exist in the schema
+    struct InvalidPropertyException : public std::logic_error {
+        InvalidPropertyException(const std::string& object_type, const std::string& property_name);
+        const std::string object_type;
+        const std::string property_name;
+	};
+
     // The requested operation is valid, but has not yet been implemented
     struct UnimplementedOperationException : public std::logic_error {
         UnimplementedOperationException(const char *message);
@@ -219,6 +230,12 @@ public:
     template<typename Context, typename T>
     size_t index_of(Context&, T value);
 
+    // Batch updates all items in this collection with the provided value
+    // Must be called inside a transaction
+    // Throws an exception if the value does not match the type for given prop_name
+    template<typename ValueType, typename ContextType>
+    void set_property_value(ContextType& ctx, StringData prop_name, ValueType value);
+
     // Execute the query immediately if needed. When the relevant query is slow, size()
     // may cost similar time compared with creating the tableview. Use this function to
     // avoid running the query twice for size() and other accessors.
@@ -313,6 +330,29 @@ size_t Results::index_of(Context& ctx, T value)
 {
     return dispatch([&](auto t) { return this->index_of(ctx.template unbox<std::decay_t<decltype(*t)>>(value)); });
 }
+
+template <typename ValueType, typename ContextType>
+void Results::set_property_value(ContextType& ctx, StringData prop_name, ValueType value)
+{
+    // Check invariants for calling this method
+    validate_write();
+    const ObjectSchema& object_schema = get_object_schema();
+    const Property* prop = object_schema.property_for_name(prop_name);
+    if (!prop) {
+        throw InvalidPropertyException(object_schema.name, prop_name);
+    }
+    if (prop->is_primary && !m_realm->is_in_migration()) {
+        throw ModifyPrimaryKeyException(m_object_schema->name, prop->name);
+    }
+
+    // Update all objects in this ResultSets
+    size_t size = this->size();
+    for (size_t i = 0; i < size; ++i) {
+        Object obj(m_realm, *m_object_schema, get(i));
+        obj.set_property_value_impl(ctx, *prop, value, true, false, false);
+    }
+}
+
 } // namespace realm
 
 #endif // REALM_RESULTS_HPP

+ 10 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/shared_realm.cpp

@@ -41,10 +41,12 @@
 
 #include <realm/sync/history.hpp>
 #include <realm/sync/permissions.hpp>
+#include <realm/sync/version.hpp>
 #else
 namespace realm {
 namespace sync {
     struct PermissionsCache {};
+    struct TableInfoCache {};
 }
 }
 #endif
@@ -712,6 +714,7 @@ void Realm::invalidate()
     }
 
     m_permissions_cache = nullptr;
+    m_table_info_cache = nullptr;
     m_shared_group->end_read();
     m_group = nullptr;
 }
@@ -889,6 +892,7 @@ void Realm::close()
     }
 
     m_permissions_cache = nullptr;
+    m_table_info_cache = nullptr;
     m_group = nullptr;
     m_shared_group = nullptr;
     m_history = nullptr;
@@ -1020,7 +1024,13 @@ bool Realm::init_permission_cache()
 
     // Admin users bypass permissions checks outside of the logic in PermissionsCache
     if (m_config.sync_config && m_config.sync_config->is_partial && !m_config.sync_config->user->is_admin()) {
+#if REALM_SYNC_VER_MAJOR == 3 && (REALM_SYNC_VER_MINOR < 13 || (REALM_SYNC_VER_MINOR == 13 && REALM_SYNC_VER_PATCH < 3))
         m_permissions_cache = std::make_unique<sync::PermissionsCache>(read_group(), m_config.sync_config->user->identity());
+#else
+        m_table_info_cache = std::make_unique<sync::TableInfoCache>(read_group());
+        m_permissions_cache = std::make_unique<sync::PermissionsCache>(read_group(), *m_table_info_cache,
+                                                                       m_config.sync_config->user->identity());
+#endif
         return true;
     }
     return false;

+ 2 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/src/shared_realm.hpp

@@ -59,6 +59,7 @@ namespace _impl {
 }
 namespace sync {
     struct PermissionsCache;
+    struct TableInfoCache;
 }
 
 // How to handle update_schema() being called on a file which has
@@ -388,6 +389,7 @@ private:
     bool m_dynamic_schema = true;
 
     std::shared_ptr<_impl::RealmCoordinator> m_coordinator;
+    std::unique_ptr<sync::TableInfoCache> m_table_info_cache;
     std::unique_ptr<sync::PermissionsCache> m_permissions_cache;
 
     // File format versions populated when a file format upgrade takes place during realm opening

+ 217 - 5
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/object.cpp

@@ -53,12 +53,12 @@ struct TestContext : CppContext {
     { }
 
     util::Optional<util::Any>
-    default_value_for_property(ObjectSchema const& object, std::string const& prop)
+    default_value_for_property(ObjectSchema const& object, Property const& prop)
     {
         auto obj_it = defaults.find(object.name);
         if (obj_it == defaults.end())
             return util::none;
-        auto prop_it = obj_it->second.find(prop);
+        auto prop_it = obj_it->second.find(prop.name);
         if (prop_it == obj_it->second.end())
             return util::none;
         return prop_it->second;
@@ -141,6 +141,13 @@ TEST_CASE("object") {
         {"nullable string pk", {
             {"pk", PropertyType::String|PropertyType::Nullable, Property::IsPrimary{true}},
         }},
+        {"person", {
+            {"name", PropertyType::String, Property::IsPrimary{true}},
+            {"age", PropertyType::Int},
+            {"scores", PropertyType::Array|PropertyType::Int},
+            {"assistant", PropertyType::Object|PropertyType::Nullable, "person"},
+            {"team", PropertyType::Array|PropertyType::Object, "person"},
+        }},
     };
     config.schema_version = 0;
     auto r = Realm::get_shared_realm(config);
@@ -290,9 +297,21 @@ TEST_CASE("object") {
     }
 
     TestContext d(r);
-    auto create = [&](util::Any&& value, bool update) {
+    auto create = [&](util::Any&& value, bool update, bool update_only_diff = false) {
+        r->begin_transaction();
+        auto obj = Object::create(d, r, *r->schema().find("all types"), value, update, update_only_diff);
+        r->commit_transaction();
+        return obj;
+    };
+    auto create_sub = [&](util::Any&& value, bool update, bool update_only_diff = false) {
         r->begin_transaction();
-        auto obj = Object::create(d, r, *r->schema().find("all types"), value, update);
+        auto obj = Object::create(d, r, *r->schema().find("link target"), value, update, update_only_diff);
+        r->commit_transaction();
+        return obj;
+    };
+    auto create_company = [&](util::Any&& value, bool update, bool update_only_diff = false) {
+        r->begin_transaction();
+        auto obj = Object::create(d, r, *r->schema().find("person"), value, update, update_only_diff);
         r->commit_transaction();
         return obj;
     };
@@ -466,7 +485,9 @@ TEST_CASE("object") {
     }
 
     SECTION("create with update") {
-        auto obj = create(AnyDict{
+        CollectionChangeSet change;
+        bool callback_called;
+        Object obj = create(AnyDict{
             {"pk", INT64_C(1)},
             {"bool", true},
             {"int", INT64_C(5)},
@@ -486,12 +507,24 @@ TEST_CASE("object") {
             {"date array", AnyVec{}},
             {"object array", AnyVec{AnyDict{{"value", INT64_C(20)}}}},
         }, false);
+
+        auto token = obj.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr) {
+            change = c;
+            callback_called = true;
+        });
+        advance_and_notify(*r);
+
         create(AnyDict{
             {"pk", INT64_C(1)},
             {"int", INT64_C(6)},
             {"string", "a"s},
         }, true);
 
+        callback_called = false;
+        advance_and_notify(*r);
+        REQUIRE(callback_called);
+        REQUIRE_INDICES(change.modifications, 0);
+
         auto row = obj.row();
         REQUIRE(row.get_int(0) == 1);
         REQUIRE(row.get_bool(1) == true);
@@ -503,6 +536,185 @@ TEST_CASE("object") {
         REQUIRE(row.get_timestamp(7) == Timestamp(10, 20));
     }
 
+    SECTION("create with update - only with diffs") {
+        CollectionChangeSet change;
+        bool callback_called;
+        AnyDict adam {
+            {"name", "Adam"s},
+            {"age", INT64_C(32)},
+            {"scores", AnyVec{INT64_C(1), INT64_C(2)}},
+        };
+        AnyDict brian {
+            {"name", "Brian"s},
+            {"age", INT64_C(33)},
+        };
+        AnyDict charley {
+            {"name", "Charley"s},
+            {"age", INT64_C(34)},
+            {"team", AnyVec{adam, brian}}
+        };
+        AnyDict donald {
+            {"name", "Donald"s},
+            {"age", INT64_C(35)},
+        };
+        AnyDict eddie {
+            {"name", "Eddie"s},
+            {"age", INT64_C(36)},
+            {"assistant", donald},
+            {"team", AnyVec{donald, charley}}
+        };
+        Object obj = create_company(eddie, true);
+
+        auto table = r->read_group().get_table("class_person");
+        REQUIRE(table->size() == 5);
+        Results result(r, *table);
+        auto token = result.add_notification_callback([&](CollectionChangeSet c, std::exception_ptr) {
+            change = c;
+            callback_called = true;
+        });
+        advance_and_notify(*r);
+
+        // First update unconditionally
+        create_company(eddie, true, false);
+
+        callback_called = false;
+        advance_and_notify(*r);
+        REQUIRE(callback_called);
+        REQUIRE_INDICES(change.modifications, 0, 1, 2, 3, 4);
+
+        // Now, only update where differences (there should not be any diffs - so no update)
+        create_company(eddie, true, true);
+
+        REQUIRE(table->size() == 5);
+        callback_called = false;
+        advance_and_notify(*r);
+        REQUIRE(!callback_called);
+
+        // Now, only update sub-object)
+        donald["scores"] = AnyVec{INT64_C(3), INT64_C(4), INT64_C(5)};
+        // Insert the new donald
+        eddie["assistant"] = donald;
+        create_company(eddie, true, true);
+
+        REQUIRE(table->size() == 5);
+        callback_called = false;
+        advance_and_notify(*r);
+        REQUIRE(callback_called);
+        REQUIRE_INDICES(change.modifications, 1);
+
+        // Shorten list
+        donald["scores"] = AnyVec{INT64_C(3), INT64_C(4)};
+        eddie["assistant"] = donald;
+        create_company(eddie, true, true);
+
+        REQUIRE(table->size() == 5);
+        callback_called = false;
+        advance_and_notify(*r);
+        REQUIRE(callback_called);
+        REQUIRE_INDICES(change.modifications, 1);
+    }
+
+    SECTION("create with update - identical sub-object") {
+        bool callback_called;
+        bool sub_callback_called;
+        Object sub_obj = create_sub(AnyDict{{"value", INT64_C(10)}}, false);
+        Object obj = create(AnyDict{
+            {"pk", INT64_C(1)},
+            {"bool", true},
+            {"int", INT64_C(5)},
+            {"float", 2.2f},
+            {"double", 3.3},
+            {"string", "hello"s},
+            {"data", "olleh"s},
+            {"date", Timestamp(10, 20)},
+            {"object", sub_obj},
+        }, false);
+
+        auto token1 = obj.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) {
+            callback_called = true;
+        });
+        auto token2 = sub_obj.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) {
+            sub_callback_called = true;
+        });
+        advance_and_notify(*r);
+
+        auto table = r->read_group().get_table("class_link target");
+        REQUIRE(table->size() == 1);
+
+        create(AnyDict{
+            {"pk", INT64_C(1)},
+            {"bool", true},
+            {"int", INT64_C(5)},
+            {"float", 2.2f},
+            {"double", 3.3},
+            {"string", "hello"s},
+            {"data", "olleh"s},
+            {"date", Timestamp(10, 20)},
+            {"object", AnyDict{{"value", INT64_C(10)}}},
+        }, true, true);
+
+        REQUIRE(table->size() == 1);
+        callback_called = false;
+        sub_callback_called = false;
+        advance_and_notify(*r);
+        REQUIRE(!callback_called);
+        REQUIRE(!sub_callback_called);
+
+        // Now change sub object
+        create(AnyDict{
+            {"pk", INT64_C(1)},
+            {"bool", true},
+            {"int", INT64_C(5)},
+            {"float", 2.2f},
+            {"double", 3.3},
+            {"string", "hello"s},
+            {"data", "olleh"s},
+            {"date", Timestamp(10, 20)},
+            {"object", AnyDict{{"value", INT64_C(11)}}},
+        }, true, true);
+
+        callback_called = false;
+        sub_callback_called = false;
+        advance_and_notify(*r);
+        REQUIRE(!callback_called);
+        REQUIRE(sub_callback_called);
+    }
+
+    SECTION("create with update - identical array of sub-objects") {
+        bool callback_called;
+        auto dict = AnyDict{
+            {"pk", INT64_C(1)},
+            {"bool", true},
+            {"int", INT64_C(5)},
+            {"float", 2.2f},
+            {"double", 3.3},
+            {"string", "hello"s},
+            {"data", "olleh"s},
+            {"date", Timestamp(10, 20)},
+            {"object array", AnyVec{ AnyDict{{"value", INT64_C(20)}}, AnyDict{{"value", INT64_C(21)}} } },
+        };
+        Object obj = create(dict, false);
+
+        auto token1 = obj.add_notification_callback([&](CollectionChangeSet, std::exception_ptr) {
+            callback_called = true;
+        });
+        advance_and_notify(*r);
+
+        create(dict, true, true);
+
+        callback_called = false;
+        advance_and_notify(*r);
+        REQUIRE(!callback_called);
+
+        // Now change list
+        dict["object array"] = AnyVec{AnyDict{{"value", INT64_C(23)}}};
+        create(dict, true, true);
+
+        callback_called = false;
+        advance_and_notify(*r);
+        REQUIRE(callback_called);
+    }
+
     SECTION("set existing fields to null with update") {
         AnyDict initial_values{
             {"pk", INT64_C(1)},

+ 176 - 0
Carthage/Checkouts/realm-cocoa/Realm/ObjectStore/tests/results.cpp

@@ -23,6 +23,7 @@
 #include "util/templated_test_case.hpp"
 #include "util/test_file.hpp"
 
+#include "impl/object_accessor_impl.hpp"
 #include "impl/realm_coordinator.hpp"
 #include "binding_context.hpp"
 #include "object_schema.hpp"
@@ -40,7 +41,30 @@
 #include "sync/sync_session.hpp"
 #endif
 
+
 using namespace realm;
+using namespace std::string_literals;
+
+namespace {
+    using AnyDict = std::map<std::string, util::Any>;
+    using AnyVec = std::vector<util::Any>;
+}
+
+struct TestContext : CppContext {
+    std::map<std::string, AnyDict> defaults;
+
+    using CppContext::CppContext;
+    TestContext(TestContext& parent, realm::Property const& prop)
+            : CppContext(parent, prop)
+            , defaults(parent.defaults)
+    { }
+
+    void will_change(Object const&, Property const&) {}
+    void did_change() {}
+    std::string print(util::Any) { return "not implemented"; }
+    bool allow_missing(util::Any) { return false; }
+};
+
 
 TEST_CASE("notifications: async delivery") {
     _impl::RealmCoordinator::assert_no_open_realms();
@@ -2767,6 +2791,158 @@ TEMPLATE_TEST_CASE("results: aggregate", ResultsFromTable, ResultsFromQuery, Res
     }
 }
 
+TEST_CASE("results: set property value on all objects", "[batch_updates]") {
+
+    InMemoryTestFile config;
+    config.automatic_change_notifications = false;
+    config.cache = false;
+    config.schema = Schema{
+        {"AllTypes", {
+            {"pk", PropertyType::Int, Property::IsPrimary{true}},
+            {"bool", PropertyType::Bool},
+            {"int", PropertyType::Int},
+            {"float", PropertyType::Float},
+            {"double", PropertyType::Double},
+            {"string", PropertyType::String},
+            {"data", PropertyType::Data},
+            {"date", PropertyType::Date},
+            {"object", PropertyType::Object|PropertyType::Nullable, "AllTypes"},
+            {"list", PropertyType::Array|PropertyType::Object, "AllTypes"},
+
+            {"bool array", PropertyType::Array|PropertyType::Bool},
+            {"int array", PropertyType::Array|PropertyType::Int},
+            {"float array", PropertyType::Array|PropertyType::Float},
+            {"double array", PropertyType::Array|PropertyType::Double},
+            {"string array", PropertyType::Array|PropertyType::String},
+            {"data array", PropertyType::Array|PropertyType::Data},
+            {"date array", PropertyType::Array|PropertyType::Date},
+            {"object array", PropertyType::Array|PropertyType::Object, "AllTypes"},
+        }, {
+           {"parents", PropertyType::LinkingObjects|PropertyType::Array, "AllTypes", "object"},
+        }}
+    };
+    config.schema_version = 0;
+    auto realm = Realm::get_shared_realm(config);
+    auto table = realm->read_group().get_table("class_AllTypes");
+    realm->begin_transaction();
+    table->add_empty_row(2);
+    realm->commit_transaction();
+    Results r(realm, *table);
+
+    TestContext ctx(realm);
+
+    SECTION("non-existing property name") {
+        realm->begin_transaction();
+        REQUIRE_THROWS_AS(r.set_property_value(ctx, "i dont exist", util::Any(false)), Results::InvalidPropertyException);
+        realm->cancel_transaction();
+    }
+
+    SECTION("readonly property") {
+        realm->begin_transaction();
+        REQUIRE_THROWS_AS(r.set_property_value(ctx, "parents", util::Any(false)), ReadOnlyPropertyException);
+        realm->cancel_transaction();
+    }
+
+    SECTION("primarykey property") {
+        realm->begin_transaction();
+        REQUIRE_THROWS_AS(r.set_property_value(ctx, "pk", util::Any(1)), std::logic_error);
+        realm->cancel_transaction();
+    }
+
+    SECTION("set property value") {
+        realm->begin_transaction();
+
+        r.set_property_value<util::Any>(ctx, "bool", util::Any(true));
+        for (size_t i = 0; i < r.size(); i++) {
+            CHECK(r.get(i).get_bool(1) == true);
+        }
+
+        r.set_property_value(ctx, "int", util::Any(INT64_C(42)));
+        for (size_t i = 0; i < r.size(); i++) {
+            CHECK(r.get(i).get_int(2) == 42);
+        }
+
+        r.set_property_value(ctx, "float", util::Any(1.23f));
+        for (size_t i = 0; i < r.size(); i++) {
+            CHECK(r.get(i).get_float(3) == 1.23f);
+        }
+
+        r.set_property_value(ctx, "double", util::Any(1.234));
+        for (size_t i = 0; i < r.size(); i++) {
+            CHECK(r.get(i).get_double(4) == 1.234);
+        }
+
+        r.set_property_value(ctx, "string", util::Any(std::string("abc")));
+        for (size_t i = 0; i < r.size(); i++) {
+            CHECK(r.get(i).get_string(5) == "abc");
+        }
+
+        r.set_property_value(ctx, "data", util::Any(std::string("abc")));
+        for (size_t i = 0; i < r.size(); i++) {
+            CHECK(r.get(i).get_binary(6) == BinaryData("abc", 3));
+        }
+
+        util::Any timestamp = Timestamp(1, 2);
+        r.set_property_value(ctx, "date", timestamp);
+        for (size_t i = 0; i < r.size(); i++) {
+            CHECK(r.get(i).get_timestamp(7) == any_cast<Timestamp>(timestamp));
+        }
+
+        size_t object_ndx = table->add_empty_row();
+        Object linked_obj(realm, "AllTypes", object_ndx);
+        r.set_property_value(ctx, "object", util::Any(linked_obj));
+        for (size_t i = 0; i < r.size(); i++) {
+            CHECK(r.get(i).get_link(8) == object_ndx);
+        }
+
+        size_t list_object_ndx = table->add_empty_row();
+        Object list_object(realm, "AllTypes", list_object_ndx);
+        r.set_property_value(ctx, "list", util::Any(AnyVector{list_object, list_object}));
+        for (size_t i = 0; i < r.size(); i++) {
+            auto list = r.get(i).get_linklist(9);
+            CHECK(list->size() == 2);
+            CHECK(list->get(0).get_index() == list_object_ndx);
+            CHECK(list->get(1).get_index() == list_object_ndx);
+        }
+
+        auto check_array = [&](size_t col, auto... values) {
+            size_t rows = r.size();
+            for (size_t i = 0; i < rows; ++i) {
+                RowExpr row = r.get(i);
+                auto table = row.get_subtable(col);
+                size_t j = 0;
+                for (auto& value : {values...}) {
+                    CAPTURE(j);
+                    REQUIRE(j < row.get_subtable_size(col));
+                    REQUIRE(value == table->get<typename std::decay<decltype(value)>::type>(0, j));
+                    ++j;
+                }
+            }
+        };
+
+        r.set_property_value(ctx, "bool array", util::Any(AnyVec{true, false}));
+        check_array(10, true, false);
+
+        r.set_property_value(ctx, "int array", util::Any(AnyVec{INT64_C(5), INT64_C(6)}));
+        check_array(11, INT64_C(5), INT64_C(6));
+
+        r.set_property_value(ctx, "float array", util::Any(AnyVec{1.1f, 2.2f}));
+        check_array(12, 1.1f, 2.2f);
+
+        r.set_property_value(ctx, "double array", util::Any(AnyVec{3.3, 4.4}));
+        check_array(13, 3.3, 4.4);
+
+        r.set_property_value(ctx, "string array", util::Any(AnyVec{"a"s, "b"s, "c"s}));
+        check_array(14, StringData("a"), StringData("b"), StringData("c"));
+ 
+        r.set_property_value(ctx, "data array", util::Any(AnyVec{"d"s, "e"s, "f"s}));
+        check_array(15, BinaryData("d",1), BinaryData("e",1), BinaryData("f",1));
+
+        r.set_property_value(ctx, "date array", util::Any(AnyVec{Timestamp(10,20), Timestamp(20,30), Timestamp(30,40)}));
+        check_array(16, Timestamp(10,20), Timestamp(20,30), Timestamp(30,40));
+    }
+}
+
 TEST_CASE("results: limit", "[limit]") {
     InMemoryTestFile config;
     config.cache = false;

+ 3 - 3
Carthage/Checkouts/realm-cocoa/Realm/RLMAccessor.hpp

@@ -64,9 +64,9 @@ public:
     void will_change(realm::Object& obj, realm::Property const& prop) { will_change(obj.row(), prop); }
     void did_change();
 
-    RLMOptionalId value_for_property(id dict, std::string const&, size_t prop_index);
+    RLMOptionalId value_for_property(id dict, realm::Property const&, size_t prop_index);
     RLMOptionalId default_value_for_property(realm::ObjectSchema const&,
-                                             std::string const& prop);
+                                             realm::Property const& prop);
 
     bool is_same_list(realm::List const& list, id v) const noexcept;
 
@@ -78,7 +78,7 @@ public:
     }
 
     template<typename T>
-    T unbox(id v, bool create = false, bool update = false);
+    T unbox(id v, bool create = false, bool update = false, bool = false, size_t = 0);
 
     bool is_null(id v) { return v == NSNull.null; }
     id null_value() { return NSNull.null; }

+ 15 - 15
Carthage/Checkouts/realm-cocoa/Realm/RLMAccessor.mm

@@ -668,34 +668,34 @@ id RLMAccessorContext::box(realm::Results&& r) {
 }
 
 template<>
-realm::Timestamp RLMAccessorContext::unbox(__unsafe_unretained id const value, bool, bool) {
+realm::Timestamp RLMAccessorContext::unbox(__unsafe_unretained id const value, bool, bool, bool, size_t) {
     id v = RLMCoerceToNil(value);
     return RLMTimestampForNSDate(v);
 }
 
 template<>
-bool RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool) {
+bool RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool, bool, size_t) {
     return [v boolValue];
 }
 template<>
-double RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool) {
+double RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool, bool, size_t) {
     return [v doubleValue];
 }
 template<>
-float RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool) {
+float RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool, bool, size_t) {
     return [v floatValue];
 }
 template<>
-long long RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool) {
+long long RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool, bool, size_t) {
     return [v longLongValue];
 }
 template<>
-realm::BinaryData RLMAccessorContext::unbox(id v, bool, bool) {
+realm::BinaryData RLMAccessorContext::unbox(id v, bool, bool, bool, size_t) {
     v = RLMCoerceToNil(v);
     return RLMBinaryDataForNSData(v);
 }
 template<>
-realm::StringData RLMAccessorContext::unbox(id v, bool, bool) {
+realm::StringData RLMAccessorContext::unbox(id v, bool, bool, bool, size_t) {
     v = RLMCoerceToNil(v);
     return RLMStringDataWithNSString(v);
 }
@@ -707,24 +707,24 @@ static auto to_optional(__unsafe_unretained id const value, Fn&& fn) {
 }
 
 template<>
-realm::util::Optional<bool> RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool) {
+realm::util::Optional<bool> RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool, bool, size_t) {
     return to_optional(v, [&](__unsafe_unretained id v) { return (bool)[v boolValue]; });
 }
 template<>
-realm::util::Optional<double> RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool) {
+realm::util::Optional<double> RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool, bool, size_t) {
     return to_optional(v, [&](__unsafe_unretained id v) { return [v doubleValue]; });
 }
 template<>
-realm::util::Optional<float> RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool) {
+realm::util::Optional<float> RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool, bool, size_t) {
     return to_optional(v, [&](__unsafe_unretained id v) { return [v floatValue]; });
 }
 template<>
-realm::util::Optional<int64_t> RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool) {
+realm::util::Optional<int64_t> RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool, bool, size_t) {
     return to_optional(v, [&](__unsafe_unretained id v) { return [v longLongValue]; });
 }
 
 template<>
-realm::RowExpr RLMAccessorContext::unbox(__unsafe_unretained id const v, bool create, bool update) {
+realm::RowExpr RLMAccessorContext::unbox(__unsafe_unretained id const v, bool create, bool update, bool, size_t) {
     RLMObjectBase *link = RLMDynamicCast<RLMObjectBase>(v);
     if (!link) {
         if (!create)
@@ -779,7 +779,7 @@ void RLMAccessorContext::did_change() {
 }
 
 RLMOptionalId RLMAccessorContext::value_for_property(__unsafe_unretained id const obj,
-                                                     std::string const&, size_t propIndex) {
+                                                     realm::Property const&, size_t propIndex) {
     auto prop = _info.rlmObjectSchema.properties[propIndex];
     id value = propertyValue(obj, propIndex, prop);
     if (value) {
@@ -801,9 +801,9 @@ RLMOptionalId RLMAccessorContext::value_for_property(__unsafe_unretained id cons
 }
 
 RLMOptionalId RLMAccessorContext::default_value_for_property(realm::ObjectSchema const&,
-                                                             std::string const& prop)
+                                                             realm::Property const& prop)
 {
-    return RLMOptionalId{defaultValue(@(prop.c_str()))};
+    return RLMOptionalId{defaultValue(@(prop.name.c_str()))};
 }
 
 bool RLMAccessorContext::is_same_list(realm::List const& list, __unsafe_unretained id const v) const noexcept {

+ 1 - 1
Carthage/Checkouts/realm-cocoa/Realm/RLMObjectStore.mm

@@ -140,7 +140,7 @@ void RLMAddObjectToRealm(__unsafe_unretained RLMObjectBase *const object,
     object->_objectSchema = info.rlmObjectSchema;
     try {
         realm::Object::create(c, realm->_realm, *info.objectSchema, (id)object,
-                              createOrUpdate, &object->_row);
+                              createOrUpdate, /* diff */ false, -1, &object->_row);
     }
     catch (std::exception const& e) {
         @throw RLMException(e);

+ 42 - 15
Carthage/Checkouts/realm-cocoa/Realm/RLMRealm+Sync.mm

@@ -19,6 +19,7 @@
 #import "RLMRealm+Sync.h"
 
 #import "RLMObjectBase.h"
+#import "RLMQueryUtil.hpp"
 #import "RLMObjectSchema.h"
 #import "RLMRealm_Private.hpp"
 #import "RLMResults_Private.hpp"
@@ -26,30 +27,56 @@
 #import "RLMSyncSession.h"
 
 #import "results.hpp"
-#import "sync/partial_sync.hpp"
 #import "shared_realm.hpp"
+#import "sync/partial_sync.hpp"
+#import "sync/subscription_state.hpp"
 
 using namespace realm;
 
 @implementation RLMRealm (Sync)
 
 - (void)subscribeToObjects:(Class)type where:(NSString *)query callback:(RLMPartialSyncFetchCallback)callback {
-    NSString *className = [type className];
-    auto cb = [=](Results results, std::exception_ptr err) {
-        if (err) {
-            try {
-                rethrow_exception(err);
-            }
-            catch (...) {
-                NSError *error = nil;
-                RLMRealmTranslateException(&error);
-                callback(nil, error);
-            }
+    [self verifyThread];
+
+    RLMClassInfo& info = _info[[type className]];
+    Query q = RLMPredicateToQuery([NSPredicate predicateWithFormat:query],
+                                  info.rlmObjectSchema, self.schema, self.group);
+    struct Holder {
+        partial_sync::Subscription subscription;
+        partial_sync::SubscriptionNotificationToken token;
+
+        Holder(partial_sync::Subscription&& s) : subscription(std::move(s)) { }
+    };
+    auto state = std::make_shared<Holder>(partial_sync::subscribe(Results(_realm, std::move(q)), util::none));
+    state->token = state->subscription.add_notification_callback([=]() mutable {
+        if (!callback) {
             return;
         }
-        callback([RLMResults resultsWithObjectInfo:_info[className] results:std::move(results)], nil);
-    };
-    partial_sync::register_query(_realm, className.UTF8String, query.UTF8String, std::move(cb));
+        switch (state->subscription.state()) {
+            case partial_sync::SubscriptionState::Invalidated:
+            case partial_sync::SubscriptionState::Pending:
+            case partial_sync::SubscriptionState::Creating:
+                return;
+
+            case partial_sync::SubscriptionState::Error:
+                try {
+                    rethrow_exception(state->subscription.error());
+                }
+                catch (...) {
+                    NSError *error = nil;
+                    RLMRealmTranslateException(&error);
+                    callback(nil, error);
+                }
+                break;
+
+            case partial_sync::SubscriptionState::Complete:
+                callback([RLMResults emptyDetachedResults], nil);
+                break;
+        }
+
+        callback = nil;
+        state->token = {};
+    });
 }
 
 - (RLMSyncSession *)syncSession {

+ 4 - 4
Carthage/Checkouts/realm-cocoa/Realm/RLMRealm.mm

@@ -460,6 +460,10 @@ REALM_NOINLINE static void translateSharedGroupOpenException(RLMRealmConfigurati
         if (!readOnly) {
             // initializing the schema started a read transaction, so end it
             [realm invalidate];
+
+            RLMAddSkipBackupAttributeToItemAtPath(config.path + ".management");
+            RLMAddSkipBackupAttributeToItemAtPath(config.path + ".lock");
+            RLMAddSkipBackupAttributeToItemAtPath(config.path + ".note");
         }
     }
 
@@ -470,10 +474,6 @@ REALM_NOINLINE static void translateSharedGroupOpenException(RLMRealmConfigurati
     if (!readOnly) {
         realm->_realm->m_binding_context = RLMCreateBindingContext(realm);
         realm->_realm->m_binding_context->realm = realm->_realm;
-
-        RLMAddSkipBackupAttributeToItemAtPath(config.path + ".management");
-        RLMAddSkipBackupAttributeToItemAtPath(config.path + ".lock");
-        RLMAddSkipBackupAttributeToItemAtPath(config.path + ".note");
     }
 
     return RLMAutorelease(realm);

+ 1 - 1
Carthage/Checkouts/realm-cocoa/Realm/RLMSyncManager.mm

@@ -110,7 +110,7 @@ static RLMSyncManager *s_sharedManager = nil;
         bool should_encrypt = !getenv("REALM_DISABLE_METADATA_ENCRYPTION") && !RLMIsRunningInPlayground();
         auto mode = should_encrypt ? SyncManager::MetadataMode::Encryption : SyncManager::MetadataMode::NoEncryption;
         rootDirectory = rootDirectory ?: [NSURL fileURLWithPath:RLMDefaultDirectoryForBundleIdentifier(nil)];
-        SyncManager::shared().configure_file_system(rootDirectory.path.UTF8String, mode, none, true);
+        SyncManager::shared().configure(rootDirectory.path.UTF8String, mode, "", none, true);
         return self;
     }
     return nil;

+ 1 - 2
Carthage/Checkouts/realm-cocoa/Realm/RLMSyncSession.mm

@@ -221,8 +221,7 @@ static RLMSyncConnectionState convertConnectionState(SyncSession::ConnectionStat
     if (!config) {
         return nil;
     }
-    auto path = SyncManager::shared().path_for_realm(*config->user, config->realm_url());
-    if (auto session = config->user->session_for_on_disk_path(path)) {
+    if (auto session = config->user->session_for_on_disk_path(realm->_realm->config().path)) {
         return [[RLMSyncSession alloc] initWithSyncSession:session];
     }
     return nil;

+ 0 - 6
Carthage/Checkouts/realm-cocoa/Realm/RLMSyncSubscription.mm

@@ -81,12 +81,6 @@ using namespace realm;
 - (void)unsubscribe {
     partial_sync::unsubscribe(*_subscription);
 }
-
-- (RLMResults *)results {
-    auto results = _subscription->results();
-    return [RLMResults resultsWithObjectInfo:_realm->_info[RLMStringDataToNSString(results.get_object_type())]
-                                     results:std::move(results)];
-}
 @end
 
 @implementation RLMResults (SyncSubscription)

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

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

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

@@ -1,4 +1,4 @@
-VERSION=3.11.1
-REALM_CORE_VERSION=5.10.2
-REALM_SYNC_VERSION=3.12.2
+VERSION=3.11.2
+REALM_CORE_VERSION=5.12.1
+REALM_SYNC_VERSION=3.13.3
 REALM_OBJECT_SERVER_VERSION=3.11.5

+ 4 - 0
Nextcloud.xcodeproj/project.pbxproj

@@ -405,6 +405,7 @@
 		F78ACD56219047E90088454D /* NCSectionHeader.xib in Resources */ = {isa = PBXBuildFile; fileRef = F78ACD55219047E90088454D /* NCSectionHeader.xib */; };
 		F78ACD58219048040088454D /* NCSectionHeaderMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = F78ACD57219048040088454D /* NCSectionHeaderMenu.xib */; };
 		F78BFEE11D31126B00E513CF /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F78BFEDE1D31126B00E513CF /* MainInterface.storyboard */; };
+		F78E7065219F096B006F23E4 /* NCAvatar.swift in Sources */ = {isa = PBXBuildFile; fileRef = F78E7064219F096B006F23E4 /* NCAvatar.swift */; };
 		F78F74342163757000C2ADAD /* NCTrash.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F78F74332163757000C2ADAD /* NCTrash.storyboard */; };
 		F78F74362163781100C2ADAD /* NCTrash.swift in Sources */ = {isa = PBXBuildFile; fileRef = F78F74352163781100C2ADAD /* NCTrash.swift */; };
 		F790110E21415BF600D7B136 /* NCViewerRichdocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = F790110D21415BF600D7B136 /* NCViewerRichdocument.swift */; };
@@ -1153,6 +1154,7 @@
 		F78D6F461F0B7CB9002F9619 /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/Localizable.strings"; sourceTree = "<group>"; };
 		F78D6F4D1F0B7CE4002F9619 /* nb-NO */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "nb-NO"; path = "nb-NO.lproj/Localizable.strings"; sourceTree = "<group>"; };
 		F78D6F541F0B7D47002F9619 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = "<group>"; };
+		F78E7064219F096B006F23E4 /* NCAvatar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCAvatar.swift; sourceTree = "<group>"; };
 		F78F6FAE1CC8CCB700F4EA25 /* CCSection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCSection.h; sourceTree = "<group>"; };
 		F78F6FAF1CC8CCB700F4EA25 /* CCSection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCSection.m; sourceTree = "<group>"; };
 		F78F74332163757000C2ADAD /* NCTrash.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = NCTrash.storyboard; sourceTree = "<group>"; };
@@ -2625,6 +2627,7 @@
 			children = (
 				F76C3B871C638A4C00DC4301 /* CCError.h */,
 				F70BFC7320E0FA7C00C67599 /* NCUtility.swift */,
+				F78E7064219F096B006F23E4 /* NCAvatar.swift */,
 				F76C3B881C638A4C00DC4301 /* CCError.m */,
 				F7A54C341C6267B500E2C8BF /* CCExifGeo.h */,
 				F7A54C351C6267B500E2C8BF /* CCExifGeo.m */,
@@ -3697,6 +3700,7 @@
 				F7A5541F204EF8AF008468EC /* TOScrollBar.m in Sources */,
 				F7A321651E9E37960069AD1B /* CCActivity.m in Sources */,
 				F7FB1D3E215E191D00D669EA /* NCViewerDocumentWeb.swift in Sources */,
+				F78E7065219F096B006F23E4 /* NCAvatar.swift in Sources */,
 				F7DFB7F0219C5B8000680748 /* NCCreateFormUploadAssets.swift in Sources */,
 				F762CB0C1EACB66200B38484 /* XLFormSectionDescriptor.m in Sources */,
 				F77B0E131D118A16002130FE /* AppDelegate.m in Sources */,

+ 1 - 1
iOSClient/Brand/File_Provider_Extension.plist

@@ -19,7 +19,7 @@
 	<key>CFBundleShortVersionString</key>
 	<string>2.22.6</string>
 	<key>CFBundleVersion</key>
-	<string>10</string>
+	<string>11</string>
 	<key>NSExtension</key>
 	<dict>
 		<key>NSExtensionFileProviderDocumentGroup</key>

+ 1 - 1
iOSClient/Brand/Notification_Service_Extension.plist

@@ -19,7 +19,7 @@
 	<key>CFBundleShortVersionString</key>
 	<string>2.22.6</string>
 	<key>CFBundleVersion</key>
-	<string>10</string>
+	<string>11</string>
 	<key>NSExtension</key>
 	<dict>
 		<key>NSExtensionPointIdentifier</key>

+ 1 - 1
iOSClient/Brand/Share.plist

@@ -19,7 +19,7 @@
 	<key>CFBundleShortVersionString</key>
 	<string>2.22.6</string>
 	<key>CFBundleVersion</key>
-	<string>10</string>
+	<string>11</string>
 	<key>NSAppTransportSecurity</key>
 	<dict>
 		<key>NSAllowsArbitraryLoads</key>

+ 1 - 1
iOSClient/Brand/iOSClient.plist

@@ -69,7 +69,7 @@
 		</dict>
 	</array>
 	<key>CFBundleVersion</key>
-	<string>10</string>
+	<string>11</string>
 	<key>FIREBASE_ANALYTICS_COLLECTION_DEACTIVATED</key>
 	<true/>
 	<key>Fabric</key>

+ 25 - 25
iOSClient/Main/Create cloud/NCCreateFormUploadScanDocument.swift

@@ -25,7 +25,7 @@
 import Foundation
 import PDFGenerator
 
-class NCCreateFormUploadScanDocument: XLFormViewController, CCMoveDelegate {
+class NCCreateFormUploadScanDocument: XLFormViewController, NCSelectDelegate {
     
     enum typeDpiQuality {
         case low
@@ -271,23 +271,23 @@ class NCCreateFormUploadScanDocument: XLFormViewController, CCMoveDelegate {
     
     // MARK: - Action
     
-    func moveServerUrl(to serverUrlTo: String!, title: String!, type: String!) {
+    func dismissSelect(serverUrl: String?, metadata: tableMetadata?, type: String) {
         
-        self.serverUrl = serverUrlTo
-        
-        if let title = title {
+        if serverUrl != nil {
             
-            self.titleServerUrl = title
+            self.serverUrl = serverUrl!
             
-        } else {
+            if serverUrl == CCUtility.getHomeServerUrlActiveUrl(appDelegate.activeUrl) {
+                self.titleServerUrl = "/"
+            } else {
+                self.titleServerUrl = (serverUrl! as NSString).lastPathComponent
+            }
             
-            self.titleServerUrl = "/"
+            // Update
+            let row : XLFormRowDescriptor  = self.form.formRow(withTag: "ButtonDestinationFolder")!
+            row.title = self.titleServerUrl
+            self.updateFormRow(row)
         }
-        
-        // Update
-        let row : XLFormRowDescriptor  = self.form.formRow(withTag: "ButtonDestinationFolder")!
-        row.title = self.titleServerUrl
-        self.updateFormRow(row)
     }
     
     @objc func save() {
@@ -441,18 +441,18 @@ class NCCreateFormUploadScanDocument: XLFormViewController, CCMoveDelegate {
         
         self.deselectFormRow(sender)
         
-        let storyboard : UIStoryboard = UIStoryboard(name: "CCMove", bundle: nil)
-        let navigationController = storyboard.instantiateViewController(withIdentifier: "CCMove") as! UINavigationController
-        let viewController : CCMove = navigationController.topViewController as! CCMove
-        
-        viewController.delegate = self;
-        viewController.tintColor = NCBrandColor.sharedInstance.brandText
-        viewController.barTintColor = NCBrandColor.sharedInstance.brand
-        viewController.tintColorTitle = NCBrandColor.sharedInstance.brandText
-        viewController.move.title = NSLocalizedString("_select_", comment: "");
-        viewController.networkingOperationQueue =  appDelegate.netQueue
-        // E2EE
-        viewController.includeDirectoryE2EEncryption = true;
+        let storyboard = UIStoryboard(name: "NCSelect", bundle: nil)
+        let navigationController = storyboard.instantiateInitialViewController() as! UINavigationController
+        let viewController = navigationController.topViewController as! NCSelect
+        
+        viewController.delegate = self
+        viewController.hideButtonCreateFolder = false
+        viewController.includeDirectoryE2EEncryption = true
+        viewController.includeImages = false
+        viewController.layoutViewSelect = k_layout_view_move
+        viewController.selectFile = false
+        viewController.titleButtonDone = NSLocalizedString("_select_", comment: "")
+        viewController.type = ""
         
         navigationController.modalPresentationStyle = UIModalPresentationStyle.formSheet
         self.present(navigationController, animated: true, completion: nil)

+ 2 - 2
iOSClient/Main/Main.storyboard

@@ -701,7 +701,7 @@
                                                     </constraints>
                                                 </imageView>
                                                 <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Jgw-HO-Z7g">
-                                                    <rect key="frame" x="63" y="15" width="522" height="20"/>
+                                                    <rect key="frame" x="63" y="15" width="297" height="20"/>
                                                     <fontDescription key="fontDescription" type="system" pointSize="16"/>
                                                     <nil key="textColor"/>
                                                     <nil key="highlightedColor"/>
@@ -726,7 +726,7 @@
                                     <outlet property="delegate" destination="Frr-j9-xav" id="46E-kI-GUB"/>
                                 </connections>
                             </tableView>
-                            <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="avatar" translatesAutoresizingMaskIntoConstraints="NO" id="oJg-wO-2iv" customClass="SwiftyAvatar" customModule="Nextcloud" customModuleProvider="target">
+                            <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="avatar" translatesAutoresizingMaskIntoConstraints="NO" id="oJg-wO-2iv" customClass="NCAvatar" customModule="Nextcloud" customModuleProvider="target">
                                 <rect key="frame" x="15" y="79" width="50" height="50"/>
                                 <constraints>
                                     <constraint firstAttribute="width" constant="50" id="moA-ZW-N2y"/>

+ 60 - 0
iOSClient/Utility/NCAvatar.swift

@@ -0,0 +1,60 @@
+//
+//  NCAvatar.swift
+//  Nextcloud
+//
+//  Created by Marino Faggiana on 16/11/2018.
+//  Copyright © 2018 TWS. All rights reserved.
+//
+
+import Foundation
+
+@IBDesignable class NCAvatar: UIImageView {
+    
+    @IBInspectable var roundness: CGFloat = 2 {
+        didSet{
+            layoutSubviews()
+        }
+    }
+    
+    @IBInspectable var borderWidth: CGFloat = 5 {
+        didSet{
+            layoutSubviews()
+        }
+    }
+    
+    @IBInspectable var borderColor: UIColor = UIColor.blue {
+        didSet{
+            layoutSubviews()
+        }
+    }
+    
+    @IBInspectable var background: UIColor = UIColor.clear {
+        didSet{
+            layoutSubviews()
+        }
+    }
+    
+    required init?(coder aDecoder: NSCoder) {
+        super.init(coder: aDecoder)
+    }
+    
+    override init(frame: CGRect) {
+        super.init(frame: frame)
+    }
+    
+    override func layoutSubviews() {
+        super.layoutSubviews()
+        
+        layer.cornerRadius = bounds.width / roundness
+        layer.borderWidth = borderWidth
+        layer.borderColor = borderColor.cgColor
+        layer.backgroundColor = background.cgColor
+        clipsToBounds = true
+        
+        let path = UIBezierPath(roundedRect: bounds.insetBy(dx: 0.5, dy: 0.5), cornerRadius: bounds.width / roundness)
+        let mask = CAShapeLayer()
+        
+        mask.path = path.cgPath
+        layer.mask = mask
+    }
+}

+ 0 - 44
iOSClient/Utility/NCUtility.swift

@@ -245,47 +245,3 @@ class NCUtility: NSObject {
     }
 }
 
-//MARK: -
-
-@IBDesignable class NCAvatar: UIImageView {
-    
-    @IBInspectable var roundness: CGFloat = 2 {
-        didSet{
-            layoutSubviews()
-        }
-    }
-    
-    @IBInspectable var borderWidth: CGFloat = 5 {
-        didSet{
-            layoutSubviews()
-        }
-    }
-    
-    @IBInspectable var borderColor: UIColor = UIColor.blue {
-        didSet{
-            layoutSubviews()
-        }
-    }
-    
-    @IBInspectable var background: UIColor = UIColor.clear {
-        didSet{
-            layoutSubviews()
-        }
-    }
-    
-    override func layoutSubviews() {
-        super.layoutSubviews()
-        
-        layer.cornerRadius = bounds.width / roundness
-        layer.borderWidth = borderWidth
-        layer.borderColor = borderColor.cgColor
-        layer.backgroundColor = background.cgColor
-        clipsToBounds = true
-        
-        let path = UIBezierPath(roundedRect: bounds.insetBy(dx: 0.5, dy: 0.5), cornerRadius: bounds.width / roundness)
-        let mask = CAShapeLayer()
-        
-        mask.path = path.cgPath
-        layer.mask = mask
-    }
-}