How to persist a Realm List property in Swift 4? - ios

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>()

Related

Is there any way to optimize struct copy in Swift?

I'm developing an iOS app and have the following data model:
struct Student {
var name: String?
var age: UInt?
var hobbies: String?
...
}
This model is used as the data source in one view controller, where each property value will be filled in an UITextfield instance so that the user can edit a student's information. Every time a user finishes typing an item, e.g. the name, the new value will override the old model's corresponding property.
The problem is, since struct is a value type instead of a reference type, a new model instance is generated every time I assign a new property value to it. There may be above 20 properties in my model and I think so many copies are quite a waste. For some reasons I'm not allowed to use class. Is there any way to optimize this? Will these copies cause any performance issues?
you can create a func with mutating keyword like below
struct Point {
var x = 0.0
mutating func add(_ t: Double){
x += t
}
}
find more here

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")
}
}

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

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

Swift + Realm newbie: Problems with a simple Realm object and its initializers

I've been a long time Objective-C developer and heard about Realm some weeks ago. On the other hand I've always wanted to migrate little by little to Swift, so I created a small project involving Realm + Swift.
What does it mean? I'm a Swift + Realm newbie.
Anyway, I created a small demo/Proof of concept for a project I have in mind and I thought it had to be easier. But Xcode compiler says otherwise.
My problem is with one of my Objects' initializer(s).
My intentions were simple, but apparently Realm needs more initializers than I wanted.
The code of one of my Realm Objects is this:
import Foundation
import Realm
import RealmSwift
class Partida: Object
{
dynamic var id_partida: String
dynamic var inventario: Inventory?
required init ()
{
inventario = Inventory()
id_partida = "id_partida_test"
super.init()
}
required init(value: AnyObject, schema: RLMSchema) {
//fatalError("init(value:schema:) has not been implemented")
super.init(value: value, schema: schema)
}
required init(realm: RLMRealm, schema: RLMObjectSchema) {
//fatalError("init(realm:schema:) has not been implemented")
super.init(realm: realm, schema: schema)
}
override class func primaryKey() -> String? {
return "id_partida"
}
}
My original code only had the "normal" init initializer. But Xcode forced me to create two additional initializers more (value and realm ones).
If I compile the code I've just pasted above, Xcode complains in the 2nd and 3rd required initializers, specifically in the super.init part.
It says:
Property 'self.id_partida' not initialized at super.init call
I understand the meaning of it, but I don't know how to avoid the error because if I remove both super.init lines, the program crashes in runtime.
if I uncomment the fatalError lines, they also crashes in runtime.
In fact I don't want to use these 2 initializers. If I could, I wouldn't add them, but Xcode needs to, apparently. The only code I really want to add to my object init function is "the simple" init function, which was the only part of code considered mine.
I think I might have some concept misunderstandings in Realm + Swift + initializers.
I'm also having the feeling Xcode is forcing me to add code I don't need and/or I don't understand either.
Any help on understanding "required init" initializers in Realm will be more than welcome.
Official Realm + Swift documentation is beyond my knowledge as I don't understand many of its concepts even after re-reading them many times.
Google and StackOverflow haven't been really helpful this time...
Thanks.
Initializers in Swift definitely behave a bit differently to Objective-C, so I can definitely see the angle you're coming from here.
In this case though, since you're just using the initializer to set some default values, it's wholly un-necessary since you should be able to assign the default values to the properties themselves:
class Partida: Object
{
dynamic var id_partida = "id_partida_test"
dynamic var inventario: Inventory? = Inventory()
override class func primaryKey() -> String? {
return "id_partida"
}
}
Let me know if that still doesn't work! :)
Because it already has init () in Object class, you are using subclass of Object, so you already have its init in Realm object, you should give your var init value, like dynamic var id_partida: String = "id_partida_test", and then if you call let test = Partida() it already has your 2 init value, other init should be marked with convenience
When you save the Object to persistent store, it should be always have value, you can use Realm's optional then need read the documentation
Here's my sample Realm class so that u got the idea:
import Foundation
import RealmSwift
import SwiftyJSON
class ProjectModel: Object {
dynamic var id: Int = 0
dynamic var name: String = ""
//Dont need this, call init() already have value
required init() {
id = 0
name = ""
super.init()
}
convenience init(fromJson json: JSON!){
self.init()
if json == nil {
return
}
id = json["id"].intValue
name = json["name"].stringValue
}
override class func primaryKey() -> String? {
return "id"
}
}

Struct value types in Swift

I understand the difference between 'Value Types' and 'Reference Types'. I know 'Structures' are 'Value Types' and according to the Swift documentation all the values stored by the structure are themselves value types. Now my question is what if I have a stored property in a Struct that is an instance of a class. In that case, would the whole class would be copied or just its address?
Any help would be appreciated.
It copies the pointer to the instance. I just tested this in a playground.
struct MyStruct {
var instance: MyClass
}
class MyClass {
var name: String
init(name: String) {
self.name = name
println("inited \( self.name )") // Prints "inited Alex" only once
}
}
var foo = MyClass(name: "Alex") // make just one instance
var a = MyStruct(instance: foo) // make a struct that contains that instance
var b = a // copy the struct that references the instance
foo.name = "Wayne" // Update the instance
// Check to see if instance was updated everywhere.
a.instance.name // Wayne
b.instance.name // Wayne
What is different though, is that it's now two different references to the same object. So if you change one struct to a different instance, you are only hanging it for that struct.
b.instance = MyClass(name: "Vik")
// a and b no longer reference the same instance
a.instance.name // Wayne
b.instance.name // Vik
The playground is a great way to test out questions like these. I did not know the answer definitively when I read this question. But now I do :)
So don't be afraid to go play.
I think you misread the documentation. According to the The Swift Programming Language,
All structures and enumerations are value types in Swift. This means that any structure and enumeration instances you create—and any value types they have as properties—are always copied when they are passed around in your code.
Since classes are reference types, not value types, they are not copied even if they are properties of a value type, so only the address is copied.

Resources