A blog about Swift and iOS development.

Three Quick Tips

Three quick tips before the new year.

Stop Using &&

Everyone else has probably arrived at this already, but Swift 3's rejiggering of the where clause affects more than guard's syntax. It also eliminates the need for && operators in ifs:

// Equivalent in Swift 3:
if this && that { ... }
if this, that { ... }

This clearly isn't a big deal when evaluating two simple Bools. But when working with expressions (especially those involving other logical operators), we have to consider things like grouping and order of operations and I usually give up and put parentheses around everything just to be safe:

// Maybe a regex or something?
if (x > y) && (xyzzy(q) || q != 0) { ... }

// Two expressions, no operator ambiguity.
if x > y, xyzzy(q) || q != 0 { ... }

This syntax also has the knock-on benefit of making it clear where to break lines.1

// Use operator to end lines?
if this &&
   that &&
   theOther {

// Or begin the new lines?
if this
&& that
&& theOther {

// Nevermind.
if this,
   theOther {

Give Modules Distinct Names

Swift's module-based approach to implicit namespacing works great most of the time. Even if I do something daft like implement my own String type:

// In MyModule:
public struct String { ... }

I can use module names to disambiguate what would otherwise be a collision with the standard library:

import MyModule

let myString = MyModule.String("foo")
let swiftString = Swift.String("foo")

But, what if MyModule exports a public type also named MyModule?

// In MyModule:
public struct String { ... }
public struct MyModule { ... }

Oops! Now, when we import MyModule, it binds the symbol MyModule to our struct, not the module. Swift now thinks we're asking our struct for a member called String which, of course, doesn't exist:

import MyModule

let myString = MyModule.String("foo")
//> Value of type 'MyModule' has no member 'String'

Even if we're careful not to clobber the stdlib, there's no telling what types other 3rd party modules (or future versions of Swift) might introduce. Collisions are inevitable. Best to plan for them ahead of time by not doubling up on module and type names.

Dealing with Non-distinct Modules

Unfortunately, naming our Swift modules after their primary class seems to be something of a(n anti-)pattern in the community. Here's a quick hack for when we need to work around collisions between modules with non-distinct names:

  1. Imports are scoped to file, so create a new file and import only one of the conflicting modules.
  2. Because only one module is imported, references to the conflicting type are now unambiguous. Use this to create a typealias for it.
  3. Repeat this procedure for the other troublesome module.
  4. Use the aliases to avoid conflicts in files where both modules are imported.

So if we have two modules, Foo and Bar, both of which declare a type Thing (and another type with the same name as the module, preventing standard disambiguation), we could:

// In "FooTypes.swift"
import Foo
typealias FooThing = Thing
// In "BarTypes.swift"
import Bar
typealias BarThing = Thing
// In "MyWidget.swift"
import Foo
import Bar
//Use aliases instead of Foo.Thing and Bar.Thing
let thing: FooThing = BarThing().someValue

Out with the old, in with the new. Everyone have a safe and productive 2017!

1: Yes. This is a real argument I've had. More than once.↩︎

Hit me up on twitter (@jemmons) to continue the conversation.