How to add fields to a Realm model class - swift

My class has only 4 fields:
class List: Object {
dynamic var name = ""
dynamic var date = ""
dynamic var notes = ""
dynamic var info = ""
}
And I want to add 2 more:
dynamic var website = ""
dynamic var telephone = ""
If I add these fields to the class then Realm gives me an error due to the new fields. How can I update this class while saving all user data?

There are two alternatives to consider:
If your app is under development and has not been released yet, then you can delete the installed app and reinstall it. Realm will use the updated model class schema when recreating the database.
If your app is already released or you otherwise want to preserve the data in the Realm file, you can gracefully upgrade the existing Realm to the new schema. You can do this by performing a migration, which instructs Realm to update the schema on the file on disk to match the model classes in your application, and gives you a chance to perform any modifications you may need to the data within the Realm file to accommodate the changes in your model classes.

This happens as Realm creates an internal structure with your model. Each time you need to change your model (happens quite a lot), you need to migrate your current model to new one.
The way to do this is:
Make whatever changes you want to make to your Data Model
Add this inside your application(application:didFinishLaunchingWithOptions:) in app delegate
let config = Realm.Configuration(
schemaVersion: 1, //Increment this each time your schema changes
migrationBlock: { migration, oldSchemaVersion in
if (oldSchemaVersion < 1) {
//If you need to transfer any data
//(in your case you don't right now) you will transfer here
}
})
Realm.Configuration.defaultConfiguration = config
let realm = try! Realm()
Now each time you change your schema, you simply add one to the schemaVersion. Of course migrations can be trickier, but for your model, this will do the trick.
For more information refer Realm Swift Documentation

Related

Time Complexity of Realm Lists in Swift

I was wondering how the Realm Lists were implemented in swift. To be more specific, I was wondering if they are Linked Lists or if they work similarly to Array Lists. For example, what would the time complexity of retrieving an item at an index be. Let's say I have Realm List A of length m and I want to retrieve A[n], would this operation run in O(n) like a Linked List or O(k) like in an Array List.
Edit:
Here is a little more clarification to the question:
Let's say I want to store my user's friend list in a Realm Database. The way I would do this is by defining a new Class User as follows:
Class User: Object{
#objc dynamic var name: String?
#objc dynamic var userName: String?
#objc dynamic var email: String?
var friendList = RealmSwift.List<String>() //Contains the names of all friends
}
Let's say that I've added some friends to my user's friendList and somewhere else in my app I want to access this friend list and display a specific friend as such:
var realm = try! Realm()
var user = realm.objects(User.self)[0]
var friendToDisplay = user.friendList[4]
print(friendToDisplay)
I'm wondering what is the time complexity of the user.friendList[4] operation. If Realm would treat the RealmSwift.List as a linked list, then the complexity would have to be O(n) while if when user is called to memory, RealmSwift.List is implemented as an ArrayList then accessing the fourth memory address in the list would be O(1).
My curiosity comes from wondering which would be faster:
Looping through the List and finding the name of the friend we want to display as such:
var realm = try! Realm()
var user = realm.objects(User.self)[0]
var friendName = "Peter"
for friend in user.friendList{
if friend == friendName {
print(friend)
}
}
or
Knowing the index of the friend we want to display let's say index 4 as such:
var realm = try! Realm()
var user = realm.objects(User.self)[0]
var friendToDisplay = user.friendList[4]
print(friendToDisplay)
In both of the cases let's say that the friend's name or the index that we are using exist in the friendList List.
The question is not going to be directly answerable due to the number of variables involved and the inherent nature of Realm. I will provide some info but let me clarify one point that's important.
Realm doesn't support primitives in Lists (very well).
EDIT: Release 10.7 added support for filters/queries as well as aggregate functions on primitives so the below info is no longer completely valid. However, it's still something to be aware of.
So you won't want to do this
var friendList = RealmSwift.List<String>()
You will however want to define a FriendClass that holds details about your friend
class FriendClass: Object {
#objc dynamic var friendName = ""
}
and then do this
let friendList = List<FriendClass>()
Now on to your question:
Your question asks about finding a friend by looping through a list or knowing the index. While you can do either programmatically, neither is an ideal solution.
If you want to want to find your FriendClass object for your friend Peter, this is the fastest way to do it
if let peter = realm.objects(FriendClass.self).filter("friendName == 'Peter'").first {
//do something with the peter object
}
keep in mind that is working with ALL of the friend objects in realm. You can likewise perform the same thing on a specific list instead. So if your user object has a friendsList list:
if let peter = user.friendsList.filter("friendName == 'Peter'").first {
//do something with the peter object
}
Here's the variable bit: Realm objects are lazily loaded. This allows Realm to contain millions of objects but not load them into memory until they are needed. That prevents overwhelming the device.
That's a huge variable as things like disk access and if the object is already loaded can have an impact. However, using the technique I presented above, whether it's one or 10,000 friends in your friends list the query is virtually instantaneous.
One of our projects has 10GB of data to which a filter like shown above presents an immediate response from Realm.
Now here come the other variables:
Primary Keys
If your objects have primary keys you can directly read that object without messing about
let peter = realm.object(ofType: FriendClass.self, forPrimaryKey: "peters key")
For clarity, objects with primary keys take O(log n) time. n is the number of objects of that type in the database. In a nutshell, the time it takes to retrieve a friend is unrelated to the number total objects in Realm.
Indexing
Indexed objects make writes a tad slower but make queries faster which, in the above example, if name were set as a indexedProperty it would increase the over all read speed when querying on that property
Lists
A list is another layer if indirection which, generally speaking would be 'slower' than reading objects directly. That being said, 'slower' is highly subjective and relative to the use case. Taking .03 seconds to retrieve a friend from a list of 100,000 is not impactful.

CloudKit - How to retrieve the ckRecordID of the record just saved using CKModifyRecordsOperation

I'm using CoreData for keeping a local cache of records in CloudKit. When saving a new record, I do the following:
Insert record to CoreData. I flag this record as not updated in CloudKit. Just in case my CKModifyRecordsOperation fails, I can still update it at a later time to CloudKit using this flag.
Insert record to CloudKit using CKModifyRecordsOperation.
Try fetching the ckRecordID of the record inserted in step #2. (That's where my logic fails as I'm not sure how I can achieve this). I do not have any other keys (reference) and wish to use only CKRecordID as a reference between CoreData and CloudKit.
Update the ckRecordID (fetched in step #3) to CoreData.
What would be the best logic to solve the above? Thank you for your time and responses.
I solved this by creating a CKRecordID locally and updating it in CloudKit. Below is the quote from apple documentation:
To assign a custom record ID to a new record, you must create the
CKRecordID object first. You need to know the intended name and zone
information for that record, which might also require creating a
CKRecordZone.ID object. After creating the record ID object,
initialize your new record using its init(__recordType:recordID:)
method.
Here's my code:
let zone = CKRecordZone(zoneName: Schema.Zone.group)
let uuid = NSUUID()
let recordType = Schema.RecordType.group
let recordName = uuid.uuidString
let recordID = CKRecordID(recordName: recordName, zoneID: zone.zoneID)
let newRecord = CKRecord(recordType: recordType, recordID: recordID)

way to purge all but one object types (models) in a realm

I want to realm.delete() all but one model in my realm. Is there any way to do this without listing all of them?
Maybe a way to loop through all the types currently existing in a realm?
You can access the types from your Realm configuration, filter them to exclude the one you want to keep than delete each object of each type that you don't want to keep.
let typeToBeKept = MyObjectClass.self
realm.configuration.objectTypes?.filter{$0 != typeToBeKept}.forEach{ type in
try! realm.write {
realm.delete(realm.objects(type.self))
}
}

Initialize Realm object without data in Swift

So suppose I have a Dog object that contains a reference to a Toy object:
class Dog: Object {
dynamic var toy: Toy!
convenience init(toyId: String) {
self.init()
let realm = try! Realm()
toy = realm.object(ofType: Toy.self, forPrimaryKey: toyId)
}
}
In this example, suppose I have a bunch of toys that have already been created in Realm, and I want to create a new instance of a dog and all I have is the toy id (not the actual toy object).
In the example above, I can make this work by doing a lookup for the toy, but if I'm creating a bunch of dogs all at once, this seems inefficient.
The other option, I suppose, is to fetch all the toys upfront and then pass the actual toy object to the dog initializer.
My question is, if I'm just trying to create a new dog, and link it to an existing toy, can that be done without having to fetch the toy?
I'm new to Realm, but when I was using Parse in the past they had a special initializer for this kind of scenario:
PFObject(withoutDataWithClassName: <String>, objectId: <String?>)
The idea was you could reference an object from the primary key, and only fetch the data if you end up needing it. Something like that, it seems, would be ideal for what I'm trying to do.
My question is, if I'm just trying to create a new dog, and link it to an existing toy, can that be done without having to fetch the toy?
It is necessary to fetch the toy to establish the relationship between it and the dog.
It's worth noting that looking up an object by primary key does not result in any properties of that object being loaded into memory. The returned object is just a pointer to a specific object in the Realm file on disk. The object's properties are read into memory only when they're accessed.

how to audit log inserts (adds) of records using tracker enabled dbcontext

We are using https://github.com/bilal-fazlani/tracker-enabled-dbcontext
to create an audit trail of changes. We'd also like to record inserts into the trail of new records. we can loop though the entities just added but there seems to be no way of getting the ID for the entity just added?
A year ago there was an article written suggesting it's a limitation / not possible - https://www.exceptionnotfound.net/entity-change-tracking-using-dbcontext-in-entity-framework-6/
but there are also some comments suggesting there is a way. we studied those and the related code but are not any clearer, is it actually possible to audit inserts properly with this framework?
foreach (var entryAdded in addedEntities)
{
var entityKeyObject = objectContext.ObjectStateManager.GetObjectStateEntry(entryAdded.Entity).EntityKey;
var entityKey = entityKeyObject.EntityKeyValues.Length >= 1 ? entityKeyObject.EntityKeyValues[0].Value : 0;
// insert into audit log here..
}
Yes, it's possible to get inserted ID for entity just added.
In short, you simply need to handle two events:
PreSaveChanges: Get information not related to primary key
PostSaveChanges: Get information related to the primary key (which has been generated)
All the codes can be found using the link. So I cannot answer how to make it with this library but at least, I can ensure you at 100% it's possible.
One alternative is using another Entity Framework Audit Library
Disclaimer: I'm the owner of the project Entity Framework Plus
Wiki: EF+ Audit
This library allows you to audit & save information in a database. By default, it already supports ID added.
// using Z.EntityFramework.Plus; // Don't forget to include this.
var ctx = new EntityContext();
// ... ctx changes ...
var audit = new Audit();
audit.CreatedBy = "ZZZ Projects"; // Optional
ctx.SaveChanges(audit);
// Access to all auditing information
var entries = audit.Entries;
foreach(var entry in entries)
{
foreach(var property in entry.Properties)
{
}
}