any way to chain == and || operands
Try this:
if [a, b, c].contains(foo) {
//do stuff
}
Swift Best Pracices for compare a enum key to various key
switch (keyType) {
case .keyA,.keyB,.keyStart :
//do something
default :
//do something else
}
What is the best way to determine if a string contains a character from a set in Swift
You can create a CharacterSet
containing the set of your custom characters
and then test the membership against this character set:
Swift 3:
let charset = CharacterSet(charactersIn: "aw")
if str.rangeOfCharacter(from: charset) != nil {
print("yes")
}
For case-insensitive comparison, use
if str.lowercased().rangeOfCharacter(from: charset) != nil {
print("yes")
}
(assuming that the character set contains only lowercase letters).
Swift 2:
let charset = NSCharacterSet(charactersInString: "aw")
if str.rangeOfCharacterFromSet(charset) != nil {
print("yes")
}
Swift 1.2
let charset = NSCharacterSet(charactersInString: "aw")
if str.rangeOfCharacterFromSet(charset, options: nil, range: nil) != nil {
println("yes")
}
Can you define an enum to represent values explicitly known to your app, but still handle unknown values decoded from the backend?
TL:DR - The Solution
For those who just want to see the solution, here it is in its entirety. This allows you to define an enum with known cases, but which can handle any raw value thrown at it at runtime, and do so in a lossless way for re-encoding purposes.
enum FeatureFlag : RawRepresentable, CaseIterable, Codable {
typealias RawValue = String
case allowsTrading
case allowsFundScreener
case allowsFundsTransfer
case unknown(RawValue)
static let allCases: AllCases = [
.allowsTrading,
.allowsFundScreener,
.allowsFundsTransfer
]
init(rawValue: RawValue) {
self = Self.allCases.first{ $0.rawValue == rawValue }
?? .unknown(rawValue)
}
var rawValue: RawValue {
switch self {
case .allowsTrading : return "ALLOWS_TRADING"
case .allowsFundScreener : return "ALLOWS_FUND_SCREENER"
case .allowsFundsTransfer : return "ALLOWS_FUNDS_TRANSFER"
case let .unknown(value) : return value
}
}
}
The Explanation
As mentioned above, our app has a certain set of known feature flags. At first, one may define them like so.
enum FeatureFlag : String, CaseIterable, Codable {
case allowsTrading
case allowsFundScreener
case allowsFundsTransfer
}
Simple enough. But again, now any value defined with the type FeatureFlag
can only handle one of those specific known types.
Now say thanks to a new feature in the backend, a new flag allowsSavings
is defined and pushed down to your app. Unless you have manually written the decoding logic (or resorted to optionals), the decoder will fail.
But what if the enum can handle unknown cases automatically for you, and do so in a completely transparent way?
It can! The trick is to define one additional case, unknown
with an associated value of type RawValue
. This new case handles all the unknown types handed to it when being decoded or even re-encoded.
Let's start by updating our enum with the new unknown
case.
enum FeatureFlag : String, CaseIterable, Codable {
case allowsTrading
case allowsFundScreener
case allowsFundsTransfer
case unknown(RawValue)
}
This of course throws a ton of compiler errors thanks to that new case. Because of it, both RawRepresentable
and CaseIterable
can no longer be automatically synthesized by the compiler, so we have to manually implement them ourselves. Let's start with...
Manually Implementing the CaseIterable protocol
This is the easiest of the two steps. Since this 'version' of our app only knows about the first three cases, we can safely ignore all others. As such, to satisfy the protocol, we define a static allCases
property that only specifies those cases we do care about.
Of Note: The property type AllCases
here is an alias for [FeatureFlag]
or more succinctly [Self]
, which we get for free when conforming to CaseIterable
.
static let allCases: AllCases = [
.allowsTrading,
.allowsFundScreener,
.allowsFundsTransfer
]
With the above, this satisfies the CaseIterable
protocol. Let's move on to...
Manually Implementing the RawRepresentable protocol
This is a little more complex/verbose, but this is where the 'magic' happens.
Specifying the RawValue type
Normally, to indicate your enum can be represented by a raw value, you specify a data type after an enum's name. In actuality, this is shorthand for telling the compiler you are conforming your enum to the RawRepresentable
protocol and setting a RawValue
typealias for that data type. However, again, because of our unknown
type having an associated value, the compiler can't do that implicitly, so we must do so explicitly.
To do so, replace your raw type with RawRepresentable
in the definition, then manually set the RawValue
typealias inside, like so...
enum FeatureFlag : RawRepresentable, CaseIterable, Codable {
typealias RawValue = String
case allowsTrading
case allowsFundScreener
case allowsFundsTransfer
case unknown(RawValue)
}
implementing the rawValue: property (rawValues matching the case names)
Next up, we have to implement the rawValue
property. For known cases where the raw value matches the case name, the implementation is simple as we can just return String(describing: self)
and for the unknown cases, we return the associated value. Here's that implementation
var rawValue: RawValue {
switch self {
case let .unknown(value) : return value
default : return String(describing: self)
}
}
implementing the rawValue: property (rawValues unrelated to the case names)
But what if we wanted to express different values from the case names, or even a different data type entirely? In that situation, we have to manually expand out the switch
statement and return the appropriate values like so....
var rawValue: RawValue {
switch self {
case .allowsTrading : return "ALLOWS_TRADING"
case .allowsFundScreener : return "ALLOWS_FUND_SCREENER"
case .allowsFundsTransfer : return "ALLOWS_FUNDS_TRANSFER"
case let .unknown(value) : return value
}
}
*Note: You must specify the raw values here and not up with the case definitions using the equals (=
) as that's really syntactic sugar for the compiler to create what we're doing manually here, which again we have to since the compiler can't do it for us.
implementing init(rawValue:)... the 'Magic Sauce'
As mentioned, the entire purpose of this exercise is to allow your code to work with known types as usual, but to also gracefully handle unknown cases that are thrown at it. But how do we achieve that?
The trick is in the initializer, you first search for a known type within allCases
and if a match is found, you use it. If however a match isn't found, rather than return nil
like in the default implementation, you instead use the newly-defined unknown
case, placing the unknown raw value inside.
This has the additional benefit of guaranteeing to always return an enum value from the initializer, so we can define it as a non-optional initializer as well (which is different from the implicitly created one from the compiler) making the call-site code easier to use as well.
Here's the implementation of the initializer:
init(rawValue: String) {
self = Self.allCases.first{ $0.rawValue == rawValue }
?? .unknown(rawValue)
}
In some situations, you may want to log when an unknown value is being passed for debugging purposes. That can be done with a simple guard
statement (or an if
if you prefer) like so...
init(rawValue: String) {
guard let knownCase = Self.allCases.first(where: { $0.rawValue == rawValue }) else {
print("Unrecognized \(FeatureFlag.self): \(rawValue)")
self = .unknown(rawValue)
return
}
self = knownCase
}
Interesting Behaviors on Equality and Hashability
One of the interesting things is that enums based on a raw value actually use that value for equality comparisons. Thanks to that piece of information, all three of these values are equal...
let a = FeatureFlag.allowsTrading // Explicitly setting a known case
let b = FeatureFlag(rawValue: "allowsTrading") // Using the initializer with a raw value from a known case
let c = FeatureFlag.unknown("allowsTrading") // Explicitly setting the 'unknown' case but with a raw value from a known case
print(a == b) // prints 'true'
print(a == c) // prints 'true'
print(b == c) // prints 'true'
Additionally, if your raw value conforms to Hashable
, you can make the entire enum conform to Hashable
by simply specifying its conformance to that protocol.
extension FeatureFlag : Hashable {}
With that conformance, now you can use it in sets or as keys in a dictionary as well, which again, thanks to the rules of equality above, provides for some interesting, but logically expected, behaviors.
Again, using 'a', 'b' and 'c' as defined above, you can use them like so...
var items = [FeatureFlag: Int]()
items[a] = 42 // Set using a known case
print(items[a] ?? 0) // prints 42 // Read using a known case
print(items[b] ?? 0) // prints 42 // Read using the case created from the initializer with a raw value from the known case
print(items[c] ?? 0) // prints 42 // Read using the 'unknown' case but with a raw value from the known case
Side-Benefit: Lossless Encoding/Decoding
One often-overlooked/underappreciated side-benefit of this approach is that serilization/deserialization is lossless and transparent, even for unknown values. In other words, when your app decodes data containing values you don't know about, the unknown
case is still capturing and holding them.
This means if you were to then re-encode/re-serialize that data back out again, those unknown values would be re-written identically to how they would be if your app did know about them.
This is incredibly powerful!
This means for instance if an older version of your app reads in data from a server containing newer, unknown values, even if it has to re-encode that data to push it back out again, the re-encoded data looks exactly the same as if your app did know about those values without having to worry about versioning, etc. They're just silently and happily passed back.
Summary
With the above in place, you can now encode or decode any string into this enumeration type, but still have access to the known cases you care about, all without having to write any custom decoding logic in your model types. And when you do 'know' about the new type, simply add the new case as appropriate and you're good to go!
Enjoy!
Swift: Testing optionals for nil
In Xcode Beta 5, they no longer let you do:
var xyz : NSString?
if xyz {
// Do something using `xyz`.
}
This produces an error:
does not conform to protocol 'BooleanType.Protocol'
You have to use one of these forms:
if xyz != nil {
// Do something using `xyz`.
}
if let xy = xyz {
// Do something using `xy`.
}
Sort Dictionary by keys
let dictionary = [
"A" : [1, 2],
"Z" : [3, 4],
"D" : [5, 6]
]
let sortedKeys = Array(dictionary.keys).sorted(<) // ["A", "D", "Z"]
EDIT:
The sorted array from the above code contains keys only, while values have to be retrieved from the original dictionary. However, 'Dictionary'
is also a 'CollectionType'
of (key, value) pairs and we can use the global 'sorted'
function to get a sorted array containg both keys and values, like this:
let sortedKeysAndValues = sorted(dictionary) { $0.0 < $1.0 }
println(sortedKeysAndValues) // [(A, [1, 2]), (D, [5, 6]), (Z, [3, 4])]
EDIT2: The monthly changing Swift syntax currently prefers
let sortedKeys = Array(dictionary.keys).sort(<) // ["A", "D", "Z"]
The global sorted
is deprecated.
Related Topics
Fatal Error: Array Index Out of Range in Swift Xcode6
Swift Encode Tuple Using Nscoding
Open a Filedialog in Swiftui on MACos
How to Read a Property List from Data in Swift 3
Core Data Predicate Not Working
How to Avoid That My Swift Async Method Runs on the Main Thread in Swiftui
Swift Mutable Set: Duplicate Element Found
Firebase Uid VS Document-Id and Firestore Rules
Find Multiple Quoted Words in a String with Regex
Toggle Selection in a List - Swiftui
Why Swift Throws Error When Using Optional Param in Closure Func
Animate Cell When Pressed Using Swift 3
How to Save Document to "Files" App in Swift
How to Compare Just the Time of a Date in Swift