10

What is the simplest way to write a piece of code that can be executed only once?

I know a way but has a problem.

first, I write a Boolean variable that has negative value but can be set to positive and cannot change after that

 var hasTheFunctionCalled : Bool = false {
   didSet{
       hasTheFunctionCalled = true
   }
} 

and then write the function and the code inside it:

func theFunction(){
   if !hasTheFunctionCalled{
      //do the thing
   }
   hasTheFunctionCalled = true
 } 

but the problem is that the variable can be changed from somewhere else in the scope and this solution doesn't really look so simple and concrete.

mfaani
  • 33,269
  • 19
  • 164
  • 293
Hamoonist
  • 2,286
  • 3
  • 19
  • 36

6 Answers6

29

A simple solution is to take advantage of lazy variables in the following way:

// Declare your "once-only" closure like this
private lazy var myFunction: Void = {
    // Do something once
}()

...

// Then to execute it, just call
_ = myFunction

This ensures that the code inside the myFunction closure is only executed the first time that the program runs _ = myFunction


Edit: Another approach is to use so called "dispatch once tokens". This comes from Objective-C and was available in Swift until Swift 3. It is still possible to make it work, however you will need to add a little bit of custom code. You can find more information on this post -> dispatch_once after the Swift 3 GCD API changes


Edit2: Should be _ = myFunction and not _ = myFunction(), as JohnMontgomery pointed out.

Julien Perrenoud
  • 1,401
  • 11
  • 20
8

You might use a static bool inside a struct nested into the function itself doing so:

func theFunction(){
    struct Holder { static var called = false }

    if !Holder.called {
        Holder.called = true
        //do the thing
    }
}
mugx
  • 9,869
  • 3
  • 43
  • 55
6

One possible technique is to put the code into the initializer of a static type property, which is guaranteed to be lazily initialized only once (even when accessed across multiple threads simultaneously):

func theFunction() {
    struct Once {
        static let once = Once()
        init() {
            print("This should be executed only once during the lifetime of the program")
        }
    }
    _ = Once.once
}

(Compare Singleton in the "Using Swift with Cocoa and Objective-C" reference.)

Example:

print("Call #1")
theFunction()
print("Call #2")
theFunction()
print("Done")

Output:

Call #1
This should be executed only once during the lifetime of the program
Call #2
Done
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
2

You can also use UserDefaults, and the knowledge that the default UserDefault Bool is false:

if !UserDefaults.standard.bool(forKey: "ExecuteOnce") {
    func()
    UserDefaults.standard.set(true, forKey: "ExecuteOnce")
}

This code will execute exactly once.

2

You can do smth like:

class Once {

  var already: Bool = false

  func run(@noescape block: () -> Void) {
    guard !already else { return }

    block()
    already = true
  }
}

and than use it like

class ViewController: UIViewController {
  let once = Once()

  override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)

    once.run {
      cameraMan.setup()
    }
  }
}

ref: https://dev.to/onmyway133/how-to-run-action-once-in-swift-3k7o

Peter Lapisu
  • 19,915
  • 16
  • 123
  • 179
1

Depending on what you are doing inside your method : you may check if the end result has already been accomplished :

e.g. if you instantiate a class, check if it is different from nil

Sirmyself
  • 1,464
  • 1
  • 14
  • 29