case keyword used without switch

In TicTacToe example there is in the override method "PasscodeViewController.viewDidLoad()" this snippet of code:

		if let browseResult = browseResult,
			case let NWEndpoint.service(name: name, type: _, domain: _, interface: _) = browseResult.endpoint {
			title = "Join \(name)"
		}

What confuses me:

  1. The use of the "case" keyword without a switch statement.

  2. The "case" keyword does not have a constant to compare with to decide if will branch here.

  3. And what of the method call to NWEndpoint.service() being set equal to something? Is this actually defining what the service method will do when the system calls it?

Answered by Claude31 in 756426022

And what of

if let browseResult = browseResult,

Does this test whether "browseResult" has a nil value?

This test unwraps browseResult

  • if nil, it fails
  • if not nil, now browseResult is browseResult!, no need to unwrap later

in recent Swift, you can simply write

if let browseResult

As for the = sign, as Eskimo explained, the point is that it comes with case let:

if case let =

Which can be read

  • if I can make NWEndpoint.service(), with the correct parameters, match browseResult.endpoint,
  • then it is true,
  • and the parameters are set (except those defined as _, which are simply ignored).

I found useful to have such a sentence in mind (if I can make NWEndpoint.service(), with the correct parameters, match browseResult.endpoint) when writing such clauses.

This is the « if case let » pattern, which is really hard to grasp at first.

If we break the statement, we have:

if case let NWEndpoint.service(name: name, type: _, domain: _, interface: _) = browseResult.endpoint

What it means:

  • if I can match browseResult.endpoint with NWEndpoint.service(…), then test is OK
  • and parameters are set accordingly.
  • it does not mean that you set NWEndPoint to something, it means a pattern matching where you test what is on the right of the = sign to see if you can match with the expression on the left. That´s what makes it so hard to grasp.

There is another way of writing it, which is longer but easier to understand :

switch browseResult.endpoint {
  case NWEndpoint.service(…) : // do something
  default: break
}

in the compact form, you switch the order and introduce the =.

It may help to first write the expended form before converting to compact.

See an interesting answer here by eskimo:

https://developer.apple.com/forums/thread/111213

So if "browseResult.enpoint" is equal the value returned by the method call to " NWEndpoint.service(name: name, type: _, domain: _, interface: _)" the variable "title" is set equal to "Join (name)".

I am accustomed to "==" being used for a Boolean equality test, and "=" being used to change value of something. Why does Swift break with that tradition from C, and C++, here?

Another point of confusion for me are the underlined characters used as parameter values in the method call. What does that mean? Does it mean default values are to be used?

And what of

if let browseResult = browseResult,

? Does this test whether "browseResult" has a nil value?

So if browseResult.enpoint is equal the value returned by the method call to NWEndpoint.service(name: name, type: _, domain: _, interface: _) the variable title is set equal to Join (name).

Yes and no. That’s the effect you’re seeing here but it’s best not to think of it in terms of equality. Rather, think of it in terms of pattern matching. The case clause in the guard statement matches the browseResult.endpoint value against the let NWEndpoint.service(name: name, type: _, domain: _, interface: _) pattern, and then binds name to the endpoint’s associated value.

Personally I would’ve used a slightly different syntax for this:

if let browseResult = browseResult,
    case NWEndpoint.service(name: let name, type: _, domain: _, interface: _) = browseResult.endpoint {
    title = "Join \(name)"
}

By moving the let closer to the binding site, it makes it clearer that you’re binding that specific value [1].

I am accustomed to "==" being used for a Boolean equality test, and "=" being used to change value of something. Why does Swift break with that tradition from C, and C++, here?

Because this isn’t about equality, it’s about pattern matching.

As Claude31 explained, you can think of:

if case <pattern> = <value> {
    <match code>
} else {
    <no match code>
}

as being a transformation of:

switch <value> {
case <pattern>:
    <match code>
default:
    <no match code>
}

Swift switch statements don’t just use equality, they use pattern matching, which is way more powerful (and way more complicated). Hence Claude31 linking to my cheat sheet.

Now you could argument that this shouldn’t use the = sigil, and I think that’s fair. However, it shouldn’t use the == sigil because that’s also confusing. Something like ~= would’ve been nice, because that’s reminiscent of the standard pattern matching operator. The choice to use = was made many years ago, perhaps even before the advent of Swift Evolution. You could bounce over there and try to convince folks to change it, but I don’t fancy your chances.

Another point of confusion for me are the underlined characters used as parameter values in the method call. What does that mean?

Again, this is much clearer once you start thinking in terms of pattern matching. In the pattern matching syntax, _ is treated as a wildcard.

And what of

if let browseResult = browseResult,

? Does this test whether browseResult has a nil value?

Again, yes and no. In an if statement, a let clause tries to unwrap the optional and:

  • If it’s nil, the clause evaluates to false

  • If it’s not nil, the clause evaluates to true and it binds the new name to the unwrapped value

It’s less confusing if you use different names on both sides. For example:

let browseResultQ: NWBrowser.Result? = … something …
if let browseResult = browseResultQ {
    … use the non-optional `browseResult` …
}

is roughly equivalent to:

let browseResultQ: NWBrowser.Result? = … something …
if browseResultQ != nil {
    let browseResult = browseResultQ!
    … use the non-optional `browseResult` …
}

If you’re coming from a C background, understanding the concept of optionals is critical to understanding Swift.

In C, every pointer has a ‘magic’ value (0, nil, NULL, nullptr, and so on) that’s invalid. There’s no way, in the type system, to say “I only work on non-nil pointers.” In Swift that’s not the case. An object reference, like NWConnection, is different from an optional object reference, written as NWConnection?.

Swift optionals are made up of two separate things [2]:

  • The Optional type itself

  • A bunch of syntactic sugar

The Optional type looks like this:

enum Optional<Wrapped> {
    case none
    case some(Wrapped)
}

That is, an optional value is either .none or .some with an associated wrapped value. So, if you want an optional NWConnection value that’s currently nil, you can write this:

var connectionQ: Optional<NWConnection> = .none

However, that’s kinda ugly, so Swift has a bunch of syntactic sugar. Instead of the above you write:

var connectionQ: NWConnection? = nil

where NWConnection? is shorthand for Optional<NWConnection> and nil is shorthand for Optional<NWConnection>.none.

Note One nice thing about this is that optionals are not limited to pointer types. You can, for example, have an optional integer, written as Int?.

Swift has a lot of sugar surrounding optionals. This makes sense when you consider its background. One key goal of Swift was good interoperability between it and C, Objective-C, and now C++. Those languages are full of pointers that might be optional, and writing out the optional syntax in full each time would be really clunky.

The most common sugared forms you’re likely to encounter are:

  • Optional types When writing a type T, T? is shorthand for Optional<T>.

  • Optional force unwrapping When referencing an optional value, vQ! is shorthand for:

    switch vQ {
    case .none:
        fatalError()
    case .some(let v):
        return v
    }
    
  • Optional chaining When referencing a member of an optional value, v?.m is shorthand for:

    switch vQ {
    case .none:
        return Optional<M>.none
    case .some(let v):
        return Optional<M>.some(v.m)
    }
    
  • A let clause in an if or guard statement I explained its expansion above.

But really this is just touching the surface. There’s a world of sugar beyond what I’ve explained above.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] You can go much further than that. For example, you can drop the NWEndpoint because Swift can infer that from the value you’re binding to:

if let browseResult = browseResult,
    case .service(name: let name, type: _, domain: _, interface: _) = browseResult.endpoint {
    title = "Join \(name)"
}

In modern Swift you can simplify the first clause:

if let browseResult,
    case .service(name: let name, type: _, domain: _, interface: _) = browseResult.endpoint {
    title = "Join \(name)"
}

But you can also use the optional pattern to get rid of that first clause entirely:

if case .service(name: let name, type: _, domain: _, interface: _)? = browseResult?.endpoint {
    title = "Join \(name)"
}

[2] There’s also some ABI stuff. For example, Swift knows that an object reference can’t be 0 so, at runtime, it encodes an optional object reference in the same way as Objective-C does, using 0 to reference the .none case.

Accepted Answer

And what of

if let browseResult = browseResult,

Does this test whether "browseResult" has a nil value?

This test unwraps browseResult

  • if nil, it fails
  • if not nil, now browseResult is browseResult!, no need to unwrap later

in recent Swift, you can simply write

if let browseResult

As for the = sign, as Eskimo explained, the point is that it comes with case let:

if case let =

Which can be read

  • if I can make NWEndpoint.service(), with the correct parameters, match browseResult.endpoint,
  • then it is true,
  • and the parameters are set (except those defined as _, which are simply ignored).

I found useful to have such a sentence in mind (if I can make NWEndpoint.service(), with the correct parameters, match browseResult.endpoint) when writing such clauses.

I see now that NWEndpoint is not a class, or struct, and .service is not a method. NWEndpoint is an enumeration defined in the file Network.h, and that .service is a case element in the NWEndpoint struct. Now I can see the pattern matching. It is testing whether the name is the same in both NWEndpoint.service, and browseResult.endpoint.

Thanks everyone for your help.

Yes, NWEndpoint is an enum with associated values. Hence the pattern matching.

.

It is testing whether the name is the same in both NWEndpoint.service, and browseResult.endpoint.

Not only, it is testing if it can match all the parameters as defined in one of the enum cases.

Good continuation and don't forget to close the thread.

NWEndpoint is an enum with associated values.

To further complicate matters, NWEndpoint is both an enum and a class (-: The latter is part of the (should be deprecated but we haven’t quite got there yet) in-provider networking support in Network Extension framework.

But, yeah, if you stick with using Network framework, NWEndpoint is an enum (-:

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

@eskimo how is it possible to be both an enumand a class ? I would not have imagined that could be possible in Swift with its strong typing.

how is it possible to be both an enumand a class ?

Because, in Swift, symbols are scoped by their module (aka framework). So there are two separate types, NetworkExtension.NWEndpoint and Network.NWEndpoint. And, if you ever find yourself in a situation wehre you need to import both frameworks, you can use the full name to disambiguate:

import Network
import NetworkExtension

print(Network.NWEndpoint.self)
print(NetworkExtension.NWEndpoint.self)

Having said that, this is ambiguity is obviously bad and we plan to resolve it by deprecating the old in-provider networking types; we just haven’t quite got there yet.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

case keyword used without switch
 
 
Q