I'm using RealmSwift in my project.
When trying to update one of the parameters of an existing object, I get the following error:
Ambiguous reference to member 'create(_:value:update:)'
And here is the code I'm working on:
let newProduct = ShoppingBagObject(value: [product.id, product.name!, product.price!, product.oldPrice!, product.weight!, count])
try? realm?.write {
realm?.create(newProduct, value: ["count": 3], update: .modified)
}
I want to update the 'count' parameter only.
And here is the reference from realm.io official documentation:
try! realm.write {
realm.create(Book.self, value: ["id": 1, "price": 9000.0], update: .modified)
// the book's `title` property will remain unchanged.
}
EDIT:
Here is my model class:
class ShoppingBagObject: Object {
#objc dynamic var id = 0
#objc dynamic var name = ""
#objc dynamic var price = 0
#objc dynamic var oldPrice = 0
#objc dynamic var weight = 0
#objc dynamic var count = 1
override static func primaryKey() -> String? {
return "id"
}
}
What am I doing wrong ?
Can you try
try? realm?.write {
realm?.create(ShoppingBagObject.self, value: ["id":product.id, "count":3], update: .modified)
}
From the docs:
If a Book object with a primary key value of ‘1’ already exists in the
database, then that object will simply be updated. If it does not
exist, then a completely new Book object will be created and added to
the database.
You can also partially update objects with primary keys by passing
just a subset of the values you wish to update, along with the primary
key
Related
I'm building an object that stores a List<Int> (which Realm now handles in version 3+) and I want to be able to do a fast lookup, so am also keeping a dictionary of type [Int: Int] populated as I add items into the list.
Realm obviously can't store the dictionary, but it can store the List. This pattern works fine when I use it in my main object, but when I put it into an object that's referenced by the main Top object, the dictionary keeps getting reset to empty. To test this I created a simple project that only has a single View Controller, and the only thing it does is create the realm, create a main object, and add some items into a list in the sub-object, and print out the result (all in the viewDidLoad method).
Sample code that demonstrates the issue:
#objcMembers class Top: Object {
dynamic var id = UUID().uuidString
dynamic var inside: Inside? = Inside()
func addInside(_ value: Int) {
inside?.add(value)
}
}
#objcMembers class Inside: Object {
var name = UUID().uuidString
dynamic var list = List<Int>()
var dictionary = [Int: Int]()
override static func ignoredProperties() -> [String] { return ["dictionary", "name"] }
func add(_ value: Int) {
list.append(value)
dictionary[value] = value
print("Name: \(name); Dictionary: \(dictionary); List: \(list)")
}
}
override func viewDidLoad() {
super.viewDidLoad()
let realm = try! Realm()
try! realm.write {
realm.deleteAll()
}
let topObj = Top()
try! realm.write {
realm.add(topObj)
topObj.addInside(12)
topObj.addInside(32)
topObj.addInside(42)
}
}
The output of this in the console shows:
Name: 977DBC69-BEAA-4F33-BB38-60643DFEA72D; Dictionary: [12: 12]; List: List<int> <0x604000102f40> (
[0] 12
)
Name: 4EB8F28E-0401-4CB7-9E77-A9F769EFAC64; Dictionary: [32: 32]; List: List<int> <0x600000103f90> (
[0] 12,
[1] 32
)
Name: 453608E0-2DEA-4D43-AF73-929137D8E16A; Dictionary: [42: 42]; List: List<int> <0x600000104020> (
[0] 12,
[1] 32,
[2] 42
)
I added the UUID string into the Inside class and made it a non managed property also to see if it was merely a problem with Dictionary; but both have issues. This example shows that Realm is creating a new instance of the object every time I call a method on it - although I would expect the object to remain in memory because I am still referencing the Top object that holds it.
Is there a configuration that will tell Realm to stop creating new instances of the sub object in these simple cases? If I had reloaded the original Top object from the db I'd expect to have lost those ignored properties, but this just doesn't make sense to me. What else am I missing? Thanks for the help.
I have three classes, Folder, Object & Item.
For every Folder there are a list of objects, and for every Object there should be a list of items. However when creating an Object with a default list of items I am getting the following error;
*** Terminating app due to uncaught exception 'RLMException', reason: 'Attempting to create an object of type 'Item' with an existing
primary key value '1'.'
For each class I have a function that counts all the objects and increments the ID, this works fine but there must be something wrong with how I am adding Item to an Object.items list property.
Item
class Item: Object {
#objc dynamic var id: Int = 0
#objc dynamic var object: Object?
#objc dynamic var title: String = ""
override static func primaryKey() -> String? {
return "id"
}
func IncrementaID() -> Int{
let realm = try! Realm()
var num = 1
let allItems = realm.objects(Milestone.self)
if allItems.count >= 1 {
num = allItems.count + 1
}
return num
}
}
When creating an Object I am also trying creates Item's to append to the objects items property like so
func addObjectToFolder(_ title: String, folder: Folder) {
let object = Object()
object = object.IncrementaID()
object.title = title
let defaultList = ["1", "2", "3", "4", "5"]
for i in defaultList {
let item = Item()
item.id = item.IncrementaID()
item.title = i
object.items.append(item)
}
try! realm.write {
folder.objects.append(object)
}
}
Does an body know how I am going wrong when creating the default values for the Objects.items list ?
The value returned by your IncrementaID function only changes when the number of Milestone objects stored in your Realm changes. Your code contains a loop that creates multiple Item instances and initialized their id property to the value returned by IncrementaID. No Milestone objects are created or stored in the Realm between calls, so all of the Item objects end up with the same ID.
My suggestion: use the string representation of a UUID for your primary key, rather than trying to manage unique integers like this.
I'm currently having trouble querying data that I stored in an Inverse relationship. I have the following code
class Form: Object {
dynamic var id: String = NSUUID().uuidString
...
var answers = List<FormAnswer>()
override static func primaryKey() -> String? {
return "id"
}
...
}
and
class FormAnswer: Object {
dynamic var key = ""
dynamic var answer = ""
let form = LinkingObjects(fromType: Form.self, property: "answers")
override static func primaryKey() -> String? {
return "key"
}
}
When I create a FormAnswer object I do the following:
try! realm.write {
let answer = FormAnswer(value: ["key": key, "answer": answer, "form" : parentForm!]) // parentForm is of type "form"
realm.add(answer, update: true)
}
And when I try to query it, I get nothing!
let previousValue = realm.objects(FormAnswer.self).filter("key == %# AND ANY form.id == %#", key, parentForm!.id).first?.answer
I've checked the realm file with Realm Browser, and there's an entry for FormAnswer. But there are only 2 fields (key and answer) and there doesn't appear to be a link to my Form object.
Does anyone have any ideas on how I can fix this?
Thanks
LinkingObjects is a computed property and cannot be mutated directly. Instead you modify its values by changing the other side of the relationship.
Try:
try! realm.write {
parentForm.answers.add(FormAnswer(value: ["key": key, "answer": answer]))
}
This adds the new answer directly to the Form's answer list, and will result in the answer's form property containing parentForm.
Given the following data model, is there a way to update or set a child relationship without updating the (existing) child object?
Data Model
class Person: Object {
dynamic var id = 0
dynamic var dog: Dog?
override static func primaryKey() -> String? {
return "id"
}
}
class Dog: Object {
dynamic var id = 0
dynamic var name = ""
override static func primaryKey() -> String? {
return "id"
}
}
Code
let realm = Realm()
// query existing dog
let existingDog = realm.objectForPrimaryKey(Dog.self, key: 42)
print(existingDog) // id: 42, name: Rex
// create new dog and person
let newDog = Dog(value: ["id": 42, "name": "Pluto"])
let person = Person()
realm.write {
// this shouldn't update the existing dog
// i.e. the name of the dog with id: 42 in the database should still be "Rex"
person.dog = newDog
realm.add(person, update: true)
}
I've also tried to do this via realm.create but had no success either. Any ideas?
The id property in your Dog class is a primaryKey. Primary keys have the purpose to mark an entry in a Database as unique. So if you create an Object using the same primary key, you will override any existing data in the Database, which has the primary key.
I don't understand, what you exactly want to achieve, but there should be no reason for your Dog Table to have multiple Objects with the same primary key. That's not how Relational Databases are meant to work.
If you need however to have a Dog named Pluto and a Dog named Rex with the same id value, then should probably create a new attribute which is not unique (-> not marked as primary key).
For now I am using this fetching with Predicate to know if the same primary key value is already available:
Category class:
class Category: Object
{
dynamic var name = ""
override static func primaryKey() -> String? {
return "name"
}
}
Validating if Category Object with primary key (name) value already exists.
let predicate = NSPredicate(format: "name == %#", newCategoryName)
let realm = try Realm()
let categories = realm.objects(Category).filter(predicate).sorted("name")
if categories.count > 0
{
//Duplicate Object with newCategoryName found
}
Is there any simpler way?
Here's how you use it to check if there's already an object with that primary key:
let category = Realm().objectForPrimaryKey(Category.self, key: newCategoryName)
if (category != nil) {
//Duplicate Object with newCategoryName found
}
You can use Realm.add(_:update:) or Realm.create(_:update:) with update == true to update an existing object with the same primary key value. Alternatively, you can use Realm.objectForPrimaryKey(_:key:) to fetch an existing object from the primary key.