103

I'm trying to figure out how to declare a static variable scoped only locally to a function in Swift.

In C, this might look something like this:

int foo() {
    static int timesCalled = 0;
    ++timesCalled;
    return timesCalled;
}

In Objective-C, it's basically the same:

- (NSInteger)foo {
    static NSInteger timesCalled = 0;
    ++timesCalled;
    return timesCalled;
}

But I can't seem to do anything like this in Swift. I've tried declaring the variable in the following ways:

static var timesCalledA = 0
var static timesCalledB = 0
var timesCalledC: static Int = 0
var timesCalledD: Int static = 0

But these all result in errors.

  • The first complains "Static properties may only be declared on a type".
  • The second complains "Expected declaration" (where static is) and "Expected pattern" (where timesCalledB is)
  • The third complains "Consecutive statements on a line must be separated by ';'" (in the space between the colon and static) and "Expected Type" (where static is)
  • The fourth complains "Consecutive statements on a line must be separated by ';'" (in the space between Int and static) and "Expected declaration" (under the equals sign)
nhgrif
  • 61,578
  • 25
  • 134
  • 173
  • Has this changed at all? Does Swift enable the creation of static variables or constants in functions now? – daniel Jul 06 '22 at 12:09

4 Answers4

173

I don't think Swift supports static variable without having it attached to a class/struct. Try declaring a private struct with static variable.

func foo() -> Int {
    struct Holder {
        static var timesCalled = 0
    }
    Holder.timesCalled += 1
    return Holder.timesCalled
}

  7> foo()
$R0: Int = 1
  8> foo()
$R1: Int = 2
  9> foo()
$R2: Int = 3
mfaani
  • 33,269
  • 19
  • 164
  • 293
Bryan Chen
  • 45,816
  • 18
  • 112
  • 143
  • Yeah, I continued playing around a bit and this was basically the really clunky solution I came up with as well. – nhgrif Aug 18 '14 at 00:29
  • 26
    Upwoted, but I’m sad we have to resort to this. – Tricertops Feb 23 '16 at 09:51
  • 1
    Type properties and methods belong to a type (i.e. a Class, Struct or Enum) and cannot belong to a function alone. [Apple Documentation on Type Properties](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Properties.html). @Tricertops. Another way to go about this would be to put the function "foo" in a class, create a type property for that class and use it inside the function. – NSCoder Mar 02 '16 at 13:54
  • 7
    @NSCoder But it’s possible to declare `struct Holder {…}` inside multiple functions and they will not collide. Swift could support `static let` without this `struct` boilerplate around. – Tricertops Mar 09 '16 at 09:31
  • This answer is outdated. See other answers or perhaps Bryan can update his answer – mfaani Nov 08 '16 at 20:50
  • 1
    @Honey I am sorry but I can't find more updated other answer? – Bryan Chen Nov 08 '16 at 20:55
  • @BryanChen Oh. Then I must have mistaken. Isn't the answer of Daniel more of a modern approach? – mfaani Nov 08 '16 at 21:06
  • I wouldn't say it is more modern compare to my answer. It is just a different approach that have different use cases. – Bryan Chen Nov 08 '16 at 21:09
  • @Tricertops He's not saying that a struct is a type properties. "Type property" is a bad name IMO but that's what Apple calls it. It means a property belonging to a type, aka not an instance var, aka a static var. He is correct, it's just hard to read. U should read the link he posted and scroll down to Type Properties header though. – BTRUE Apr 16 '17 at 15:41
  • You might want to use Enum instead of Struct when you only want to hold informations in it. Because instanciating this kind of object wouldn't make sense, hence the enum (with no case) choice. – itMaxence Apr 15 '22 at 15:34
23

Another solution

func makeIncrementerClosure() -> () -> Int {
    var timesCalled = 0
    func incrementer() -> Int {
        timesCalled += 1
        return timesCalled
    }
    return incrementer
}

let foo = makeIncrementerClosure()
foo()  // returns 1
foo()  // returns 2
Eric
  • 16,003
  • 15
  • 87
  • 139
monadis
  • 282
  • 1
  • 7
  • 3
    this is a typical javascript way to do it – Bryan Chen Oct 29 '14 at 00:05
  • 1
    But if I call ba() again, the inner function returns 1 on first call. This is different from a static variable. – nhgrif Nov 16 '14 at 14:55
  • 2
    This is also taught in Apple's docs here: https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Closures.html It seems to be the best solution just to keep in lines with "functional programming", but there are other solutions as well. This should be the accepted answer though. – datWooWoo Jan 15 '15 at 16:36
  • 2
    I'm sorry, but this is an ugly hack adding more complexity for the same problem. What's your point? In that case I prefer a simple class property. @Brian Chen's answer is the closest one you can get. I use his answer for a flipflop kind of solution. Daniel is maybe the best conforming Apple's rules of Swift programming. – AndaluZ Oct 02 '15 at 12:34
  • 1
    I particularly liked this solution. This is a perfect example of using a higher-order function to achieve the same result as a static variable inside the scope of a function. Static function vars are not natively supported in Swift for good reasons. It's the natural evolution of programming. Trying to code in old fashion ways requires hacks. In my opinion, adding an extra nested data type as opposed to utilising variable capturing reduces code readability. – nstein Jul 25 '16 at 12:07
  • you can defer increment to start from 0 – Vladimir Borodko Apr 22 '18 at 14:46
18

Swift 1.2 with Xcode 6.3 now supports static as expected. From the Xcode 6.3 beta release notes:

“static” methods and properties are now allowed in classes (as an alias for “class final”). You are now allowed to declare static stored properties in classes, which have global storage and are lazily initialized on first access (like global variables). Protocols now declare type requirements as “static” requirements instead of declaring them as “class” requirements. (17198298)

It appears that functions cannot contain static declarations (as asked in question). Instead, the declaration must be done at the class level.

Simple example showing a static property incremented inside a class (aka static) function, although a class function is not required:

class StaticThing
{
    static var timesCalled = 0

    class func doSomething()
    {
        timesCalled++

        println(timesCalled)
    }
}

StaticThing.doSomething()
StaticThing.doSomething()
StaticThing.doSomething()

Output:

1
2
3
Daniel
  • 8,794
  • 4
  • 48
  • 71
  • 1
    I suspect this difference in the meaning of `static` might be intentional on Apple's part, though one is always welcome to [file a bug](http://bugreport.apple.com) to request a change. In C, `static` limits *storage* of a variable to source-file scope (which is not always the same as class scope), while the placement of the variable declaration determines the *lexical* scope (i.e. global vs within function vs many-nested-`{}`s). In Swift, storage scope always follows lexical scope, so you can't have a variable that's lexical to a function and that has global storage. – rickster Feb 10 '15 at 21:23
  • 8
    Daniel, this is actually subtly (but importantly) different from what the question asks. I appreciate the answer though. @rickster I understand what you are saying and think your comment could be expanded to a good answer for this question. – nhgrif Feb 11 '15 at 13:02
  • @nhgrif Yep, I indicated in answer that this doesn't address the specific question. I was just thinking that the changes in Swift 1.2 address the core need for this use case (certainly a better story than pre Swift 1.2). But it sounds like it's important for you to have variable scoped to the function - which currently is not possible. – Daniel Feb 11 '15 at 23:26
  • @rickster in C I think static is always stored globally actually. I'm not sure though. I think that's the problem Apple is trying to address here. In swift, it is now always lexically and storage scoped to the class – BTRUE Apr 16 '17 at 15:48
  • @nhgrif With my previous comment said, I think Daniel's answer actually should be the accepted answer, because although you can lexically declare a static var in a function in objc, it wasn't scoped there, having the same effect as using a static Type property in swift. the only difference is, the swift declaration point is a lot more descriptive and not misleading as to what the scope of the variable is. – BTRUE Apr 16 '17 at 15:50
0

Another solution

class Myclass {
    static var timesCalled = 0
    func foo() -> Int {
        Myclass.timesCalled += 1
        return Myclass.timesCalled
    }
}
J.q
  • 1