Matt W Matt W - 2 months ago 32
Swift Question

How do I correct this Swift enum?

In the PluralSight course "Swift First Look" there is a section on Enums which appears to be out of date. The code provided is this:

//: Playground - noun: a place where people can play

import UIKit

enum CardType: String {
case AmericanExpress = "AmericanExpress"
case Visa = "Visa"
case Discover = "Discover"
case MasterCard = "MasterCard"
case None = "None"

private func regularExpression() -> NSRegularExpression {
switch self {
case .Visa:
return NSRegularExpression(pattern: "^4[0-9]{12}(?:[0-9]{3})?$", options: nil, error: nil)
case .AmericanExpress:
return NSRegularExpression(pattern: "^4[47][0-9]{13}$", options: nil, error: nil)
case .MasterCard:
return NSRegularExpression(pattern: "^5[1-5][0-9]{14}$", options: nil, error: nil)
case .Discover:
return NSRegularExpression(pattern: "^6(?:011|5[0-9]{2})[0-9]{12}$", options:nil, error:nil)
default:
return NSRegularExpression(pattern: ".*", options:nil, error:nil)
}
}

func isValidFor(cardNumber: String) -> Bool {
let re = self.regularExpression()
let range = NSRange(0..<cardNumber.lengthOfBytes(using: String.Encoding.utf8))
let matches = re.numberOfMatches(in: cardNumber, options: .anchored, range: range)
return matches > 0
}

static let allValues = [Visa, AmericanExpress, Discover, MasterCard]

static func from(cardNumber: String) -> CardType? {
for type in self.allValues {
if type.isValidFor(cardNumber: cardNumber) {
return type
}
}
}
}

CardType.Visa.isValidFor(cardNumber: "4242424242424242")
CardType.Visa.isValidFor(cardNumber: "1234")

CardType.from(cardNumber: "4242424242424242").rawValue


The problem with this is that the return statements in the switch block all have this error reported by the latest version of XCode:

Type of expression is ambiguous without more context


Also, the last line reports this error:

Value of optional type 'CardType?' not unwrapped; did you mean to use '!' or '?'?


How do I correct this and what is the current version of Swift requiring here?

Note: Yes, I'm just starting with Swift.

Answer

The NSRegularExpression() initializer has changed. You have to pass [] instead of nil for options (but since that is the default value, you can leave off options altogether), and you have to deal with the fact that it can throw an error. Since you are dealing with static patterns that won't fail, you can call them with try!. In general, that is dangerous because it will crash if an error is returned. With these static patterns, that won't happen (or you'll fix it when it does).

Change:

return NSRegularExpression(pattern: "^4[0-9]{12}(?:[0-9]{3})?$", options: nil, error: nil)

to:

return try! NSRegularExpression(pattern: "^4[0-9]{12}(?:[0-9]{3})?$")

and repeat that for all of the NSRegularExpressions.

For the last line, you just need a ?:

CardType.from(cardNumber: "4242424242424242")?.rawValue

Finally, this function needs a return nil to account for the case when a CardType is not found in the for loop:

static func from(cardNumber: String) -> CardType? {
    for type in self.allValues {
        if type.isValidFor(cardNumber: cardNumber) {
            return type
        }
    }
    return nil
}