In a following class
class Foo {
let _defaultValue = "N/A"
let value: String
init (dict: NSDictionary) {
self.value = dict["bar"] as? String! ?? _defaultValue
}
}
compiler fails with the message
constant 'self.value' captured by a closure before being initialized
As far as I can see, no operators read `self.value. The message seems somewhat confusing.
I accidentally came up with a workaround. I should say it confuses me even more:
class Foo {
let value: String
init (dict: NSDictionary) {
let _defaultValue = "N/A"
self.value = dict["bar"] as? String! ?? _defaultValue
}
}
Declaring _defaultValue and initializing it within the constructor makes the code compile.
It'd be an immense help if someone could explain why an error occurs in the first case and what is the compiler happier in the second case?
The reason for the error message is that the nil-coalescing operator
is defined as
public func ??<T>(optional: T?, defaultValue: #autoclosure () throws -> T) rethrows -> T
and does a "auto closure" on the second argument (in order to get
a short-circuiting behaviour). So
self.value = dict["bar"] as? String ?? _defaultValue
is transformed by the compiler to
self.value = dict["bar"] as? String ?? { self._defaultValue }()
and here the compiler complains because self is captured before
being fully initialised. (The error messages are slightly different
between Swift 2 and Swift 3).
Possible workarounds. You can assign the property to a local variable first:
init(dict: NSDictionary){
let defValue = _defaultValue
self.value = dict["bar"] as? String! ?? defValue
}
Or you can make it a static property of the class:
class Foo {
static let _defaultValue = "N/A"
let value: String
init(dict: NSDictionary) {
self.value = dict["bar"] as? String ?? Foo._defaultValue
}
}
Or replace ?? by an if-statement:
class Foo {
let _defaultValue = "N/A"
let value: String
init (dict: NSDictionary) {
if let value = dict["bar"] as? String {
self.value = value
} else {
self.value = _defaultValue
}
}
}
Addendum: Related resources:
Autoclosure of self in initializers in the Swift forum.
SR-944 Cannot use 'self' in RHS of && before all properties are initialized bug report.
Quote from the bug report:
Jordan Rose: This is true since && is implemented using #autoclosure, but it's certainly suboptimal.
Related
I'd like to understand how could the function below sometimes generates a Swift "_assertionFailure" in line:
if let s = dict![key] as? String
I suppose that if dict![key] is not found, a nil would return, so the if let would get a nil value and the condition would fail, with no errors, no assertions. Where I am getting wrong?
func getDictKey(_ dict: NSDictionary?, key: String) -> String?
{
var value: String?;
if (dict != nil && !key.isEmpty)
{
if let s = dict![key] as? String {
value = s;
}
}
return value;
}
Your syntax is very, very objective-c-ish.
In Swift you can simply write
func getDictKey(_ dict: NSDictionary?, key: String) -> String?
{
return dict?[key] as? String
}
It contains all checks except the empty string check. The question mark after dictaborts the chain if the dictionary is nil.
You should not use NSDictionary in Swift but a more meaningful naming
func getValue(from dict: [String:Any]?, forKey key: String) -> String?
{
return dict?[key] as? String
}
Using the PartialKeyPath API, how can you access a value of a key path's reference? For example, this works for non-optional values, but not with Optional values.
The issue I'm having is that self[keyPath: keyPath] returns a non-optional Any value.
struct Element {
let name: String
let mass: Double?
func stringValue(_ keyPath: PartialKeyPath<Element>) -> String {
let value = self[keyPath: keyPath]
switch value {
case let string as String:
return string.capitalized
case nil:
return "N/A"
case let value:
return String(describing: value)
}
}
}
let element = Element(name: "Helium", mass: 4.002602)
let string = element.stringValue(\Element.mass) /* Optional(4.002602) */
The result is that case nil is never executed and the last case is being printed as Optional(value).
How can I unwrap value properly to extract the optional?
The solution was to use Mirror to unwrap the optional which seems less than optimal. Looking forward to better Reflection support in Swift!
func unwrap(_ value: Any) -> Any? {
let mirror = Mirror(reflecting: value)
if mirror.displayStyle != .optional {
return value
}
if let child = mirror.children.first {
return child.value
} else {
return nil
}
}
struct Element {
let name: String
let mass: Double?
func stringValue(_ keyPath: PartialKeyPath<AtomicElement>) -> String {
guard let value = unwrap(self[keyPath: keyPath]) else {
return "N/A"
}
switch value {
case let string as String:
return string.capitalized
default:
return String(describing: value)
}
}
}
let element = Element(name: "Helium", mass: 4.002602)
let string = element.stringValue(\Element.mass) /* 4.002602 */
I have a Gfycat struct that represents the data I want to store after making a network call to the Gfycat API.
typealias JSONDictionary = [String: Any]
struct Gfycat {
let id: String
let number: Int
}
In an extension to the Gfycat struct, I wrote a failable initializer that takes a dictionary of type [String: Any] as its argument. This dictionary is then used to assign values to the struct's properties. This is the original init method I wrote:
extension Gfycat {
init?(dictionary: JSONDictionary) {
guard let id = dictionary["gfyId"] as? String,
let number = dictionary["gfyNumber"] as? Int { return nil }
self.id = id
self.number = number
}
}
The problem is that when accessing a value from the dictionary, I cannot downcast the value from Any to Int. I must first downcast Any to String, then convert that string to Int. Is this a bug or rather a feature of Swift that I don't understand?
This was my solution:
extension Gfycat {
init?(dictionary: JSONDictionary) {
guard let id = dictionary["gfyId"] as? String,
let uncastedNumber = dictionary["gfyNumber"] as? String,
let number = Int(uncastedNumber) else { return nil }
self.id = id
self.number = number
}
}
I must first downcast Any to String, then convert that string to Int. Is this a bug or rather a feature of Swift that I don't understand?
It's neither a bug nor a feature of Swift. It's a fact about the dictionary you're working with. This thing is a String, not an Int. So you cannot cast it to an Int.
In a following class
class Foo {
let _defaultValue = "N/A"
let value: String
init (dict: NSDictionary) {
self.value = dict["bar"] as? String! ?? _defaultValue
}
}
compiler fails with the message
constant 'self.value' captured by a closure before being initialized
As far as I can see, no operators read `self.value. The message seems somewhat confusing.
I accidentally came up with a workaround. I should say it confuses me even more:
class Foo {
let value: String
init (dict: NSDictionary) {
let _defaultValue = "N/A"
self.value = dict["bar"] as? String! ?? _defaultValue
}
}
Declaring _defaultValue and initializing it within the constructor makes the code compile.
It'd be an immense help if someone could explain why an error occurs in the first case and what is the compiler happier in the second case?
The reason for the error message is that the nil-coalescing operator
is defined as
public func ??<T>(optional: T?, defaultValue: #autoclosure () throws -> T) rethrows -> T
and does a "auto closure" on the second argument (in order to get
a short-circuiting behaviour). So
self.value = dict["bar"] as? String ?? _defaultValue
is transformed by the compiler to
self.value = dict["bar"] as? String ?? { self._defaultValue }()
and here the compiler complains because self is captured before
being fully initialised. (The error messages are slightly different
between Swift 2 and Swift 3).
Possible workarounds. You can assign the property to a local variable first:
init(dict: NSDictionary){
let defValue = _defaultValue
self.value = dict["bar"] as? String! ?? defValue
}
Or you can make it a static property of the class:
class Foo {
static let _defaultValue = "N/A"
let value: String
init(dict: NSDictionary) {
self.value = dict["bar"] as? String ?? Foo._defaultValue
}
}
Or replace ?? by an if-statement:
class Foo {
let _defaultValue = "N/A"
let value: String
init (dict: NSDictionary) {
if let value = dict["bar"] as? String {
self.value = value
} else {
self.value = _defaultValue
}
}
}
Addendum: Related resources:
Autoclosure of self in initializers in the Swift forum.
SR-944 Cannot use 'self' in RHS of && before all properties are initialized bug report.
Quote from the bug report:
Jordan Rose: This is true since && is implemented using #autoclosure, but it's certainly suboptimal.
An object of mine has an integer ID. Since this is a required property I am not defining it as an optional and I am requiring it in the designated initializer:
class Thing {
var uniqueID: Int
var name: String?
init (uniqueID: Int) {
self.uniqueID = uniqueID
}
}
Since I am creating one of these from some JSON, the usage is along the lines of:
if let uniqueID = dictionary["id"] as? Int {
let thing = Thing(uniqueID: unique)
}
Next, I would like to be able to add a convenience initializer to the Thing class that accepts the dictionary object and sets the properties accordingly. This includes the required uniqueID and some other optional properties. My best effort so far is:
convenience init (dictionary: [String: AnyObject]) {
if let uniqueID = dictionary["id"] as? Int {
self.init(uniqueID: uniqueID)
//set other values here?
}
//or here?
}
But of course this isn't sufficient since the designated initializer isn't called on all paths of the conditional.
How should I be handling this scenario? Is it even possible? Or should I accept that uniqueID must be an optional?
You have a couple of options with this one. One is a failable initialisers:
convenience init?(dictionary: [String: AnyObject]) {
if let uniqueID = dictionary["id"] as? Int {
self.init(uniqueID: uniqueID)
} else {
self.init(uniqueID: -1)
return nil
}
}
Technically this can be tweaked a bit (mainly depending on your preference/version of swift), but my person preference is something as follows:
class func fromDictionary(dictionary: [String: AnyObject]) -> Thing? {
if let uniqueID = dictionary["id"] as? Int {
return self.init(uniqueID: uniqueID)
}
return nil
}
All together, as a playground:
class Thing {
var uniqueID: Int
var name: String?
init(uniqueID: Int) {
self.uniqueID = uniqueID
}
convenience init?(dictionary: [String: AnyObject]) {
if let uniqueID = dictionary["id"] as? Int {
self.init(uniqueID: uniqueID)
} else {
self.init(uniqueID: -1)
return nil
}
}
class func fromDictionary(dictionary: [String: AnyObject]) -> Thing? {
if let uniqueID = dictionary["id"] as? Int {
return self.init(uniqueID: uniqueID)
}
return nil
}
}
let firstThing = Thing(uniqueID: 1)
let secondThing = Thing(dictionary: ["id": 2])
let thirdThing = Thing(dictionary: ["not_id": 3])
let forthThing = Thing.fromDictionary(["id": 4])
let fithThing = Thing.fromDictionary(["not_id": 4])
The best solution is probably to use a failable initializer, which will either return an instantiated object or nil.
Because Swift objects cannot be partially constructed and convenience initializers must call a non-convenience initializer, we must still do something in the failure case.
The result will look something like this:
convenience init?(dictionary: [String: AnyObject]) {
if let uniqueID = dictionary["id"] as? Int {
self.init(uniqueID: uniqueID)
} else {
self.init(uniqueID: 0)
return nil
}
}
Generally speaking, our non-convenience initializer(s) should be one that accepts all arguments, and convenience initializers should be methods which don't require some of the arguments.
For example, I might make my default initializer look like this:
init(uniqueID: Int, name: String? = nil) {
self.uniqueID = uniqueID
self.name = name
}
This allows us to call the constructor in several different ways:
let thing1 = Thing(1)
let thing2 = Thing(2, nil)
let thing3 = Thing(3, "foo")
let thing4 = Thing(4, myUnwrappedStringVar)
let thing5 = Thing(5, myWrappedStringOptional)
And that already covers a lot of use cases for us.
So, let's add another convenience initializer that accepts an optional Int.
convenience init?(uniqueID: Int? = nil, name: String? = nil) {
if let id = uniqueID {
self.init(uniqueID: id, name: name)
} else {
self.init(uniqueID: 0)
return nil
}
}
Now we can take an Int? for our uniqueID argument and just fail when it's nil.
So, one more to accept the dictionary.
convenience init?(dictionary: [String: AnyObject]) {
let uniqueID = dictionary["id"] as? Int
let name = dictionary["name"] as? String
self.init(uniqueID: uniqueID, name: name)
}
We still have the slightly weird initialize then return nil pattern in our first convenience constructor, but everything else we build on top of this can simply call that convenience initializer and doesn't require the weird pattern.
In the initializer that takes the dictionary, if there's no id key, or if it's something that's not an Int, then the let uniqueID will be nil, so when we call the other constructor, it will call the one that accepts an Int?, be passed nil, return nil, and therefore the one we called will return nil.