Swift Cannot Assign to Self in a Class Init Method

Swift cannot assign to self in a class init method

Class functions that return instances of that class seems to be an anti-pattern in Swift. You'll notice that the "with" Objective-C class methods like [NSString stringWithString:@"some other string"] made the transition as "with"-less convenience initializers: NSString(string: "some other string").

Furthermore, you'll need to delegate to a designated initializer from within a convenience initializer.

Also, since you're 1) defining the original class and 2) don't need the convenience initializer scoped differently than the designated initializer, I don't see any reason to place it in an extension.

Putting those together:

class Letter {
init() { … }

convenience init(filePath: String) {
self.init()
loadFromFile(filePath)
}

func loadFromFile(filePath: String) { … }
}

let letter1 = Letter()
letter1.loadFromFile("path1")

let letter2 = Letter(filePath: "path2")

In summary, the analogy for assigning to self in Swift is calling an initializer.

Let me know if this works for you!

Cannot assign to self out of a method in the init family?

If your method is an initializer, it must start with init:

- (instancetype)initWithDropDown:(UIButton *)b:(CGFloat *)height:(NSArray *)arr:(NSString *)direction

Otherwise, you can change it to be a class method, and return a new instance:

+ (instancetype)showDropDown:(UIButton *)b:(CGFloat *)height:(NSArray *)arr:(NSString *)direction
{
btnSender = b;
animationDirection = direction;
YourClassName obj = [[self alloc] init];
if (obj) {
// Initialization code
// …
}
return obj;
}

Why does Swift disallow assignment to self in class init, but not in protocol init?

It seems this currently behaves as expected.

The whole problem has been discussed on the swift forums: Assigning to Self in Protocol Extensions

The last time this quirk came up in internal discussions, the thought some of us had was that it might be worthwhile to prohibit classes from conforming to protocols with mutating requirements altogether. If you think about it, this makes some amount of sense — it seems like it would be quite hard to write code that can operate on both mutable values and mutable references generically, since the latter do not have value semantics:

var x = y
x.mutatingProtocolRequirement()
// did y change too?

However the discussion sort of fizzled out.

Cannot assign to self in a method - Swift

Assigning to self is for value types only, struct and enum.

In your case you should do like this:

public convenience init?(context: NSManagedObjectContext) {
let entity = NSEntityDescription.entityForName("Contact", inManagedObjectContext: context);
self.init(entity: entity!, insertIntoManagedObjectContext: context)

if User.loggedInUserId(context) == nil {
context.deleteObject(self)
return nil
}
}

You must call self.init(..) unconditionally, because, it's a rule for convenience initializer.
If the condition is false, deleteObject(self) and return nil. Hmm... I think you should not use
failable initializers for this.

Initialising member to class function causes 'self' used in method call error

It might be better to not use a Bool, but rather a nested Enum, which is also more extendible if you wanna add some other modes of haptic feedback later on.

I have a generalized solution for a generalized problem of your question. So either you do:


public class FunctionOwner {

private let mode: Mode

public init(`do` mode: Mode = .default) {
self.mode = mode
}
}

public extension FunctionOwner {

enum Mode {
case foo, bar
}

func fooOrBar() {
switch mode {
case .foo: foo()
case .bar: bar()
}
}
}

private extension FunctionOwner {
func foo() {
print("doing foo")
}

func bar() {
print("doing bar")
}
}

public extension FunctionOwner.Mode {
static var `default`: FunctionOwner.Mode {
return .foo
}
}

// USAGE
FunctionOwner(do: .bar).fooOrBar() // prints "doing foo"
FunctionOwner(do: .foo).fooOrBar() // prints "doing bar"

Or if you for some reason do want to keep the stored Mode, you can do this (might be relevant for your actual question on how you do a workaround of referencing self in the init.):

public class FunctionOwner {

private let _function: (FunctionOwner) -> Void

public init(`do` mode: Mode = .default) {
_function = { functionOwner in
switch mode {
case .foo: functionOwner.foo()
case .bar: functionOwner.bar()
}
}
}
}

public extension FunctionOwner {

enum Mode {
case foo, bar
}

func fooOrBar() {
_function(self)
}
}

// The rest of the code is the same as the example above

Unexpected Cannot assign to property 'self' is immutable compile time error IN CLASS

It's a bug, but not the bug you might think (and the error message is no help at all). It's a known bug caused by the fact that the keyboardAppearance property of the UITextInputTraits protocol is an optional property (meaning that it might not be implemented). This is an Objective C feature, not a Swift feature, and the bug is that such properties are not directly settable in Swift, even when they are marked {get set}.

To see this, let's emulate the same schema for ourselves:

@objc public protocol P {
@objc optional var keyboardAppearance : NSString {get set}
}

open class C : NSObject {
private var wrapped: P
public init(wrapped: P) {
self.wrapped = wrapped
super.init()
}
open var keyboardAppearance : NSString {
get {
wrapped.keyboardAppearance ?? "" as NSString
}
set {
wrapped.keyboardAppearance = newValue
}
}
}

We get the same error. But if you delete the keyword optional, the error goes away. This proves that the optional is what causes the issue. But you cannot delete optional in your situation, because that protocol doesn't belong to you.

The workaround is the same as for the original bug — use a key path:

let kp = \C.wrapped.keyboardAppearance
self[keyPath:kp] = newValue

Not able to pass self (which implements a protocol) to init method of a class instantiation.(Swift)

This actually doesn't appear to be anything to do with your generics or protocol conformance. It's simply that you're trying to access self in your property assignment before self has been initialised (default property values are assigned during initialisation).

The solution therefore is to use a lazy property like so:

lazy var fooInstant : Foo<MyType> = Foo(_dep : self)

This will now be created when it's first accessed, and therefore after self has been initialised.

swift how to update viewModel parameter while init

If you want to “reference” self later (e.g., update the same instance later), self should be a reference type, not a value type.

So you can solve this by making the view model a class (a reference type) rather than a struct (a value type).

“Model” objects are good candidates for value types, but “view models” are generally reference types. See Value and Reference Types for a discussion of when you might use a value type and when you might use a reference type. For a general discussion, see The Swift Programming Language: Structures and Classes.


By the way, the closure’s capture list syntax (the stuff in between the [ and the ]) has different practical implications if the captured variable is a reference type vs when it is a value type.

With a value type, the capture list makes sure you have a copy of the value:

struct Person {…}

var person = Person(…)
foo { [person] in
// this code will be dealing with a safe copy of the original `Person`
}

// maybe you’ll mutate the original `Person` instance here,
// but the above closure will have a copy of the original one

Contrast that with a reference type, where the capture list makes sure you have a strong reference to the reference type:

class ViewModel {
func bar() {
baz { [self] in
// this has a strong reference to this view model instance, not a copy of the instance
}
}
}

let viewModel = ViewModel(…)
viewModel.bar() // any updates that bar does asynchronously, will update this instance of the view model, not a copy of it

As an aside, with reference types, you will often see a capture list with the weak keyword, e.g., [weak self], if you want a “weak” reference to the class rather than a strong reference. This is common if you need to prevent a “strong reference cycle” (a topic beyond the scope of this conversation). This is discussed in The Swift Programming Language: Strong Reference Cycles for Closures.

But, whether a strong reference or weak reference, you are not dealing with a copy of the object, but rather a “reference” to the same original instance.



Related Topics



Leave a reply



Submit