Storing an array of Realm objects inside another Realm object (Swift) - ios

Im creating a music like app. So far I am able to create and save song objects and save them to realm. The song objects are made up of simple "songTitle" and "songArtist" string variables.
I would like to add playlist-like functionality and I believe the best way would be through arrays. The playlist object would contain a "songsInPlaylist" array and that array would be populated with a list of previously created song objects. I have looked over the documentation and I cant get a lead on where to start.
In short, how do you create a realm object that contains an array of other realm objects.
I am using Swift 2.0
Click to see visual representation...

Using array of Realm Objects is simple, just use List container data structure to define to-many relation. Check this example:
class Task: Object {
dynamic var name = ""
dynamic var createdAt = NSDate()
dynamic var notes = ""
dynamic var isCompleted = false
}
class TaskList: Object {
dynamic var name = ""
dynamic var createdAt = NSDate()
let tasks = List<Task>()
}
You can have a look to my sample Todo app using Ream in Github

In mapper,
(map["key"], ArrayTransform<Object>())
"key" is JSON key
"Object" is your custom object

Related

How to convert Firebase result to List in SwiftUI

I wanted to make a list in swiftui from firebase data. So here's my UI
I've already make a data for that username "mike_one", and it's perfectly working,
this is my xcode result
{
IC2ol5bimucKu2d89u2YBz0Bqot2 = {
name = mike;
photo = "https://firebasestorage.googleapis.com/v0/b/instagram-ios-1ed09.appspot.com/o/photo%2Fa2.jpg?alt=media&token=9b6c58f1-eedc-4190-bc63-3f3325c84d77";
username = "mike_one";
};}
And this is my database
So what i'm asking right now is, How to make the result of the database to be a model? so I can use it as a list.
Please help me.
I appreciate your answer!
Strictly speaking, it would not be possible to convert textual firebase data to a List object. A SwiftUI List is defined as
A container that presents rows of data arranged in a single column
in other words it's a UI element, not a data element.
That being said a List is often backed by an Array or other data storage element. So you'll read Firebase data and store that in an array. Then that array backs your UI List object.
In this case, I would suggest creating a UserClass to hold the Firebase data
class UserClass {
var name = ""
var photoUrl = ""
var username = ""
}
and then array to store your users in
var userArray = [UserClass]()
then as data is read from Firebase, create the user objects and populate the array. Your Firebase code wasn't included so in brief
firebase.observe.... { snapshot in
let user = UserClass()
...populate user properites from the snapshot
self.userArray.append(user)
}
Then in your view, access the array elements to show them in the List object
struct ContentView: View {
var body: some View {
List(userArray) { user in
//do something with each user object
// like Text(user.username)
}
}
}

How to create initial Realm objects that get added upon installation of app

Say I am creating an object that takes two strings and acts like a dictionary.
class WordInDictionary: Object {
#objc dynamic var word: String = ""
#objc dynamic var meaning: String = ""
What should I do if I wanted to have some initial objects that get added to the database just once upon installation/update of the app?
Also, is there a way to make it so that just those initial objects can't be deleted?
"What should I do if I wanted to have some initial objects that get added to the database just once upon installation/update of the app?"
One option would be to have some code near the realm initialisation that checks if there are any WordInDictionary objects already in the realm - if not then add the required default objects.
E.g.
let realm = try! Realm()
if realm.objects(WordInDictionary.self).isEmpty
{
// Add required words here
}
"Also, is there a way to make it so that just those initial objects can't be deleted?"
I don't know of a way to make realm objects read-only. You'd have to implement this in code in some way, e.g. have a isDeletable boolean member which is true for every user-created object and false for your default members, then only delete those from realm.
E.g. for your deletion code:
func deleteWords(wordsToDelete: Results<WordInDictionary>)
{
try! realm.write
{
realm.delete(wordsToDelete.filter("isDeletable = true")
}
}

How to persist a Realm List property in Swift 4?

Using Swift 4 and Realm 3.0.1, I'd like to store a list of Realm objects in a property of a parent Realm
object. I ran into the following problem:
In Swift 4, properties that should be persisted into Realm have to be #objc dynamic, e.g. #objc dynamic var id: String = "". However, Realm's Array replacement type, List, can not be stored that way: #objc dynamic var children: List<Child>? = nil causes this compiler error:
Property cannot be marked #objc because its type cannot be represented in Objective-C
For more context, here's a full example:
final class Child: Object {
#objc dynamic var name: String = ""
}
final class Parent: Object {
// this fails to compile
#objc dynamic var children1: List<Child>?
// this compiles but the children will not be persisted
var children2: List<Child>?
}
So is there another way to store object lists in Realm and Swift 4?
Realm Lists can never be nil, and they don’t need the #objc dynamic. They should only be let, although I can't find that specifically called out in the documentation, there is a comment from a realm contributor that calls it out specifically
There is a cheat sheet for properties in the documentation.
let dogs = List<Dog>()

Use Realm with Collection View Data Source Best Practise

I'll make it short as possible.
I have an API request that I fetch data from (i.e. Parse).
When I'm getting the results I'm writing it to Realm and then adding them to a UICollectionView's data source.
There are requests that take a bit more time, which run asynchronous. I'm getting the needed results after the data source and collection view was already reloaded.
I'm writing the needed update from the results to my Realm database.
I have read that it's possible to use Realm's Results. But I honestly didn't understood it. I guess there is a dynamic and safe way working with collection views and Realm. Here is my approach for now.
This is how I populate the collection view's data source at the moment:
Declaration
var dataSource = [Realm_item]()
where Realm_item is a Realm Object type.
Looping and Writing
override func viewDidLoad() {
super.viewDidLoad()
for nowResult in FetchedResultsFromAPI
{
let item = Realm_item()
item.item_Title = nowResult["Title"] as! String
item.item_Price = nowResult["Price"] as! String
// Example - Will write it later after the collectionView Done - Async request
GetFileFromImageAndThanWriteRealm(x.image)
// Example - Will write it later after the collectionView Done - Async request
dataSource.append(item)
}
//After finish running over the results *Before writing the image data*
try! self.realm.write {
self.realm.add(self.dataSource)
}
myCollectionView.reloadData()
}
After I write the image to Realm to an already created "object". Will the same Realm Object (with the same primary key) automatically update over in the data source?
What is the right way to update the object from the data source after I wrote the update to same object from the Realm DB?
Update
Model class
class Realm_item: Object {
dynamic var item_ID : String!
dynamic var item_Title : String!
dynamic var item_Price : String!
dynamic var imgPath : String?
override class func primaryKey() -> String {
return "item_ID"
}
}
First I'm checking whether the "object id" exists in the Realm. If it does, I fetch the object from Realm and append it to the data source. If it doesn't exist, I create a new Realm object, write it and than appending it.
Fetching the data from Parse
This happens in the viewDidLoad method and prepares the data source:
var query = PFQuery(className:"Realm_item")
query.limit = 100
query.findObjectsInBackgroundWithBlock { (respond, error) -> Void in
if error == nil
{
for x in respond!
{
if let FetchedItem = self.realm.objectForPrimaryKey(Realm_item.self, key: x.objectId!)
{
self.dataSource.append(FetchedItem)
}
else
{
let item = Realm_item()
item.item_ID = x.objectId
item.item_Title = x["Title"] as! String
item.item_Price = x["Price"] as! String
let file = x["Images"] as! PFFile
RealmHelper().getAndSaveImageFromPFFile(file, named: x.objectId!)
self.dataSource.append(item)
}
}
try! self.realm.write {
self.realm.add(self.dataSource)
}
self.myCollectionView.reloadData()
print(respond?.count)
}
}
Thank you!
You seem to have a few questions and problems here, so I'll do my best.
I suggest you use the Results type as your data source, something like:
var dataSource: Results<Realm_item>?
Then, in your viewDidLoad():
dataSource = realm.objects(Realm_item).
Be sure to use the relevant error checking before using dataSource. We use an optional Results<Realm_item> because the Realm object you're using it from needs to be initialised first. I.e., you'll get something like "Instance member * cannot be used on type *" if you try declaring the results like let dataSource = realm.objects(Realm_item).
The Realm documentation (a very well-written and useful reference to have when you're using Realm as beginner like myself), has this to say about Results...
Results are live, auto-updating views into the underlying data, which means results never have to be re-fetched. Modifying objects that affect the query will be reflected in the results immediately.
Your mileage may vary depending on how you have everything set up. You could try posting your Realm models and Parse-related code for review and comment.
Your last question:
What is the right way to update the "object" from the Data Source after i wrote the update to same object from the Realm DB?
I gather you're asking the best way to update your UI (CollectionView) when the underlying data has been updated? If so...
You can subscribe to Realm notifications to know when Realm data is updated, indicating when your app’s UI should be refreshed for example, without having to re-fetch your Results.

Correct way to use CoreData Entity in this example

I have a CoreDataentity called "Person" with 3 attributes
import Foundation
import CoreData
class Person: NSManagedObject {
#NSManaged var name: String
#NSManaged var image: NSData
#NSManaged var info: String
}
EDITED:
EXAMPLE: (I think my first "Table/chair" example caused more confusion then explanation.)
I have 7 UIButtons. When a UIButton is tapped, My TableView is modally pushed and TableView cells are populated with attribute values of all available Person via
var request = NSFetchRequest(entityName: "Person")
request.returnsObjectsAsFaults = false;
results = context.executeFetchRequest(request, error: nil)!
I want to be able to double-tap a cell, which will close TableView and return to mainViewController the Person selected, and attach it to the UIButton that called the TableView. This UIButton will now display SelectedPerson's image, name, and info (or have direct access to attribute values).
How should I go about making this possible?
Is there a NSFetchRequest Method that I could simply input something like...
NSFetchRequest(entityName: "Person", attributeName: "name", String == "John Doe")
If so I could just pass the "name" value from TableView to MainViewController, and store a Person reference in MainViewController via something like this...
var PassedNameString = "John Doe"
var Person1: Person = NSFetchRequest(entityName: "Person", attributeName: "name", String == PassedNameString) as! Person
This way I'll have a reference to what Person entity is filling a "seat" and have direct access to it's info to read or edit.
My CoreData understanding is very low.
I've only learned how to create/store a file. I do not have a understanding of what is needed to recall a CoreData file and Edit if needed. I also don't know what kind of demand is put on the device when Fetching Vs holding a Reference to CoreData entry.
In your view controller have an array (or a dictionary if it suits you better) for the chairs. Each slot in the array represents a chair, and the index of the array links to the buttons you have (also should be in an array). The chair array holds instances of Person and is initially empty.
The table view controller should use a fetched results controller, with a batch size on the fetch. This is used to populate the table as usual. When a table row is selected the associated Person should be passed back and stored in the chair array.
Doing a second fetch is pointless and leads to name duplication issues.
Think of CoreData as your database, that stores the data that is categorized by your model structures.
From your description, I would do a fetch request somewhere in your ViewController's loading sequence, say in ViewDidLoad, and store the results (Person objects) in an array of persons ... var persons = [Person](). This can be a class variable. Then I would access the Person objects by calling array[index].
Let me know if this helps you with your thinking, or where you are confused.
var persons = [Person]()
override func viewDidLoad() {
...do your fetch call...get back an array of objects as a result
for person:Person in result {
self.persons.append(person)
}
// now to access your Person objects anywhere in the class just call
// self.persons[index] to get back an Person object
}

Resources