Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Assigning let variable in fallible initializer swift 1.2

I have a struct with a fallible initializer, not an instance method, but an initializer. After updating to 1.2, when I try to assign a let property inside the initializer, I receive the following error Cannot assign to 'aspectRatio' in self. My code below:

import Foundation

public struct MediaItem
{
public let url: NSURL!
public let aspectRatio: Double

public var description: String { return (url.absoluteString ?? "no url") + " (aspect ratio = \(aspectRatio))" }

// MARK: - Private Implementation

init?(data: NSDictionary?) {
    var valid = false
    if let urlString = data?.valueForKeyPath(TwitterKey.MediaURL) as? NSString {
        if let url = NSURL(string: urlString as String) {
            self.url = url
            let h = data?.valueForKeyPath(TwitterKey.Height) as? NSNumber
            let w = data?.valueForKeyPath(TwitterKey.Width) as? NSNumber
            if h != nil && w != nil && h?.doubleValue != 0 {
                aspectRatio = w!.doubleValue / h!.doubleValue
                valid = true
            }
        }
    }
    if !valid {
        return nil
    }
}

struct TwitterKey {
    static let MediaURL = "media_url_https"
    static let Width = "sizes.small.w"
    static let Height = "sizes.small.h"
}
}

My question is what do I do to fix this?

like image 898
thank_you Avatar asked Apr 10 '15 20:04

thank_you


People also ask

How do you designate a Failable initializer Swift?

You write a failable initializer by placing a question mark after the init keyword ( init? ). Note: You cannot define a failable and a nonfailable initializer with the same parameter types and names. A failable initializer creates an optional value of the type it initializes.

Do you have to initialize variables in Swift?

Variables and constants do not require initialization when declared. However, the variable or constant requires type annotation so that when the compiler reads the code line-by-line, it can determine the data type at the time of the build. A Use of undeclared type error will be thrown otherwise.

Does initializer return any value in Swift?

You implement this initialization process by defining initializers, which are like special methods that can be called to create a new instance of a particular type. Unlike Objective-C initializers, Swift initializers don't return a value.

What is required Initializers in Swift?

An initializer is a special type of function that is used to create an object of a class or struct. In Swift, we use the init() method to create an initializer. For example, class Wall { ... // create an initializer init() { // perform initialization ... } }


1 Answers

Swift 1.2 has closed a loophole having to do with let properties:

The new rule is that a let constant must be initialized before use (like a var), and that it may only be initialized, not reassigned or mutated after initialization.

That rule is exactly what you are trying to violate. aspectRatio is a let property and you have already given it a value in its declaration:

public let aspectRatio: Double = 0

So before we ever get to the initializer, aspectRatio has its initial value — 0. And that is the only value it can ever have. The new rule means that you can never assign to aspectRatio ever again, not even in an initializer.

The solution is (and this was always the right way): assign it no value in its declaration:

public let aspectRatio: Double

Now, in the initializer, either assign it 0 or assign it w!.doubleValue / h!.doubleValue. In other words, take care of every possibility in the initializer, once. That will be the only time, one way or another, that you get to assign aspectRatio a value.

If you think about it, you'll realize that this is a much more sensible and consistent approach; previously, you were sort of prevaricating on the meaning of let, and the new rule has stopped you, rightly, from doing that.


In your rewrite of the code, you are failing to initialize all properties in the situation where you intend to bail out and return nil. I know it may seem counterintuitive, but you cannot do that. You must initialize all properties even if you intend to bail out. I discuss this very clearly in my book:

A failable class initializer cannot say return nil until after it has completed all of its own initialization duties. Thus, for example, a failable subclass designated initializer must see to it that all the subclass’s properties are initialized and must call super.init(...) before it can say return nil. (There is a certain delicious irony here: before it can tear the instance down, the initializer must finish building the instance up.)

EDIT: Please note that starting in Swift 2.2, this requirement will be lifted. It will be legal to return nil before initializing properties. This will put class initializers on a par with struct initializers, where this was already legal.

like image 111
matt Avatar answered Sep 19 '22 11:09

matt



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!