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
  • 28,843
  • 15
  • 145
  • 252
Hamoonist
  • 1,907
  • 1
  • 18
  • 35

6 Answers6

27

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,321
  • 10
  • 20
7

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
    }
}
Andrea Mugnaini
  • 9,425
  • 3
  • 40
  • 50
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
  • 510,973
  • 84
  • 1,183
  • 1,314
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,150
  • 15
  • 117
  • 168
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,392
  • 1
  • 11
  • 25
1

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.