Nonfailable Enum Initializer with Default Value

Nonfailable enum initializer with default value

You mean something like that?

enum ErrorCode: Int {
case NoErr = 0, Err1, Err2, LastErr, DefaultErr

init(value: Int) {
if (value > LastErr.rawValue) {
self = .DefaultErr
} else {
self = ErrorCode(rawValue: value)!
}
}
}

let error: ErrorCode = .LastErr
let anotherError: ErrorCode = ErrorCode(value: 99)

Here is another variation:

enum ErrorCode: Int {
case NoErr = 0, Err1, Err2, LastErr

init?(value: Int) {
if (value > 3) {
return nil
} else {
self = ErrorCode(rawValue: value)!
}

}
}

let error: ErrorCode = .LastErr
let anotherError: ErrorCode? = ErrorCode(value: 99)

which is equivalent to :

enum ErrorCode: Int {
case NoErr = 0, Err1, Err2, LastErr
}

let anotherError: ErrorCode? = ErrorCode(rawValue: 99)

because as Apple doc is stating:

NOTE

The raw value initializer is a failable initializer, because not every
raw value will return an enumeration member. For more information, see
Failable Initializers.

But in general, if you want to use enum with rawvalue, you should expect an optional and treat the nil returned value as a default error case outside the enum definition. That would be my recommendation.

Overriding Enum init?(rawValue: String) to not be optional

The default initializer is failable. It means that if the received parameter does not match a valid enum case it does return nil.

Now you want to do 2 incompatibles things:

  1. You want to redefine the default initializer making it not failable. In fact you want a default enum value created when the received param is not valid.
  2. Inside your redefined initializer you want to call a failable initializer (which no longer exists) using the same name of the new one.

This is not possible, I the 3 possible solutions as follows:

  1. Creating a different init

You define a new not failable initializer with a default value, a different parameter name and inside it you call the default failable initializer.

enum Language: String {

case english = "English", italian = "Italian", french = "French"

init(fromRawValue: String) {
self = Language(rawValue: fromRawValue) ?? .english
}
}

  1. Redefining the default init

You redefine the default initializer, you make it not failable and you write the full logic inside it.

enum Language: String {

case english = "English", italian = "Italian", french = "French"

init(rawValue: String) {
switch rawValue {
case "Italian": self = .italian
case "French": self = .french
default: self = .english
}
}
}

  1. Creating a static func
enum Language: String {

case english = "English", italian = "Italian", french = "French"

static func build(rawValue: String) -> Language {
return Language(rawValue: rawValue) ?? .english
}
}

Now you can build a Language value writing:

let italian = Language.build(rawValue: "Italian") // Italian
let defaultValue = Language.build(rawValue: "Wrong input") // English

Parameterless Failable initializer for an NSObject subclass

As you are subclassing NSObject, you cannot have a failable no-parameter initialiser as NSObject's no parameter initialiser is not failable.

You could create a class factory method that returns an instance or nil depending on the iOS version

Can a Swift enum have a function/closure as a raw value?

It's not as straight forward, but you could use OptionSet, see this page:

Unlike enumerations, option sets provide a nonfailable init(rawValue:) initializer to convert from a raw value, because option sets don’t have an enumerated list of all possible cases. Option set values have a one-to-one correspondence with their associated raw values.

Could be something like this:

func doSomething() {}
func doSomethingElse() {}

struct MyClosures: OptionSet {

static let closureOne = MyClosures(rawValue: doSomething)
static let closureTwo = MyClosures(rawValue: doSomethingElse)

let rawValue: () -> Void

init(rawValue: @escaping () -> Void) {
self.rawValue = rawValue
}

init() {
rawValue = {}
}

mutating func formUnion(_ other: __owned MyClosures) {
// whatever makes sense for your case
}

mutating func formIntersection(_ other: MyClosures) {
// whatever makes sense for your case
}

mutating func formSymmetricDifference(_ other: __owned MyClosures) {
// whatever makes sense for your case
}

static func == (lhs: MyClosures, rhs: MyClosures) -> Bool {
// whatever makes sense for your case
return false
}
}

And so you can use it as:

let myClosures: MyClosures = [ .closureOne, .closureTwo ]

HOWEVER looking at your explanation in the comment:

So I'm trying to find the most efficient way to run a function given the state of a variable.

I think what you actually want is some sort of state machine. Some examples are available here and here

Why doesn't a class have to provide a failable initializer if it implements a protocol that declares one?

This is for the same reason that this compiles:

class A {
init?(s:String) {}
init() {}
}
class B : A {
override init(s:String) {super.init()}
}

init can override (i.e. be substituted for) init?.

See also the docs (when something is so clearly documented, it seems silly to ask "why"; it's just a fact about the language):

A failable initializer requirement can be satisfied by a failable or nonfailable initializer on a conforming type.

(As pointed out in the comments on the question and on the answer, this makes perfect sense if you think about the difference between an init? that happens never to fail and an init with the same signature — namely, there is no effective difference. To put it another way: You can tell me that I may fail, but you cannot tell me that I must fail.)

In Swift, how do I create a convenience init for a class where the init's implementation creates the class value instead of calling an existing init

Saying that factory initializers are "not supported yet" in Swift is fallacious. Their exclusion is a design decision, and their use intended to be covered by failable initializers; quoting the following Apple Swift blog post

Failable initializers eliminate the most common reason for factory
methods in Swift, which were previously the only way to report failure
when constructing this object.

...

Using the failable initializer allows greater use of Swift’s uniform
construction syntax, which simplifies the language by eliminating
the confusion and duplication between initializers and factory
methods
.

So in your case, you're probably looking for a convenience failable initializer. E.g., something along the lines

extension NSData {
convenience init?(JSONObject: AnyObject) {
do {
let foo = try NSJSONSerialization.dataWithJSONObject(JSONObject, options: [])
self.init(data: foo)
}
catch {
return nil
}
}
}

/* Example usage */
let foo : AnyObject = ["Foo":"bar"]
let bar = NSData.init(JSONObject: foo)

In the title of your question you include "... instead of calling an existing init". When making use of convenience initializer, a designated initializer (of same class) must be called at some point (even via other convenience initializers). From the Swift Language Guide - Initialization - Class Inheritance and Initialization:

...

Rule 2

A convenience initializer must call another initializer from the same
class.

Rule 3

A convenience initializer must ultimately call a designated
initializer.

The example code above, however, allows an early escape (failure) of the convenience initializer if NSJSONSerialization.dataWithJSONObject(...) fails, but if it succeeds, sooner or later a designated initializer needs to be called (in this case init(data:) designated initializer).


For details on failable initializers, see the Swift Language Guide - Initialization - Failable Initializers. For an additional remark regarding the initializer chain (convenience -> ... -> designated initializer), see rickster:s comment below.



Related Topics



Leave a reply



Submit