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

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

Related

ios swift models structure with realm database

I am developing my iOs App and I am using Realm database. As I am totally new to ios developing (also swift and xcode) I have question about structuring data (I've already read some general project structure guidelines but couldn't find the answer). My thinking is connected with Java structures
For Realm databases (RealmSiwft) I created a model like this:
#objcMembers class Patient: Object {
dynamic var patientId:Int = 0
dynamic var refNumber:String = ""
convenience init(id:Int, refNumber:String){
self.init()
self.patinetID = id
self.refNumber = refNumber
}
}
Now, it looks just like a POJO class in Java. But as I learned, this model structure is made that way so it can be able to use Realm.
So the question is, if I need somewhere else in my project to use Patient objects, is this Realm-POJO-model good to use? I mean, should I use it just like a normal Model even when I dont need to make database operations on it? Or should I make this Realm model alike DAO class for databases operations and make another model class like Patient.swift for whenever I want to play with Patient without using databases (I hope not, cause it's so much code duplicating)
And what if I need variables in that Patient Model that won't be stored in database? Can I make it without dynamic? What about init than? That blows my mind, as far as I learn swift it seems so ugly and unstructured, or I just can't switch to it yet...
if I need somewhere else in my project to use Patient objects, is this
Realm-POJO-model good to use?
even when I dont need to make database operations on it?
You can use your Patient object without savings to the DB, move them to different controllers and so on.
what if I need variables in that Patient Model that won't be stored
in database?
Look to ignoredProperties() method.
Can I make it without dynamic?
No you can't because of Realm based on Objective-C object, so this is necessary type.
What about init than?
You can create different Constructors methods, look to the Initialization doc. In case with Realm you should setup values to noticed variables (if you don't give them Default Property Values)
Your class should look like this:
class Patient: Object {
// MARK: - Properties
#objc dynamic var patientId: Int = 0
#objc dynamic var refNumber: String = ""
// MARK: - Meta
// to set the model’s primary key
override class func primaryKey() -> String? {
return "patientId"
}
//Ignoring properties
override static func ignoredProperties() -> [String] {
return ["tmpID"]
}
//It's ok
convenience init(id:Int, refNumber:String){
self.init()
self.patientId = id
self.refNumber = refNumber
}
}
All other detail information you can find in: realm docs
Also you can extend you base code with swift extension:
extension Patient {
var info: String {
return "\(patientId) " + refNumber
}
func isAvailableRefNumber() -> Bool {
return refNumber.length > 6
}
}

How NSMapTable works

I'm trying to figure out how NSMapTable works
So I'm trying in playground the following code:
class Person {
var name: String
init(name: String ) {
self.name = name
print("\(name) is being initialized")
}
deinit {
print("\(name) is being deinitialized")
}
}
var hobyePerson : NSMapTable? = NSMapTable<Person, NSMutableString>
(keyOptions: .weakMemory, valueOptions: .weakMemory)
var rob : Person? = Person(name: "Rob Appleseed") // print : Rob Appleseed is being initialized
hobyePerson?.setObject("golf", forKey: rob)
hobyePerson?.count // return : 1
rob = nil // print : Rob Appleseed is being deinitialized
hobyePerson?.count // return : 1 (WHY ???!!!!)
as written in the documentation: "Keys and/or values are optionally held “weakly” such that entries are removed when one of the objects is reclaimed."
why even though I initialized the object so that it has a weak reference to the key-value pair when rob is deallocated, I still have an element in hobyePerson?
NSMapTable's weak behavior options work best when you don't care when keys/values are released, but rather, you do care that the keys/values aren't strongly retained and will be released at some point after the object of interest becomes nil.
Why so?
As a Foundation class, the authors of NSMapTable had to balance both features and performance.
Consequently, as an "optimization" for performance, they chose that weakly referenced objects that become nil are NOT immediately removed from the map table...! Rather, this happens "later" when it can be efficiently done -- such as when the map table internally gets resized, etc.
As #Luke also mentions in his answer, see this excellent writeup about an experiment done on NSMapTable's behavior for more details:
http://cocoamine.net/blog/2013/12/13/nsmaptable-and-zeroing-weak-references/
Yes, this is a strange and unfortunate behavior. This article goes into it in some depth. Although it doesn't explore weak-to-weak specifically, the behavior described is the same. As that author notes, hobyePerson.keyEnumerator().allObjects.count and hobyePerson.objectEnumerator().allObjects.count will contain 0 as expected at the end of all this. He also points out that Apple has sort of documented this behavior in the Mountain Lion release notes.
However, weak-to-strong NSMapTables are not currently recommended, as
the strong values for weak keys which get zero’d out do not get
cleared away (and released) until/unless the map table resizes itself.
Sorry I don't have a better explanation for you.
It didn't work for me so I implemented simple weak map like this.. Will improve it overtime but for now works:
import Foundation
private struct WeakValue<Value:AnyObject> {
weak var value: Value?
}
public class CSWeakValueDictionary<Key:AnyObject, Value:AnyObject> {
private let dictionary = NSMutableDictionary()
public subscript(source: Key) -> Value? {
get {
let value = (dictionary["\(source)"] as? WeakValue<Value>)?.value
if value == nil { dictionary.removeObject(forKey: "\(source)") }
return value
}
set { dictionary["\(source)"] = WeakValue(value: newValue) }
}
}

Swift - casting a nil core data string as an optional value

I have a field stored on a core data object called "metadata" which is of type String (no optional, because Apple docs say not to mess with optionals in CD). Sometimes, the metadata field is nil. In checking whether this value is nil, I do the following check:
if object.metadata as String? != nil {
...
}
However, my code continuously crashes on this line as an EXC_BAD_ACCESS. I have also tried:
if let metadata = object.metadata as String? {
...
}
Which doesn't work either. I cast objects successfully to optionals in other parts of my code, so I don't understand why this particular case isn't working. How do you check whether a core data property is a nil string?
It looks like what you really want is this:
if object.metadata != nil {
...
}
or this:
if let metadata = object.metadata as? String {
// You can now freely access metadata as a non-optional
...
}
--EDIT--
My mistake, I didn't read the first part of your question thoroughly enough. It looks like the duplicate answer has a solution for this. Essentially, the generated managed object subclass is a bug and you should modify the properties to be either optional or implicitly unwrapped. You can check both of those using the first method for implicitly unwrapped and second for optionals.
There are several questions which discuss the issue of the generated subclasses not producing optional properties. I wouldn't be too concerned about editing the subclasses; there's nothing special about them except that Apple is making it easier to create them.
Check if property is set in Core Data?
Swift + CoreData: Cannot Automatically Set Optional Attribute On Generated NSManagedObject Subclass
--Edit2--
If you really don't want to touch the subclass you can access the property using valueForKey() and could add that as an extension if you wanted something a bit cleaner.
if let metadata = object.valueForKey("metadata") as String? {
...
}
In an extension:
extension ObjectClass {
var realMetadata: String? {
set {
self.setValue(newValue, forKey: "metadata")
}
get {
return self.valueForKey("metadata") as String?
}
}
}

Subclassing NSObject in Swift - Best Practice with Initializers

Here is the layout of an example Class, can someone guide me on what's best practice when creating a subclass of NSObject?
class MyClass: NSObject {
var someProperty: NSString! = nil
override init() {
self.someProperty = "John"
super.init()
}
init(fromString string: NSString) {
self.someProperty = string
super.init()
}
}
Is this correct, am I following best practice here?
I wonder if I'm correctly setting up the initializers (one that sets the string to a default, and one which I can pass in a string)?
Should I call super.init() at the end of each of the initializers?
Should my more specific (the one that takes a string) initializer simply call self.init() at the end rather than super.init()?
What is the right way to set up the initializers in Swift when subclassing NSObject? - and how should I call the super init ?
This question (albeit in Objective C) suggests you should have an init, which you always call and simply set the properties in more specific inits: Objective-C Multiple Initialisers
I'm not Swift ninja but I would write MyClass as:
class MyClass: NSObject {
var someProperty: NSString // no need (!). It will be initialised from controller
init(fromString string: NSString) {
self.someProperty = string
super.init() // can actually be omitted in this example because will happen automatically.
}
convenience override init() {
self.init(fromString:"John") // calls above mentioned controller with default name
}
}
See the initialization section of the documentation
If someProperty can be nil, then I think you want to define the property as:
var someProperty: NSString?
This also eliminates the need for a custom initializer (at least, for this property), since the property doesn't require a value at initialization time.
In complement to the answers, a good idea is to call super.init() before other statements. I think it's a stronger requirement in Swift because allocations are implicit.

updateValue not working for Dictionary

I'm creating a test app using Swift in Xcode, and I've run into an annoying issue. I'm writing a simple class that will act as a cache using a Dictionary object. My implementation is below:
import Foundation
import UIKit
class ImageCache {
var dict:Dictionary<String,NSData>?;
init() {
dict = Dictionary<String,NSData>();
}
func exists(id:String) -> Bool {
return dict!.indexForKey(id)!==nil;
}
func getImage(id:String) -> UIImage? {
if(!exists(id)) {
return nil;
}
return UIImage(data: (dict!)[id]);
}
func setData(id:String, data:NSData) {
dict!.updateValue(data, forKey: id);
}
}
The issue is in the last method, with Xcode stating "Could not find member 'UpdateValue'". This is weird, because the code hint seems to show it just fine:
But when I try to compile:
Could this potentially be a bug in Xcode? Or am I missing something super-obvious?
this is not a bug or a quirk in the compiler.
it is how Optional implemented (which may be flawed or not)
what happened is that the Optional store the Dictionary as immutable object (with let perhaps). So even Optional it is mutable, you can't modify the underlying Dictionaryobject directly (without reassign the Optional object).
updateValue(forKey:) is mutating method, you can't call it on immutable object and hence the error.
you can workaround it by doing
var d = dict!
d.updateValue(data, forKey: id)
because you copy the dictionary to another mutable variable, which then is mutable and able to call mutating method on it
but without dict = d, your change won't be applied on dict because Dictionary is value type, it makes copy on every assignment
related answer
Smells like a bug or a quirk in the compiler.
I just tried something like
var d = dict!
d.updateValue(data, forKey: id)
and it works as expected.

Resources