Reputation: 227
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
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 {
//...
}
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
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
//...
is all the code within the scope of do
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 functiondoSomething
has a break statement that can break itself anytime by referring to it's label nameIt 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
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 ofbreak 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
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