I think I probably missed the point of how this works but I have a class that needs to use a global optional value in several of its methods and right now I unwrapped it inside every method but I thought I could just unwrap the value in init(). Am I doing it wrong or is this now how it's supposed to work? - Thank you.
let iCloudPath = FileManager.default.url(forUbiquityContainerIdentifier: nil)?.appendingPathComponent("Documents")
class iCloudManager {
init() {
guard let iCloudPath = iCloudPath else { return }
}
function1(){
// uses iCloudPath but returns 'Value of optional type 'URL?' must be unwrapped to a value of type 'URL''
}
function2(){
// uses iCloudPath but returns 'Value of optional type 'URL?' must be unwrapped to a value of type 'URL''
}
}
Store the result as a property of your objects. Better yet, use a static property, not a global.
class iCloudManager {
static let defaultPath = FileManager.default.url(forUbiquityContainerIdentifier: nil)?.appendingPathComponent("Documents")
let path: URL
init?() {
guard let path = iCloudManager.defaultPath else { return nil }
self.path = path
}
func function1() {
// uses self.path
}
func function2() {
// uses self.path
}
}
Related
I'm looking for the right way of unwrapping optional to an instance variable in swift.
I have a singleton class, shared property of which can be nil, and I'm trying to assign the instance of the singleton in the failable initializer of another class so that I can use the singleton instance without caring of unwrapping it every time.
It looks like this:
class A {
static let shared = A()
let b = 1
private init?() {
return nil
}
}
class B {
let a: A
init?() {
if A.shared != nil {
a = A.shared!
} else {
return nil
}
print(a.b)
}
}
B()
Is there any way of doing this in a shorter way (using guard, if, etc.)?
You can write the init? of B as follows:
init?() {
guard let a = A.shared else { return nil }
self.a = a
print(a.b)
}
It's best to avoid code in the pattern:
if someVar != nil {
x = someVar!
}
guard let or if let exist to avoid such constructs.
But there is no way to avoid dealing with your optional singleton other than not making it optional. It's unusual to have an optional singleton instance.
This question already has an answer here:
Propagate an optional through a function (or Init) in Swift
(1 answer)
Closed 5 years ago.
I have a class with an optional zoomURL property let zoomURL : String?
I've been playing around with optional chaining to try and shorten the amount of nil checks I make. I know for the below I could easily check if the let zoomURLString = meeting.zoomURL but is it possible to skip this step and use it immediately as an argument within a function and check if this function is nil?
For example: (This fails)
if let parsedZoomURL = URL(string: meeting.zoomURL?){
//do stuff
}
You can use the
public func flatMap<U>(_ transform: (Wrapped) throws -> U?) rethrows -> U?
method of Optional:
if let parsedZoomURL = zoomURL.flatMap( { URL(string: $0) }) {
//do stuff
}
or shorter (as someone noticed in a now deleted comment):
if let parsedZoomURL = zoomURL.flatMap(URL.init) {
//do stuff
}
The optional binding succeeds only if zoomURL is not nil
and the closure (which is then called with the unwrapped value
of zoomURL) does not return nil.
Swift will not chain nils on method calls, but it will let you use the same chaining syntax with an extension property:
extension String {
var asURL : URL {
get {
return URL(string: self)!
}
}
}
Now you can use chaining as usual:
if let parsedZoomURL = meeting.zoomURL?.asURL {
//do stuff
}
If you really want to you can create an init extension for URL that supports nil values to be passed. Returns nil if the string is nil.
extension URL {
init?(string: String?) {
guard let url = string else { return nil }
self.init(string: url)
}
}
Usage:
if let url = URL(string: meeting.zoomUrl) {
// Do stuff
}
I have an swift class
class ApplicationManager {
var fanMode: FanMode
init()
{
self.applyDefaultSettings()
}
func applyDefaultSettings()
{
if let unwrappedFanMode = userDefaults.valueForKey(Consts.kStoredFanMode) as? FanMode {
self.fanMode = unwrappedFanMode
}
}
}
The code above throws this issue:
Use of 'self' in method call 'applyDefaultSettings' before all stored properties are initialized
What should I do here? So as message say I need to initialize all stored properties before I call any other method of class. So it means in init method I should initialize at least fanMode property. But I want to have method that apply kind of default settings for my properties to provide simple readability and clean code architecture. But maybe it's ok to use initializer of class to init all needed fields.
You also can do it by using this code:
var fanMode: FanMode = {
if let unwrappedFanMode = userDefaults.valueForKey(Consts.kStoredFanMode) as? FanMode {
return unwrappedFanMode
} else {
return FanMode()//some default implementation
}
}()
It is readable as You want.
As per Apple documentation, Swift does not allow you to left uninitialised variables or constants. If you want to set some default settings then assign your variables with initial values that will act as your default settings and later you can change them.
All instance properties must be initialized in the init method. You can either move the initialization to the init (defaultMode would be your default value if userDefaults is nil):
init() {
fanMode = (userDefaults?.valueForKey(Consts.kStoredFanMode) as? FanMode) ?? defaultMode
}
Set a default value for that property, for example:
var fanMode: FanMode = defaultMode
Or you can make your fanMode nullable:
var fanMode: FanMode? = nil
You can use an implicity unwrapped optional. Just add a ! to the variable declaration.
class ApplicationManager {
var fanMode: FanMode! //Implicitly unwrapped optional.
init()
{
self.applyDefaultSettings()
}
func applyDefaultSettings()
{
if let unwrappedFanMode = userDefaults.valueForKey(Consts.kStoredFanMode) as? FanMode {
self.fanMode = unwrappedFanMode
}
}
}
Basically it tricks xCode into telling it "Hey this variable is initialized and value will never be nil". But you want to be careful using these as if it does turn out to be nil your program will crash. But in your case it should be fine since you initialize it in the init method so it will never be nil before using it.
I'm trying to get a better understanding of protocols in Swift. Specifically optional protocol methods. I thought the issue might have to do with my protocol being defined / used in a different file, but if you put the following in a playground you'll get the same issue:
import Foundation
#objc protocol MyProtocol {
optional func shouldJump() -> Bool
}
extension NSObject : MyProtocol {}
class Test {
func testJump() {
let object = NSObject()
let jump = object.shouldJump?() ?? true
print("should jump: \(jump)")
}
}
let t = Test()
t.testJump()
Here is the error message:
error: value of type 'NSObject' has no member 'shouldJump'
let jump = object.shouldJump?() ?? true
^~~~~~ ~~~~~~~~~~
For some reason it doesn't accept that the protocol has been defined on NSObject. Code completion finds it, but the compiler doesn't let it pass.
I'm not sure if my ?? true part will work, but I want that to be a default value incase the method isn't defined.
How do I get this to work?
Your NSObject conforms to MyProtocol, but because it doesn't implement the optional protocol method, the compiler knows it does not have the Selector shouldJump:
let object = NSObject()
object.conformsToProtocol(MyProtocol) // true
object.respondsToSelector("shouldJump") // false
One way to solve this is to implement the protocol method in the extension in order for the object to perform that selector:
extension NSObject : MyProtocol {
func shouldJump() -> Bool {
// some logic here
return true
}
}
class Test {
func testJump() {
let object = NSObject()
let jump = object.shouldJump()
print("should jump: \(jump)")
}
}
let t = Test()
t.testJump() // works
If you don't want to implement the optional method in the extension, you have to cast your NSObject as MyProtocol and verify that it responds to the optional Selector:
class Test {
func testJump() {
let object = NSObject()
let obj = object as MyProtocol
if object.respondsToSelector("shouldJump") {
let jump = obj.shouldJump?()
print("should jump: \(jump)")
} else {
print("nope")
}
}
}
You can also skip the respondsToSelector step and use an if let or guard to verify that shouldJump() returns non-nil.
class Test {
func testJump() {
let object = NSObject()
guard let obj: MyProtocol = object else {
return // object does not conform to MyProtocol
}
if let jump = obj.shouldJump?() { // if shouldJump() returns non-nil
print("should jump: \(jump)")
} else {
print("nope")
}
}
}
I think this is because the compiler knowns NSObject doesn't have shouldJump method, so the call object.shouldJump?() makes no sense. You can cast object to your protocol:
let jump = (object as MyProtocol).shouldJump?() ?? true
Swift is a type safe language. In order to be able to use shouldJump?() you first must have an object conformant to MyProtocol. In this case you can simply cast your type:
let jump = (object as MyProtocol).shouldJump?() ?? true
You can also store it in a variable:
let jumper = object as MyProtocol
let jump = jumper?.shouldJump() ?? true
With the following code I try to define a simple model class and it's failable initializer, which takes a (json-) dictionary as parameter. The initializer should return nil if the user name is not defined in the original json.
1.
Why doesn't the code compile? The error message says:
All stored properties of a class instance must be initialized before returning nil from an initializer.
That doesn't make sense. Why should I initialize those properties when I plan to return nil?
2.
Is my approach the right one or would there be other ideas or common patterns to achieve my goal?
class User: NSObject {
let userName: String
let isSuperUser: Bool = false
let someDetails: [String]?
init?(dictionary: NSDictionary) {
if let value: String = dictionary["user_name"] as? String {
userName = value
}
else {
return nil
}
if let value: Bool = dictionary["super_user"] as? Bool {
isSuperUser = value
}
someDetails = dictionary["some_details"] as? Array
super.init()
}
}
That doesn't make sense. Why should I initialize those properties when
I plan to return nil?
According to Chris Lattner this is a bug. Here is what he says:
This is an implementation limitation in the swift 1.1 compiler,
documented in the release notes. The compiler is currently unable to
destroy partially initialized classes in all cases, so it disallows
formation of a situation where it would have to. We consider this a
bug to be fixed in future releases, not a feature.
Source
EDIT:
So swift is now open source and according to this changelog it is fixed now in snapshots of swift 2.2
Designated class initializers declared as failable or throwing may now return nil or throw an error, respectively, before the object has been fully initialized.
Update: From the Swift 2.2 Change Log (released March 21, 2016):
Designated class initializers declared as failable or throwing may now return nil or throw an error, respectively, before the object has been fully initialized.
For Swift 2.1 and earlier:
According to Apple's documentation (and your compiler error), a class must initialize all its stored properties before returning nil from a failable initializer:
For classes, however, a failable initializer can trigger an
initialization failure only after all stored properties introduced by
that class have been set to an initial value and any initializer
delegation has taken place.
Note: It actually works fine for structures and enumerations, just not classes.
The suggested way to handle stored properties that can't be initialized before the initializer fails is to declare them as implicitly unwrapped optionals.
Example from the docs:
class Product {
let name: String!
init?(name: String) {
if name.isEmpty { return nil }
self.name = name
}
}
In the example above, the name property of the Product class is
defined as having an implicitly unwrapped optional string type
(String!). Because it is of an optional type, this means that the name
property has a default value of nil before it is assigned a specific
value during initialization. This default value of nil in turn means
that all of the properties introduced by the Product class have a
valid initial value. As a result, the failable initializer for Product
can trigger an initialization failure at the start of the initializer
if it is passed an empty string, before assigning a specific value to
the name property within the initializer.
In your case, however, simply defining userName as a String! does not fix the compile error because you still need to worry about initializing the properties on your base class, NSObject. Luckily, with userName defined as a String!, you can actually call super.init() before you return nil which will init your NSObject base class and fix the compile error.
class User: NSObject {
let userName: String!
let isSuperUser: Bool = false
let someDetails: [String]?
init?(dictionary: NSDictionary) {
super.init()
if let value = dictionary["user_name"] as? String {
self.userName = value
}
else {
return nil
}
if let value: Bool = dictionary["super_user"] as? Bool {
self.isSuperUser = value
}
self.someDetails = dictionary["some_details"] as? Array
}
}
I accept that Mike S's answer is Apple's recommendation, but I don't think it's best practice. The whole point of a strong type system is to move runtime errors to compile time. This "solution" defeats that purpose. IMHO, better would be to go ahead and initialize the username to "" and then check it after the super.init(). If blank userNames are allowed, then set a flag.
class User: NSObject {
let userName: String = ""
let isSuperUser: Bool = false
let someDetails: [String]?
init?(dictionary: [String: AnyObject]) {
if let user_name = dictionary["user_name"] as? String {
userName = user_name
}
if let value: Bool = dictionary["super_user"] as? Bool {
isSuperUser = value
}
someDetails = dictionary["some_details"] as? Array
super.init()
if userName.isEmpty {
return nil
}
}
}
Another way to circumvent the limitation is to work with a class-functions to do the initialisation.
You might even want to move that function to an extension:
class User: NSObject {
let username: String
let isSuperUser: Bool
let someDetails: [String]?
init(userName: String, isSuperUser: Bool, someDetails: [String]?) {
self.userName = userName
self.isSuperUser = isSuperUser
self.someDetails = someDetails
super.init()
}
}
extension User {
class func fromDictionary(dictionary: NSDictionary) -> User? {
if let username: String = dictionary["user_name"] as? String {
let isSuperUser = (dictionary["super_user"] as? Bool) ?? false
let someDetails = dictionary["some_details"] as? [String]
return User(username: username, isSuperUser: isSuperUser, someDetails: someDetails)
}
return nil
}
}
Using it would become:
if let user = User.fromDictionary(someDict) {
// Party hard
}
Although Swift 2.2 has been released and you no longer have to fully initialize the object before failing the initializer, you need to hold your horses until https://bugs.swift.org/browse/SR-704 is fixed.
I found out this can be done in Swift 1.2
There are some conditions:
Required properties should be declared as implicitly unwrapped optionals
Assign a value to your required properties exactly once. This value may be nil.
Then call super.init() if your class is inheriting from another class.
After all your required properties have been assigned a value, check if their value is as expected. If not, return nil.
Example:
class ClassName: NSObject {
let property: String!
init?(propertyValue: String?) {
self.property = propertyValue
super.init()
if self.property == nil {
return nil
}
}
}
A failable initializer for a value type (that is, a structure or
enumeration) can trigger an initialization failure at any point within
its initializer implementation
For classes, however, a failable initializer can trigger an
initialization failure only after all stored properties introduced by
that class have been set to an initial value and any initializer
delegation has taken place.
Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks. https://itun.es/sg/jEUH0.l
You can use convenience init:
class User: NSObject {
let userName: String
let isSuperUser: Bool = false
let someDetails: [String]?
init(userName: String, isSuperUser: Bool, someDetails: [String]?) {
self.userName = userName
self.isSuperUser = isSuperUser
self.someDetails = someDetails
}
convenience init? (dict: NSDictionary) {
guard let userName = dictionary["user_name"] as? String else { return nil }
guard let isSuperUser = dictionary["super_user"] as? Bool else { return nil }
guard let someDetails = dictionary["some_details"] as? [String] else { return nil }
self.init(userName: userName, isSuperUser: isSuperUser, someDetails: someDetails)
}
}