New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support overriding Object.init() #1849

Closed
marcvanolmen opened this Issue May 6, 2015 · 23 comments

Comments

Projects
None yet
9 participants
@marcvanolmen

marcvanolmen commented May 6, 2015

class CMSimpleRealmModel: Object {
    dynamic var myKey: String? = NSUUID().UUIDString
    dynamic var name: String? =  ""


    override init() {
        self.myKey = NSUUID().UUIDString
        self.name = ""

        super.init()
    }

    override class func primaryKey() -> String {
        return "myKey"
    }

}

let aDefaultRealm = Realm()

var anObject = CMSimpleRealmModel()
aDefaultRealm.write {
    aDefaultRealm.add(anObject)
}

causes the following error to be generated,

/Users/marcvano/dev/cameo/Cameo2/Cameo/CMSoundtrack.swift: 14: 7: fatal error: use of unimplemented initializer 'init(realm:schema:)' for class 'Cameo.CMSimpleRealmModel'

but reading through the doc it says this is the public one and init(realm:schema:) is the private one.

@thiagoricieri

This comment has been minimized.

thiagoricieri commented May 6, 2015

Experiencing the same error here, trying to find a workaround (Realm 0.92.0)

@jpsim jpsim self-assigned this May 6, 2015

@jpsim

This comment has been minimized.

Contributor

jpsim commented May 6, 2015

Overriding init() isn't quite supported just yet. I'll rename the issue accordingly.

Meanwhile, you can create and use convenience initializers like this:

class CMSimpleRealmModel: Object {
    dynamic var myKey: String = NSUUID().UUIDString
    dynamic var name: String =  ""

    convenience init(myKey: String, name: String) {
        self.init()
        self.myKey = myKey
        self.name = name
    }

    override class func primaryKey() -> String {
        return "myKey"
    }
}

let aDefaultRealm = Realm()

var anObject = CMSimpleRealmModel(myKey: "0", name: "John")
aDefaultRealm.write {
    aDefaultRealm.add(anObject)
}

Keep in mind that you can set default property values inline with the property declaration rather than in init().

@jpsim jpsim changed the title from RealmSwift override the init causes a runtime error to Support overriding Object.init() May 6, 2015

@jpsim jpsim removed their assignment May 7, 2015

@sandalsoft

This comment has been minimized.

sandalsoft commented May 20, 2015

Anyone having issues with custom initializers and/or coming from issue #1101, the @jpsim comment above - #1849 (comment) - worked for me!

@ataibarkai

This comment has been minimized.

ataibarkai commented May 21, 2015

Just to be clear, that means we can only use var properties, and not let properties with RealmSwift until proper custom init support is implemented?

Any timeline for when that might be? #1101 seemed to suggest this was being actively worked on.

@jpsim

This comment has been minimized.

Contributor

jpsim commented May 22, 2015

You should only be using dynamic var properties if you want them backed and stored by Realm (except List properties, which should be let). More information is available in the Models section of our Swift docs.

Custom initializers are supported (see my earlier comment: #1849 (comment)), but this issue is tracking adding the ability to override init() itself.

@ataibarkai

This comment has been minimized.

ataibarkai commented May 26, 2015

@jpsim thanks for the clarification regarding vars.

Regarding initialization - I should have been more specific, I meant support for a custom designated init rather than for a convenience init.
Is this still being worked on?

This is important because in Swift convenience initializers can't be called by subclasses' initializers.
This makes subclassing a stored Realm object and calling its super.init(...) infeasible (e.g. Vehicle: Object, Bicycle: Vehicle).

I don't know how the Realm implementation works, but it seems that the problem can be solved on Realm's end by marking all initializers besides the empty init() as convenience initializers.

If the underlying 'problematic' initializers are written in Obj-C, this may be of help:

For consistency and simplicity, Objective-C factory methods get mapped as convenience initializers in Swift. This mapping allows them to be used with the same concise, clear syntax as initializers.

https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithObjective-CAPIs.html#//apple_ref/doc/uid/TP40014216-CH4-XID_26

@jpsim

This comment has been minimized.

Contributor

jpsim commented May 26, 2015

Thanks for the tips, @ataibarkai, we'll take those into consideration when looking for options to make overriding Object.init() possible.

@ataibarkai

This comment has been minimized.

ataibarkai commented May 26, 2015

Thanks for the quick reply @jpsim.
Any idea regarding the timeline for implementing this? Is it being actively worked on yet?

I've actually come up with a hack around this that you guys might also find useful in allowing designated initializes (including overriding init() ).

It compiles fine but I haven't been able to test it yet, since I need to have objects of type RLMRealm, RLMObjectSchema, and RLMSchema.

Any idea how to get this work with RealmSwift?

I've tried simply having import Realm / import Realm.Private but while this compiles fine, it crashes during runtime with dyld: Library not loaded: @rpath/Realm.framework/Realm.

I've also tried to directly add the compiled Realm framework onto the project the same way I added the RealmSwift framework (I'm using Carthage) but I got tons of errors due to ambiguity in which kind of a class to use (the Realm version or the RealmSwift version).

Any pointers would be much appreciated.

@jpsim

This comment has been minimized.

Contributor

jpsim commented May 26, 2015

To be honest, this isn't our highest priority at the moment (just see the list of GH issues marked P1 and you'll understand). Also, there's a fairly simple workaround for your situation (inability to override convenience initializers): use a configuration method:

class MyModel: Object {
  func configure(param1:param2:...) {
    // configure self
  }
}

You can override this method for subclasses.

@ataibarkai

This comment has been minimized.

ataibarkai commented May 26, 2015

Gotcha, completely understandable.

I'm actually doing exactly what you suggested now in my code, though I was hoping for a more elegant solution.

But this is far from a pressing issue, so as I said, completely understandable.

@rafalkitta

This comment has been minimized.

rafalkitta commented Oct 1, 2015

Hi, I encountered the same error with unsupported overriding init() method in Object subclass. Are you going to add ability to override init()?

Actually I am forced to use Realm (instead RealmSwift) in my Swift project.

@jpsim

This comment has been minimized.

Contributor

jpsim commented Oct 1, 2015

This was actually fixed in the months since this issue was last active and we neglected to update it.

Overriding Object.init() is now supported:

public class MyModel: Object {
    required public init() {
        super.init()
        // Perform further initialization
    }
}

Note that Object.init() is a required initializer, and thus must be implemented in order to provide convenience initializers (see #1928).

@tsheaff

This comment has been minimized.

tsheaff commented Sep 15, 2016

@jpsim this is an old thread, but still relevant to me now.

Given the following file:

import Foundation
import RealmSwift

@objc class TestObject: Object {
    dynamic var testProperty: String

    public required init() {
        super.init()
    }

    convenience init(testProperty: String) {
        self.testProperty = testProperty
        self.init()
    }
}

I get errors at line super.init() that testProperty is not properly initialized yet, which makes sense. Typically in this case I would just make init(testProperty:) a designated initializer, but because I'm required to implement an init() with no arguments if I want and custom initializer, I'm unsure how to properly initialize my stored properties with values from the caller of the init.

In this case something like a func configure(testProperty:) also doesn't work, as I get an error that TestObject has no initializers because no initializer is setting testProperty's value.

Does this mean I'm not allowed to have stored properties without default values in an Object subclass? I don't want to make every stored property optional, should I simply set dummy values like dynamic var testProperty: String = ""? This feels kinda gross and un-swifty

@jpsim

This comment has been minimized.

Contributor

jpsim commented Sep 15, 2016

Does this mean I'm not allowed to have stored properties without default values in an Object subclass? I don't want to make every stored property optional, should I simply set dummy values like dynamic var testProperty: String = ""? This feels kinda gross and un-swifty

Yes, non-optional properties on RealmSwift.Object must have a default property value due to limitations in Swift's runtime type reflection mechanism. We agree that this is gross and not switfty, but Swift lacks the capabilities for us to avoid this.

I believe we've filed bugs towards bugs.swift.org to this regard, and likely have GitHub issues on this repo tracking making improvements to this, but I can't find them at the moment. I encourage you to file a radar or Swift bug to request a richer reflection mechanism that could allow us to make this nicer.

@tsheaff

This comment has been minimized.

tsheaff commented Sep 15, 2016

Thanks for the clarification. This is helpful. Perhaps adding this more explicitly to the docs could clear up some confusion here.

@tsheaff

This comment has been minimized.

tsheaff commented Sep 15, 2016

@jpsim have you guys made a pull-request with a proposal on swift-evolution? That seems like the best way to get officially enrolled in apple's swift feature pipeline, although I don't know a ton about the internal process.

A simple search for reflection on swift-evolution indicates that this is not on their current to-do list.

@jpsim

This comment has been minimized.

Contributor

jpsim commented Sep 15, 2016

Reflection improvements was explicitly listed under non-goals for Swift 3, which meant that it was counter-productive to bring it up through the Swift Evolution process at that time. Now that Swift 3 is officially out, Swift Evolution will start considering reflection-related suggestions, so moving forward if someone had the time and motivation to do so, a proposal could be submitted.

@tsheaff

This comment has been minimized.

tsheaff commented Sep 15, 2016

Nice. I'd be interested in spending some time looking at this. Any other internal Realm discussions I could start with?

@jpsim

This comment has been minimized.

Contributor

jpsim commented Sep 15, 2016

There's this really scary and likely-unfit-for-production-use proof of concept implementation that @JadenGeller did a few weeks ago here: https://gist.github.com/JadenGeller/61178fac92ffe595a5a2243e3996400c

There's another discussion about this in #1615.

The code in ObjectUtil, RLMObjectSchema and RLMSchema will also likely be useful to read through and understand. git blame through the code I've highlighted will show you its evolution and different commits and PRs discussing the mechanisms at play.

@tsheaff

This comment has been minimized.

tsheaff commented Sep 15, 2016

Thanks

@JadenGeller

This comment has been minimized.

Contributor

JadenGeller commented Sep 19, 2016

@jpsim Reflection is listed as a possible Swift 4 Stage 2 effort, so I imagine it could be worthwhile to participate this upcoming Spring.

@jpsim

This comment has been minimized.

Contributor

jpsim commented Sep 19, 2016

Indeed, that's what I was trying to say in #1849 (comment)

@harrisrap

This comment has been minimized.

harrisrap commented Nov 18, 2017

How would we initialize a List property using default property values inline with the property declaration (rather than having to override init)?

In the example below, I want anytime I create a new CMSimpleRealmModel, that myValues is a list of 10 blank strings. How do I do that?

class CMSimpleRealmModel: Object {
    dynamic var myKey: String = NSUUID().UUIDString
    dynamic var name: String =  ""
    dynamic var myValues = List<String>()

}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment