I'd like my class init() in Swift to throw an error if something goes wrong with loading a file into a string within the class. Once the file is loaded, the string will not be altered, so I would prefer to use let. This works:
class FileClass { var text: NSString = "" init() throws { do { text = try NSString( contentsOfFile: "/Users/me/file.txt", encoding: NSUTF8StringEncoding ) } catch let error as NSError { text = "" throw error } } }
but when I replace
var text: NSString = ""
with
let text: NSString
I get an All stored properties of a class instance must be initialized before throwing from an initializer error.
I've tried various approaches such as to make text optional
let text: NSString?
but haven't found any that work. It it possible to have text be loaded from a file, immutable, and for init() to throw an error? Can I have my cake and eat it three?
Many thanks in advance!
Swift init() Initialization is the process of preparing an instance of a class, structure, or enumeration for use. This process involves setting an initial value for each stored property on that instance and performing any other setup or initialization that is required before the new instance is ready for use.
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 ... } }
In a failable initializer, return nil indicates that initialization has failed; no other value can be returned. In the example, failure occurs when the string could not be parsed as an integer. Otherwise, self is initialized to the parsed value.
Since Swift 2.2 you can break the execution of a class initialiser without the need of populating all stored properties
class FileStruct { let text: String init() throws { do { text = try String(contentsOfFile: "/Users/me/file.txt", encoding: NSUTF8StringEncoding ) } catch let error as NSError { throw error } } }
Currently in Swift class
you cannot break the execution of an initialiser before every stored property has been initialized.
On the other hand, you don't have this constraint with structs
so
struct FileStruct { var text: String init() throws { do { text = try String(contentsOfFile: "/Users/me/file.txt", encoding: NSUTF8StringEncoding ) } catch let error as NSError { throw error } } }
You can also avoid the do/catch block
struct FileStruct { var text: String init() throws { text = try String(contentsOfFile: "/Users/me/file.txt", encoding: NSUTF8StringEncoding) } }
Finally I replaced NSString
with String
since we are using Swift, not Objective-C ;-)
You can use a failable initializer instead, as is more suited for this kind of scenarios.
class FileClass { let text: String init?() { guard let fileContents = try? NSString( contentsOfFile: "/Users/me/file.txt", encoding: NSUTF8StringEncoding ) else { text = "" return nil } text = fileContents as String } }
or, if you want to print the error:
class FileClass { let text: String init?() { do { text = try String( contentsOfFile: "/Users/me/file.txt", encoding: NSUTF8StringEncoding ) } catch let error as NSError { print("Error while reading: \(error)") text = "" return nil } } }
Usage is easier than with a throwing initializer, as you can use if-let
or guard-let
:
if let file = FileClass() { }
, or
guard let file = FileClass() else { return }
versus
let file: FileClass do { file = FileClass() } catch { }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With