how to increment a swift int enumeration
This is not C, where enums are integers. In swift an enum is a proper type in it's own right and you cannot perform math operations on it.
However, you can grab the raw value which is an integer, and do math on that. Then create a new enum:
var newState = MainState(rawValue: state.rawValue + 1)
Note that "newState" is an optional. You'll get null
if rawValue + 1 doesn't exist in the enum.
Swift Increment through Enum
Update Starting with Swift 4.2 you can make use of the newly added support CaseIterable
protocol, which adds compiler support for generating a list of all cases for an enum. Though @ninestones's comment pointed put that we are not guaranteed for allCases
to return the cases in the same order as defined, the synthesized implementation does this, and it's unlikely that definition will change.
Your enum could then look something like this (no more hardcoded start value):
enum CopyState: CaseIterable {
case binary, hex, both
mutating func next() {
let allCases = type(of: self).allCases
self = allCases[(allCases.index(of: self)! + 1) % allCases.count]
}
}
You can make this piece of functionality available to all CaseIterable
enums:
extension CaseIterable where Self: Equatable {
mutating func next() {
let allCases = Self.allCases
// just a sanity check, as the possibility of a enum case to not be
// present in `allCases` is quite low
guard let selfIndex = allCases.index(of: self) else { return }
let nextIndex = Self.allCases.index(after: selfIndex)
self = allCases[nextIndex == allCases.endIndex ? allCases.startIndex : nextIndex]
}
}
enum CopyState: CaseIterable {
case binary, hex, both
}
var state = CopyState.hex
state.next()
print(state) // both
state.next()
print(state) // binary
Or, a little bit more verbose, but with a better separation of concerns:
extension Collection {
// adding support for computing indexes in a circular fashion
func circularIndex(after i: Index) -> Index {
let nextIndex = index(after: i)
return nextIndex == endIndex ? startIndex : nextIndex
}
}
extension Collection where Element: Equatable {
// adding support for retrieving the next element in a circular fashion
func circularElement(after element: Element) -> Element? {
return index(of: element).map { self[circularIndex(after: $0)] }
}
}
// Protocol to allow iterating in place (similar to a type conforming to both Sequence and IteratorProtocol)
protocol InPlaceIterable {
mutating func next()
}
extension InPlaceIterable where Self: CaseIterable, Self: Equatable {
// adding default implementation for enums
mutating func next() {
self = type(of: self).allCases.circularElement(after: self)!
}
}
// now the enums need only the protocol conformances, they get the
// functionalities for free
enum CopyState: CaseIterable, InPlaceIterable {
case binary, hex, both
}
You could use Int
as raw value for your enum (note that this is also the default raw value if you don't specify it), and use it like this:
enum CopyState: Int {
case binary, hex, both
mutating func next(){
self = CopyState(rawValue: rawValue + 1) ?? .binary
}
}
var state = CopyState.hex
state.next()
print(state) // both
state.next()
print(state) // binary
This works fine as long as you have the raw values of the enum cases in consecutive order. By default the compiler assigns consecutive raw values.
You'd also need to keep in mind to update the next()
method if the first case changes, otherwise it will no longer correctly work.
An alternative to the above limitation, suggested by @MartinR, is to force unwrap the raw value zero:
mutating func next(){
self = CopyState(rawValue: rawValue + 1) ?? CopyState(rawValue: 0)!
}
The above code won't require updating the method when the first enum case changes, however it has the potential of crashing the app if the starting raw value of the enum changes.
How to get next case of enum(i.e. write a circulating method) in Swift 4.2
Some problems with your approach are:
- The
Collection
protocol does not define alast
property. - In order to compare the elements with
==
they have to beEquatable
. - Collection indices are not necessarily integers, they must be incremented
withindex(after:)
.
This seems to be a working solution (tested with Xcode 10.0 beta 2):
extension CaseIterable where Self: Equatable {
func next() -> Self {
let all = Self.allCases
let idx = all.firstIndex(of: self)!
let next = all.index(after: idx)
return all[next == all.endIndex ? all.startIndex : next]
}
}
Example:
enum Direction: CaseIterable {
case east, south, west, north
}
print(Direction.east.next()) // south
print(Direction.north.next()) // east
Remarks:
- Only enumerations without associated values are
CaseIterable
, and
those are alsoEquatable
(but the compiler does not figure out that
by itself). ThereforeSelf: Equatable
is not a
real restriction. Self.allCases
can be used in Swift 4.2 to access the type property
from an instance method.- The forced unwrapping is safe because we know that the value is
an element ofallCases
. - Your
enum Direction: CaseIterable
compiles because the concreteenum Direction
type isEquatable
, and itsDirection.allCases
is anArray
– which has integer indices and alast
property.
Is there a pretty way to increment an optional Int?
For the sake of completeness, Optional
has a map()
method:
/// If `self == nil`, returns `nil`. Otherwise, returns `f(self!)`.
@warn_unused_result
@rethrows public func map<U>(@noescape f: (Wrapped) throws -> U) rethrows -> U?
Therefore
index != nil ? index! + 1 : nil
is equivalent to
index.map { $0 + 1 }
How do I get the count of a Swift enum?
As of Swift 4.2 (Xcode 10) you can declare
conformance to the CaseIterable
protocol, this works for all
enumerations without associated values:
enum Stuff: CaseIterable {
case first
case second
case third
case forth
}
The number of cases is now simply obtained with
print(Stuff.allCases.count) // 4
For more information, see
- SE-0194 Derived Collection of Enum Cases
+=' produces '()'. How can i return an incremented value from a closure
Just another way, use defer
to increment counter after return
func countingClosure() -> (() -> Int) {
var counter = 0
let incrementCounter: () -> Int = {
defer {
counter += 1
}
return counter
}
return incrementCounter
}
How to write mutating function for enum in Swift
Using self = .apple(x)
will do the trick. It is customary, but you already knew this.
enum Fruit {
case apple(Int)
case banana(Int, Int, Int)
mutating func increment() {
switch self {
case let .apple(x):
self = .apple(x + 1)
case let .banana(x, y, z):
self = .banana(x + 1, y + 1, z + 1)
}
}
}
var a = Fruit.banana(100, 200, 300)
a.increment()
You could also add support for generic operators:
enum Fruit {
case apple(Int)
case banana(Int, Int, Int)
mutating func operate(_ op: (Int, Int) -> Int,_ num: Int) {
switch self {
case let .apple(x):
self = .apple(op(x, num))
case let .banana(x, y, z):
self = .banana(op(x, num), op(y, num), op(z, num))
}
}
}
var a = Fruit.banana(100, 200, 300)
a.operate(+, 1) // adds all by 1
a.operate(*, 500) // multiplies all by 500
You could also add support for an array of generic operators:
enum Fruit {
case apple(Int)
case banana(Int, Int, Int)
mutating func operate(_ nest: [((Int, Int) -> Int, Int)]) {
for i in nest {
switch self {
case let .apple(x):
self = .apple(i.0(x, i.1))
case let .banana(x, y, z):
self = .banana(i.0(x, i.1), i.0(y, i.1), i.0(z, i.1))
}
}
}
}
var a = Fruit.banana(100, 200, 300)
a.operate([(+, 500), (*, 2)]) // Adds all by 500, then multiply all by 2
Here's a nice simplification
If it were up to me, this is what I would do to get rid of repeating self =
. You can simplify this further if you wish.
enum FruitList: String { case apple, banana }
struct Fruit {
static func apple(_ one: Int) -> Fruit {
return Fruit.init(.apple, [one])
}
static func banana(_ one: Int,_ two: Int,_ three: Int) -> Fruit {
return Fruit.init(.apple, [one, two, three])
}
var picked: (FruitList, [Int])
init(_ fruit: FruitList,_ list: [Int]) {
picked = (fruit, list)
}
mutating func operate(_ nest: [((Int, Int) -> Int, Int)]) {
for i in nest {
for j in 0..<picked.1.count {
picked.1[j] = i.0(picked.1[j], i.1)
}
}
}
}
var a = Fruit.apple(100)
a.operate([(+, 500), (*, 2)]) // Add all by 500, then multiply all by 2
fast enumeration for loop - Is there a counter?
You can't. Or better, you can use an external counter and manually increment it at every cycle, but then it would be easier to use "classical" for.
How do I increment a label value with a button press in Swift
You can fix this error by changing the offending code to the following:
cur = Int(money.text!)!;
Read more about initializers here. An excerpt:
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.
Related Topics
Proper Way of Editing a Cocoapod Library
Generating Random Doable Math Problems Swift
Xcode 6.1 Swift Extensions - Sourcekit Service Crash
Can Not Get Correct Position of View in Swiftui
Access Each Header and Controls in The Tableview in Swift
Prevent Retain Cycle in Swift Function Pointers
How to Call Https Url in UIwebview (Swift)
Why Is My App Returning Hundreds of Errors That Say "Undefinedsymbol"
Core Data Appears to Lose Data After Xcode Upgrade
Swift 3/4 Dash to Camel Case (Snake to Camelcase)
How to Horizontally Center Content of Horizontal Scrollview
Error Domain=Nscocoaerrordomain Code=3840 "Invalid Value Around Character 0
Swift: Trunc a Floating Number to Show It in a Label