55

I'm regularly using local scopes in Objective-C to make naming clearer.

{
    UILabel *label = [[UILabel alloc] init];
    [self addSubview:label];
    self.titleLabel = label;
}

I am trying to rewrite this code in Swift like this:

{
    let label = UILabel()
    self.addSubview(label)
    self.titleLabel = label
}

This gives me get the following error:

Error: Braced block of statements is an unused closure.

So how can I create a local scope in Swift?

Anton
  • 5,932
  • 5
  • 36
  • 51
  • 2
    try 'let' instead of 'var' – Andrea Jun 03 '14 at 09:14
  • 8
    You have a semicolon in your Swift code ;) – FD_ Jun 03 '14 at 09:36
  • Not any longer! Will take some time to get used to this for sure :) – Anton Jun 03 '14 at 09:52
  • 5
    I usually take that (i.e. scope introduction) as a sign that a function is doing too much and needs splitting up. – molbdnilo Jun 03 '14 at 10:13
  • 4
    @molbdnilo: I used to think that too. Until I met that properly split up function. I looked into the first one: it calls 3 other functions. I looked into the first one: it calls 5 other functions. .... i got up one level and continued on level 2 ... i forgot what I was doing here anyways. Of course there were no documentation. I prefer the one big function with 100 lines of code, at least when I have to read it and there is no documentation. Splitting up only helps when you know what the individual function calls are intended to do, and if they are split up in a coherent way. – Michael Aug 19 '18 at 09:32

5 Answers5

83

Update: In Swift 2.0, you just use the do keyword:

do {
    let label = UILabel()
    self.addSubview(label)
    self.titleLabel = label
}

This was true for Swift pre-2.0:

You can define something similar to this:

func locally(@noescape work: () -> ()) {
    work()
}

And then use such a locally block as follows:

locally {
    let g = 42
    println(g)
}

(Inspired by locally in Scala's Predef object.)

Jean-Philippe Pellet
  • 59,296
  • 21
  • 173
  • 234
  • 2
    This is a really cool solution that opens the possibility for a lot more blocks. I could see something similar to C#'s `using` created similarly with a `Disposable` protocol. – Erik Jun 07 '14 at 06:18
  • This looks like a great solution; is there any overhead to it, or does the compiler successfully optimise it so it's just like a good old plain block? – Haravikk Jan 27 '15 at 20:01
  • @Haravikk I don't know. I would assume there *is* some overhead. – Jean-Philippe Pellet Jan 27 '15 at 20:34
  • 2
    @Pang, Jean-Philippe: As of Swift 1.2, you can add the `@noescape` attribute, then explicitly specifying `self` is no longer needed. Compare http://stackoverflow.com/questions/28427436/noescape-attribute-in-swift-1-2. – Martin R Apr 28 '15 at 09:52
  • Pre 2.0 you can use: do { /* code here */ } while false – don Jul 14 '15 at 17:21
5

As of Swift 2, you can create a local scope with the do-statement:

do {
    let x = 7
    print(x)
}
print(x) // error: use of unresolved identifier 'x'

The main use-case however seems to be error-handling with do-try-catch, as documented in "Error Handling" in "The Swift Programming Language", for example:

do {
    let jsonObj = try NSJSONSerialization.JSONObjectWithData(jsonData, options: [])
    // success, do something with `jsonObj`...

} catch let error as NSError {
    // failure
    print("Invalid JSON data: \(error.localizedDescription)")
}
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
4

I don't think it is possible.

At least a grammar that is in the book that is available in iBooks store does not mention it.

You could do this,

if (true) {
    let a = 4
}

but I think, it is a bad practice.

Kai Huppmann
  • 10,705
  • 6
  • 47
  • 78
Tomáš Linhart
  • 13,509
  • 5
  • 51
  • 54
  • Not so sure its bad practice - too early to tell, no? This will not generate any additional code, as the if(true) will surely get discarded. – David H Jun 03 '14 at 14:33
  • You assertion that it makes the code less readable is an opinion, not a fact. You could say the same about using C blocks - the '{', indented code, and trailing '}' make it less readable too. Not in my opinion, but everyone will surely have one. I use #ifdef 0 all the time in my code, as well as #if var == 1, and I find that quite useful. You on the other hand might hate it. YMMV as they say. – David H Jun 03 '14 at 14:46
  • 3
    I say it because when you see if (expression) {... then you expect some conditional code and if you see if (true) you might wonder if it is a temporary change or some hack or why would someone write code like that. Of course, if nobody else reads your code, it doesn't matter. – Tomáš Linhart Jun 03 '14 at 16:33
  • You could leave off the ( ) around true too. Another thought: `{ ... }()`, although currently that crashes the compiler. – nielsbot Jun 26 '14 at 18:46
3

As noted in comments, anonymous nested scopes in C are often a sign that you could be writing better code. For example, instead of simply doing work in a nested scope that ultimately sets self.titleLabel, you could you could instead make that assignment the result of evaluating an inline closure:

self.titleLabel = {
    let label = UILabel()
    label.text = "some text"
    // ... set other properties ...
    self.addSubview(label)
    return label
}()

This not only keeps label as a nice short name that's scoped only to the chunk of code that creates and configures one, but keeps that chunk of code associated with the property it's creating a value for. And it's more modular, in that you could replace the whole closure with a call to some other label-creating function should it ever become useful to factor out that code.

If you find yourself doing this sort of thing frequently, you could try making a generic function that lets you cut your construction code down to this:

self.titleLabel = makeSubview(UILabel()) { label in
    label.text = "some text"
    // other label properties
}

But I'll leave defining such a function as an exercise for the reader. ;)


As noted in Jean-Philippe Pellet's answer, in Swift 2.0 and later the do construct is the explicitly language-provided way to do this. (And the function-based solution he suggests is a decent option for anyone still using Swift 1.x.)

Another Swift 1.x solution — without defining a new function — is to (explicitly) throw away the result of an immediately-executed closure:

 _ = {
     print("foo")
 }() 
Community
  • 1
  • 1
rickster
  • 124,678
  • 26
  • 272
  • 326
  • I suggested an edit for this answer by removing `println` and the `let` statement as they are depreciated / unnecessary to my knowledge :). Thank you for showing this though, I upvoted, @rickster – Fluidity Aug 25 '16 at 15:58
0

Also Naming scope:

scope: do {
  guard let foo = foo else { break scope }
  ....
}
Bimawa
  • 3,535
  • 2
  • 25
  • 45