ikajava
ikajava

Reputation: 227

UI: do { } in Swift

What is the reason for wrapping all this code in a UI: do { } block and where can I get clear instructions about it?

UI: do {
    backgroundButton.setImage(UIImage.init(named: "search"), for: .normal)
    backgroundButton.backgroundColor = Colors.backgroundButtonBackgroundColor
    backgroundButton.tintColor = Colors.backgroundButtonTintColor
}

Upvotes: 5

Views: 4009

Answers (3)

staticVoidMan
staticVoidMan

Reputation: 20234

Other than labels being used to break loops (change control flow), in your particular example it's probably being used to organize the code.

So, in:

UI: do {
    //...
}
  1. UI: is a Labelled Statement where UI is a user defined label name that should be descriptive enough to indicate or hint to the reader of it's purpose

    Labeled Statement

    You can prefix a loop statement, an if statement, a switch statement, or a do statement with a statement label, which consists of the name of the label followed immediately by a colon (:). Use statement labels with break and continue statements to be explicit about how you want to change control flow in a loop statement or a switch statement

    Ref: https://docs.swift.org/swift-book/ReferenceManual/Statements.html#ID439

  2. do { } is a Do Statement

    Do Statement

    The do statement is used to introduce a new scope and can optionally contain one or more catch clauses, which contain patterns that match against defined error conditions. Variables and constants declared in the scope of a do statement can be accessed only within that scope.

    Ref: https://docs.swift.org/swift-book/ReferenceManual/Statements.html#ID533

  3. //... is all the code within the scope of do


Usage Example:

In a monolithic function, in order to improve code readability and segregate the internal logics, a labeled do statement can be used.

So, if this is a monolithic function:

func update() {
    var hasUpdatedDatasource = false

    print("setting datasource")
    //N lines of code related to datasource
    let strings = ["update", "Datasource"]
    print(strings.joined())
    hasUpdatedDatasource = strings.count > 2

    print("setting something")
    //N lines of code related to something
    if hasUpdatedDatasource {
        print("setting some more stuff")
        //N lines of code related to something more
    }

    print("setting UI")
    //N lines of code related to UI
}

Here we see multiple lines of code in which you may be creating/modifying variables. Basically alot of soup code that would make it hard to figure out which set of code lines is handling which feature or part of the function.

Using a labeled do statement, as in your case, will make the code a bit more readable like so:

func update() {
    var hasUpdatedDatasource = false

    updateDatasource: do {
        //do datasource related modification
        //N lines of code go here
        let datasource = ["update", "Datasource"]
        print(datasource.joined())

        hasUpdatedDatasource = strings.count > 2
    }

    doSomething: do {
        print("doSomething")
        //N lines of code go here

        guard hasUpdatedDatasource else { break doSomething }
        print("doSomething: More")
        //N lines of code go here
    }

    updateUI: do {
        print("updateUI")
        //N lines of code go here
    }
}

This allows you to make a set of code lines into block of codes with a descriptive label name and shows the logic flow more clearly.
You can access and modify variables from above it's do scope, and since it has it's own scope, variables created inside are only accessible here.
This can prevent variables from lingering unnecessarily till the end of the function.

NOTES:

  • updateDatasource created a local variable datasource which won't be available outside it's scope, AND... modified a variable hasUpdatedDatasource which is local to the entire function
  • doSomething has a break statement that can break itself anytime by referring to it's label name

It does make the code more readable but not necessarily more maintainable as it's statefull.
Personally, I prefer splitting large functions into smaller or nested functions. But this does not mean that labeled do statements don't have their place.
If it makes your code better, go for it.

Upvotes: 6

Shubham Bakshi
Shubham Bakshi

Reputation: 572

The UI in your code is a labeled statement which is useful in cases where you want to escape from an outer scope(or block) while you're inside an inner scope(or block)

Consider an example where you want to break out of the outer loop when a certain condition arises while you're inside another loop(or, innerLoop in our case)

outerLoop: for outerCount in 1...5 {

    innerLoop: for innerCount in 1...5 {

        // Condition for breaking out of outer loop 
        if outerCount == 3 {
            break outerLoop
        }
        print("Outer Count: \(outerCount) Inner Count: \(innerCount)")
    }
}

If we had used break instead of break outerLoop in above case, we would just be able to break innerLoop and would still be inside outerLoop scope

The do clause in your case , as @Rob suggested , can also be used to simply encapsulate a series of statements within their own scope . What it does is, it provides a scope for some variables that are initialised inside do and will be deallocated once the do scope ends

Consider the following case for do scope which will automatically deinit the object as soon as the do scope ends

class Demo {
    init() {
        print("Instance initialised")
    }

    deinit {
        print("Instance Deinitalised")
    }
}

do {
    let demoObject = Demo()
}

Output of above code will be

Instance initialised

Instance Deinitalised

Upvotes: 1

ielyamani
ielyamani

Reputation: 18581

It's a labeled statement:

A labeled statement is indicated by placing a label on the same line as the statement’s introducer keyword, followed by a colon.

and it can be used to break away from outsider scopes. An illustrative example is given here.

Upvotes: 5

Related Questions