Olexiy  Pyvovarov
Olexiy Pyvovarov

Reputation: 890

Swift cannot assign to self in a class init method

I have a class called Letter

class Letter
{    
    init() {}
}

And I have an extension for this class:

extension Letter
{
    convenience init(file_path:String) {
       self = Letter.loadFromFile(file_path)
    }

    class func loadFromFile(file_path:String)->Letter {...}
}

I need to create and init with path to file and when i call Letter(file_path) I need a new object that returned by a func loadFromFile. How to assign in an init method or to return a new object?

It gives the error:

Cannot assign to value: 'self' is immutable

Upvotes: 5

Views: 8681

Answers (3)

Kostiantyn Koval
Kostiantyn Koval

Reputation: 8483

  1. Convenience initializer must delegate up to designated initializer

It says that convenience init(file_path:String) should call other initialiser

convenience init(file_path:String) {
  self.init()
  //here can set other properties
}
  1. Convenience initialiser usually provide some default parameters

Convenience initialiser are designed to make creation of class instance less complicated. It means that you don't need to pass all arguments to constructor. In your example the class should look like this

  • Designated initializer takess all possible arguments.
  • Convenience provide default value

Code example

// Create instance of a Letter
Letter()
Letter(file_path: "path.txt")
Letter(file_path: "path.txt", option: 0, other: 0)

//Class Implementation 

class Letter
{
  init(file_path: String , option: Int, other: Int) {
    // Instansiate class
  }
}


extension Letter {

  convenience init() {
    self.init(file_path:"a")
  }

  convenience init(file_path:String) {
    self.init(file_path: file_path , option: 0, other: 0)
  }

  class func loadFromFile(file_path:String) -> Letter {
    return Letter()
  }
}

Now you can create instance of Letter this way -

Upvotes: 1

Daniel T.
Daniel T.

Reputation: 33967

You can't assign to self. What about something like this:

class Letter {
}

extension Letter {
    convenience init(filePath: String) {
        self.init()
        // code to load a Letter from a file goes here.
    }

    class func loadFromFile(filePath: String) -> Letter {
        return Letter(filePath: filePath)
    }
}

Upvotes: 0

Andrew
Andrew

Reputation: 2468

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!

Upvotes: 2

Related Questions