diff --git a/Realm.xcodeproj/project.pbxproj b/Realm.xcodeproj/project.pbxproj index 1bd052c725..2236cfc60a 100644 --- a/Realm.xcodeproj/project.pbxproj +++ b/Realm.xcodeproj/project.pbxproj @@ -100,8 +100,6 @@ 1A33C4311DAEE445001E87AA /* RLMSyncSessionRefreshHandle.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A33C42D1DAEE445001E87AA /* RLMSyncSessionRefreshHandle.mm */; }; 1A3623681D8384BA00945A54 /* RLMSyncConfiguration.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A3623661D8384BA00945A54 /* RLMSyncConfiguration.mm */; }; 1A4FFC991D35A71000B4B65C /* RLMSyncUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = 1A4FFC971D35A71000B4B65C /* RLMSyncUtil.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 1A5CBA481E71DDFD007A95C2 /* binding_callback_thread_observer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1A5CBA461E71DDFD007A95C2 /* binding_callback_thread_observer.cpp */; }; - 1A5CBA4A1E71DE17007A95C2 /* binding_callback_thread_observer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1A5CBA461E71DDFD007A95C2 /* binding_callback_thread_observer.cpp */; }; 1A64CA8B1D8763B400BC0F9B /* keychain_helper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1A64CA891D8763B400BC0F9B /* keychain_helper.cpp */; }; 1A6921D41D779774004C3232 /* RLMTokenModels.m in Sources */ = {isa = PBXBuildFile; fileRef = 1A6921D21D779774004C3232 /* RLMTokenModels.m */; }; 1A7003081D5270C400FD9EE3 /* RLMSyncSession.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1AD3870B1D4A7FBB00479110 /* RLMSyncSession.mm */; }; @@ -178,6 +176,8 @@ 3F2E66651CA0BA12004761D5 /* NotificationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3F2E66611CA0B9D5004761D5 /* NotificationTests.m */; }; 3F336E8A1DA2FA14006CB5A0 /* RLMSyncConfiguration_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 1A36236A1D83868F00945A54 /* RLMSyncConfiguration_Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; 3F336E8B1DA2FA15006CB5A0 /* RLMSyncConfiguration_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 1A36236A1D83868F00945A54 /* RLMSyncConfiguration_Private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 3F5B5D301E84230B00953B33 /* binding_callback_thread_observer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3F5B5D2E1E84230B00953B33 /* binding_callback_thread_observer.cpp */; }; + 3F5B5D321E84230E00953B33 /* binding_callback_thread_observer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3F5B5D2E1E84230B00953B33 /* binding_callback_thread_observer.cpp */; }; 3F643BED1CEA655800F6D0C8 /* mixed-column.realm in Resources */ = {isa = PBXBuildFile; fileRef = 3F643BEB1CEA654D00F6D0C8 /* mixed-column.realm */; }; 3F643BEE1CEA655800F6D0C8 /* mixed-column.realm in Resources */ = {isa = PBXBuildFile; fileRef = 3F643BEB1CEA654D00F6D0C8 /* mixed-column.realm */; }; 3F6468371E3A9363007BD064 /* thread_safe_reference.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1AB2D36C1E16EB91007D0A3F /* thread_safe_reference.cpp */; }; @@ -654,8 +654,6 @@ 1A36236A1D83868F00945A54 /* RLMSyncConfiguration_Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RLMSyncConfiguration_Private.h; sourceTree = ""; }; 1A4AC06D1D8BA86200DC9736 /* RLMSyncConfiguration_Private.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = RLMSyncConfiguration_Private.hpp; sourceTree = ""; }; 1A4FFC971D35A71000B4B65C /* RLMSyncUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RLMSyncUtil.h; sourceTree = ""; }; - 1A5CBA461E71DDFD007A95C2 /* binding_callback_thread_observer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = binding_callback_thread_observer.cpp; sourceTree = ""; }; - 1A5CBA471E71DDFD007A95C2 /* binding_callback_thread_observer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = binding_callback_thread_observer.hpp; sourceTree = ""; }; 1A64CA891D8763B400BC0F9B /* keychain_helper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = keychain_helper.cpp; sourceTree = ""; }; 1A64CA8A1D8763B400BC0F9B /* keychain_helper.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = keychain_helper.hpp; sourceTree = ""; }; 1A6921D11D779774004C3232 /* RLMTokenModels.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RLMTokenModels.h; sourceTree = ""; }; @@ -714,6 +712,7 @@ 29EDB8E01A77070200458D80 /* RLMRealm_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RLMRealm_Private.h; sourceTree = ""; }; 29EDB8E51A7710B700458D80 /* RLMResults_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RLMResults_Private.h; sourceTree = ""; }; 29EDB8E91A7712E500458D80 /* RLMObjectSchema_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RLMObjectSchema_Private.h; sourceTree = ""; }; + 3F0338491E6F466D00F9E288 /* RLMAccessor.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = RLMAccessor.hpp; sourceTree = ""; }; 3F04EA2D1992BEE400C2CE2E /* PerformanceTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PerformanceTests.m; sourceTree = ""; }; 3F0543E91C56F71500AA5322 /* realm_coordinator.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = realm_coordinator.hpp; sourceTree = ""; }; 3F0543EA1C56F71500AA5322 /* realm_coordinator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = realm_coordinator.cpp; sourceTree = ""; }; @@ -739,6 +738,8 @@ 3F44109E19953F5900223146 /* RLMTestObjects.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RLMTestObjects.h; sourceTree = ""; }; 3F452EC519C2279800AFC154 /* RLMSwiftSupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RLMSwiftSupport.m; path = Realm/RLMSwiftSupport.m; sourceTree = SOURCE_ROOT; }; 3F4E324B1B98C6C700183A69 /* RLMSchema_Private.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = RLMSchema_Private.hpp; sourceTree = ""; }; + 3F5B5D2E1E84230B00953B33 /* binding_callback_thread_observer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = binding_callback_thread_observer.cpp; sourceTree = ""; }; + 3F5B5D2F1E84230B00953B33 /* binding_callback_thread_observer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = binding_callback_thread_observer.hpp; sourceTree = ""; }; 3F62BA9E1BA0AB9000A4CEB2 /* binding_context.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = binding_context.hpp; sourceTree = ""; }; 3F643BEB1CEA654D00F6D0C8 /* mixed-column.realm */ = {isa = PBXFileReference; lastKnownFileType = file; path = "mixed-column.realm"; sourceTree = ""; }; 3F6468381E3A98B1007BD064 /* RLMSyncSessionRefreshHandle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RLMSyncSessionRefreshHandle.h; sourceTree = ""; }; @@ -1016,8 +1017,8 @@ 3FF0B0A31BA861F200E74157 /* impl */, 1A1536491DB045A800C0EC93 /* sync */, 5DB591A51D063DE5001D8F93 /* util */, - 1A5CBA461E71DDFD007A95C2 /* binding_callback_thread_observer.cpp */, - 1A5CBA471E71DDFD007A95C2 /* binding_callback_thread_observer.hpp */, + 3F5B5D2E1E84230B00953B33 /* binding_callback_thread_observer.cpp */, + 3F5B5D2F1E84230B00953B33 /* binding_callback_thread_observer.hpp */, 3F62BA9E1BA0AB9000A4CEB2 /* binding_context.hpp */, 3F9801A91C8E4F6B000A8B07 /* collection_notifications.cpp */, 3F9801A81C8E4F6B000A8B07 /* collection_notifications.hpp */, @@ -1153,12 +1154,12 @@ 1AF7EA941D340AF70001A9B5 /* RLMSyncManager.h */, 1AF7EA951D340AF70001A9B5 /* RLMSyncManager.mm */, 1AF7EA981D340D1F0001A9B5 /* RLMSyncManager_Private.h */, - 1ADE093B1E897EE9008AB1D3 /* RLMSyncPermissionValue.h */, - 1ADE093C1E897EE9008AB1D3 /* RLMSyncPermissionValue.mm */, - 1ADE09411E8990DF008AB1D3 /* RLMSyncPermissionValue_Private.hpp */, 1AD6E79E1E8C217000D4C8B4 /* RLMSyncPermissionResults.h */, 1AD6E79F1E8C217000D4C8B4 /* RLMSyncPermissionResults.mm */, 1AD6E7A41E8C221600D4C8B4 /* RLMSyncPermissionResults_Private.hpp */, + 1ADE093B1E897EE9008AB1D3 /* RLMSyncPermissionValue.h */, + 1ADE093C1E897EE9008AB1D3 /* RLMSyncPermissionValue.mm */, + 1ADE09411E8990DF008AB1D3 /* RLMSyncPermissionValue_Private.hpp */, 1AD3870A1D4A7FBB00479110 /* RLMSyncSession.h */, 1AD3870B1D4A7FBB00479110 /* RLMSyncSession.mm */, 1ABF256A1D528B9900BAC441 /* RLMSyncSession_Private.hpp */, @@ -1490,6 +1491,7 @@ 1AF6EA441D36B1220014EB85 /* Sync */, E8D89B9D1955FC6D00CF2B9A /* Realm.h */, E81A1F631955FC9300FDED82 /* RLMAccessor.h */, + 3F0338491E6F466D00F9E288 /* RLMAccessor.hpp */, E81A1F641955FC9300FDED82 /* RLMAccessor.mm */, E83591941B3DF05C0035F2F3 /* RLMAnalytics.hpp */, E83591931B3DF05C0035F2F3 /* RLMAnalytics.mm */, @@ -1629,7 +1631,6 @@ 5D659EB81BE04556006515A0 /* RLMObjectStore.h in Headers */, 5D659EBA1BE04556006515A0 /* RLMOptionalBase.h in Headers */, 5D659EBC1BE04556006515A0 /* RLMProperty.h in Headers */, - 1AD6E7A01E8C217000D4C8B4 /* RLMSyncPermissionResults.h in Headers */, 5D659EBD1BE04556006515A0 /* RLMProperty_Private.h in Headers */, 5D659EBF1BE04556006515A0 /* RLMRealm.h in Headers */, 5D659EC01BE04556006515A0 /* RLMRealm_Dynamic.h in Headers */, @@ -1654,8 +1655,9 @@ 14C0998D1E43B99E00891909 /* RLMSyncPermissionOffer_Private.h in Headers */, 3F0D87461E2EEC4C008C92AC /* RLMSyncPermissionOfferResponse.h in Headers */, 14C0998E1E43B99E00891909 /* RLMSyncPermissionOfferResponse_Private.h in Headers */, - 1AD3870C1D4A7FBB00479110 /* RLMSyncSession.h in Headers */, + 1AD6E7A01E8C217000D4C8B4 /* RLMSyncPermissionResults.h in Headers */, 1ADE093D1E897EE9008AB1D3 /* RLMSyncPermissionValue.h in Headers */, + 1AD3870C1D4A7FBB00479110 /* RLMSyncSession.h in Headers */, 1ABDCDAE1D792FEB003489E3 /* RLMSyncUser.h in Headers */, 1A4FFC991D35A71000B4B65C /* RLMSyncUtil.h in Headers */, E8C6EAF51DD66C0C00EC1A03 /* RLMSyncUtil_Private.h in Headers */, @@ -1674,6 +1676,7 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + C2CAAE741E9643120025454C /* network_reachability.hpp in Headers */, C281E9281E8A9CB50015BA4A /* network_reachability_observer.hpp in Headers */, 3F73BC971E3A879700FE80B6 /* NSError+RLMSync.h in Headers */, 5DD755A31BE056DE002800DA /* Realm.h in Headers */, @@ -1699,15 +1702,12 @@ 5DD755BE1BE056DE002800DA /* RLMRealm_Dynamic.h in Headers */, 5DD755BF1BE056DE002800DA /* RLMRealm_Private.h in Headers */, 1AFEF8421D52CD8F00495005 /* RLMRealmConfiguration+Sync.h in Headers */, - 1AD6E7A21E8C218A00D4C8B4 /* RLMSyncPermissionResults.h in Headers */, 5DD755C01BE056DE002800DA /* RLMRealmConfiguration.h in Headers */, 5DD755C11BE056DE002800DA /* RLMRealmConfiguration_Private.h in Headers */, 5DD755C31BE056DE002800DA /* RLMResults.h in Headers */, - C2CAAE741E9643120025454C /* network_reachability.hpp in Headers */, 5DD755C41BE056DE002800DA /* RLMResults_Private.h in Headers */, 5DD755C51BE056DE002800DA /* RLMSchema.h in Headers */, 5DD755C61BE056DE002800DA /* RLMSchema_Private.h in Headers */, - C2CAAE7A1E9BB5760025454C /* system_configuration.hpp in Headers */, 5DD755C71BE056DE002800DA /* RLMSwiftSupport.h in Headers */, 1A0512761D8746CD00806AEC /* RLMSyncConfiguration.h in Headers */, 3F336E8A1DA2FA14006CB5A0 /* RLMSyncConfiguration_Private.h in Headers */, @@ -1721,15 +1721,17 @@ 14C099901E43B99E00891909 /* RLMSyncPermissionOffer_Private.h in Headers */, 3F0D874C1E2EEC57008C92AC /* RLMSyncPermissionOfferResponse.h in Headers */, 14C099911E43B99E00891909 /* RLMSyncPermissionOfferResponse_Private.h in Headers */, + 1AD6E7A21E8C218A00D4C8B4 /* RLMSyncPermissionResults.h in Headers */, + 1ADE093F1E897EF0008AB1D3 /* RLMSyncPermissionValue.h in Headers */, 1A7003111D5270FF00FD9EE3 /* RLMSyncSession.h in Headers */, 1ABDCDB11D793012003489E3 /* RLMSyncUser.h in Headers */, 1A7DE70B1D3847670029F0AE /* RLMSyncUtil.h in Headers */, - 1ADE093F1E897EF0008AB1D3 /* RLMSyncPermissionValue.h in Headers */, E8C6EAF41DD66C0C00EC1A03 /* RLMSyncUtil_Private.h in Headers */, 3F67DB401E26D6A20024533D /* RLMThreadSafeReference.h in Headers */, 3FAB084A1E1EC3A2001BC8DA /* sync_client.hpp in Headers */, 3FAB084B1E1EC3A2001BC8DA /* sync_file.hpp in Headers */, 3FAB084C1E1EC3A2001BC8DA /* sync_metadata.hpp in Headers */, + C2CAAE7A1E9BB5760025454C /* system_configuration.hpp in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2226,6 +2228,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 3F5B5D301E84230B00953B33 /* binding_callback_thread_observer.cpp in Sources */, 3F7A3FAE1CC6EB7300301A17 /* collection_change_builder.cpp in Sources */, 3F9801AB1C8E4F6B000A8B07 /* collection_notifications.cpp in Sources */, 3F9801A41C8E4F55000A8B07 /* collection_notifier.cpp in Sources */, @@ -2234,15 +2237,13 @@ 5D659E821BE04556006515A0 /* index_set.cpp in Sources */, 1A64CA8B1D8763B400BC0F9B /* keychain_helper.cpp in Sources */, 3F9026111C625C5D006AE98E /* list.cpp in Sources */, - 1AABD4021E9552BA00115A75 /* uuid.cpp in Sources */, 3F9801A51C8E4F55000A8B07 /* list_notifier.cpp in Sources */, + C281E9301E8BC7AC0015BA4A /* network_reachability_observer.cpp in Sources */, 3F73BC961E3A878500FE80B6 /* NSError+RLMSync.m in Sources */, - C2CAAE771E9BB5760025454C /* system_configuration.cpp in Sources */, 3FAB08481E1EC382001BC8DA /* object.cpp in Sources */, 3FAB08881E1EC51F001BC8DA /* object_notifier.cpp in Sources */, 5D659E831BE04556006515A0 /* object_schema.cpp in Sources */, 5D659E841BE04556006515A0 /* object_store.cpp in Sources */, - 1A5CBA481E71DDFD007A95C2 /* binding_callback_thread_observer.cpp in Sources */, 3F0543EC1C56F71500AA5322 /* realm_coordinator.cpp in Sources */, 3F75566B1BE94CCC0058BC7E /* results.cpp in Sources */, 3F9801B01C90FD2D000A8B07 /* results_notifier.cpp in Sources */, @@ -2253,7 +2254,6 @@ 1AF6EA481D36B1850014EB85 /* RLMAuthResponseModel.m in Sources */, 3F9863BB1D36876B00641C98 /* RLMClassInfo.mm in Sources */, 3FBEF67B1C63D66100F6935B /* RLMCollection.mm in Sources */, - C281E9301E8BC7AC0015BA4A /* network_reachability_observer.cpp in Sources */, 5D659E891BE04556006515A0 /* RLMConstants.m in Sources */, 5D659E8A1BE04556006515A0 /* RLMListBase.mm in Sources */, 5D659E8B1BE04556006515A0 /* RLMMigration.mm in Sources */, @@ -2261,7 +2261,6 @@ 5D659E8C1BE04556006515A0 /* RLMObject.mm in Sources */, 5D659E8D1BE04556006515A0 /* RLMObjectBase.mm in Sources */, 5D659E8E1BE04556006515A0 /* RLMObjectSchema.mm in Sources */, - 1AD6E7A11E8C217000D4C8B4 /* RLMSyncPermissionResults.mm in Sources */, 5D659E8F1BE04556006515A0 /* RLMObjectStore.mm in Sources */, 5D659E901BE04556006515A0 /* RLMObservation.mm in Sources */, 5D659E911BE04556006515A0 /* RLMOptionalBase.mm in Sources */, @@ -2276,7 +2275,6 @@ 5D659E981BE04556006515A0 /* RLMSchema.mm in Sources */, 5D659E991BE04556006515A0 /* RLMSwiftSupport.m in Sources */, 1A3623681D8384BA00945A54 /* RLMSyncConfiguration.mm in Sources */, - 1ADE093E1E897EE9008AB1D3 /* RLMSyncPermissionValue.mm in Sources */, 1AAF4D221D66585B00058FAD /* RLMSyncCredentials.m in Sources */, C2841D5E1DDC673400943503 /* RLMSyncErrorResponseModel.m in Sources */, 1AF7EA971D340AF70001A9B5 /* RLMSyncManager.mm in Sources */, @@ -2284,6 +2282,8 @@ 14C4B4411DC33481002FDEC8 /* RLMSyncPermissionChange.m in Sources */, 3F0D87471E2EEC4C008C92AC /* RLMSyncPermissionOffer.m in Sources */, 3F0D87481E2EEC4C008C92AC /* RLMSyncPermissionOfferResponse.m in Sources */, + 1AD6E7A11E8C217000D4C8B4 /* RLMSyncPermissionResults.mm in Sources */, + 1ADE093E1E897EE9008AB1D3 /* RLMSyncPermissionValue.mm in Sources */, 1AD3870D1D4A7FBB00479110 /* RLMSyncSession.mm in Sources */, 1A33C4301DAEE445001E87AA /* RLMSyncSessionRefreshHandle.mm in Sources */, 1ABDCDB01D793008003489E3 /* RLMSyncUser.mm in Sources */, @@ -2296,12 +2296,14 @@ 5D659E9D1BE04556006515A0 /* shared_realm.cpp in Sources */, 1A1536721DB0464800C0EC93 /* sync_file.cpp in Sources */, 1A1536581DB045B500C0EC93 /* sync_manager.cpp in Sources */, - 1ADE09371E897BCA008AB1D3 /* sync_permission.cpp in Sources */, 1A1536741DB0464800C0EC93 /* sync_metadata.cpp in Sources */, + 1ADE09371E897BCA008AB1D3 /* sync_permission.cpp in Sources */, 1A15365C1DB045B500C0EC93 /* sync_session.cpp in Sources */, 1A15365E1DB045B500C0EC93 /* sync_user.cpp in Sources */, + C2CAAE771E9BB5760025454C /* system_configuration.cpp in Sources */, 1AB2D36E1E16EB91007D0A3F /* thread_safe_reference.cpp in Sources */, 5D659E9E1BE04556006515A0 /* transact_log_handler.cpp in Sources */, + 1AABD4021E9552BA00115A75 /* uuid.cpp in Sources */, 5D274C4D1D6D15D2006FEBB1 /* weak_realm_notifier.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2337,6 +2339,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + E8AE7C261EA436F800CDFF9A /* CompactionTests.swift in Sources */, 5D6610151BE98D880021E04F /* KVOTests.swift in Sources */, 5D6610161BE98D880021E04F /* ListTests.swift in Sources */, 5D6610171BE98D880021E04F /* MigrationTests.swift in Sources */, @@ -2353,7 +2356,6 @@ 5D6610211BE98D880021E04F /* RealmTests.swift in Sources */, 5D6610221BE98D880021E04F /* SchemaTests.swift in Sources */, 5D6610231BE98D880021E04F /* SortDescriptorTests.swift in Sources */, - E8AE7C261EA436F800CDFF9A /* CompactionTests.swift in Sources */, 5D6610241BE98D880021E04F /* SwiftLinkTests.swift in Sources */, 5D6610251BE98D880021E04F /* SwiftTestObjects.swift in Sources */, 5D6610261BE98D880021E04F /* SwiftUnicodeTests.swift in Sources */, @@ -2367,6 +2369,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 3F5B5D321E84230E00953B33 /* binding_callback_thread_observer.cpp in Sources */, 3F7A3FAF1CC6EB7300301A17 /* collection_change_builder.cpp in Sources */, 3F9801AC1C8E4F6F000A8B07 /* collection_notifications.cpp in Sources */, 3F9801A61C8E4F5A000A8B07 /* collection_notifier.cpp in Sources */, @@ -2375,15 +2378,13 @@ 5DD755801BE056DE002800DA /* index_set.cpp in Sources */, E8FD2E381D93345100569F10 /* keychain_helper.cpp in Sources */, 3F9026131C625C63006AE98E /* list.cpp in Sources */, - 1AABD4041E9552C200115A75 /* uuid.cpp in Sources */, 3F9801A71C8E4F5A000A8B07 /* list_notifier.cpp in Sources */, + C281E9291E8A9CC90015BA4A /* network_reachability_observer.cpp in Sources */, 3F73BC981E3A879E00FE80B6 /* NSError+RLMSync.m in Sources */, - C2CAAE781E9BB5760025454C /* system_configuration.cpp in Sources */, 3FAB08491E1EC385001BC8DA /* object.cpp in Sources */, 3FAB08891E1EC526001BC8DA /* object_notifier.cpp in Sources */, 5DD755811BE056DE002800DA /* object_schema.cpp in Sources */, 5DD755821BE056DE002800DA /* object_store.cpp in Sources */, - 1A5CBA4A1E71DE17007A95C2 /* binding_callback_thread_observer.cpp in Sources */, 3F0543ED1C56F71900AA5322 /* realm_coordinator.cpp in Sources */, 3F75566D1BE94CEA0058BC7E /* results.cpp in Sources */, 3F9801B11C90FD31000A8B07 /* results_notifier.cpp in Sources */, @@ -2394,7 +2395,6 @@ 1A70030A1D5270CF00FD9EE3 /* RLMAuthResponseModel.m in Sources */, 3F9863BC1D36876B00641C98 /* RLMClassInfo.mm in Sources */, 3FBEF67C1C63D66400F6935B /* RLMCollection.mm in Sources */, - C281E9291E8A9CC90015BA4A /* network_reachability_observer.cpp in Sources */, 5DD755871BE056DE002800DA /* RLMConstants.m in Sources */, 5DD755881BE056DE002800DA /* RLMListBase.mm in Sources */, 5DD755891BE056DE002800DA /* RLMMigration.mm in Sources */, @@ -2402,7 +2402,6 @@ 5DD7558A1BE056DE002800DA /* RLMObject.mm in Sources */, 5DD7558B1BE056DE002800DA /* RLMObjectBase.mm in Sources */, 5DD7558C1BE056DE002800DA /* RLMObjectSchema.mm in Sources */, - 1AD6E7A31E8C218F00D4C8B4 /* RLMSyncPermissionResults.mm in Sources */, 5DD7558D1BE056DE002800DA /* RLMObjectStore.mm in Sources */, 5DD7558E1BE056DE002800DA /* RLMObservation.mm in Sources */, 5DD7558F1BE056DE002800DA /* RLMOptionalBase.mm in Sources */, @@ -2417,7 +2416,6 @@ 5DD755961BE056DE002800DA /* RLMSchema.mm in Sources */, 5DD755971BE056DE002800DA /* RLMSwiftSupport.m in Sources */, 1A0512721D873F3300806AEC /* RLMSyncConfiguration.mm in Sources */, - 1ADE09401E897EF0008AB1D3 /* RLMSyncPermissionValue.mm in Sources */, 17051FD01D93E0CC00EF8E67 /* RLMSyncCredentials.m in Sources */, C2841D5F1DDC673400943503 /* RLMSyncErrorResponseModel.m in Sources */, 1A90FCBB1D3D37F50086A57F /* RLMSyncManager.mm in Sources */, @@ -2425,6 +2423,8 @@ 14C4B4421DC33481002FDEC8 /* RLMSyncPermissionChange.m in Sources */, 3F0D874D1E2EEC57008C92AC /* RLMSyncPermissionOffer.m in Sources */, 3F0D874E1E2EEC57008C92AC /* RLMSyncPermissionOfferResponse.m in Sources */, + 1AD6E7A31E8C218F00D4C8B4 /* RLMSyncPermissionResults.mm in Sources */, + 1ADE09401E897EF0008AB1D3 /* RLMSyncPermissionValue.mm in Sources */, 1A7003081D5270C400FD9EE3 /* RLMSyncSession.mm in Sources */, 1A33C4311DAEE445001E87AA /* RLMSyncSessionRefreshHandle.mm in Sources */, 17051FCE1D93DA0A00EF8E67 /* RLMSyncUser.mm in Sources */, @@ -2437,12 +2437,14 @@ 5DD7559B1BE056DE002800DA /* shared_realm.cpp in Sources */, 1A1536761DB0464F00C0EC93 /* sync_file.cpp in Sources */, 1A1536631DB045CB00C0EC93 /* sync_manager.cpp in Sources */, - 1ADE09391E897BEF008AB1D3 /* sync_permission.cpp in Sources */, 1A1536771DB0465400C0EC93 /* sync_metadata.cpp in Sources */, + 1ADE09391E897BEF008AB1D3 /* sync_permission.cpp in Sources */, 1A1536671DB045D200C0EC93 /* sync_session.cpp in Sources */, 1A1536691DB045D600C0EC93 /* sync_user.cpp in Sources */, + C2CAAE781E9BB5760025454C /* system_configuration.cpp in Sources */, 3F6468371E3A9363007BD064 /* thread_safe_reference.cpp in Sources */, 5DD7559C1BE056DE002800DA /* transact_log_handler.cpp in Sources */, + 1AABD4041E9552C200115A75 /* uuid.cpp in Sources */, 5D274C4E1D6D15FD006FEBB1 /* weak_realm_notifier.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2454,7 +2456,6 @@ 1A2713D71E3BBAC8001F6BFC /* RLMAncillaryObjectServerTests.m in Sources */, 1A2D7A521DA5BCEC006AD7D6 /* RLMMultiProcessTestCase.m in Sources */, E8267FF11D90B8E700E001C7 /* RLMObjectServerTests.mm in Sources */, - 1A877BEF1EAE9F79001BEC40 /* SwiftPermissionsAPITests.swift in Sources */, 1AD6E7A61E8C2BDF00D4C8B4 /* RLMPermissionsAPITests.m in Sources */, 1AF64DCF1DA3042B0081EB15 /* RLMSyncManager+ObjectServerTests.m in Sources */, 3F73BC911E3A877300FE80B6 /* RLMSyncSessionRefreshHandle+ObjectServerTests.m in Sources */, @@ -2463,6 +2464,7 @@ E86E61241D91E4E200DC2419 /* RLMTestCase.m in Sources */, 3F73BC921E3A877300FE80B6 /* RLMTestUtils.m in Sources */, 1AA5AEA11D98C99800ED8C27 /* SwiftObjectServerTests.swift in Sources */, + 1A877BEF1EAE9F79001BEC40 /* SwiftPermissionsAPITests.swift in Sources */, 1AA5AE981D989BE400ED8C27 /* SwiftSyncTestCase.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2473,6 +2475,7 @@ files = ( E856D214195615A900FB2FCF /* ArrayPropertyTests.m in Sources */, 3F7556761BE95A0D0058BC7E /* AsyncTests.mm in Sources */, + E8DA16F91E81210D0055141C /* CompactionTests.m in Sources */, E856D216195615A900FB2FCF /* DynamicTests.m in Sources */, 021A88331AAFB5C900EEAC84 /* EncryptionTests.mm in Sources */, E856D217195615A900FB2FCF /* EnumeratorTests.m in Sources */, @@ -2501,7 +2504,6 @@ 3F8DCA7719930FCB0008BD7F /* SwiftArrayTests.swift in Sources */, 3F8DCA7819930FCB0008BD7F /* SwiftDynamicTests.swift in Sources */, 3F8DCA7919930FCB0008BD7F /* SwiftLinkTests.swift in Sources */, - E8DA16F91E81210D0055141C /* CompactionTests.m in Sources */, 3F8DCA7B19930FCB0008BD7F /* SwiftObjectInterfaceTests.swift in Sources */, 3F8DCA7C19930FCB0008BD7F /* SwiftPropertyTypeTest.swift in Sources */, 3F8DCA7D19930FCB0008BD7F /* SwiftRealmTests.swift in Sources */, @@ -2519,6 +2521,7 @@ files = ( E81A1FD51955FE0100FDED82 /* ArrayPropertyTests.m in Sources */, 3F7556751BE95A0C0058BC7E /* AsyncTests.mm in Sources */, + E8DA16F81E81210D0055141C /* CompactionTests.m in Sources */, 02E334C31A5F41C7009F8810 /* DynamicTests.m in Sources */, 021A88321AAFB5C800EEAC84 /* EncryptionTests.mm in Sources */, E81A1FDB1955FE0100FDED82 /* EnumeratorTests.m in Sources */, @@ -2530,7 +2533,6 @@ 3F2E66641CA0BA11004761D5 /* NotificationTests.m in Sources */, 3FEB383F1E70AC8800F22712 /* ObjectCreationTests.mm in Sources */, E81A1FE11955FE0100FDED82 /* ObjectInterfaceTests.m in Sources */, - E8DA16F81E81210D0055141C /* CompactionTests.m in Sources */, 021A88361AAFB5CD00EEAC84 /* ObjectSchemaTests.m in Sources */, E81A1FE31955FE0100FDED82 /* ObjectTests.m in Sources */, 5D03FB1F1E0DAFBA007D53EA /* PredicateUtilTests.mm in Sources */, diff --git a/Realm/ObjectStore b/Realm/ObjectStore index 872a154b1b..ab5d5361f9 160000 --- a/Realm/ObjectStore +++ b/Realm/ObjectStore @@ -1 +1 @@ -Subproject commit 872a154b1bfd843f7dad98cab73e23a5aff2ee66 +Subproject commit ab5d5361f9c2995940cdc8868082814f2d92051d diff --git a/Realm/RLMAccessor.h b/Realm/RLMAccessor.h index cc405230e3..59c625a581 100644 --- a/Realm/RLMAccessor.h +++ b/Realm/RLMAccessor.h @@ -18,14 +18,7 @@ #import - -@class RLMObjectSchema, RLMProperty, RLMObjectBase, RLMProperty; - -#ifdef __cplusplus -typedef NSUInteger RLMCreationOptions; -#else -typedef NS_OPTIONS(NSUInteger, RLMCreationOptions); -#endif +@class RLMObjectSchema, RLMProperty, RLMObjectBase; NS_ASSUME_NONNULL_BEGIN @@ -45,7 +38,7 @@ FOUNDATION_EXTERN id __nullable RLMDynamicGet(RLMObjectBase *obj, RLMProperty *p FOUNDATION_EXTERN id __nullable RLMDynamicGetByName(RLMObjectBase *obj, NSString *propName, bool asList); // by property/column -void RLMDynamicSet(RLMObjectBase *obj, RLMProperty *prop, id val, RLMCreationOptions options); +void RLMDynamicSet(RLMObjectBase *obj, RLMProperty *prop, id val); // // Class modification diff --git a/Realm/RLMAccessor.hpp b/Realm/RLMAccessor.hpp new file mode 100644 index 0000000000..e33f48c842 --- /dev/null +++ b/Realm/RLMAccessor.hpp @@ -0,0 +1,116 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2017 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 "RLMAccessor.h" + +#import "object_accessor.hpp" + +#import "RLMUtil.hpp" + +@class RLMRealm; +class RLMClassInfo; +class RLMObservationInfo; + +// realm::util::Optional doesn't work due to that explicitly invoking the +// destructor isn't allowed under ARC, so this covers the subset of Optional +// that we actually need. +struct RLMOptionalId { + id value; + RLMOptionalId(id value) : value(value) { } + explicit operator bool() const noexcept { return value; } + id operator*() const noexcept { return value; } +}; + +class RLMAccessorContext { +public: + // Accessor context interfact + RLMAccessorContext(RLMAccessorContext& parent, realm::Property const& property); + + id box(realm::List&&); + id box(realm::Results&&); + id box(realm::Object&&); + id box(realm::RowExpr); + + id box(realm::BinaryData v) { return RLMBinaryDataToNSData(v); } + id box(bool v) { return @(v); } + id box(double v) { return @(v); } + id box(float v) { return @(v); } + id box(long long v) { return @(v); } + id box(realm::StringData v) { return RLMStringDataToNSString(v); } + id box(realm::Timestamp v) { return RLMTimestampToNSDate(v); } + id box(realm::Mixed v) { return RLMMixedToObjc(v); } + + id box(realm::util::Optional v) { return v ? @(*v) : nil; } + id box(realm::util::Optional v) { return v ? @(*v) : nil; } + id box(realm::util::Optional v) { return v ? @(*v) : nil; } + id box(realm::util::Optional v) { return v ? @(*v) : nil; } + + void will_change(realm::Row const&, realm::Property const&); + 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 default_value_for_property(realm::ObjectSchema const&, + std::string const& prop); + + template + void enumerate_list(__unsafe_unretained const id v, Func&& func) { + for (id value in v) { + func(value); + } + } + + template + T unbox(id v, bool create = false, bool update = false); + + bool is_null(id v) { return v == NSNull.null; } + id null_value() { return NSNull.null; } + id no_value() { return nil; } + bool allow_missing(id v) { return [v isKindOfClass:[NSArray class]]; } + + std::string print(id obj) { return [obj description].UTF8String; } + + // Internal API + RLMAccessorContext(RLMObjectBase *parentObject, const realm::Property *property = nullptr); + RLMAccessorContext(RLMRealm *realm, RLMClassInfo& info, bool promote=true); + + // The property currently being accessed; needed for KVO things for boxing + // List and Results + RLMProperty *currentProperty; + +private: + __unsafe_unretained RLMRealm *const _realm; + RLMClassInfo& _info; + // If true, promote unmanaged RLMObjects passed to box() with create=true + // rather than copying them + bool _promote_existing = true; + // Parent object of the thing currently being processed, for KVO purposes + __unsafe_unretained RLMObjectBase *const _parentObject = nil; + + // Cached default values dictionary to avoid having to call the class method + // for every property + NSDictionary *_defaultValues; + + bool _nilHack = false; + + RLMObservationInfo *_observationInfo = nullptr; + NSString *_kvoPropertyName = nil; + + id defaultValue(NSString *key); + id propertyValue(id obj, size_t propIndex, __unsafe_unretained RLMProperty *const prop); +}; diff --git a/Realm/RLMAccessor.mm b/Realm/RLMAccessor.mm index ff93d173a5..6bb303c148 100644 --- a/Realm/RLMAccessor.mm +++ b/Realm/RLMAccessor.mm @@ -16,7 +16,7 @@ // //////////////////////////////////////////////////////////////////////////// -#import "RLMAccessor.h" +#import "RLMAccessor.hpp" #import "RLMArray_Private.hpp" #import "RLMListBase.h" @@ -33,239 +33,174 @@ #import "property.hpp" #import +#import #import +#pragma mark - Helper functions + +namespace { template -static inline T get(__unsafe_unretained RLMObjectBase *const obj, NSUInteger index) { +T get(__unsafe_unretained RLMObjectBase *const obj, NSUInteger index) { RLMVerifyAttached(obj); - return obj->_row.get_table()->get(obj->_info->objectSchema->persisted_properties[index].table_column, - obj->_row.get_index()); + return obj->_row.get(obj->_info->objectSchema->persisted_properties[index].table_column); } template -static NSNumber *getBoxed(__unsafe_unretained RLMObjectBase *const obj, NSUInteger index) { +id getBoxed(__unsafe_unretained RLMObjectBase *const obj, NSUInteger index) { RLMVerifyAttached(obj); - auto col = obj->_info->objectSchema->persisted_properties[index].table_column; + auto& prop = obj->_info->objectSchema->persisted_properties[index]; + auto col = prop.table_column; if (obj->_row.is_null(col)) { return nil; } - return @(obj->_row.get_table()->get(col, obj->_row.get_index())); -} - -// long getter/setter -static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, long long val, bool setDefault) { - RLMVerifyInWriteTransaction(obj); - obj->_row.get_table()->set_int(colIndex, obj->_row.get_index(), val, setDefault); -} -// float getter/setter -static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, float val, bool setDefault) { - RLMVerifyInWriteTransaction(obj); - obj->_row.get_table()->set_float(colIndex, obj->_row.get_index(), val, setDefault); -} - -// double getter/setter -static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, double val, bool setDefault) { - RLMVerifyInWriteTransaction(obj); - obj->_row.get_table()->set_double(colIndex, obj->_row.get_index(), val, setDefault); + RLMAccessorContext ctx(obj, &prop); + return ctx.box(obj->_row.get(col)); } -// bool getter/setter -static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, BOOL val, bool setDefault) { +template +void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, T val) { RLMVerifyInWriteTransaction(obj); - obj->_row.get_table()->set_bool(colIndex, obj->_row.get_index(), val, setDefault); + obj->_row.set(colIndex, val); } -// string getter/setter -static inline NSString *RLMGetString(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex) { - return RLMStringDataToNSString(get(obj, colIndex)); -} -static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, __unsafe_unretained NSString *const val, bool setDefault) { - RLMVerifyInWriteTransaction(obj); +template +void translateError(Fn&& fn) { try { - obj->_row.get_table()->set_string(colIndex, obj->_row.get_index(), RLMStringDataWithNSString(val), setDefault); + fn(); } catch (std::exception const& e) { @throw RLMException(e); } } -static inline void setNull(realm::Table& table, size_t colIndex, size_t rowIndex, bool setDefault) { - try { - table.set_null(colIndex, rowIndex, setDefault); - } - catch (std::exception const& e) { - @throw RLMException(e); - } +void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, + __unsafe_unretained NSString *const val) { + RLMVerifyInWriteTransaction(obj); + translateError([&] { + obj->_row.set(colIndex, RLMStringDataWithNSString(val)); + }); } -// date getter/setter -static inline NSDate *RLMGetDate(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex) { - return RLMTimestampToNSDate(get(obj, colIndex)); +[[gnu::noinline]] +void setNull(realm::Row& row, size_t col) { + translateError([&] { row.set_null(col); }); } -static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, - __unsafe_unretained NSDate *const date, bool setDefault) { + +void setValue(__unsafe_unretained RLMObjectBase *const obj, + NSUInteger colIndex, __unsafe_unretained NSDate *const date) { RLMVerifyInWriteTransaction(obj); if (date) { - obj->_row.get_table()->set_timestamp(colIndex, obj->_row.get_index(), RLMTimestampForNSDate(date), setDefault); + obj->_row.set(colIndex, RLMTimestampForNSDate(date)); } else { - setNull(*obj->_row.get_table(), colIndex, obj->_row.get_index(), setDefault); + setNull(obj->_row, colIndex); } } -// data getter/setter -static inline NSData *RLMGetData(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex) { - return RLMBinaryDataToNSData(get(obj, colIndex)); -} -static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, __unsafe_unretained NSData *const data, bool setDefault) { +void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, + __unsafe_unretained NSData *const data) { RLMVerifyInWriteTransaction(obj); - - try { - obj->_row.get_table()->set_binary(colIndex, obj->_row.get_index(), RLMBinaryDataForNSData(data), setDefault); - } - catch (std::exception const& e) { - @throw RLMException(e); - } -} - -static inline RLMObjectBase *RLMGetLinkedObjectForValue(__unsafe_unretained RLMRealm *const realm, - __unsafe_unretained NSString *const className, - __unsafe_unretained id const value, - RLMCreationOptions creationOptions) NS_RETURNS_RETAINED; -static inline RLMObjectBase *RLMGetLinkedObjectForValue(__unsafe_unretained RLMRealm *const realm, - __unsafe_unretained NSString *const className, - __unsafe_unretained id const value, - RLMCreationOptions creationOptions) { - RLMObjectBase *link = RLMDynamicCast(value); - if (!link || ![link->_objectSchema.className isEqualToString:className]) { - // create from non-rlmobject - return RLMCreateObjectInRealmWithValue(realm, className, value, creationOptions & RLMCreationOptionsCreateOrUpdate); - } - - if (link.isInvalidated) { - @throw RLMException(@"Adding a deleted or invalidated object to a Realm is not permitted"); - } - - if (link->_realm == realm) { - return link; - } - - if (creationOptions & RLMCreationOptionsPromoteUnmanaged) { - if (!link->_realm) { - RLMAddObjectToRealm(link, realm, creationOptions & RLMCreationOptionsCreateOrUpdate); - return link; - } - @throw RLMException(@"Can not add objects from a different Realm"); - } - - // copy from another realm or copy from unmanaged - return RLMCreateObjectInRealmWithValue(realm, className, link, creationOptions & RLMCreationOptionsCreateOrUpdate); -} - -// link getter/setter -static inline RLMObjectBase *RLMGetLink(__unsafe_unretained RLMObjectBase *const obj, NSUInteger propertyIndex) { - RLMVerifyAttached(obj); - auto colIndex = obj->_info->objectSchema->persisted_properties[propertyIndex].table_column; - - if (obj->_row.is_null_link(colIndex)) { - return nil; - } - NSUInteger index = obj->_row.get_link(colIndex); - return RLMCreateObjectAccessor(obj->_realm, obj->_info->linkTargetType(propertyIndex), index); + translateError([&] { + obj->_row.set(colIndex, RLMBinaryDataForNSData(data)); + }); } -static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, - __unsafe_unretained RLMObjectBase *const val, bool setDefault) { - RLMVerifyInWriteTransaction(obj); +void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, + __unsafe_unretained RLMObjectBase *const val) { if (!val) { + RLMVerifyInWriteTransaction(obj); obj->_row.nullify_link(colIndex); return; } - RLMObjectBase *link = RLMGetLinkedObjectForValue(obj->_realm, val->_objectSchema.className, - val, RLMCreationOptionsPromoteUnmanaged); + RLMAddObjectToRealm(val, obj->_realm, false); // make sure it is the correct type - if (link->_row.get_table() != obj->_row.get_table()->get_link_target(colIndex)) { + if (val->_row.get_table() != obj->_row.get_table()->get_link_target(colIndex)) { @throw RLMException(@"Can't set object of type '%@' to property of type '%@'", val->_objectSchema.className, obj->_info->propertyForTableColumn(colIndex).objectClassName); } - obj->_row.get_table()->set_link(colIndex, obj->_row.get_index(), link->_row.get_index(), setDefault); + obj->_row.set_link(colIndex, val->_row.get_index()); } // array getter/setter -static inline RLMArray *RLMGetArray(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex) { +RLMArray *getArray(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex) { RLMVerifyAttached(obj); auto prop = obj->_info->rlmObjectSchema.properties[colIndex]; return [[RLMArrayLinkView alloc] initWithParent:obj property:prop]; } -static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, - __unsafe_unretained id const array, __unused bool setDefault) { +void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, + __unsafe_unretained id const value) { RLMVerifyInWriteTransaction(obj); - realm::LinkViewRef linkView = obj->_row.get_linklist(colIndex); - // remove all old - // FIXME: make sure delete rules don't purge objects - linkView->clear(); - for (RLMObjectBase *link in array) { - RLMObjectBase * addedLink = RLMGetLinkedObjectForValue(obj->_realm, link->_objectSchema.className, link, RLMCreationOptionsPromoteUnmanaged); - linkView->add(addedLink->_row.get_index()); + realm::List list(obj->_realm->_realm, obj->_row.get_linklist(colIndex)); + list.remove_all(); + if (!value || (id)value == NSNull.null) { + return; } + + RLMAccessorContext ctx(obj->_realm, + obj->_info->linkTargetType(obj->_info->propertyForTableColumn(colIndex).index)); + translateError([&] { + for (id element in value) { + list.add(ctx, element); + } + }); } -static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, - __unsafe_unretained NSNumber *const intObject, bool setDefault) { +void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, + __unsafe_unretained NSNumber *const intObject) { RLMVerifyInWriteTransaction(obj); if (intObject) { - obj->_row.get_table()->set_int(colIndex, obj->_row.get_index(), intObject.longLongValue, setDefault); + obj->_row.set(colIndex, intObject.longLongValue); } else { - setNull(*obj->_row.get_table(), colIndex, obj->_row.get_index(), setDefault); + setNull(obj->_row, colIndex); } } -static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, - __unsafe_unretained NSNumber *const floatObject, bool setDefault) { +void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, + __unsafe_unretained NSNumber *const floatObject) { RLMVerifyInWriteTransaction(obj); if (floatObject) { - obj->_row.get_table()->set_float(colIndex, obj->_row.get_index(), floatObject.floatValue, setDefault); + obj->_row.set(colIndex, floatObject.floatValue); } else { - setNull(*obj->_row.get_table(), colIndex, obj->_row.get_index(), setDefault); + setNull(obj->_row, colIndex); } } -static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, - __unsafe_unretained NSNumber *const doubleObject, bool setDefault) { +void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, + __unsafe_unretained NSNumber *const doubleObject) { RLMVerifyInWriteTransaction(obj); if (doubleObject) { - obj->_row.get_table()->set_double(colIndex, obj->_row.get_index(), doubleObject.doubleValue, setDefault); + obj->_row.set(colIndex, doubleObject.doubleValue); } else { - setNull(*obj->_row.get_table(), colIndex, obj->_row.get_index(), setDefault); + setNull(obj->_row, colIndex); } } -static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, - __unsafe_unretained NSNumber *const boolObject, bool setDefault) { +void setValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger colIndex, + __unsafe_unretained NSNumber *const boolObject) { RLMVerifyInWriteTransaction(obj); if (boolObject) { - obj->_row.get_table()->set_bool(colIndex, obj->_row.get_index(), boolObject.boolValue, setDefault); + obj->_row.set(colIndex, (bool)boolObject.boolValue); } else { - setNull(*obj->_row.get_table(), colIndex, obj->_row.get_index(), setDefault); + setNull(obj->_row, colIndex); } } -static inline RLMLinkingObjects *RLMGetLinkingObjects(__unsafe_unretained RLMObjectBase *const obj, - __unsafe_unretained RLMProperty *const property) { +RLMLinkingObjects *getLinkingObjects(__unsafe_unretained RLMObjectBase *const obj, + __unsafe_unretained RLMProperty *const property) { RLMVerifyAttached(obj); auto& objectInfo = obj->_realm->_info[property.objectClassName]; auto linkingProperty = objectInfo.objectSchema->property_for_name(property.linkOriginPropertyName.UTF8String); @@ -275,120 +210,87 @@ static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSU } // any getter/setter -static inline id RLMGetAnyProperty(__unsafe_unretained RLMObjectBase *const obj, NSUInteger col_ndx) { - RLMVerifyAttached(obj); - return RLMMixedToObjc(obj->_row.get_mixed(col_ndx)); -} -static inline void RLMSetValue(__unsafe_unretained RLMObjectBase *const obj, NSUInteger, __unsafe_unretained id, bool) { - RLMVerifyInWriteTransaction(obj); +void setValue(__unsafe_unretained RLMObjectBase *const, NSUInteger, __unsafe_unretained id) { @throw RLMException(@"Modifying Mixed properties is not supported"); } +template +id makeGetter(NSUInteger index) { + return ^(__unsafe_unretained RLMObjectBase *const obj) { + return static_cast(get(obj, index)); + }; +} + +template +id makeBoxedGetter(NSUInteger index) { + return ^(__unsafe_unretained RLMObjectBase *const obj) { + return getBoxed(obj, index); + }; +} +template +id makeOptionalGetter(NSUInteger index) { + return ^(__unsafe_unretained RLMObjectBase *const obj) { + return getBoxed>(obj, index); + }; +} +template +id makeNumberGetter(NSUInteger index, bool boxed, bool optional) { + if (optional) { + return makeOptionalGetter(index); + } + if (boxed) { + return makeBoxedGetter(index); + } + return makeGetter(index); +} + // dynamic getter with column closure -static id RLMAccessorGetter(RLMProperty *prop, const char *type) { +id managedGetter(RLMProperty *prop, const char *type) { NSUInteger index = prop.index; - bool boxed = prop.optional || *type == '@'; + bool boxed = *type == '@'; switch (prop.type) { case RLMPropertyTypeInt: - if (boxed) { - return ^(__unsafe_unretained RLMObjectBase *const obj) { - return getBoxed(obj, index); - }; + if (prop.optional || boxed) { + return makeNumberGetter(index, boxed, prop.optional); } switch (*type) { - case 'c': - return ^(__unsafe_unretained RLMObjectBase *const obj) { - return static_cast(get(obj, index)); - }; - case 's': - return ^(__unsafe_unretained RLMObjectBase *const obj) { - return static_cast(get(obj, index)); - }; - case 'i': - return ^(__unsafe_unretained RLMObjectBase *const obj) { - return static_cast(get(obj, index)); - }; - case 'l': - return ^(__unsafe_unretained RLMObjectBase *const obj) { - return static_cast(get(obj, index)); - }; - case 'q': - return ^(__unsafe_unretained RLMObjectBase *const obj) { - return static_cast(get(obj, index)); - }; + case 'c': return makeGetter(index); + case 's': return makeGetter(index); + case 'i': return makeGetter(index); + case 'l': return makeGetter(index); + case 'q': return makeGetter(index); default: @throw RLMException(@"Unexpected property type for Objective-C type code"); } case RLMPropertyTypeFloat: - if (boxed) { - return ^(__unsafe_unretained RLMObjectBase *const obj) { - return getBoxed(obj, index); - }; - } - return ^(__unsafe_unretained RLMObjectBase *const obj) { - return get(obj, index); - }; + return makeNumberGetter(index, boxed, prop.optional); case RLMPropertyTypeDouble: - if (boxed) { - return ^(__unsafe_unretained RLMObjectBase *const obj) { - return getBoxed(obj, index); - }; - } - return ^(__unsafe_unretained RLMObjectBase *const obj) { - return get(obj, index); - }; + return makeNumberGetter(index, boxed, prop.optional); case RLMPropertyTypeBool: - if (boxed) { - return ^(__unsafe_unretained RLMObjectBase *const obj) { - return getBoxed(obj, index); - }; - } - return ^(__unsafe_unretained RLMObjectBase *const obj) { - return get(obj, index); - }; + return makeNumberGetter(index, boxed, prop.optional); case RLMPropertyTypeString: - return ^(__unsafe_unretained RLMObjectBase *const obj) { - return RLMGetString(obj, index); - }; + return makeBoxedGetter(index); case RLMPropertyTypeDate: - return ^(__unsafe_unretained RLMObjectBase *const obj) { - return RLMGetDate(obj, index); - }; + return makeBoxedGetter(index); case RLMPropertyTypeData: - return ^(__unsafe_unretained RLMObjectBase *const obj) { - return RLMGetData(obj, index); - }; + return makeBoxedGetter(index); case RLMPropertyTypeObject: - return ^id(__unsafe_unretained RLMObjectBase *const obj) { - return RLMGetLink(obj, index); - }; + return makeBoxedGetter(index); case RLMPropertyTypeArray: return ^(__unsafe_unretained RLMObjectBase *const obj) { - return RLMGetArray(obj, index); + return getArray(obj, index); }; case RLMPropertyTypeAny: @throw RLMException(@"Cannot create accessor class for schema with Mixed properties"); case RLMPropertyTypeLinkingObjects: return ^(__unsafe_unretained RLMObjectBase *const obj) { - return RLMGetLinkingObjects(obj, prop); + return getLinkingObjects(obj, prop); }; } } -template -static void RLMWrapSetter(__unsafe_unretained RLMObjectBase *const obj, __unsafe_unretained NSString *const name, Function&& f) { - if (RLMObservationInfo *info = RLMGetObservationInfo(obj->_observationInfo, obj->_row.get_index(), *obj->_info)) { - info->willChange(name); - f(); - info->didChange(name); - } - else { - f(); - } -} - template -static id makeSetter(__unsafe_unretained RLMProperty *const prop) { +id makeSetter(__unsafe_unretained RLMProperty *const prop) { NSUInteger index = prop.index; NSString *name = prop.name; if (prop.isPrimary) { @@ -396,16 +298,26 @@ static id makeSetter(__unsafe_unretained RLMProperty *const prop) { @throw RLMException(@"Primary key can't be changed after an object is inserted."); }; } + return ^(__unsafe_unretained RLMObjectBase *const obj, ArgType val) { - RLMWrapSetter(obj, name, [&] { - RLMSetValue(obj, obj->_info->objectSchema->persisted_properties[index].table_column, - static_cast(val), false); - }); + auto set = [&] { + setValue(obj, obj->_info->objectSchema->persisted_properties[index].table_column, + static_cast(val)); + }; + if (RLMObservationInfo *info = RLMGetObservationInfo(obj->_observationInfo, + obj->_row.get_index(), *obj->_info)) { + info->willChange(name); + set(); + info->didChange(name); + } + else { + set(); + } }; } // dynamic setter with column closure -static id RLMAccessorSetter(RLMProperty *prop, const char *type) { +id managedSetter(RLMProperty *prop, const char *type) { bool boxed = prop.optional || *type == '@'; switch (prop.type) { case RLMPropertyTypeInt: @@ -426,19 +338,19 @@ static id RLMAccessorSetter(RLMProperty *prop, const char *type) { case RLMPropertyTypeDouble: return boxed ? makeSetter *>(prop) : makeSetter(prop); case RLMPropertyTypeBool: - return boxed ? makeSetter *>(prop) : makeSetter(prop); + return boxed ? makeSetter *>(prop) : makeSetter(prop); case RLMPropertyTypeString: return makeSetter(prop); case RLMPropertyTypeDate: return makeSetter(prop); case RLMPropertyTypeData: return makeSetter(prop); case RLMPropertyTypeObject: return makeSetter(prop); - case RLMPropertyTypeArray: return makeSetter(prop); + case RLMPropertyTypeArray: return makeSetter>(prop); case RLMPropertyTypeAny: return makeSetter(prop); case RLMPropertyTypeLinkingObjects: return nil; } } // call getter for superclass for property at colIndex -static id RLMSuperGet(RLMObjectBase *obj, NSString *propName) { +id superGet(RLMObjectBase *obj, NSString *propName) { typedef id (*getter_type)(RLMObjectBase *, SEL); RLMProperty *prop = obj->_objectSchema[propName]; Class superClass = class_getSuperclass(obj.class); @@ -447,7 +359,7 @@ static id RLMSuperGet(RLMObjectBase *obj, NSString *propName) { } // call setter for superclass for property at colIndex -static void RLMSuperSet(RLMObjectBase *obj, NSString *propName, id val) { +void superSet(RLMObjectBase *obj, NSString *propName, id val) { typedef void (*setter_type)(RLMObjectBase *, SEL, RLMArray *ar); RLMProperty *prop = obj->_objectSchema[propName]; Class superClass = class_getSuperclass(obj.class); @@ -456,29 +368,27 @@ static void RLMSuperSet(RLMObjectBase *obj, NSString *propName, id val) { } // getter/setter for unmanaged object -static id RLMAccessorUnmanagedGetter(RLMProperty *prop, const char *) { +id unmanagedGetter(RLMProperty *prop, const char *) { // only override getters for RLMArray and linking objects properties if (prop.type == RLMPropertyTypeArray) { NSString *objectClassName = prop.objectClassName; NSString *propName = prop.name; return ^(RLMObjectBase *obj) { - id val = RLMSuperGet(obj, propName); + id val = superGet(obj, propName); if (!val) { val = [[RLMArray alloc] initWithObjectClassName:objectClassName]; - RLMSuperSet(obj, propName, val); + superSet(obj, propName, val); } return val; }; } - else if (prop.type == RLMPropertyTypeLinkingObjects) { - return ^(RLMObjectBase *){ - return [RLMResults emptyDetachedResults]; - }; + if (prop.type == RLMPropertyTypeLinkingObjects) { + return ^(RLMObjectBase *) { return [RLMResults emptyDetachedResults]; }; } return nil; } -static id RLMAccessorUnmanagedSetter(RLMProperty *prop, const char *) { +id unmanagedSetter(RLMProperty *prop, const char *) { if (prop.type != RLMPropertyTypeArray) { return nil; } @@ -489,51 +399,13 @@ static id RLMAccessorUnmanagedSetter(RLMProperty *prop, const char *) { // make copy when setting (as is the case for all other variants) RLMArray *standaloneAr = [[RLMArray alloc] initWithObjectClassName:objectClassName]; [standaloneAr addObjects:ar]; - RLMSuperSet(obj, propName, standaloneAr); + superSet(obj, propName, standaloneAr); }; } -// implement the class method className on accessors to return the className of the -// base object -void RLMReplaceClassNameMethod(Class accessorClass, NSString *className) { - Class metaClass = object_getClass(accessorClass); - IMP imp = imp_implementationWithBlock(^(Class){ return className; }); - class_addMethod(metaClass, @selector(className), imp, "@@:"); -} - -// implement the shared schema method -void RLMReplaceSharedSchemaMethod(Class accessorClass, RLMObjectSchema *schema) { - Class metaClass = object_getClass(accessorClass); - IMP imp = imp_implementationWithBlock(^(Class cls) { - if (cls == accessorClass) { - return schema; - } - - // If we aren't being called directly on the class this was overriden - // for, the class is either a subclass which we haven't initialized yet, - // or it's a runtime-generated class which should use the parent's - // schema. We check for the latter by checking if the immediate - // descendent of the desired class is a class generated by us (there - // may be further subclasses not generated by us for things like KVO). - Class parent = class_getSuperclass(cls); - while (parent != accessorClass) { - cls = parent; - parent = class_getSuperclass(cls); - } - - static const char accessorClassPrefix[] = "RLM:"; - if (!strncmp(class_getName(cls), accessorClassPrefix, sizeof(accessorClassPrefix) - 1)) { - return schema; - } - - return [RLMSchema sharedSchemaForClass:cls]; - }); - class_addMethod(metaClass, @selector(sharedSchema), imp, "@@:"); -} - -static void addMethod(Class cls, __unsafe_unretained RLMProperty *const prop, - id (*getter)(RLMProperty *, const char *), - id (*setter)(RLMProperty *, const char *)) { +void addMethod(Class cls, __unsafe_unretained RLMProperty *const prop, + id (*getter)(RLMProperty *, const char *), + id (*setter)(RLMProperty *, const char *)) { SEL sel = prop.getterSel; auto getterMethod = class_getInstanceMethod(cls, sel); if (!getterMethod) { @@ -557,11 +429,11 @@ static void addMethod(Class cls, __unsafe_unretained RLMProperty *const prop, } } -static Class RLMCreateAccessorClass(Class objectClass, - RLMObjectSchema *schema, - const char *accessorClassName, - id (*getterGetter)(RLMProperty *, const char *), - id (*setterGetter)(RLMProperty *, const char *)) { +Class createAccessorClass(Class objectClass, + RLMObjectSchema *schema, + const char *accessorClassName, + id (*getterGetter)(RLMProperty *, const char *), + id (*setterGetter)(RLMProperty *, const char *)) { REALM_ASSERT_DEBUG(RLMIsObjectOrSubclass(objectClass)); // create and register proxy class which derives from object class @@ -584,14 +456,56 @@ static Class RLMCreateAccessorClass(Class objectClass, return accClass; } +} // anonymous namespace + +#pragma mark - Public Interface Class RLMManagedAccessorClassForObjectClass(Class objectClass, RLMObjectSchema *schema, const char *name) { - return RLMCreateAccessorClass(objectClass, schema, name, RLMAccessorGetter, RLMAccessorSetter); + return createAccessorClass(objectClass, schema, name, managedGetter, managedSetter); } Class RLMUnmanagedAccessorClassForObjectClass(Class objectClass, RLMObjectSchema *schema) { - return RLMCreateAccessorClass(objectClass, schema, [@"RLM:Unmanaged " stringByAppendingString:schema.className].UTF8String, - RLMAccessorUnmanagedGetter, RLMAccessorUnmanagedSetter); + return createAccessorClass(objectClass, schema, + [@"RLM:Unmanaged " stringByAppendingString:schema.className].UTF8String, + unmanagedGetter, unmanagedSetter); +} + +// implement the class method className on accessors to return the className of the +// base object +void RLMReplaceClassNameMethod(Class accessorClass, NSString *className) { + Class metaClass = object_getClass(accessorClass); + IMP imp = imp_implementationWithBlock(^(Class){ return className; }); + class_addMethod(metaClass, @selector(className), imp, "@@:"); +} + +// implement the shared schema method +void RLMReplaceSharedSchemaMethod(Class accessorClass, RLMObjectSchema *schema) { + Class metaClass = object_getClass(accessorClass); + IMP imp = imp_implementationWithBlock(^(Class cls) { + if (cls == accessorClass) { + return schema; + } + + // If we aren't being called directly on the class this was overriden + // for, the class is either a subclass which we haven't initialized yet, + // or it's a runtime-generated class which should use the parent's + // schema. We check for the latter by checking if the immediate + // descendent of the desired class is a class generated by us (there + // may be further subclasses not generated by us for things like KVO). + Class parent = class_getSuperclass(cls); + while (parent != accessorClass) { + cls = parent; + parent = class_getSuperclass(cls); + } + + static const char accessorClassPrefix[] = "RLM:"; + if (!strncmp(class_getName(cls), accessorClassPrefix, sizeof(accessorClassPrefix) - 1)) { + return schema; + } + + return [RLMSchema sharedSchemaForClass:cls]; + }); + class_addMethod(metaClass, @selector(sharedSchema), imp, "@@:"); } void RLMDynamicValidatedSet(RLMObjectBase *obj, NSString *propName, id val) { @@ -609,78 +523,34 @@ void RLMDynamicValidatedSet(RLMObjectBase *obj, NSString *propName, id val) { val, propName, obj->_objectSchema.className); } - RLMDynamicSet(obj, prop, RLMCoerceToNil(val), RLMCreationOptionsPromoteUnmanaged); + RLMDynamicSet(obj, prop, RLMCoerceToNil(val)); } // Precondition: the property is not a primary key -void RLMDynamicSet(__unsafe_unretained RLMObjectBase *const obj, __unsafe_unretained RLMProperty *const prop, - __unsafe_unretained id const val, RLMCreationOptions creationOptions) { +void RLMDynamicSet(__unsafe_unretained RLMObjectBase *const obj, + __unsafe_unretained RLMProperty *const prop, + __unsafe_unretained id const val) { REALM_ASSERT_DEBUG(!prop.isPrimary); - bool setDefault = creationOptions & RLMCreationOptionsSetDefault; - - auto col = obj->_info->tableColumn(prop); - RLMWrapSetter(obj, prop.name, [&] { - switch (prop.type) { - case RLMPropertyTypeInt: RLMSetValue(obj, col, (NSNumber *)val, setDefault); break; - case RLMPropertyTypeFloat: RLMSetValue(obj, col, (NSNumber *)val, setDefault); break; - case RLMPropertyTypeDouble: RLMSetValue(obj, col, (NSNumber *)val, setDefault); break; - case RLMPropertyTypeBool: RLMSetValue(obj, col, (NSNumber *)val, setDefault); break; - case RLMPropertyTypeString: RLMSetValue(obj, col, (NSString *)val, setDefault); break; - case RLMPropertyTypeDate: RLMSetValue(obj, col, (NSDate *)val, setDefault); break; - case RLMPropertyTypeData: RLMSetValue(obj, col, (NSData *)val, setDefault); break; - case RLMPropertyTypeObject: { - if (!val || val == NSNull.null) { - RLMSetValue(obj, col, (RLMObjectBase *)nil, setDefault); - } - else { - auto linkedObj = RLMGetLinkedObjectForValue(obj->_realm, prop.objectClassName, val, creationOptions); - RLMSetValue(obj, col, linkedObj, setDefault); - } - break; - } - case RLMPropertyTypeArray: - if (!val || val == NSNull.null) { - RLMSetValue(obj, col, (id)nil, setDefault); - } - else { - id rawLinks = val; - NSMutableArray *links = [NSMutableArray array]; - for (id rawLink in rawLinks) { - [links addObject:RLMGetLinkedObjectForValue(obj->_realm, prop.objectClassName, rawLink, creationOptions)]; - } - RLMSetValue(obj, col, links, setDefault); - } - break; - case RLMPropertyTypeAny: - RLMSetValue(obj, col, val, setDefault); - break; - case RLMPropertyTypeLinkingObjects: - @throw RLMException(@"Linking objects properties are read-only"); - } + realm::Object o(obj->_info->realm->_realm, *obj->_info->objectSchema, obj->_row); + RLMAccessorContext c(obj); + translateError([&] { + o.set_property_value(c, prop.name.UTF8String, val ?: NSNull.null, false); }); } id RLMDynamicGet(__unsafe_unretained RLMObjectBase *const obj, __unsafe_unretained RLMProperty *const prop) { - NSUInteger index = prop.index; - switch (prop.type) { - case RLMPropertyTypeInt: return getBoxed(obj, index); - case RLMPropertyTypeFloat: return getBoxed(obj, index); - case RLMPropertyTypeDouble: return getBoxed(obj, index); - case RLMPropertyTypeBool: return getBoxed(obj, index); - case RLMPropertyTypeString: return RLMGetString(obj, index); - case RLMPropertyTypeDate: return RLMGetDate(obj, index); - case RLMPropertyTypeData: return RLMGetData(obj, index); - case RLMPropertyTypeObject: return RLMGetLink(obj, index); - case RLMPropertyTypeArray: return RLMGetArray(obj, index); - case RLMPropertyTypeAny: return RLMGetAnyProperty(obj, index); - case RLMPropertyTypeLinkingObjects: return RLMGetLinkingObjects(obj, prop); - } + realm::Object o(obj->_realm->_realm, *obj->_info->objectSchema, obj->_row); + RLMAccessorContext c(obj); + c.currentProperty = prop; + return RLMCoerceToNil(o.get_property_value(c, prop.name.UTF8String)); } -id RLMDynamicGetByName(__unsafe_unretained RLMObjectBase *const obj, __unsafe_unretained NSString *const propName, bool asList) { +id RLMDynamicGetByName(__unsafe_unretained RLMObjectBase *const obj, + __unsafe_unretained NSString *const propName, bool asList) { RLMProperty *prop = obj->_objectSchema[propName]; if (!prop) { - @throw RLMException(@"Invalid property name '%@' for class '%@'.", propName, obj->_objectSchema.className); + @throw RLMException(@"Invalid property name '%@' for class '%@'.", + propName, obj->_objectSchema.className); } if (asList && prop.type == RLMPropertyTypeArray && prop.swiftIvar) { RLMListBase *list = object_getIvar(obj, prop.swiftIvar); @@ -692,3 +562,295 @@ id RLMDynamicGetByName(__unsafe_unretained RLMObjectBase *const obj, __unsafe_un return RLMDynamicGet(obj, prop); } + +RLMAccessorContext::RLMAccessorContext(RLMAccessorContext& parent, realm::Property const& property) +: _realm(parent._realm) +, _info(parent._info.linkTargetType(property)) +, _promote_existing(parent._promote_existing) +{ +} + +RLMAccessorContext::RLMAccessorContext(RLMRealm *realm, RLMClassInfo& info, bool promote) +: _realm(realm), _info(info), _promote_existing(promote) +{ +} + +RLMAccessorContext::RLMAccessorContext(__unsafe_unretained RLMObjectBase *const parent, + const realm::Property *prop) +: _realm(parent->_realm) +, _info(prop && prop->type == realm::PropertyType::Object ? parent->_info->linkTargetType(*prop) + : *parent->_info) +, _parentObject(parent) +{ +} + +id RLMAccessorContext::defaultValue(__unsafe_unretained NSString *const key) { + if (_nilHack) { + return nil; + } + if (!_defaultValues) { + _defaultValues = RLMDefaultValuesForObjectSchema(_info.rlmObjectSchema); + } + return _defaultValues[key]; +} + +static void validateValueForProperty(__unsafe_unretained id const obj, + __unsafe_unretained RLMProperty *const prop, + RLMClassInfo const& info) { + switch (prop.type) { + case RLMPropertyTypeString: + case RLMPropertyTypeBool: + case RLMPropertyTypeDate: + case RLMPropertyTypeInt: + case RLMPropertyTypeFloat: + case RLMPropertyTypeDouble: + case RLMPropertyTypeData: + if (!RLMIsObjectValidForProperty(obj, prop)) { + @throw RLMException(@"Invalid value '%@' for property '%@.%@'", + obj, info.rlmObjectSchema.className, prop.name); + } + break; + case RLMPropertyTypeObject: + break; + case RLMPropertyTypeArray: + if (obj && obj != NSNull.null && ![obj conformsToProtocol:@protocol(NSFastEnumeration)]) { + @throw RLMException(@"Array property value (%@) is not enumerable.", obj); + } + break; + case RLMPropertyTypeAny: + case RLMPropertyTypeLinkingObjects: + @throw RLMException(@"Invalid value '%@' for property '%@.%@'", + obj, info.rlmObjectSchema.className, prop.name); + } +} + + +id RLMAccessorContext::propertyValue(__unsafe_unretained id const obj, size_t propIndex, + __unsafe_unretained RLMProperty *const prop) { + _nilHack = false; + // Property value from an NSArray + if ([obj respondsToSelector:@selector(objectAtIndex:)]) { + return propIndex < [obj count] ? [obj objectAtIndex:propIndex] : nil; + } + + // Property value from an NSDictionary + if ([obj respondsToSelector:@selector(objectForKey:)]) { + return [obj objectForKey:prop.name]; + } + + // Property value from an instance of this object type + if ([obj isKindOfClass:_info.rlmObjectSchema.objectClass] && prop.swiftIvar) { + if (prop.type == RLMPropertyTypeArray) { + return static_cast(object_getIvar(obj, prop.swiftIvar))._rlmArray; + } + else { // optional + return static_cast(object_getIvar(obj, prop.swiftIvar)).underlyingValue; + } + } + + // Property value from some object that's KVC-compatible + id value = RLMValidatedValueForProperty(obj, [obj respondsToSelector:prop.getterSel] ? prop.getterName : prop.name, + _info.rlmObjectSchema.className); + // return value ?: NSNull.null; + + // FIXME: for compatiblity with existing code this does bad things to make + // it so that createOrUpdate: does not set existing properties to `nil` + // unless using the dictionary/array code path. Remove this in 3.0. + if (!value) { + validateValueForProperty(NSNull.null, prop, _info); + if (prop.isPrimary) + return NSNull.null; + _nilHack = true; + } + return value; +} + +id RLMAccessorContext::box(realm::List&& l) { + REALM_ASSERT(_parentObject); + REALM_ASSERT(currentProperty); + return [[RLMArrayLinkView alloc] initWithList:std::move(l) realm:_realm + parentInfo:_parentObject->_info + property:currentProperty]; +} + +id RLMAccessorContext::box(realm::Object&& o) { + return RLMCreateObjectAccessor(_realm, _info.linkTargetType(currentProperty.index), o.row().get_index()); +} + +id RLMAccessorContext::box(realm::RowExpr r) { + return RLMCreateObjectAccessor(_realm, _info, r.get_index()); +} + +id RLMAccessorContext::box(realm::Results&& r) { + return [RLMResults resultsWithObjectInfo:_realm->_info[currentProperty.objectClassName] + results:std::move(r)]; +} + +static void checkType(bool cond, __unsafe_unretained id v, + __unsafe_unretained NSString *const expected) { + if (__builtin_expect(!cond, 0)) { + @throw RLMException(@"Invalid value '%@' of type '%@' for expected type '%@'", + v, [v class], expected); + } +} + +template<> +realm::Timestamp RLMAccessorContext::unbox(__unsafe_unretained id const value, bool, bool) { + id v = RLMCoerceToNil(value); + checkType(!v || [v respondsToSelector:@selector(timeIntervalSinceReferenceDate)], v, @"date"); + return RLMTimestampForNSDate(v); +} + +// Checking for NSNumber here rather than the selectors like the other ones +// because NSString implements the same selectors and we don't want implicit +// conversions from string +template<> +bool RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool) { + checkType([v isKindOfClass:[NSNumber class]], v, @"bool"); + return [v boolValue]; +} +template<> +double RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool) { + checkType([v isKindOfClass:[NSNumber class]], v, @"double"); + return [v doubleValue]; +} +template<> +float RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool) { + checkType([v isKindOfClass:[NSNumber class]], v, @"float"); + return [v floatValue]; +} +template<> +long long RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool) { + checkType([v isKindOfClass:[NSNumber class]], v, @"int"); + return [v longLongValue]; +} +template<> +realm::BinaryData RLMAccessorContext::unbox(id v, bool, bool) { + v = RLMCoerceToNil(v); + checkType(!v || [v respondsToSelector:@selector(bytes)], v, @"data"); + return RLMBinaryDataForNSData(v); +} +template<> +realm::StringData RLMAccessorContext::unbox(id v, bool, bool) { + v = RLMCoerceToNil(v); + checkType(!v || [v respondsToSelector:@selector(UTF8String)], v, @"string"); + return RLMStringDataWithNSString(v); +} + +template +static auto to_optional(__unsafe_unretained id const value, Fn&& fn) { + id v = RLMCoerceToNil(value); + return v && v != NSNull.null ? realm::util::make_optional(fn(v)) : realm::util::none; +} + +template<> +realm::util::Optional RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool) { + return to_optional(v, [&](__unsafe_unretained id v) { + checkType([v respondsToSelector:@selector(boolValue)], v, @"bool?"); + return (bool)[v boolValue]; + }); +} +template<> +realm::util::Optional RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool) { + return to_optional(v, [&](__unsafe_unretained id v) { + checkType([v respondsToSelector:@selector(doubleValue)], v, @"double?"); + return [v doubleValue]; + }); +} +template<> +realm::util::Optional RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool) { + return to_optional(v, [&](__unsafe_unretained id v) { + checkType([v respondsToSelector:@selector(floatValue)], v, @"float?"); + return [v floatValue]; + }); +} +template<> +realm::util::Optional RLMAccessorContext::unbox(__unsafe_unretained id const v, bool, bool) { + return to_optional(v, [&](__unsafe_unretained id v) { + checkType([v respondsToSelector:@selector(longLongValue)], v, @"int?"); + return [v longLongValue]; + }); +} + +template<> +realm::RowExpr RLMAccessorContext::unbox(__unsafe_unretained id const v, bool create, bool update) { + RLMObjectBase *link = RLMDynamicCast(v); + if (!link) { + if (!create) + return realm::RowExpr(); + return RLMCreateObjectInRealmWithValue(_realm, _info.rlmObjectSchema.className, v, update)->_row; + } + + if (link.isInvalidated) { + if (create) { + @throw RLMException(@"Adding a deleted or invalidated object to a Realm is not permitted"); + } + else { + @throw RLMException(@"Object has been invalidated"); + } + } + + if (![link->_objectSchema.className isEqualToString:_info.rlmObjectSchema.className]) { + if (create && !_promote_existing) + return RLMCreateObjectInRealmWithValue(_realm, _info.rlmObjectSchema.className, link, update)->_row; + return link->_row; + } + + if (!link->_realm) { + if (!create) + return realm::RowExpr(); + if (!_promote_existing) + return RLMCreateObjectInRealmWithValue(_realm, _info.rlmObjectSchema.className, link, update)->_row; + RLMAddObjectToRealm(link, _realm, update); + } + else if (link->_realm != _realm) { + if (_promote_existing) + @throw RLMException(@"Object is already managed by another Realm. Use create instead to copy it into this Realm."); + return RLMCreateObjectInRealmWithValue(_realm, _info.rlmObjectSchema.className, v, update)->_row; + } + return link->_row; +} + +void RLMAccessorContext::will_change(realm::Row const& row, realm::Property const& prop) { + _observationInfo = RLMGetObservationInfo(nullptr, row.get_index(), _info); + if (_observationInfo) { + _kvoPropertyName = @(prop.name.c_str()); + _observationInfo->willChange(_kvoPropertyName); + } +} + +void RLMAccessorContext::did_change() { + if (_observationInfo) { + _observationInfo->didChange(_kvoPropertyName); + _kvoPropertyName = nil; + _observationInfo = nullptr; + } +} + +RLMOptionalId RLMAccessorContext::value_for_property(__unsafe_unretained id const obj, + std::string const&, size_t propIndex) { + auto prop = _info.rlmObjectSchema.properties[propIndex]; + id value = propertyValue(obj, propIndex, prop); + if (value) { + validateValueForProperty(value, prop, _info); + } + + if (_promote_existing && [obj isKindOfClass:_info.rlmObjectSchema.objectClass] && !prop.swiftIvar) { + // set the ivars for object and array properties to nil as otherwise the + // accessors retain objects that are no longer accessible via the properties + // this is mainly an issue when the object graph being added has cycles, + // as it's not obvious that the user has to set the *ivars* to nil to + // avoid leaking memory + if (prop.type == RLMPropertyTypeObject) { + ((void(*)(id, SEL, id))objc_msgSend)(obj, prop.setterSel, nil); + } + } + + return RLMOptionalId{value}; +} + +RLMOptionalId RLMAccessorContext::default_value_for_property(realm::ObjectSchema const&, + std::string const& prop) +{ + return RLMOptionalId{defaultValue(@(prop.c_str()))}; +} diff --git a/Realm/RLMArrayLinkView.mm b/Realm/RLMArrayLinkView.mm index e19064c2e5..a0bd63ff04 100644 --- a/Realm/RLMArrayLinkView.mm +++ b/Realm/RLMArrayLinkView.mm @@ -18,6 +18,7 @@ #import "RLMArray_Private.hpp" +#import "RLMAccessor.hpp" #import "RLMObjectSchema_Private.hpp" #import "RLMObjectStore.h" #import "RLMObject_Private.hpp" @@ -31,6 +32,7 @@ #import "list.hpp" #import "results.hpp" +#import "shared_realm.hpp" #import #import @@ -77,7 +79,8 @@ - (RLMArrayLinkView *)initWithList:(realm::List)list - (RLMArrayLinkView *)initWithParent:(__unsafe_unretained RLMObjectBase *const)parentObject property:(__unsafe_unretained RLMProperty *const)property { __unsafe_unretained RLMRealm *const realm = parentObject->_realm; - realm::List list(realm->_realm, parentObject->_row.get_linklist(parentObject->_info->tableColumn(property))); + auto col = parentObject->_info->tableColumn(property); + realm::List list(realm->_realm, parentObject->_row.get_linklist(col)); return [self initWithList:std::move(list) realm:realm parentInfo:parentObject->_info @@ -133,6 +136,9 @@ static void throwError(NSString *aggregateMethod) { RLMTypeToString((RLMPropertyType)e.column_type), e.column_name.data()); } + catch (std::logic_error const& e) { + @throw RLMException(e); + } } template @@ -145,26 +151,6 @@ static auto translateErrors(Function&& f, NSString *aggregateMethod=nil) { } } -static void validateObjectToAdd(__unsafe_unretained RLMArrayLinkView *const ar, - __unsafe_unretained RLMObject *const obj) { - if (!obj) { - @throw RLMException(@"Cannot add `nil` to RLMArray<%@>", ar->_objectClassName); - } - - NSString *objectClassName = obj->_objectSchema.className; - if (![objectClassName isEqualToString:ar->_objectClassName]) { - @throw RLMException(@"Cannot add object of type '%@' to RLMArray<%@>", - objectClassName, ar->_objectClassName); - } - - if (obj->_realm != ar.realm) { - [ar.realm addObject:obj]; - } - else if (obj->_realm && !obj->_row.is_attached()) { - @throw RLMException(@"Object has been deleted or invalidated."); - } -} - template static void changeArray(__unsafe_unretained RLMArrayLinkView *const ar, NSKeyValueChange kind, dispatch_block_t f, IndexSetFactory&& is) { @@ -250,35 +236,37 @@ - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state } - (id)objectAtIndex:(NSUInteger)index { - return RLMCreateObjectAccessor(_realm, *_objectInfo, - translateErrors([&] { return _backingList.get(index).get_index(); })); + return translateErrors([&] { + RLMAccessorContext context(_realm, *_objectInfo); + return _backingList.get(context, index); + }); } -static void RLMInsertObject(RLMArrayLinkView *ar, RLMObject *object, NSUInteger index) { +static void RLMInsertObject(RLMArrayLinkView *ar, id object, NSUInteger index) { if (index == NSUIntegerMax) { index = translateErrors([&] { return ar->_backingList.size(); }); } - validateObjectToAdd(ar, object); changeArray(ar, NSKeyValueChangeInsertion, index, ^{ - ar->_backingList.insert(index, object->_row.get_index()); + RLMAccessorContext context(ar->_realm, *ar->_objectInfo); + ar->_backingList.insert(context, index, object); }); } -- (void)addObject:(RLMObject *)object { +- (void)addObject:(id)object { RLMInsertObject(self, object, NSUIntegerMax); } -- (void)insertObject:(RLMObject *)object atIndex:(NSUInteger)index { +- (void)insertObject:(id)object atIndex:(NSUInteger)index { RLMInsertObject(self, object, index); } - (void)insertObjects:(id)objects atIndexes:(NSIndexSet *)indexes { changeArray(self, NSKeyValueChangeInsertion, indexes, ^{ NSUInteger index = [indexes firstIndex]; - for (RLMObject *obj in objects) { - validateObjectToAdd(self, obj); - _backingList.insert(index, obj->_row.get_index()); + RLMAccessorContext context(_realm, *_objectInfo); + for (id obj in objects) { + _backingList.insert(context, index, obj); index = [indexes indexGreaterThanIndex:index]; } }); @@ -301,9 +289,9 @@ - (void)removeObjectsAtIndexes:(NSIndexSet *)indexes { - (void)addObjectsFromArray:(NSArray *)array { changeArray(self, NSKeyValueChangeInsertion, NSMakeRange(self.count, array.count), ^{ - for (RLMObject *obj in array) { - validateObjectToAdd(self, obj); - _backingList.add(obj->_row.get_index()); + RLMAccessorContext context(_realm, *_objectInfo); + for (id obj in array) { + _backingList.add(context, obj); } }); } @@ -314,10 +302,10 @@ - (void)removeAllObjects { }); } -- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(RLMObject *)object { - validateObjectToAdd(self, object); +- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)object { changeArray(self, NSKeyValueChangeReplacement, index, ^{ - _backingList.set(index, object->_row.get_index()); + RLMAccessorContext context(_realm, *_objectInfo); + _backingList.set(context, index, object); }); } @@ -339,18 +327,11 @@ - (void)exchangeObjectAtIndex:(NSUInteger)index1 withObjectAtIndex:(NSUInteger)i }); } -- (NSUInteger)indexOfObject:(RLMObject *)object { - if (object.invalidated) { - @throw RLMException(@"Object has been deleted or invalidated"); - } - - // check that object types align - if (![_objectClassName isEqualToString:object->_objectSchema.className]) { - @throw RLMException(@"Object of type (%@) does not match RLMArray type (%@)", - object->_objectSchema.className, _objectClassName); - } - - return translateErrors([&] { return RLMConvertNotFound(_backingList.find(object->_row)); }); +- (NSUInteger)indexOfObject:(id)object { + return translateErrors([&] { + RLMAccessorContext context(_realm, *_objectInfo); + return RLMConvertNotFound(_backingList.find(context, object)); + }); } - (id)valueForKeyPath:(NSString *)keyPath { @@ -437,17 +418,15 @@ - (RLMResults *)objectsWithPredicate:(NSPredicate *)predicate { - (NSUInteger)indexOfObjectWithPredicate:(NSPredicate *)predicate { auto query = translateErrors([&] { return _backingList.get_query(); }); - query.and_query(RLMPredicateToQuery(predicate, _objectInfo->rlmObjectSchema, _realm.schema, _realm.group)); -#if REALM_VER_MAJOR >= 2 + query.and_query(RLMPredicateToQuery(predicate, _objectInfo->rlmObjectSchema, + _realm.schema, _realm.group)); + auto indexInTable = query.find(); if (indexInTable == realm::not_found) { return NSNotFound; } auto row = query.get_table()->get(indexInTable); return _backingList.find(row); -#else - return RLMConvertNotFound(query.find()); -#endif } - (NSArray *)objectsAtIndexes:(__unused NSIndexSet *)indexes { @@ -487,7 +466,7 @@ - (RLMNotificationToken *)addNotificationBlock:(void (^)(RLMArray *, RLMCollecti #pragma mark - Thread Confined Protocol Conformance - (std::unique_ptr)makeThreadSafeReference { - realm::ThreadSafeReference list_reference = _realm->_realm->obtain_thread_safe_reference(_backingList); + auto list_reference = _realm->_realm->obtain_thread_safe_reference(_backingList); return std::make_unique>(std::move(list_reference)); } @@ -504,7 +483,7 @@ + (instancetype)objectWithThreadSafeReference:(std::unique_ptr *>(reference.get())); auto list_reference = static_cast *>(reference.get()); - realm::List list = realm->_realm->resolve_thread_safe_reference(std::move(*list_reference)); + auto list = realm->_realm->resolve_thread_safe_reference(std::move(*list_reference)); if (!list.is_valid()) { return nil; } diff --git a/Realm/RLMArray_Private.hpp b/Realm/RLMArray_Private.hpp index 5db9abe9f6..ec568b4de6 100644 --- a/Realm/RLMArray_Private.hpp +++ b/Realm/RLMArray_Private.hpp @@ -47,6 +47,10 @@ class RLMObservationInfo; // @interface RLMArrayLinkView : RLMArray - (instancetype)initWithParent:(RLMObjectBase *)parentObject property:(RLMProperty *)property; +- (RLMArrayLinkView *)initWithList:(realm::List)list + realm:(__unsafe_unretained RLMRealm *const)realm + parentInfo:(RLMClassInfo *)parentInfo + property:(__unsafe_unretained RLMProperty *const)property; // deletes all objects in the RLMArray from their containing realms - (void)deleteObjectsFromRealm; diff --git a/Realm/RLMClassInfo.hpp b/Realm/RLMClassInfo.hpp index 69c822dafa..7d1d23e526 100644 --- a/Realm/RLMClassInfo.hpp +++ b/Realm/RLMClassInfo.hpp @@ -80,6 +80,9 @@ class RLMClassInfo { // Get the info for the target of the link at the given property index. RLMClassInfo &linkTargetType(size_t propertyIndex); + // Get the info for the target of the given property + RLMClassInfo &linkTargetType(realm::Property const& property); + void releaseTable() { m_table = nullptr; } private: diff --git a/Realm/RLMClassInfo.mm b/Realm/RLMClassInfo.mm index ca0ce84152..750c435e50 100644 --- a/Realm/RLMClassInfo.mm +++ b/Realm/RLMClassInfo.mm @@ -78,6 +78,11 @@ return *m_linkTargets[propertyIndex]; } +RLMClassInfo &RLMClassInfo::linkTargetType(realm::Property const& property) { + REALM_ASSERT(property.type == PropertyType::Object || property.type == PropertyType::Array); + return linkTargetType(&property - &objectSchema->persisted_properties[0]); +} + RLMSchemaInfo::impl::iterator RLMSchemaInfo::begin() noexcept { return m_objects.begin(); } RLMSchemaInfo::impl::iterator RLMSchemaInfo::end() noexcept { return m_objects.end(); } RLMSchemaInfo::impl::const_iterator RLMSchemaInfo::begin() const noexcept { return m_objects.begin(); } diff --git a/Realm/RLMObjectBase.mm b/Realm/RLMObjectBase.mm index 301e1e3f50..40bcc996c8 100644 --- a/Realm/RLMObjectBase.mm +++ b/Realm/RLMObjectBase.mm @@ -33,6 +33,7 @@ #import "RLMUtil.hpp" #import "object.hpp" +#import "shared_realm.hpp" using namespace realm; diff --git a/Realm/RLMObjectStore.h b/Realm/RLMObjectStore.h index 3d7f1942a9..6497551961 100644 --- a/Realm/RLMObjectStore.h +++ b/Realm/RLMObjectStore.h @@ -34,23 +34,6 @@ NS_ASSUME_NONNULL_BEGIN void RLMRealmCreateAccessors(RLMSchema *schema); -// -// Options for object creation -// -typedef NS_OPTIONS(NSUInteger, RLMCreationOptions) { - // Normal object creation - RLMCreationOptionsNone = 0, - // If the property is a link or array property, upsert the linked objects - // if they have a primary key, and insert them otherwise. - RLMCreationOptionsCreateOrUpdate = 1 << 0, - // Allow unmanaged objects to be promoted to managed objects - // if false objects are copied during object creation - RLMCreationOptionsPromoteUnmanaged = 1 << 1, - // Use the SetDefault instruction. - RLMCreationOptionsSetDefault = 1 << 2, -}; - - // // Adding, Removing, Getting Objects // @@ -72,7 +55,8 @@ NS_RETURNS_RETAINED; id _Nullable RLMGetObject(RLMRealm *realm, NSString *objectClassName, id _Nullable key) NS_RETURNS_RETAINED; // create object from array or dictionary -RLMObjectBase *RLMCreateObjectInRealmWithValue(RLMRealm *realm, NSString *className, id _Nullable value, bool createOrUpdate) +RLMObjectBase *RLMCreateObjectInRealmWithValue(RLMRealm *realm, NSString *className, + id _Nullable value, bool createOrUpdate) NS_RETURNS_RETAINED; diff --git a/Realm/RLMObjectStore.mm b/Realm/RLMObjectStore.mm index 6bc53c2eaa..990607ce26 100644 --- a/Realm/RLMObjectStore.mm +++ b/Realm/RLMObjectStore.mm @@ -18,7 +18,7 @@ #import "RLMObjectStore.h" -#import "RLMAccessor.h" +#import "RLMAccessor.hpp" #import "RLMArray_Private.hpp" #import "RLMListBase.h" #import "RLMObservation.hpp" @@ -85,7 +85,7 @@ void RLMInitializeSwiftAccessorGenerics(__unsafe_unretained RLMObjectBase *const } for (RLMProperty *prop in object->_objectSchema.swiftGenericProperties) { - if (prop->_type == RLMPropertyTypeArray) { + if (prop.type == RLMPropertyTypeArray) { RLMArray *array = [[RLMArrayLinkView alloc] initWithParent:object property:prop]; [object_getIvar(object, prop.swiftIvar) set_rlmArray:array]; } @@ -102,142 +102,6 @@ void RLMInitializeSwiftAccessorGenerics(__unsafe_unretained RLMObjectBase *const } } -static NSUInteger createRowForObject(RLMClassInfo const& info) { - try { - return info.table()->add_empty_row(); - } - catch (std::exception const& e) { - @throw RLMException(e); - } -} - -/* If a row exists with the specified primary key value, return its index. Otherwise, return `realm::not_found`. - * - * Precondition: `info` must refer to a class which has a primary key property - * Precondition: `primaryValue` is a validated property value that has been coerced to `nil` - */ -static NSUInteger getRowForObjectWithPrimaryKey(RLMClassInfo const& info, id primaryValue) { - REALM_ASSERT_DEBUG(info.propertyForPrimaryKey()); - - RLMProperty *const primaryProperty = info.propertyForPrimaryKey(); - const NSUInteger primaryPropertyColumn = info.tableColumn(primaryProperty); - - switch (primaryProperty.type) { - case RLMPropertyTypeString: - return info.table()->find_first_string(primaryPropertyColumn, RLMStringDataWithNSString(primaryValue)); - - case RLMPropertyTypeInt: - if (primaryValue) { - return info.table()->find_first_int(primaryPropertyColumn, [primaryValue longLongValue]); - } else { - return info.table()->find_first_null(primaryPropertyColumn); - } - - default: - REALM_UNREACHABLE(); - } -} - -/* Create a row with the specified primary key value and return its index. - * - * Precondition: `info` must refer to a class which has a valid primary key property - * Precondition: a write transaction is in progress - * Precondition: no row already exists with the specified `primaryValue` for this model - */ -static NSUInteger createRowForObjectWithPrimaryKey(RLMClassInfo const& info, id primaryValue) { - REALM_ASSERT_DEBUG(info.propertyForPrimaryKey()); - REALM_ASSERT_DEBUG(info.realm.inWriteTransaction); - REALM_ASSERT_DEBUG(getRowForObjectWithPrimaryKey(info, primaryValue) == realm::not_found); - - RLMProperty *const primaryProperty = info.propertyForPrimaryKey(); - const NSUInteger primaryColumnIndex = info.tableColumn(primaryProperty); - - // create row - const NSUInteger rowIndex = createRowForObject(info); - Row row = info.table()->get(rowIndex); - - // set value for primary key - RLMValidateValueForProperty(primaryValue, primaryProperty); - primaryValue = RLMCoerceToNil(primaryValue); - - try { - switch (primaryProperty.type) { - case RLMPropertyTypeString: - REALM_ASSERT_DEBUG(!primaryValue || [primaryValue isKindOfClass:NSString.class]); - row.set_string_unique(primaryColumnIndex, RLMStringDataWithNSString(primaryValue)); - break; - - case RLMPropertyTypeInt: - if (primaryValue) { - REALM_ASSERT_DEBUG([primaryValue isKindOfClass:NSNumber.class]); - row.set_int_unique(primaryColumnIndex, [primaryValue longLongValue]); - } else { - row.set_null(primaryColumnIndex); // FIXME: Use `set_null_unique` once Core supports it - } - break; - - default: - REALM_UNREACHABLE(); - } - } - catch (std::exception const& e) { - @throw RLMException(e); - } - return rowIndex; -} - -/* If a row exists with the specified primary key value, returns its index. Otherwise, creates a new row with the - * specified primary key value and returns its index. The out parameter `foundExisting` will be set to indicate - * whether or not a new row was created. - * - * Precondition: `info` must refer to a class which has a valid primary key property - * Precondition: a write transaction is in progress - */ -static NSUInteger createOrGetRowForObjectWithPrimaryKey(RLMClassInfo const& info, id primaryValue, - bool* foundExisting = nullptr) { - REALM_ASSERT_DEBUG(info.propertyForPrimaryKey()); - REALM_ASSERT_DEBUG(info.realm.inWriteTransaction); - - const NSUInteger existingRow = getRowForObjectWithPrimaryKey(info, primaryValue); - if (existingRow == realm::not_found) { - *foundExisting = false; - return createRowForObjectWithPrimaryKey(info, primaryValue); - } else { - *foundExisting = true; - return existingRow; - } -} - -/* If the class has a primary key, calls `valueForProperty` with that key and creates or gets the row with - * this primary key value. Otherwise if the class has no primary key, creates a new row. The out parameter - * `foundExisting` will be set to indicate whether or not a new row was created. - * - * Precondition: a write transaction is in progress - */ -template -static NSUInteger createOrGetRowForObject(RLMClassInfo const& info, F valueForProperty, - bool createOrUpdate, bool* foundExisting) { - // try to get existing row if this class has a primary key - if (RLMProperty *primaryProperty = info.propertyForPrimaryKey()) { - // get primary value - const id primaryValue = valueForProperty(primaryProperty); - - // search for existing object based on primary key type, creating a new row if one does not exist - NSUInteger rowIndex = createOrGetRowForObjectWithPrimaryKey(info, RLMCoerceToNil(primaryValue), foundExisting); - - // ensure that `createOrUpdate` is set if we found an existing row - if (*foundExisting && !createOrUpdate) { - @throw RLMException(@"Can't create object with existing primary key value '%@'.", primaryValue); - } - return rowIndex; - } - // if no existing, create row - else { - *foundExisting = false; - return createRowForObject(info); - } -} - void RLMAddObjectToRealm(__unsafe_unretained RLMObjectBase *const object, __unsafe_unretained RLMRealm *const realm, bool createOrUpdate) { @@ -249,7 +113,7 @@ void RLMAddObjectToRealm(__unsafe_unretained RLMObjectBase *const object, } if (object->_realm) { if (object->_realm == realm) { - // no-op + // Adding an object to the Realm it's already manged by is a no-op return; } // for differing realms users must explicitly create the object in the second realm @@ -259,66 +123,19 @@ void RLMAddObjectToRealm(__unsafe_unretained RLMObjectBase *const object, @throw RLMException(@"Cannot add an object with observers to a Realm"); } - // set the realm and schema - NSString *objectClassName = object->_objectSchema.className; - auto& info = realm->_info[objectClassName]; + auto& info = realm->_info[object->_objectSchema.className]; + RLMAccessorContext c{realm, info, true}; object->_info = &info; - object->_objectSchema = info.rlmObjectSchema; object->_realm = realm; - - // get or create row - bool foundExisting; - auto primaryGetter = [=](__unsafe_unretained RLMProperty *const p) { return [object valueForKey:p.name]; }; - object->_row = (*info.table())[createOrGetRowForObject(info, primaryGetter, createOrUpdate, &foundExisting)]; - - RLMCreationOptions creationOptions = RLMCreationOptionsPromoteUnmanaged; - if (createOrUpdate) { - creationOptions |= RLMCreationOptionsCreateOrUpdate; + object->_objectSchema = info.rlmObjectSchema; + try { + realm::Object::create(c, realm->_realm, *info.objectSchema, (id)object, + createOrUpdate, &object->_row); } - - // populate all properties - for (RLMProperty *prop in info.rlmObjectSchema.properties) { - // skip primary key when updating since it doesn't change - if (prop.isPrimary) - continue; - - // get object from ivar using key value coding - id value = nil; - if (prop.swiftIvar) { - if (prop.type == RLMPropertyTypeArray) { - value = static_cast(object_getIvar(object, prop.swiftIvar))._rlmArray; - } - else { // optional - value = static_cast(object_getIvar(object, prop.swiftIvar)).underlyingValue; - } - } - else if ([object respondsToSelector:prop.getterSel]) { - value = [object valueForKey:prop.getterName]; - } - - if (!value && !prop.optional) { - @throw RLMException(@"Nil value specified for required property '%@' in '%@'", - prop.name, info.rlmObjectSchema.className); - } - - // set the ivars for object and array properties to nil as otherwise the - // accessors retain objects that are no longer accessible via the properties - // this is mainly an issue when the object graph being added has cycles, - // as it's not obvious that the user has to set the *ivars* to nil to - // avoid leaking memory - if (prop.type == RLMPropertyTypeObject || prop.type == RLMPropertyTypeArray) { - if (!prop.swiftIvar) { - ((void(*)(id, SEL, id))objc_msgSend)(object, prop.setterSel, nil); - } - } - - // set in table with out validation - RLMDynamicSet(object, prop, RLMCoerceToNil(value), creationOptions); + catch (std::exception const& e) { + @throw RLMException(e); } - - // set to proper accessor class object_setClass(object, info.rlmObjectSchema.accessorClass); - RLMInitializeSwiftAccessorGenerics(object); } @@ -328,7 +145,7 @@ void RLMAddObjectToRealm(__unsafe_unretained RLMObjectBase *const object, if (createOrUpdate && RLMIsObjectSubclass([value class])) { RLMObjectBase *obj = value; - if ([obj->_objectSchema.className isEqualToString:className] && obj->_realm == realm) { + if (obj->_realm == realm && [obj->_objectSchema.className isEqualToString:className]) { // This is a no-op if value is an RLMObject of the same type already backed by the target realm. return value; } @@ -338,94 +155,23 @@ void RLMAddObjectToRealm(__unsafe_unretained RLMObjectBase *const object, @throw RLMException(@"Must provide a non-nil value."); } - // create the object auto& info = realm->_info[className]; - RLMObjectBase *object = RLMCreateManagedAccessor(info.rlmObjectSchema.accessorClass, realm, &info); - - RLMCreationOptions creationOptions = createOrUpdate ? RLMCreationOptionsCreateOrUpdate : RLMCreationOptionsNone; - - // create row, and populate - if (NSArray *array = RLMDynamicCast(value)) { - NSArray *props = info.rlmObjectSchema.properties; - if (array.count > props.count) { - @throw RLMException(@"Invalid array input: more values (%llu) than properties (%llu).", - (unsigned long long)array.count, (unsigned long long)props.count); - } + if ([value isKindOfClass:[NSArray class]] && [value count] > info.objectSchema->persisted_properties.size()) { + @throw RLMException(@"Invalid array input: more values (%llu) than properties (%llu).", + (unsigned long long)[value count], + (unsigned long long)info.objectSchema->persisted_properties.size()); + } - // get or create our accessor - bool foundExisting; - auto primaryGetter = [=](__unsafe_unretained RLMProperty *const p) { - auto index = [props indexOfObject:p]; - if (index >= array.count) { - @throw RLMException(@"Invalid array input: primary key must be present."); - } - return array[index]; - }; - object->_row = (*info.table())[createOrGetRowForObject(info, primaryGetter, - createOrUpdate, &foundExisting)]; - - // populate - NSUInteger i = 0; - for (id val in array) { - RLMProperty *prop = props[i++]; - - // skip primary key when updating since it doesn't change - if (prop.isPrimary) - continue; - - RLMValidateValueForProperty(val, prop); - RLMDynamicSet(object, prop, RLMCoerceToNil(val), creationOptions); - } + // create the object + RLMAccessorContext c{realm, info, false}; + RLMObjectBase *object = RLMCreateManagedAccessor(info.rlmObjectSchema.accessorClass, realm, &info); + try { + object->_row = realm::Object::create(c, realm->_realm, *info.objectSchema, + (id)value, createOrUpdate).row(); } - else { - __block bool foundExisting = false; - __block NSDictionary *defaultValues = nil; - __block bool usedDefault = false; - auto getValue = ^(RLMProperty *prop) { - id propValue; - if ([value respondsToSelector:prop.getterSel]) { - propValue = RLMValidatedValueForProperty(value, prop.getterName, - info.rlmObjectSchema.className); - } - else { - propValue = RLMValidatedValueForProperty(value, prop.name, info.rlmObjectSchema.className); - } - usedDefault = !propValue && !foundExisting; - if (usedDefault) { - if (!defaultValues) { - defaultValues = RLMDefaultValuesForObjectSchema(info.rlmObjectSchema); - } - propValue = defaultValues[prop.name]; - if (!propValue && (prop.type == RLMPropertyTypeObject || prop.type == RLMPropertyTypeArray)) { - propValue = NSNull.null; - } - } - return propValue; - }; - // get or create our accessor - object->_row = (*info.table())[createOrGetRowForObject(info, getValue, createOrUpdate, &foundExisting)]; - - // populate - for (RLMProperty *prop in info.rlmObjectSchema.properties) { - // skip primary key when updating since it doesn't change - if (prop.isPrimary) - continue; - - if (id propValue = getValue(prop)) { - // add SetDefault to creationoptions - RLMCreationOptions propertyCreationOptions = creationOptions; - if (usedDefault) { - propertyCreationOptions |= RLMCreationOptionsSetDefault; - } - RLMValidateValueForProperty(propValue, prop); - RLMDynamicSet(object, prop, RLMCoerceToNil(propValue), propertyCreationOptions); - } - else if (!foundExisting && !prop.optional) { - @throw RLMException(@"Property '%@' of object of type '%@' cannot be nil.", prop.name, info.rlmObjectSchema.className); - } - } + catch (std::exception const& e) { + @throw RLMException(e); } - RLMInitializeSwiftAccessorGenerics(object); return object; } @@ -441,7 +187,7 @@ void RLMDeleteObjectFromRealm(__unsafe_unretained RLMObjectBase *const object, // move last row to row we are deleting if (object->_row.is_attached()) { RLMTrackDeletions(realm, ^{ - object->_row.get_table()->move_last_over(object->_row.get_index()); + object->_row.move_last_over(); }); } @@ -484,53 +230,18 @@ void RLMDeleteAllObjectsFromRealm(RLMRealm *realm) { id RLMGetObject(RLMRealm *realm, NSString *objectClassName, id key) { RLMVerifyRealmRead(realm); - RLMClassInfo& info = realm->_info[objectClassName]; - auto primaryProperty = info.objectSchema->primary_key_property(); - if (!primaryProperty) { - @throw RLMException(@"%@ does not have a primary key", objectClassName); - } - - auto table = info.table(); - if (!table) { - // read-only realms may be missing tables since we can't add any - // missing ones on init - return nil; - } - - key = RLMCoerceToNil(key); - if (!key && !primaryProperty->is_nullable) { - @throw RLMException(@"Invalid null value for non-nullable primary key."); - } - - size_t row = realm::not_found; - switch (primaryProperty->type) { - case PropertyType::String: { - NSString *string = RLMDynamicCast(key); - if (!key || string) { - row = table->find_first_string(primaryProperty->table_column, RLMStringDataWithNSString(string)); - } else { - @throw RLMException(@"Invalid value '%@' of type '%@' for string primary key.", key, [key class]); - } - break; - } - case PropertyType::Int: - if (NSNumber *number = RLMDynamicCast(key)) { - row = table->find_first_int(primaryProperty->table_column, number.longLongValue); - } else if (!key) { - row = table->find_first_null(primaryProperty->table_column); - } else { - @throw RLMException(@"Invalid value '%@' of type '%@' for int primary key.", key, [key class]); - } - break; - default: - REALM_UNREACHABLE(); + RLMAccessorContext *c = nullptr; + auto& info = realm->_info[objectClassName]; + try { + auto obj = realm::Object::get_for_primary_key(*c, realm->_realm, *info.objectSchema, + key ?: NSNull.null); + if (!obj.is_valid()) + return nil; + return RLMCreateObjectAccessor(realm, info, obj.row()); } - - if (row == realm::not_found) { - return nil; + catch (std::exception const& e) { + @throw RLMException(e); } - - return RLMCreateObjectAccessor(realm, info, row); } RLMObjectBase *RLMCreateObjectAccessor(__unsafe_unretained RLMRealm *const realm, diff --git a/Realm/RLMOptionalBase.mm b/Realm/RLMOptionalBase.mm index 8aad1c8533..f48ec94216 100644 --- a/Realm/RLMOptionalBase.mm +++ b/Realm/RLMOptionalBase.mm @@ -49,7 +49,7 @@ - (void)setUnderlyingValue:(id)underlyingValue { if (_property.isPrimary) { @throw RLMException(@"Primary key can't be changed after an object is inserted."); } - RLMDynamicSet(_object, _property, underlyingValue, RLMCreationOptionsNone); + RLMDynamicSet(_object, _property, underlyingValue); } else { NSString *propertyName = _property.name; diff --git a/Realm/RLMResults.mm b/Realm/RLMResults.mm index a2d1f30c94..30081b6de7 100644 --- a/Realm/RLMResults.mm +++ b/Realm/RLMResults.mm @@ -18,6 +18,7 @@ #import "RLMResults_Private.h" +#import "RLMAccessor.hpp" #import "RLMArray_Private.hpp" #import "RLMCollection_Private.hpp" #import "RLMObjectSchema_Private.hpp" @@ -184,66 +185,45 @@ - (NSUInteger)indexOfObjectWithPredicate:(NSPredicate *)predicate { return NSNotFound; } - Query query = translateErrors([&] { return _results.get_query(); }); - query.and_query(RLMPredicateToQuery(predicate, _info->rlmObjectSchema, _realm.schema, _realm.group)); - query.sync_view_if_needed(); - -#if REALM_VER_MAJOR >= 2 - size_t indexInTable; - if (const auto& sort = _results.get_sort()) { - // A sort order is specified so we need to return the first match given that ordering. - TableView table_view = query.find_all(); - table_view.sort(sort); - if (!table_view.size()) { - return NSNotFound; - } - indexInTable = table_view.get_source_ndx(0); - } else { - indexInTable = query.find(); - } - if (indexInTable == realm::not_found) { - return NSNotFound; - } - return RLMConvertNotFound(_results.index_of(indexInTable)); -#else - TableView table_view; - if (const auto& sort = _results.get_sort()) { - // A sort order is specified so we need to return the first match given that ordering. - table_view = query.find_all(); - table_view.sort(sort); - } else { - table_view = query.find_all(0, -1, 1); - } - if (!table_view.size()) { - return NSNotFound; - } - return _results.index_of(table_view.get_source_ndx(0)); -#endif + return translateErrors([&] { + return RLMConvertNotFound(_results.index_of(RLMPredicateToQuery(predicate, _info->rlmObjectSchema, _realm.schema, _realm.group))); + }); } - (id)objectAtIndex:(NSUInteger)index { + RLMAccessorContext ctx(_realm, *_info); return translateErrors([&] { - return RLMCreateObjectAccessor(_realm, *_info, _results.get(index)); + return _results.get(ctx, index); }); } - (id)firstObject { - auto row = translateErrors([&] { return _results.first(); }); - return row ? RLMCreateObjectAccessor(_realm, *_info, *row) : nil; + if (!_info) { + return nil; + } + RLMAccessorContext ctx(_realm, *_info); + return translateErrors([&] { + return _results.first(ctx); + }); } - (id)lastObject { - auto row = translateErrors([&] { return _results.last(); }); - return row ? RLMCreateObjectAccessor(_realm, *_info, *row) : nil; + if (!_info) { + return nil; + } + RLMAccessorContext ctx(_realm, *_info); + return translateErrors([&] { + return _results.last(ctx); + }); } - (NSUInteger)indexOfObject:(RLMObject *)object { - if (!object || (!object->_realm && !object.invalidated)) { + if (!_info || !object || (!object->_realm && !object.invalidated)) { return NSNotFound; } - + RLMAccessorContext ctx(_realm, *_info); return translateErrors([&] { - return RLMConvertNotFound(_results.index_of(object->_row)); + return RLMConvertNotFound(_results.index_of(ctx, object)); }); } diff --git a/Realm/Tests/ArrayPropertyTests.m b/Realm/Tests/ArrayPropertyTests.m index e06fdc8301..6ea2186186 100644 --- a/Realm/Tests/ArrayPropertyTests.m +++ b/Realm/Tests/ArrayPropertyTests.m @@ -657,7 +657,7 @@ - (void)testValueForKey { XCTAssertEqualObjects([company.employees valueForKeyPath:@"@distinctUnionOfObjects.name"], (@[@"Joe"])); RLMAssertThrowsWithReasonMatching([company.employees valueForKeyPath:@"@sum.dogs.@sum.age"], @"Nested key paths.*not supported"); - + // unmanaged object company = [[CompanyObject alloc] init]; ages = [NSMutableArray array]; @@ -1011,12 +1011,26 @@ - (void)testAssignArrayProperty { ArrayPropertyObject *array = [ArrayPropertyObject createInRealm:realm withValue:@[@"arrayObject", @[], @[]]]; NSSet *stringSet = [NSSet setWithArray:@[[[StringObject alloc] initWithValue:@[@"a"]]]]; [array setValue:stringSet forKey:@"array"]; - XCTAssertEqualObjects([[array valueForKey:@"array"] valueForKey:@"stringCol"], [[stringSet allObjects] valueForKey:@"stringCol"]); + XCTAssertEqualObjects([[array valueForKey:@"array"] valueForKey:@"stringCol"], + [[stringSet allObjects] valueForKey:@"stringCol"]); [array setValue:[stringSet allObjects] forKey:@"array"]; - XCTAssertEqualObjects([[array valueForKey:@"array"] valueForKey:@"stringCol"], [[stringSet allObjects] valueForKey:@"stringCol"]); + XCTAssertEqualObjects([[array valueForKey:@"array"] valueForKey:@"stringCol"], + [[stringSet allObjects] valueForKey:@"stringCol"]); [realm commitWriteTransaction]; } +- (void)testAssignIncorrectType { + RLMRealm *realm = self.realmWithTestPath; + [realm beginWriteTransaction]; + ArrayPropertyObject *array = [ArrayPropertyObject createInRealm:realm + withValue:@[@"", @[@[@"a"]], @[@[@0]]]]; + RLMAssertThrowsWithReason(array.intArray = (id)array.array, + @"Object of type (StringObject) does not match List type"); + RLMAssertThrowsWithReason(array[@"intArray"] = array[@"array"], + @"Invalid property value"); + [realm cancelWriteTransaction]; +} + - (void)testNotificationSentInitially { RLMRealm *realm = self.realmWithTestPath; [realm beginWriteTransaction]; diff --git a/Realm/Tests/DynamicTests.m b/Realm/Tests/DynamicTests.m index 31de0b06b3..c31130166c 100644 --- a/Realm/Tests/DynamicTests.m +++ b/Realm/Tests/DynamicTests.m @@ -346,7 +346,7 @@ - (void)testLinkingObjects { XCTAssertTrue([person isEqualToObject:[child[@"parents"] firstObject]]); RLMAssertThrowsWithReason(person[@"parents"] = @[], - @"Linking objects properties are read-only"); + @"Cannot modify read-only property 'PersonObject.parents'"); [realm commitWriteTransaction]; } diff --git a/Realm/Tests/LinkTests.m b/Realm/Tests/LinkTests.m index b0edeb6bd3..1a5700cb0a 100644 --- a/Realm/Tests/LinkTests.m +++ b/Realm/Tests/LinkTests.m @@ -201,6 +201,7 @@ - (void)testCircularLinks { obj.next.data = @"b"; [realm commitWriteTransaction]; + XCTAssertEqual(1U, [CircleObject allObjectsInRealm:realm].count); CircleObject *obj1 = [CircleObject allObjectsInRealm:realm].firstObject; XCTAssertEqualObjects(obj1.data, @"b", @"data should be 'b'"); XCTAssertEqualObjects(obj1.data, obj.next.data, @"objects should be equal"); diff --git a/Realm/Tests/NotificationTests.m b/Realm/Tests/NotificationTests.m index 4513f315a9..6fbcc0c27c 100644 --- a/Realm/Tests/NotificationTests.m +++ b/Realm/Tests/NotificationTests.m @@ -934,7 +934,8 @@ - (void)testChangeAllPropertyTypes { XCTAssertEqualObjects(prop.name, _propertyNames[i]); XCTAssertNil(prop.previousValue); if ([prop.name isEqualToString:@"objectCol"]) { - XCTAssertTrue([prop.value isEqualToObject:_values[i]]); + XCTAssertTrue([prop.value isEqualToObject:_values[i]], + @"%d: %@ %@", (int)i, prop.value, _values[i]); } else { XCTAssertEqualObjects(prop.value, _values[i]); diff --git a/Realm/Tests/ObjectCreationTests.mm b/Realm/Tests/ObjectCreationTests.mm index e9c9039ec4..f582526289 100644 --- a/Realm/Tests/ObjectCreationTests.mm +++ b/Realm/Tests/ObjectCreationTests.mm @@ -393,14 +393,14 @@ - (void)testCreateWithInvalidArray { auto realm = RLMRealm.defaultRealm; [realm beginWriteTransaction]; - RLMAssertThrowsWithReasonMatching(([DogObject createInRealm:realm withValue:@[@"name", @"age"]]), - @"Invalid value 'age' for property 'age'"); - RLMAssertThrowsWithReasonMatching(([DogObject createInRealm:realm withValue:@[@"name", NSNull.null]]), - @"Invalid value '' for property 'age'"); - RLMAssertThrowsWithReasonMatching(([DogObject createInRealm:realm withValue:@[@"name", @5, @"too many values"]]), - @"Invalid array input: more values \\(3\\) than properties \\(2\\)."); - RLMAssertThrowsWithReasonMatching(([PrimaryStringObject createInRealm:realm withValue:@[]]), - @"Invalid array input: primary key must be present."); + RLMAssertThrowsWithReason(([DogObject createInRealm:realm withValue:@[@"name", @"age"]]), + @"Invalid value 'age' for property 'DogObject.age'"); + RLMAssertThrowsWithReason(([DogObject createInRealm:realm withValue:@[@"name", NSNull.null]]), + @"Invalid value '' for property 'DogObject.age'"); + RLMAssertThrowsWithReason(([DogObject createInRealm:realm withValue:@[@"name", @5, @"too many values"]]), + @"Invalid array input: more values (3) than properties (2)."); + RLMAssertThrowsWithReason(([PrimaryStringObject createInRealm:realm withValue:@[]]), + @"Missing value for property 'PrimaryStringObject.stringCol'"); [realm cancelWriteTransaction]; } @@ -463,10 +463,10 @@ - (void)testCreateWithInvalidDictionary { auto realm = RLMRealm.defaultRealm; [realm beginWriteTransaction]; - RLMAssertThrowsWithReasonMatching(([DogObject createInRealm:realm withValue:@{@"name": @"a", @"age": NSNull.null}]), - @"Invalid value '' for property 'age'"); + RLMAssertThrowsWithReason(([DogObject createInRealm:realm withValue:@{@"name": @"a", @"age": NSNull.null}]), + @"Invalid value '' for property 'DogObject.age'"); RLMAssertThrowsWithReasonMatching(([DogObject createInRealm:realm withValue:@{@"name": @"a", @"age": NSDate.date}]), - @"Invalid value '20.*' for property 'age'"); + @"Invalid value '20.*' for property 'DogObject.age'"); [realm cancelWriteTransaction]; } @@ -522,7 +522,7 @@ - (void)testCreateWithInvalidObject { // Same property names, but different types RLMAssertThrowsWithReasonMatching([BizzaroDog createInRealm:realm withValue:dog], - @"Invalid value 'Fido' for property 'dogName'"); + @"Invalid value 'Fido' for property 'BizzaroDog.dogName'"); [realm cancelWriteTransaction]; } @@ -856,7 +856,11 @@ - (void)testCreateOrUpdateWithMissingValuesAndNoExistingObject { auto realm = [RLMRealm defaultRealm]; [realm beginWriteTransaction]; RLMAssertThrowsWithReason([PrimaryStringObject createOrUpdateInRealm:realm withValue:@{@"stringCol": @"pk"}], - @"Property 'intCol' of object of type 'PrimaryStringObject' cannot be nil."); + @"Missing value for property 'PrimaryStringObject.intCol'"); + RLMAssertThrowsWithReason(([PrimaryStringObject createOrUpdateInRealm:realm + withValue:@{@"stringCol": @"pk", + @"intCol": NSNull.null}]), + @"Invalid value '' for property 'PrimaryStringObject.intCol'"); [realm cancelWriteTransaction]; } @@ -1066,7 +1070,7 @@ - (void)testAddToDifferentRealmThrows { auto co = [CompanyObject new]; [co.employees addObject:eo]; RLMAssertThrowsWithReason([realm2 addObject:co], - @"Can not add objects from a different Realm"); + @"Object is already managed by another Realm. Use create instead to copy it into this Realm."); XCTAssertEqual(co.realm, realm2); [realm cancelWriteTransaction]; @@ -1101,7 +1105,7 @@ - (void)testAddObjectWithNilValueForRequiredProperty { auto realm = [RLMRealm defaultRealm]; [realm beginWriteTransaction]; RLMAssertThrowsWithReason([realm addObject:[[RequiredPropertiesObject alloc] init]], - @"Nil value specified for required property 'stringCol' in 'RequiredPropertiesObject'"); + @"Invalid value '' for property 'RequiredPropertiesObject.stringCol'"); [realm cancelWriteTransaction]; } @@ -1186,7 +1190,7 @@ - (void)testAddObjectWithNilValueForRequiredPropertyDoesNotUseExistingValue { [PrimaryKeyAndRequiredString createInRealm:realm withValue:@[@0, @"value"]]; RLMAssertThrowsWithReason([realm addOrUpdateObject:[[PrimaryKeyAndRequiredString alloc] init]], - @"Nil value specified for required property"); + @"Invalid value '' for property 'PrimaryKeyAndRequiredString.value'"); [realm cancelWriteTransaction]; } @@ -1285,7 +1289,7 @@ - (void)testAddOrUpdateOnManagedObjectInDifferentRealmThrows { auto co = [PrimaryCompanyObject new]; [co.employees addObject:eo]; RLMAssertThrowsWithReason([realm2 addOrUpdateObject:co], - @"Can not add objects from a different Realm"); + @"Object is already managed by another Realm. Use create instead to copy it into this Realm."); XCTAssertEqual(co.realm, realm2); [realm cancelWriteTransaction]; diff --git a/Realm/Tests/ObjectTests.m b/Realm/Tests/ObjectTests.m index b38504d7b8..999752b990 100644 --- a/Realm/Tests/ObjectTests.m +++ b/Realm/Tests/ObjectTests.m @@ -350,7 +350,7 @@ - (void)testObjectSubclass { RLMAssertThrowsWithReasonMatching(linkObject.stringObjectCol = obj, @"Can't .*StringSubclassObject.*StringObject"); RLMAssertThrowsWithReasonMatching([linkObject.stringObjectArrayCol addObject:obj], - @"Cannot .*StringSubclassObject.*StringObject"); + @"Object of type .*StringSubclassObject.*does not match.*StringObject.*"); [realm commitWriteTransaction]; } @@ -1087,7 +1087,8 @@ - (void)testIsDeleted { RLMAssertThrowsWithReason([realm addObject:obj1], @"deleted or invalidated"); NSArray *propObject = @[@"", @[obj2], @[]]; - RLMAssertThrowsWithReason([ArrayPropertyObject createInRealm:realm withValue:propObject], @"deleted or invalidated"); + RLMAssertThrowsWithReason([ArrayPropertyObject createInRealm:realm withValue:propObject], + @"deleted or invalidated"); [realm commitWriteTransaction]; @@ -1272,11 +1273,11 @@ - (void)testObjectForKey { // wrong PK type RLMAssertThrowsWithReasonMatching([PrimaryStringObject objectForPrimaryKey:@0], - @"Invalid value '0' of type '.*Number.*' for string"); + @"Invalid value '0' of type '.*Number.*' for expected type 'string'"); RLMAssertThrowsWithReasonMatching([PrimaryStringObject objectForPrimaryKey:@[]], - @"of type '.*Array.*' for string"); + @"of type '.*Array.*' for expected type 'string'"); RLMAssertThrowsWithReasonMatching([PrimaryIntObject objectForPrimaryKey:@""], - @"Invalid value '' of type '.*String.*' for int"); + @"Invalid value '' of type '.*String.*' for expected type 'int'"); RLMAssertThrowsWithReason([PrimaryIntObject objectForPrimaryKey:NSNull.null], @"Invalid null value for non-nullable primary key."); RLMAssertThrowsWithReason([PrimaryIntObject objectForPrimaryKey:nil], diff --git a/Realm/Tests/Swift/SwiftLinkTests.swift b/Realm/Tests/Swift/SwiftLinkTests.swift index f7a41ecc78..5b2dea1064 100644 --- a/Realm/Tests/Swift/SwiftLinkTests.swift +++ b/Realm/Tests/Swift/SwiftLinkTests.swift @@ -114,6 +114,7 @@ class SwiftLinkTests: RLMTestCase { realm.add(source) try! realm.commitWriteTransaction() + XCTAssertNotNil(target.realm) XCTAssertEqual(1, target.backlinks!.count) XCTAssertEqual(1234, (target.backlinks!.firstObject() as! SwiftLinkSourceObject).id) } diff --git a/RealmSwift/Tests/ObjectTests.swift b/RealmSwift/Tests/ObjectTests.swift index 2691d412e3..9928f3cb57 100644 --- a/RealmSwift/Tests/ObjectTests.swift +++ b/RealmSwift/Tests/ObjectTests.swift @@ -222,7 +222,8 @@ class ObjectTests: TestCase { assertDifferentPropertyValues(DynamicDefaultObject(), DynamicDefaultObject()) let realm = try! Realm() try! realm.write { - assertDifferentPropertyValues(realm.create(DynamicDefaultObject.self), realm.create(DynamicDefaultObject.self)) + assertDifferentPropertyValues(realm.create(DynamicDefaultObject.self), + realm.create(DynamicDefaultObject.self)) } }