ObjectTests.swift 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796
  1. ////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright 2015 Realm Inc.
  4. //
  5. // Licensed under the Apache License, Version 2.0 (the "License");
  6. // you may not use this file except in compliance with the License.
  7. // You may obtain a copy of the License at
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing, software
  12. // distributed under the License is distributed on an "AS IS" BASIS,
  13. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. // See the License for the specific language governing permissions and
  15. // limitations under the License.
  16. //
  17. ////////////////////////////////////////////////////////////////////////////
  18. import XCTest
  19. import RealmSwift
  20. import Foundation
  21. private var dynamicDefaultSeed = 0
  22. private func nextDynamicDefaultSeed() -> Int {
  23. dynamicDefaultSeed += 1
  24. return dynamicDefaultSeed
  25. }
  26. class SwiftDynamicDefaultObject: Object {
  27. @objc dynamic var intCol = nextDynamicDefaultSeed()
  28. @objc dynamic var floatCol = Float(nextDynamicDefaultSeed())
  29. @objc dynamic var doubleCol = Double(nextDynamicDefaultSeed())
  30. @objc dynamic var dateCol = Date(timeIntervalSinceReferenceDate: TimeInterval(nextDynamicDefaultSeed()))
  31. @objc dynamic var stringCol = UUID().uuidString
  32. @objc dynamic var binaryCol = UUID().uuidString.data(using: .utf8)
  33. override static func primaryKey() -> String? {
  34. return "intCol"
  35. }
  36. }
  37. class ObjectTests: TestCase {
  38. // init() Tests are in ObjectCreationTests.swift
  39. // init(value:) tests are in ObjectCreationTests.swift
  40. func testRealm() {
  41. let standalone = SwiftStringObject()
  42. XCTAssertNil(standalone.realm)
  43. let realm = try! Realm()
  44. var persisted: SwiftStringObject!
  45. try! realm.write {
  46. persisted = realm.create(SwiftStringObject.self, value: [:])
  47. XCTAssertNotNil(persisted.realm)
  48. XCTAssertEqual(realm, persisted.realm!)
  49. }
  50. XCTAssertNotNil(persisted.realm)
  51. XCTAssertEqual(realm, persisted.realm!)
  52. dispatchSyncNewThread {
  53. autoreleasepool {
  54. XCTAssertNotEqual(try! Realm(), persisted.realm!)
  55. }
  56. }
  57. }
  58. func testObjectSchema() {
  59. let object = SwiftObject()
  60. let schema = object.objectSchema
  61. XCTAssert(schema as AnyObject is ObjectSchema)
  62. XCTAssert(schema.properties as AnyObject is [Property])
  63. XCTAssertEqual(schema.className, "SwiftObject")
  64. XCTAssertEqual(schema.properties.map { $0.name },
  65. ["boolCol", "intCol", "intEnumCol", "floatCol", "doubleCol", "stringCol", "binaryCol", "dateCol", "objectCol", "arrayCol"]
  66. )
  67. }
  68. func testObjectSchemaForObjectWithConvenienceInitializer() {
  69. let object = SwiftConvenienceInitializerObject(stringCol: "abc")
  70. let schema = object.objectSchema
  71. XCTAssert(schema as AnyObject is ObjectSchema)
  72. XCTAssert(schema.properties as AnyObject is [Property])
  73. XCTAssertEqual(schema.className, "SwiftConvenienceInitializerObject")
  74. XCTAssertEqual(schema.properties.map { $0.name }, ["stringCol"])
  75. }
  76. func testSharedSchemaUnmanaged() {
  77. let object = SwiftObject()
  78. XCTAssertEqual(type(of: object).sharedSchema(), SwiftObject.sharedSchema())
  79. }
  80. func testSharedSchemaManaged() {
  81. let object = SwiftObject()
  82. XCTAssertEqual(type(of: object).sharedSchema(), SwiftObject.sharedSchema())
  83. }
  84. func testInvalidated() {
  85. let object = SwiftObject()
  86. XCTAssertFalse(object.isInvalidated)
  87. let realm = try! Realm()
  88. try! realm.write {
  89. realm.add(object)
  90. XCTAssertFalse(object.isInvalidated)
  91. }
  92. try! realm.write {
  93. realm.deleteAll()
  94. XCTAssertTrue(object.isInvalidated)
  95. }
  96. XCTAssertTrue(object.isInvalidated)
  97. }
  98. func testDescription() {
  99. let object = SwiftObject()
  100. // swiftlint:disable line_length
  101. assertMatches(object.description, "SwiftObject \\{\n\tboolCol = 0;\n\tintCol = 123;\n\tintEnumCol = 1;\n\tfloatCol = 1\\.23;\n\tdoubleCol = 12\\.3;\n\tstringCol = a;\n\tbinaryCol = <.*61.*>;\n\tdateCol = 1970-01-01 00:00:01 \\+0000;\n\tobjectCol = SwiftBoolObject \\{\n\t\tboolCol = 0;\n\t\\};\n\tarrayCol = List<SwiftBoolObject> <0x[0-9a-f]+> \\(\n\t\n\t\\);\n\\}")
  102. let recursiveObject = SwiftRecursiveObject()
  103. recursiveObject.objects.append(recursiveObject)
  104. assertMatches(recursiveObject.description, "SwiftRecursiveObject \\{\n\tobjects = List<SwiftRecursiveObject> <0x[0-9a-f]+> \\(\n\t\t\\[0\\] SwiftRecursiveObject \\{\n\t\t\tobjects = List<SwiftRecursiveObject> <0x[0-9a-f]+> \\(\n\t\t\t\t\\[0\\] SwiftRecursiveObject \\{\n\t\t\t\t\tobjects = <Maximum depth exceeded>;\n\t\t\t\t\\}\n\t\t\t\\);\n\t\t\\}\n\t\\);\n\\}")
  105. let renamedObject = LinkToSwiftRenamedProperties1()
  106. renamedObject.linkA = SwiftRenamedProperties1()
  107. assertMatches(renamedObject.description, "LinkToSwiftRenamedProperties1 \\{\n\tlinkA = SwiftRenamedProperties1 \\{\n\t\tpropA = 0;\n\t\tpropB = ;\n\t\\};\n\tlinkB = \\(null\\);\n\tarray1 = List<SwiftRenamedProperties1> <0x[0-9a-f]+> \\(\n\t\n\t\\);\n\\}")
  108. assertMatches(renamedObject.linkA!.linking1.description, "LinkingObjects<LinkToSwiftRenamedProperties1> <0x[0-9a-f]+> \\(\n\n\\)")
  109. let realm = try! Realm()
  110. try! realm.write { realm.add(renamedObject) }
  111. assertMatches(renamedObject.description, "LinkToSwiftRenamedProperties1 \\{\n\tlinkA = SwiftRenamedProperties1 \\{\n\t\tpropA = 0;\n\t\tpropB = ;\n\t\\};\n\tlinkB = \\(null\\);\n\tarray1 = List<SwiftRenamedProperties1> <0x[0-9a-f]+> \\(\n\t\n\t\\);\n\\}")
  112. assertMatches(renamedObject.linkA!.linking1.description, "LinkingObjects<LinkToSwiftRenamedProperties1> <0x[0-9a-f]+> \\(\n\t\\[0\\] LinkToSwiftRenamedProperties1 \\{\n\t\tlinkA = SwiftRenamedProperties1 \\{\n\t\t\tpropA = 0;\n\t\t\tpropB = ;\n\t\t\\};\n\t\tlinkB = \\(null\\);\n\t\tarray1 = List<SwiftRenamedProperties1> <0x[0-9a-f]+> \\(\n\t\t\n\t\t\\);\n\t\\}\n\\)")
  113. // swiftlint:enable line_length
  114. }
  115. func testSchemaHasPrimaryKey() {
  116. XCTAssertNil(Object.primaryKey(), "primary key should default to nil")
  117. XCTAssertNil(SwiftStringObject.primaryKey())
  118. XCTAssertNil(SwiftStringObject().objectSchema.primaryKeyProperty)
  119. XCTAssertEqual(SwiftPrimaryStringObject.primaryKey()!, "stringCol")
  120. XCTAssertEqual(SwiftPrimaryStringObject().objectSchema.primaryKeyProperty!.name, "stringCol")
  121. }
  122. func testCannotUpdatePrimaryKey() {
  123. let realm = self.realmWithTestPath()
  124. let primaryKeyReason = "Primary key can't be changed .*after an object is inserted."
  125. let intObj = SwiftPrimaryIntObject()
  126. intObj.intCol = 1
  127. intObj.intCol = 0; // can change primary key unattached
  128. XCTAssertEqual(0, intObj.intCol)
  129. let optionalIntObj = SwiftPrimaryOptionalIntObject()
  130. optionalIntObj.intCol.value = 1
  131. optionalIntObj.intCol.value = 0; // can change primary key unattached
  132. XCTAssertEqual(0, optionalIntObj.intCol.value)
  133. let stringObj = SwiftPrimaryStringObject()
  134. stringObj.stringCol = "a"
  135. stringObj.stringCol = "b" // can change primary key unattached
  136. XCTAssertEqual("b", stringObj.stringCol)
  137. try! realm.write {
  138. realm.add(intObj)
  139. assertThrows(intObj.intCol = 2, reasonMatching: primaryKeyReason)
  140. assertThrows(intObj["intCol"] = 2, reasonMatching: primaryKeyReason)
  141. assertThrows(intObj.setValue(2, forKey: "intCol"), reasonMatching: primaryKeyReason)
  142. realm.add(optionalIntObj)
  143. assertThrows(optionalIntObj.intCol.value = 2, reasonMatching: "Cannot modify primary key")
  144. assertThrows(optionalIntObj["intCol"] = 2, reasonMatching: primaryKeyReason)
  145. assertThrows(optionalIntObj.setValue(2, forKey: "intCol"), reasonMatching: "Cannot modify primary key")
  146. realm.add(stringObj)
  147. assertThrows(stringObj.stringCol = "c", reasonMatching: primaryKeyReason)
  148. assertThrows(stringObj["stringCol"] = "c", reasonMatching: primaryKeyReason)
  149. assertThrows(stringObj.setValue("c", forKey: "stringCol"), reasonMatching: primaryKeyReason)
  150. }
  151. }
  152. func testIgnoredProperties() {
  153. XCTAssertEqual(Object.ignoredProperties(), [], "ignored properties should default to []")
  154. XCTAssertEqual(SwiftIgnoredPropertiesObject.ignoredProperties().count, 2)
  155. XCTAssertNil(SwiftIgnoredPropertiesObject().objectSchema["runtimeProperty"])
  156. }
  157. func testIndexedProperties() {
  158. XCTAssertEqual(Object.indexedProperties(), [], "indexed properties should default to []")
  159. XCTAssertEqual(SwiftIndexedPropertiesObject.indexedProperties().count, 8)
  160. let objectSchema = SwiftIndexedPropertiesObject().objectSchema
  161. XCTAssertTrue(objectSchema["stringCol"]!.isIndexed)
  162. XCTAssertTrue(objectSchema["intCol"]!.isIndexed)
  163. XCTAssertTrue(objectSchema["int8Col"]!.isIndexed)
  164. XCTAssertTrue(objectSchema["int16Col"]!.isIndexed)
  165. XCTAssertTrue(objectSchema["int32Col"]!.isIndexed)
  166. XCTAssertTrue(objectSchema["int64Col"]!.isIndexed)
  167. XCTAssertTrue(objectSchema["boolCol"]!.isIndexed)
  168. XCTAssertTrue(objectSchema["dateCol"]!.isIndexed)
  169. XCTAssertFalse(objectSchema["floatCol"]!.isIndexed)
  170. XCTAssertFalse(objectSchema["doubleCol"]!.isIndexed)
  171. XCTAssertFalse(objectSchema["dataCol"]!.isIndexed)
  172. }
  173. func testIndexedOptionalProperties() {
  174. XCTAssertEqual(Object.indexedProperties(), [], "indexed properties should default to []")
  175. XCTAssertEqual(SwiftIndexedOptionalPropertiesObject.indexedProperties().count, 8)
  176. XCTAssertTrue(SwiftIndexedOptionalPropertiesObject().objectSchema["optionalStringCol"]!.isIndexed)
  177. XCTAssertTrue(SwiftIndexedOptionalPropertiesObject().objectSchema["optionalDateCol"]!.isIndexed)
  178. XCTAssertTrue(SwiftIndexedOptionalPropertiesObject().objectSchema["optionalBoolCol"]!.isIndexed)
  179. XCTAssertTrue(SwiftIndexedOptionalPropertiesObject().objectSchema["optionalIntCol"]!.isIndexed)
  180. XCTAssertTrue(SwiftIndexedOptionalPropertiesObject().objectSchema["optionalInt8Col"]!.isIndexed)
  181. XCTAssertTrue(SwiftIndexedOptionalPropertiesObject().objectSchema["optionalInt16Col"]!.isIndexed)
  182. XCTAssertTrue(SwiftIndexedOptionalPropertiesObject().objectSchema["optionalInt32Col"]!.isIndexed)
  183. XCTAssertTrue(SwiftIndexedOptionalPropertiesObject().objectSchema["optionalInt64Col"]!.isIndexed)
  184. XCTAssertFalse(SwiftIndexedOptionalPropertiesObject().objectSchema["optionalDataCol"]!.isIndexed)
  185. XCTAssertFalse(SwiftIndexedOptionalPropertiesObject().objectSchema["optionalFloatCol"]!.isIndexed)
  186. XCTAssertFalse(SwiftIndexedOptionalPropertiesObject().objectSchema["optionalDoubleCol"]!.isIndexed)
  187. }
  188. func testDynamicDefaultPropertyValues() {
  189. func assertDifferentPropertyValues(_ obj1: SwiftDynamicDefaultObject, _ obj2: SwiftDynamicDefaultObject) {
  190. XCTAssertNotEqual(obj1.intCol, obj2.intCol)
  191. XCTAssertNotEqual(obj1.floatCol, obj2.floatCol)
  192. XCTAssertNotEqual(obj1.doubleCol, obj2.doubleCol)
  193. XCTAssertNotEqual(obj1.dateCol.timeIntervalSinceReferenceDate, obj2.dateCol.timeIntervalSinceReferenceDate,
  194. accuracy: 0.01)
  195. XCTAssertNotEqual(obj1.stringCol, obj2.stringCol)
  196. XCTAssertNotEqual(obj1.binaryCol, obj2.binaryCol)
  197. }
  198. assertDifferentPropertyValues(SwiftDynamicDefaultObject(), SwiftDynamicDefaultObject())
  199. let realm = try! Realm()
  200. try! realm.write {
  201. assertDifferentPropertyValues(realm.create(SwiftDynamicDefaultObject.self),
  202. realm.create(SwiftDynamicDefaultObject.self))
  203. }
  204. }
  205. func testValueForKey() {
  206. let test: (SwiftObject) -> Void = { object in
  207. XCTAssertEqual(object.value(forKey: "boolCol") as! Bool?, false)
  208. XCTAssertEqual(object.value(forKey: "intCol") as! Int?, 123)
  209. XCTAssertEqual(object.value(forKey: "floatCol") as! Float?, 1.23 as Float)
  210. XCTAssertEqual(object.value(forKey: "doubleCol") as! Double?, 12.3)
  211. XCTAssertEqual(object.value(forKey: "stringCol") as! String?, "a")
  212. let expected = object.value(forKey: "binaryCol") as! Data
  213. let actual = "a".data(using: String.Encoding.utf8)!
  214. XCTAssertEqual(expected, actual)
  215. XCTAssertEqual(object.value(forKey: "dateCol") as! Date?, Date(timeIntervalSince1970: 1))
  216. XCTAssertEqual((object.value(forKey: "objectCol")! as! SwiftBoolObject).boolCol, false)
  217. XCTAssert(object.value(forKey: "arrayCol")! is List<SwiftBoolObject>)
  218. }
  219. test(SwiftObject())
  220. let realm = try! Realm()
  221. try! realm.write {
  222. test(realm.create(SwiftObject.self, value: [:]))
  223. let addedObj = SwiftObject()
  224. realm.add(addedObj)
  225. test(addedObj)
  226. }
  227. }
  228. func testValueForKeyOptionals() {
  229. let test: (SwiftOptionalObject) -> Void = { object in
  230. XCTAssertNil(object.value(forKey: "optNSStringCol"))
  231. XCTAssertNil(object.value(forKey: "optStringCol"))
  232. XCTAssertNil(object.value(forKey: "optBinaryCol"))
  233. XCTAssertNil(object.value(forKey: "optDateCol"))
  234. XCTAssertNil(object.value(forKey: "optIntCol"))
  235. XCTAssertNil(object.value(forKey: "optInt8Col"))
  236. XCTAssertNil(object.value(forKey: "optInt16Col"))
  237. XCTAssertNil(object.value(forKey: "optInt32Col"))
  238. XCTAssertNil(object.value(forKey: "optInt64Col"))
  239. XCTAssertNil(object.value(forKey: "optFloatCol"))
  240. XCTAssertNil(object.value(forKey: "optDoubleCol"))
  241. XCTAssertNil(object.value(forKey: "optBoolCol"))
  242. XCTAssertNil(object.value(forKey: "optEnumCol"))
  243. }
  244. test(SwiftOptionalObject())
  245. let realm = try! Realm()
  246. try! realm.write {
  247. test(realm.create(SwiftOptionalObject.self, value: [:]))
  248. let addedObj = SwiftOptionalObject()
  249. realm.add(addedObj)
  250. test(addedObj)
  251. }
  252. }
  253. func testValueForKeyList() {
  254. let test: (SwiftListObject) -> Void = { object in
  255. XCTAssertNil((object.value(forKey: "int") as! List<Int>).first)
  256. XCTAssertNil((object.value(forKey: "int8") as! List<Int8>).first)
  257. XCTAssertNil((object.value(forKey: "int16") as! List<Int16>).first)
  258. XCTAssertNil((object.value(forKey: "int32") as! List<Int32>).first)
  259. XCTAssertNil((object.value(forKey: "int64") as! List<Int64>).first)
  260. XCTAssertNil((object.value(forKey: "float") as! List<Float>).first)
  261. XCTAssertNil((object.value(forKey: "double") as! List<Double>).first)
  262. XCTAssertNil((object.value(forKey: "string") as! List<String>).first)
  263. XCTAssertNil((object.value(forKey: "data") as! List<Data>).first)
  264. XCTAssertNil((object.value(forKey: "date") as! List<Date>).first)
  265. // The `as Any?` casts below are only to silence the warning about it
  266. // happening implicitly and are not functionally required
  267. XCTAssertNil((object.value(forKey: "intOpt") as! List<Int?>).first as Any?)
  268. XCTAssertNil((object.value(forKey: "int8Opt") as! List<Int8?>).first as Any?)
  269. XCTAssertNil((object.value(forKey: "int16Opt") as! List<Int16?>).first as Any?)
  270. XCTAssertNil((object.value(forKey: "int32Opt") as! List<Int32?>).first as Any?)
  271. XCTAssertNil((object.value(forKey: "int64Opt") as! List<Int64?>).first as Any?)
  272. XCTAssertNil((object.value(forKey: "floatOpt") as! List<Float?>).first as Any?)
  273. XCTAssertNil((object.value(forKey: "doubleOpt") as! List<Double?>).first as Any?)
  274. XCTAssertNil((object.value(forKey: "stringOpt") as! List<String?>).first as Any?)
  275. XCTAssertNil((object.value(forKey: "dataOpt") as! List<Data?>).first as Any?)
  276. XCTAssertNil((object.value(forKey: "dateOpt") as! List<Date?>).first as Any?)
  277. }
  278. test(SwiftListObject())
  279. let realm = try! Realm()
  280. try! realm.write {
  281. test(realm.create(SwiftListObject.self, value: [:]))
  282. let addedObj = SwiftListObject()
  283. realm.add(addedObj)
  284. test(addedObj)
  285. }
  286. }
  287. func testValueForKeyLinkingObjects() {
  288. let test: (SwiftDogObject) -> Void = { object in
  289. let owners = object.value(forKey: "owners") as! LinkingObjects<SwiftOwnerObject>
  290. if object.realm != nil {
  291. XCTAssertEqual(owners.first!.name, "owner name")
  292. }
  293. }
  294. let dog = SwiftDogObject()
  295. let owner = SwiftOwnerObject(value: ["owner name", dog])
  296. test(dog)
  297. let realm = try! Realm()
  298. try! realm.write {
  299. test(realm.create(SwiftOwnerObject.self, value: owner).dog!)
  300. realm.add(owner)
  301. test(dog)
  302. }
  303. }
  304. func testSettingUnmanagedObjectValuesWithSwiftDictionary() {
  305. let json: [String: Any] = ["name": "foo", "array": [["stringCol": "bar"]], "intArray": [["intCol": 50]]]
  306. let object = SwiftArrayPropertyObject()
  307. json.keys.forEach { key in
  308. object.setValue(json[key], forKey: key)
  309. }
  310. XCTAssertEqual(object.name, "foo")
  311. XCTAssertEqual(object.array[0].stringCol, "bar")
  312. XCTAssertEqual(object.intArray[0].intCol, 50)
  313. }
  314. func testSettingUnmanagedObjectValuesWithBadSwiftDictionary() {
  315. let json: [String: Any] = ["name": "foo", "array": [["stringCol": NSObject()]], "intArray": [["intCol": 50]]]
  316. let object = SwiftArrayPropertyObject()
  317. assertThrows({ json.keys.forEach { key in object.setValue(json[key], forKey: key) } }())
  318. }
  319. func setAndTestAllTypes(_ setter: (SwiftObject, Any?, String) -> Void,
  320. getter: (SwiftObject, String) -> (Any?), object: SwiftObject) {
  321. setter(object, true, "boolCol")
  322. XCTAssertEqual(getter(object, "boolCol") as! Bool?, true)
  323. setter(object, 321, "intCol")
  324. XCTAssertEqual(getter(object, "intCol") as! Int?, 321)
  325. setter(object, NSNumber(value: 32.1 as Float), "floatCol")
  326. XCTAssertEqual(getter(object, "floatCol") as! Float?, 32.1 as Float)
  327. setter(object, 3.21, "doubleCol")
  328. XCTAssertEqual(getter(object, "doubleCol") as! Double?, 3.21)
  329. setter(object, "z", "stringCol")
  330. XCTAssertEqual(getter(object, "stringCol") as! String?, "z")
  331. setter(object, "z".data(using: String.Encoding.utf8)! as Data, "binaryCol")
  332. let gotData = getter(object, "binaryCol") as! Data
  333. XCTAssertTrue(gotData == "z".data(using: String.Encoding.utf8)!)
  334. setter(object, Date(timeIntervalSince1970: 333), "dateCol")
  335. XCTAssertEqual(getter(object, "dateCol") as! Date?, Date(timeIntervalSince1970: 333))
  336. let boolObject = SwiftBoolObject(value: [true])
  337. setter(object, boolObject, "objectCol")
  338. assertEqual(getter(object, "objectCol") as? SwiftBoolObject, boolObject)
  339. XCTAssertEqual((getter(object, "objectCol") as! SwiftBoolObject).boolCol, true)
  340. let list = List<SwiftBoolObject>()
  341. list.append(boolObject)
  342. setter(object, list, "arrayCol")
  343. XCTAssertEqual((getter(object, "arrayCol") as! List<SwiftBoolObject>).count, 1)
  344. assertEqual((getter(object, "arrayCol") as! List<SwiftBoolObject>).first!, boolObject)
  345. list.removeAll()
  346. setter(object, list, "arrayCol")
  347. XCTAssertEqual((getter(object, "arrayCol") as! List<SwiftBoolObject>).count, 0)
  348. setter(object, [boolObject], "arrayCol")
  349. XCTAssertEqual((getter(object, "arrayCol") as! List<SwiftBoolObject>).count, 1)
  350. assertEqual((getter(object, "arrayCol") as! List<SwiftBoolObject>).first!, boolObject)
  351. setter(object, nil, "arrayCol")
  352. XCTAssertEqual((getter(object, "arrayCol") as! List<SwiftBoolObject>).count, 0)
  353. setter(object, [boolObject], "arrayCol")
  354. setter(object, NSNull(), "arrayCol")
  355. XCTAssertEqual((getter(object, "arrayCol") as! List<SwiftBoolObject>).count, 0)
  356. }
  357. func dynamicSetAndTestAllTypes(_ setter: (DynamicObject, Any?, String) -> Void,
  358. getter: (DynamicObject, String) -> (Any?), object: DynamicObject,
  359. boolObject: DynamicObject) {
  360. setter(object, true, "boolCol")
  361. XCTAssertEqual((getter(object, "boolCol") as! Bool), true)
  362. setter(object, 321, "intCol")
  363. XCTAssertEqual((getter(object, "intCol") as! Int), 321)
  364. setter(object, NSNumber(value: 32.1 as Float), "floatCol")
  365. XCTAssertEqual((getter(object, "floatCol") as! Float), 32.1 as Float)
  366. setter(object, 3.21, "doubleCol")
  367. XCTAssertEqual((getter(object, "doubleCol") as! Double), 3.21)
  368. setter(object, "z", "stringCol")
  369. XCTAssertEqual((getter(object, "stringCol") as! String), "z")
  370. setter(object, "z".data(using: String.Encoding.utf8)! as Data, "binaryCol")
  371. let gotData = getter(object, "binaryCol") as! Data
  372. XCTAssertTrue(gotData == "z".data(using: String.Encoding.utf8)!)
  373. setter(object, Date(timeIntervalSince1970: 333), "dateCol")
  374. XCTAssertEqual((getter(object, "dateCol") as! Date), Date(timeIntervalSince1970: 333))
  375. setter(object, boolObject, "objectCol")
  376. assertEqual((getter(object, "objectCol") as! DynamicObject), boolObject)
  377. XCTAssertEqual(((getter(object, "objectCol") as! DynamicObject)["boolCol"] as! Bool), true)
  378. setter(object, [boolObject], "arrayCol")
  379. XCTAssertEqual((getter(object, "arrayCol") as! List<DynamicObject>).count, 1)
  380. assertEqual((getter(object, "arrayCol") as! List<DynamicObject>).first!, boolObject)
  381. let list = getter(object, "arrayCol") as! List<DynamicObject>
  382. list.removeAll()
  383. setter(object, list, "arrayCol")
  384. XCTAssertEqual((getter(object, "arrayCol") as! List<DynamicObject>).count, 0)
  385. setter(object, [boolObject], "arrayCol")
  386. XCTAssertEqual((getter(object, "arrayCol") as! List<DynamicObject>).count, 1)
  387. assertEqual((getter(object, "arrayCol") as! List<DynamicObject>).first!, boolObject)
  388. setter(object, nil, "arrayCol")
  389. XCTAssertEqual((getter(object, "arrayCol") as! List<DynamicObject>).count, 0)
  390. }
  391. // Yields a read-write migration `SwiftObject` to the given block
  392. private func withMigrationObject(block: @escaping ((MigrationObject, Migration) -> Void)) {
  393. autoreleasepool {
  394. let realm = self.realmWithTestPath()
  395. try! realm.write {
  396. _ = realm.create(SwiftObject.self)
  397. }
  398. }
  399. autoreleasepool {
  400. var enumerated = false
  401. let configuration = Realm.Configuration(schemaVersion: 1, migrationBlock: { migration, _ in
  402. migration.enumerateObjects(ofType: SwiftObject.className()) { _, newObject in
  403. if let newObject = newObject {
  404. block(newObject, migration)
  405. enumerated = true
  406. }
  407. }
  408. })
  409. self.realmWithTestPath(configuration: configuration)
  410. XCTAssert(enumerated)
  411. }
  412. }
  413. func testSetValueForKey() {
  414. let setter: (Object, Any?, String) -> Void = { object, value, key in
  415. object.setValue(value, forKey: key)
  416. return
  417. }
  418. let getter: (Object, String) -> (Any?) = { object, key in
  419. object.value(forKey: key)
  420. }
  421. withMigrationObject { migrationObject, migration in
  422. let boolObject = migration.create("SwiftBoolObject", value: [true])
  423. self.dynamicSetAndTestAllTypes(setter, getter: getter, object: migrationObject, boolObject: boolObject)
  424. }
  425. setAndTestAllTypes(setter, getter: getter, object: SwiftObject())
  426. try! Realm().write {
  427. let persistedObject = try! Realm().create(SwiftObject.self, value: [:])
  428. self.setAndTestAllTypes(setter, getter: getter, object: persistedObject)
  429. }
  430. }
  431. func testSubscript() {
  432. let setter: (Object, Any?, String) -> Void = { object, value, key in
  433. object[key] = value
  434. return
  435. }
  436. let getter: (Object, String) -> (Any?) = { object, key in
  437. object[key]
  438. }
  439. withMigrationObject { migrationObject, migration in
  440. let boolObject = migration.create("SwiftBoolObject", value: [true])
  441. self.dynamicSetAndTestAllTypes(setter, getter: getter, object: migrationObject, boolObject: boolObject)
  442. }
  443. setAndTestAllTypes(setter, getter: getter, object: SwiftObject())
  444. try! Realm().write {
  445. let persistedObject = try! Realm().create(SwiftObject.self, value: [:])
  446. self.setAndTestAllTypes(setter, getter: getter, object: persistedObject)
  447. }
  448. }
  449. func testDynamicList() {
  450. let realm = try! Realm()
  451. let arrayObject = SwiftArrayPropertyObject()
  452. let str1 = SwiftStringObject()
  453. let str2 = SwiftStringObject()
  454. arrayObject.array.append(objectsIn: [str1, str2])
  455. try! realm.write {
  456. realm.add(arrayObject)
  457. }
  458. let dynamicArray = arrayObject.dynamicList("array")
  459. XCTAssertEqual(dynamicArray.count, 2)
  460. assertEqual(dynamicArray[0], str1)
  461. assertEqual(dynamicArray[1], str2)
  462. XCTAssertEqual(arrayObject.dynamicList("intArray").count, 0)
  463. assertThrows(arrayObject.dynamicList("noSuchList"))
  464. }
  465. func testObjectiveCTypeProperties() {
  466. let realm = try! Realm()
  467. var object: SwiftObjectiveCTypesObject!
  468. let now = NSDate()
  469. let data = "fizzbuzz".data(using: .utf8)! as Data as NSData
  470. try! realm.write {
  471. object = SwiftObjectiveCTypesObject()
  472. realm.add(object)
  473. object.stringCol = "Hello world!"
  474. object.dateCol = now
  475. object.dataCol = data
  476. }
  477. XCTAssertEqual("Hello world!", object.stringCol)
  478. XCTAssertEqual(now, object.dateCol)
  479. XCTAssertEqual(data, object.dataCol)
  480. }
  481. func testDeleteObservedObject() {
  482. let realm = try! Realm()
  483. realm.beginWrite()
  484. let object = realm.create(SwiftIntObject.self, value: [0])
  485. try! realm.commitWrite()
  486. let exp = expectation(description: "")
  487. let token = object.observe { change in
  488. if case .deleted = change {
  489. } else {
  490. XCTFail("expected .deleted, got \(change)")
  491. }
  492. exp.fulfill()
  493. }
  494. realm.beginWrite()
  495. realm.delete(object)
  496. try! realm.commitWrite()
  497. waitForExpectations(timeout: 2)
  498. token.invalidate()
  499. }
  500. func expectChange<T: Equatable, U: Equatable>(_ name: String, _ old: T?, _ new: U?) -> ((ObjectChange) -> Void) {
  501. let exp = expectation(description: "")
  502. return { change in
  503. if case .change(let properties) = change {
  504. XCTAssertEqual(properties.count, 1)
  505. if let prop = properties.first {
  506. XCTAssertEqual(prop.name, name)
  507. XCTAssertEqual(prop.oldValue as? T, old)
  508. XCTAssertEqual(prop.newValue as? U, new)
  509. }
  510. } else {
  511. XCTFail("expected .change, got \(change)")
  512. }
  513. exp.fulfill()
  514. }
  515. }
  516. func testModifyObservedObjectLocally() {
  517. let realm = try! Realm()
  518. realm.beginWrite()
  519. let object = realm.create(SwiftIntObject.self, value: [1])
  520. try! realm.commitWrite()
  521. let token = object.observe(expectChange("intCol", Int?.none, 2))
  522. try! realm.write {
  523. object.intCol = 2
  524. }
  525. waitForExpectations(timeout: 2)
  526. token.invalidate()
  527. }
  528. func testModifyObservedObjectRemotely() {
  529. let realm = try! Realm()
  530. realm.beginWrite()
  531. let object = realm.create(SwiftIntObject.self, value: [1])
  532. try! realm.commitWrite()
  533. let token = object.observe(expectChange("intCol", 1, 2))
  534. dispatchSyncNewThread {
  535. let realm = try! Realm()
  536. try! realm.write {
  537. realm.objects(SwiftIntObject.self).first!.intCol = 2
  538. }
  539. }
  540. realm.refresh()
  541. waitForExpectations(timeout: 0)
  542. token.invalidate()
  543. }
  544. func testListPropertyNotifications() {
  545. let realm = try! Realm()
  546. realm.beginWrite()
  547. let object = realm.create(SwiftRecursiveObject.self, value: [[]])
  548. try! realm.commitWrite()
  549. let token = object.observe(expectChange("objects", Int?.none, Int?.none))
  550. dispatchSyncNewThread {
  551. let realm = try! Realm()
  552. try! realm.write {
  553. let obj = realm.objects(SwiftRecursiveObject.self).first!
  554. obj.objects.append(obj)
  555. }
  556. }
  557. waitForExpectations(timeout: 2)
  558. token.invalidate()
  559. }
  560. func testOptionalPropertyNotifications() {
  561. let realm = try! Realm()
  562. let object = SwiftOptionalDefaultValuesObject()
  563. try! realm.write {
  564. realm.add(object)
  565. }
  566. var token = object.observe(expectChange("optIntCol", 1, 2))
  567. dispatchSyncNewThread {
  568. let realm = try! Realm()
  569. try! realm.write {
  570. realm.objects(SwiftOptionalDefaultValuesObject.self).first!.optIntCol.value = 2
  571. }
  572. }
  573. realm.refresh()
  574. waitForExpectations(timeout: 0)
  575. token.invalidate()
  576. token = object.observe(expectChange("optIntCol", 2, Int?.none))
  577. dispatchSyncNewThread {
  578. let realm = try! Realm()
  579. try! realm.write {
  580. realm.objects(SwiftOptionalDefaultValuesObject.self).first!.optIntCol.value = nil
  581. }
  582. }
  583. realm.refresh()
  584. waitForExpectations(timeout: 0)
  585. token.invalidate()
  586. token = object.observe(expectChange("optIntCol", Int?.none, 3))
  587. dispatchSyncNewThread {
  588. let realm = try! Realm()
  589. try! realm.write {
  590. realm.objects(SwiftOptionalDefaultValuesObject.self).first!.optIntCol.value = 3
  591. }
  592. }
  593. realm.refresh()
  594. waitForExpectations(timeout: 0)
  595. token.invalidate()
  596. }
  597. func testEqualityForObjectTypeWithPrimaryKey() {
  598. let realm = try! Realm()
  599. let pk = "123456"
  600. let testObject = SwiftPrimaryStringObject()
  601. testObject.stringCol = pk
  602. testObject.intCol = 12345
  603. let unmanaged = SwiftPrimaryStringObject()
  604. unmanaged.stringCol = pk
  605. unmanaged.intCol = 12345
  606. let otherObject = SwiftPrimaryStringObject()
  607. otherObject.stringCol = "not" + pk
  608. otherObject.intCol = 12345
  609. try! realm.write {
  610. realm.add([testObject, otherObject])
  611. }
  612. // Should not match an object that's not equal.
  613. XCTAssertNotEqual(testObject, otherObject)
  614. // Should not match an object whose fields are equal if it's not the same row in the database.
  615. XCTAssertNotEqual(testObject, unmanaged)
  616. // Should match an object that represents the same row.
  617. let retrievedObject = realm.object(ofType: SwiftPrimaryStringObject.self, forPrimaryKey: pk)!
  618. XCTAssertEqual(testObject, retrievedObject)
  619. XCTAssertEqual(testObject.hash, retrievedObject.hash)
  620. XCTAssertTrue(testObject.isSameObject(as: retrievedObject))
  621. }
  622. func testEqualityForObjectTypeWithoutPrimaryKey() {
  623. let realm = try! Realm()
  624. let pk = "123456"
  625. XCTAssertNil(SwiftStringObject.primaryKey())
  626. let testObject = SwiftStringObject()
  627. testObject.stringCol = pk
  628. let alias = testObject
  629. try! realm.write {
  630. realm.add(testObject)
  631. }
  632. XCTAssertEqual(testObject, alias)
  633. // Should not match an object even if it represents the same row.
  634. let retrievedObject = realm.objects(SwiftStringObject.self).first!
  635. XCTAssertNotEqual(testObject, retrievedObject)
  636. // Should be able to use `isSameObject(as:)` to check if same row in the database.
  637. XCTAssertTrue(testObject.isSameObject(as: retrievedObject))
  638. }
  639. func testRetrievingObjectWithRuntimeType() {
  640. let realm = try! Realm()
  641. let unmanagedStringObject = SwiftPrimaryStringObject()
  642. unmanagedStringObject.stringCol = UUID().uuidString
  643. let managedStringObject = SwiftPrimaryStringObject()
  644. managedStringObject.stringCol = UUID().uuidString
  645. // Add the object.
  646. try! realm.write {
  647. realm.add(managedStringObject)
  648. }
  649. // Shouldn't throw when using type(of:).
  650. XCTAssertNotNil(realm.object(ofType: type(of: unmanagedStringObject),
  651. forPrimaryKey: managedStringObject.stringCol))
  652. // Shouldn't throw when using type(of:).
  653. XCTAssertNotNil(realm.object(ofType: type(of: managedStringObject),
  654. forPrimaryKey: managedStringObject.stringCol))
  655. }
  656. func testRetrievingObjectsWithRuntimeType() {
  657. let realm = try! Realm()
  658. let unmanagedStringObject = SwiftStringObject()
  659. unmanagedStringObject.stringCol = "foo"
  660. let managedStringObject = SwiftStringObject()
  661. managedStringObject.stringCol = "bar"
  662. // Add the object.
  663. try! realm.write {
  664. realm.add(managedStringObject)
  665. }
  666. // Shouldn't throw when using type(of:).
  667. XCTAssertEqual(realm.objects(type(of: unmanagedStringObject)).count, 1)
  668. // Shouldn't throw when using type(of:).
  669. XCTAssertEqual(realm.objects(type(of: managedStringObject)).count, 1)
  670. }
  671. }