Many-to-one with primary key (unique constraint) - ios

I've got an Article and a Category model linked by a many-to-one relationship. However, the Category model has a unique constraint on the id property because it's the primary key as you can see below.
class Article: Object
{
dynamic var id: String = ""
dynamic var title: String = ""
dynamic var category: Category()
override static func primaryKey() -> String? {
return "id"
}
}
class Category: Object
{
dynamic var id: String = ""
dynamic var title: String = ""
override static func primaryKey() -> String? {
return "id"
}
}
This will work until an Article got the same Category and throw an exception because of the unique constraint.
How am I supposed to implement this kind of relationship ? Is there any built-in way to persist only the Category id and retrieve the corresponding Category ?
Thanks

As you can read in Realm doc (0.92.1), you have to use a List<Object> for a many-to-one relationship.
See this link :
http://realm.io/docs/swift/latest/
class Dog: Object {
dynamic var name = ""
dynamic var owner: Person? // Can be optional
}
class Person: Object {
... // other property declarations
let dogs = List<Dog>()
}
let someDogs = Realm().objects(Dog).filter("name contains 'Fido'")
jim.dogs.extend(someDogs)
jim.dogs.append(rex)
So in your case, I guess it should be something like that :
class Article: Object
{
dynamic var id: String = ""
dynamic var title: String = ""
override static func primaryKey() -> String? {
return "id"
}
}
class Category: Object
{
dynamic var id: String = ""
dynamic var title: String = ""
dynamic var articles = List<Article>()
override static func primaryKey() -> String? {
return "id"
}
}
If your Realm version is older :
class Category: Object
{
...
dynamic var categories = RLMArray(objectClassName: Article.className())
}

Related

Generic class 'SyncObject' requires that 'String' inherit from 'RealmSwiftObject'

I am using IceCream Library and want to achieve the following functionality.
In app delegate, I have written
syncEngine = SyncEngine(objects: [
SyncObject(type: Recipient.self, uListElementType: String.self),
SyncObject(type: SMSSchedule.self, uListElementType: Recipient.self),
SyncObject(type: Template.self),
SyncObject(type: ContactsGroup.self, uListElementType: Recipient.self)
])
where Recipient class is...
#objc class Recipient: Object {
#objc dynamic var rec_id = ""
#objc dynamic var firstName = ""
#objc dynamic var lastName = ""
var phoneNumbers = List<String>()
#objc dynamic var email = ""
#objc dynamic var colorTag = "#FFFFFFFF"
#objc dynamic var isDeleted = false // IceCream requirement
override static func primaryKey() -> String? {
return "rec_id"
}
// initialization code
}
Look at the phoneNumbers property. It is a list of string objects. However the SyncObject doesn't accept String as uListElementType. How can I solve this issue?

Realm: nested update issue for many to one relation

I am using Realm notification block for updating messages in a page.
let messageResult = realm.Object(MessageRealm.self)
notificationTokenMessage = messageResult.addNotificationBlock{ [weak self] (changes: RealmCollectionChange) in {
switch changes {
case .initial(_),
.update(_, _, _, _):
self?.tableView.reloadData()
default:
break
}
}
}
In MessageRealm class, there is a property, name author. An author is basically a UserRealm object.
Class MessageRealm extends Object {
dynamic var _id: String? = nil
dynamic var body: String? = nil
dynamic var author: UserRealm? = nil
override class func primaryKey() -> String? { return "_id" }
}
Class UserRealm extends Object {
dynamic var _id: String? = nil
dynamic var fullName: String? = nil
dynamic var status: String? = nil // 'online' or 'offline'
override class func primaryKey() -> String? { return "_id" }
}
When a new message is received from socket, the message page is updated as it gets notifications from Realm. But, when user update notification is received from socket, the Realm updates the message page. I don't want to update message page for an update in author object.
Probable Solutions:
Class MessageRealm extends Object {
dynamic var _id: String? = nil
dynamic var body: String? = nil
dynamic var author: UserRealm? = LinkingObjects(fromType: UserRealm.self, property: "messages")
override class func primaryKey() -> String? { return "_id" }
}
Class UserRealm extends Object {
dynamic var _id: String? = nil
dynamic var fullName: String? = nil
dynamic var status: String? = nil // 'online' or 'offline'
let messages = List<MessageRealm>()
override class func primaryKey() -> String? { return "_id" }
}
We can solve it using LinkingObjects. But, this inverse relation needs a direct relation to map. Am I right? So, need to have a property of List of Messages in User. And from MessageRealm I have to link to User. But this will be complicated to maintain.
Store author's id in MessageRealm as a foreign key like a traditional database.
What do you suggest?
How can we do normalization in Realm to avoid update issue?
Is there any convention or best practices to manage a bigger database? (I am aware of Tim's answer https://stackoverflow.com/a/31594548/2666902 )
In my opinion, the best solution would be keeping the author property as a one-to-one relationship from MessageRealm to UserRealm, since a single message can only have one author and creating an inverse relationship in UserRealm.
class MessageRealm: Object {
dynamic var _id: String? = nil
dynamic var body: String? = nil
dynamic var author: UserRealm? = nil
override class func primaryKey() -> String? { return "_id" }
}
class UserRealm: Object {
dynamic var _id: String? = nil
dynamic var fullName: String? = nil
let messages = LinkingObjects(fromType: MessageRealm.self, property: "author")
override class func primaryKey() -> String? { return "_id" }
}
This way, you only need to keep the author property of your messages updated and the messages property of UserRealm will automatically keep in sync, so any time you try to access it, you will see all MessageRealm objects, where the author equals the specific user.

EVReflection + Moya + Realm + RxSwift - Could not create an instance for type dict

I'm stuck putting all of the above together. I'll appreciate if I can get any input.
Here's my short setup:
typealias RealmObject = Object
/// Extension to ignore undefined keys when mapping
extension RealmObject : EVReflectable {
open override func setValue(_ value: Any?, forUndefinedKey key: String) { }
}
Sample Realm models:
class Product: RealmObject {
dynamic var productId: String = ""
let productLanguages = List<ProductLanguage>()
override static func primaryKey() -> String? {
return "productId"
}
}
class ProductLanguage: RealmObject {
dynamic var productLanguageId: String = ""
dynamic var languageCode: String = ""
dynamic var productName: String = ""
override static func primaryKey() -> String? {
return "productLanguageId"
}
}
To fetch product details I use Moya and RxSwift:
func getProduct(productItemKey: String) -> Observable<Product> {
return provider.request(.product(productId: productItemKey)).map(to: Product.self)
}
I think .map(to: Product.self) does not work with realm Lists out of the box. For each object inside the list I get an error:
ERROR: Could not create an instance for type
dict:{
CreateDate = "2015-10-12T11:11:50.013Z";
IngredientList = "Acao ingredient";
LanguageCode = "en-US";
ProductId = "d6bb0084-6838-11e5-9225-00ac14ef2300";
ProductLanguageId = "f96848d0-df77-4594-99b7-d390bb127891";
ProductName = Acao;
Tagline = "The smart drink - 100% organic, vegan energy booster with guara"
}
Is there any other way to map Moya response into Realm objects?
Thank you very much for any input!
Turns out it was a bug in EVReflection. Fixed in 4.17.0

Realm-iOS: Object reference set to nil after save

I have 2 classes: Company and Employee. Both inherit the Realm Object class.
class Company:Object {
var name:String = ""
var employee:Employee?
override static func primaryKey() -> String? {
return "name"
}
}
class Employee:Object {
var name:String = ""
var age:Int = 0
override static func primaryKey() -> String? {
return "name"
}
}
Populate the objects
var emp = Employee()
emp.name = "Sachin"
emp.age = 35
var comp = Company()
comp.name = "BCCI"
comp.employee = emp
println("Before: \(comp.employee)")
var realm = Realm()
realm.write {
println("Before Add: \(comp.employee)")
realm.add(comp, update: true)
println("In Block: \(comp.employee)")
}
println("After: \(comp.employee)")
RESULT:
Before: Employee {
name = Sachin;
age = 35;
}
Before Add: Employee {
name = Sachin;
age = 35;
}
In Block: nil
After: nil
QUESTION:
Why is the employee property of the Company object nil after the realm.add() operation? Any thoughts?
For all Realm Swift properties (except for List), you need to declare the properties as dynamic. Changing your model definitions to the following should help!
class Company:Object {
dynamic var name:String = ""
dynamic var employee:Employee?
override static func primaryKey() -> String? {
return "name"
}
}
class Employee:Object {
dynamic var name:String = ""
dynamic var age:Int = 0
override static func primaryKey() -> String? {
return "name"
}
}

How to set primary key in Swift for Realm model

I'm using Realm in a new iOS Swift project. I'm using Xcode 6.0.1 with iOS SDK 8.0 and Realm 0.85.0
I'm trying to use the new Realm primary key feature so I can do an addOrUpdateObject.
Here is a sample model:
import Foundation
import Realm
class Foo: RLMObject {
dynamic var id = 0
dynamic var title = ""
func primaryKey() -> Int {
return id
}
}
And how I'm trying to add/update a new object:
let foo = Foo()
foo.title = titleField.text
foo.id = 1
// Get the default Realm
let realm = RLMRealm.defaultRealm()
// Add to the Realm inside a transaction
realm.beginWriteTransaction()
realm.addOrUpdateObject(foo)
realm.commitWriteTransaction()
I get this error:
RLMExecption', reason: ''Foo' does not have a primary key and can not
be updated
Here are the docs on the primary key. I'm probably not setting it correctly:
http://realm.io/docs/cocoa/0.85.0/api/Classes/RLMObject.html#//api/name/primaryKey
Latest docs are here now:
https://realm.io/docs/objc/latest/api/Classes/RLMObject.html#//api/name/primaryKey
As of Realm Swift v10.10.0, you declare a primary key with #Persisted(primaryKey: true):
class Foo: Object {
#Persisted(primaryKey: true) var id = 0
#Persisted var title = ""
}
Older versions:
primaryKey needs to be a class function which returns the name of the property which is the primary key, not an instance method which returns the value of the primary key.
#objcMembers class Foo: RLMObject {
dynamic var id = 0
dynamic var title = ""
override class func primaryKey() -> String? {
return "id"
}
}
The return type of primaryKey() is optional:
class Foo: RLMObject {
dynamic var id = 0
dynamic var title = ""
override class func primaryKey() -> String? {
return "id"
}
}
For Swift 5:
import RealmSwift
class Signature: Object {
#objc dynamic var id = ""
override static func primaryKey() -> String? {
return "id"
}
}
To avoid: Terminating app due to uncaught exception 'RLMException', reason: 'Primary key property 'id' does not exist on object.
Realm 10.12.0 & Swift 5
Legacy property declarations using #objc:
import RealmSwift
class Signature: Object {
#objc dynamic var id = ""
override static func primaryKey() -> String? {
return "id"
}
}
When using #Persisted, use #Persisted(primaryKey: true) instead:
import Foundation
import RealmSwift
class MyModel: Object {
#Persisted var pan: String?
#Persisted var exp: String?
#Persisted var cvv: String?
#Persisted(primaryKey: true) var myId: String?
override init() {}
init(pan: String, exp: String, cvv2: String) {
super.init()
self.pan = pan
self.exp = exp
self.cvv = cvv2
}
}

Resources