Realm and Diffable Data Source - ios

Has anyone tried to use Realm in conjunction with diffable data sources? There seems to be an issue I can't get my head around.
So, when when we use the "traditional" data source API, in cellForRowAt we get the object for a particular row on an index-based basis and since Realm returns objects inside its generic Results type (e.g. let items = Results<Item>), which conforms to Collection, we can basically access the element like always: items[indexPath.row]
However, when you use diffable data source and create a snapshot, you have it like this: var snapshot = NSDiffableDataSourceSnapshot<Int, Item>. You add a section by doing snapshot.appendSections([0]) and then you have to add items to that section. My main issue here is that you can't do snapshot.appendItems(items) since items is of type Results<Item>, not Array<Item>. Am I missing something here?
Also, Realm's Object class seems to have its own implementation of Hashable so I think there's no way of ensuring the uniqueness of objects apart from overriding Realm's primaryKey and implementing your own auto increment feature. All that seems a bit weird to me to the point where I'm thinking to switch to Core Data.
Haven't found similar topics on StackOverflow so any help will be much appreciated.

You can covert Result<Items> to Array using an extension:
extension Results {
func toArray() -> [Element] {
return compactMap {
$0
}
}
}
You can use it like: results.toArray() and it will give you the array and then with your case you can append data into it.

Also, you can simply use this method :
let realmArray = Array(items!)

Try to use map() to transform an array
items = realm.objects(Item.self).map({ $0 })

Related

ParseSwift queryConstraint on object

How can i add a queryConstraint on a object?
This is my current code but it returns no objects. I guess my current code is actually to query on arrays and not objects. But I can't find a way to do this for objects.
let query = Device.query()
.where(containsString(key: "apps", substring: "Google"))
This is the database
I recommend looking at the playgrounds to see how to use ParseSwift properly. More specifically, finding objects.
The first problem is apps is an object, which is actually a dictionary. You can’t use a substring constraint on a dictionary or other object. The actual way to do it is:
let objectToFind = [“Google”: “300”]
let query = Device.query("apps" == objectToFind),

how to convert RealmSwift List to Results?

I am using a Realm List/Results as my dataSource for a UITableView. At some point I assign a list to it. like:
var dataSource:List<SomeObject>! // Or >> Results<SomeObject>!
let aRealmObject = realm.objectForPrimaryKey(SomeObject.self, key: objectId)
dataSource = aRealmObject.someList // dataSource should be List
Then I have a filter on this list If the user changed the filter dates, I do like this:
dataSource = dataSource.filter("FILTER THE DATES",newDates) // dataSource should be Results
But the line above causes an error as the return type of filter is a Results object and aRealmObject.someList is a List.
What is the best way to deal with this situation?
make dataSource as a List and convert the Results object to List? How??
make dataSource as a Results and convert the List to Results? How??
Or may be you have a better way of doing it, Please share it with me.
Thanks,
I have found A simple way to convert List to Results making use if the filter method, it always returns Results object. Just gave it a true predicate.
dataSource = aRealmObject.someList.filter("TRUEPREDICATE") //this is a Results object.
Both List and Results (as well as LinkingObjects) can be converted into an AnyRealmCollection type. I think this is probably the best way to standardize all of Realm's array-type types:
var dataSource:AnyRealmCollection!
let aRealmObject = realm.objectForPrimaryKey(SomeObject.self, key: objectId)
dataSource = AnyRealmCollection(aRealmObject.someList)

Update dictionary with previously deleted items

I have two dictionaries. Both declared in a viewController, both based on a model structure class.
// ItemDictionary
var ItemDictionary = ItemModel()
var JSONDictionary = ItemModel()
JSON data is fed into the JSONDictionary and then this data is passed to ItemDictionary which feeds a table within ViewDidLoad.
self.ItemDictionary = self.JSONDictionary
All good. The table is nicely populated from JSON data. I can also delete items from the table and the ItemDictionary. However, when I try and add items back by referring to the original dictionary (JSONDictionary) the item has gone.
I understand this is expected. If Dictionary1 = Dictionary2, a new dictionary is not actually created. Only an second address. So if you change Dictionary1, Dictionary2 also changes.
A practical example could be setting a price range. You can reduce the range and have less items displayed on the table. But you can't replace previously deleted items if you wanted to increase the price range. I do not want to recall the JSON script each time I edit the range. Advice appreciated.
As confirmed by the OP, ItemModel is a class and not a Dictionary. To fix this you need to make ItemModel a real Dictionary and thus a value type. This is probably the preferred choice but will be more work.
An alternative would be to add an convenience initializer to the ItemModel class that instantiates a new copy of itself and call that instead of setting self.ItemDictionary = self.JSONDictionary.
Something like this.
init(model: ItemDictionary) -> ItemDictionary {
// copy properties
}
Then create the new copy.
self.ItemDictionary = ItemDictionary(self.JSONDictionary)
Later you can reinitialize ItemDictionary with the same call.
Try this code out-
var dictionary1 = ["name": "Magnus"]
var dictionary2 = dictionary1
dictionary2.removeAll()
print("\(dictionary2) \(dictionary1)")
The output is :-
[:] ["name": "Magnus"]
Thus 2 new dictionaries are being created. If you refer to the Swift documentation, You will find that in swift, references are hardly present.
Some other portion of code might be responsible. Hope this helps :-)

Realm linkingObjects array vs. Results

I have a Book class and a ReadingSession class, with relationships declared like so:
public class Book: Object {
// (…)
var readingSessions: [ReadingSession] {
return linkingObjects(ReadingSession.self, forProperty: "book")
}
}
public class ReadingSession: Object {
// (…)
dynamic var book: Book?
}
Now I wanted to use Realm’s Results’ methods like filter() and sum() on that readingSessions property, but I can't, because it's a regular array.
So I added another computed property to my Book class:
var sessions: Results<ReadingSession> {
let realm = try! Realm()
return realm.objects(ReadingSession).filter("book == %#", self)
}
Now when I need to use those methods I go for the sessions porperty, and when I don't I use the readingSessions array.
So my question is: why is the first way, using the linkingObjects() method, recommended in Realm’s documentation? Is there a reason why I shouldn't completely replace that property with my latest one, using Realm’s Results? In my experience, working with Realm’s Results is usually a lot faster, besides the extra methods, and even if I need a regular array I can just convert the Results then. Is there any disadvantage to doing that?
Thanks in advance,
Daniel
For now, there is no better way than what you're currently doing. Using linkingObjects would be likely faster as the relationship can be navigated internally bidirectionally and the query doesn't need to be evaluated for the full dataset, so it also depends on the size of that. We plan to change that behavior as seen in issue #1508.

Swift and core data - Array and cell basics

If I define my array as:
var myList: Array <AnyObject> = []
and then use the viewDidLoad override function to populate this array like this:
override func viewDidAppear(animated: Bool) {
let appDel:AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
let context:NSManagedObjectContext = appDel.managedObjectContext!
let freq = NSFetchRequest(entityName: "List")
myList = context.executeFetchRequest(freq, error: nil)!
tableView.reloadData()
}
and my "List" only contains usernames as strings. What exactly is being stored in "myList"? Is it just as simple as an array of strings with the usernames?
Any thoughts and explanations would be highly appreciated.
Your myList would contain subclasses of NSManagedObject or List instances if you have defined that class. Each object would have attributes that you have defined for that core data object.
The indexPath is a class that is used to represent the row and section of the UITableView. This is also used to identify the item and section of a UICollectionView.
executeFetchRequest returns an array of managed objects from the CoreData data store. There are basically two ways to handle them.
The first is to use them as-is. They will all be instances of NSManagedObject, and you can use methods like valueForKey: to get their values.
The second way is to define your own subclass of NSManagedObject, in your case probably named List, and then define properties on that object allowing you to access the values directly.
Core Data as a whole is both insanely powerful and insanely complex. I strongly recommend you work through a tutorial, either Apple's or otherwise, to get a hang of it. (Note that some of Apple's docs recommend starting with something called Core Data Starting Point. Helpfully, Apple has retired that document, but hasn't yet removed the references to it from other documents.)

Resources