Users who have contributed to this file

Swift tips & tricks ⚡️

One of the things I really love about Swift is how I keep finding interesting ways to use it in various situations, and when I do - I usually share them on Twitter. Here's a collection of all the tips & tricks that I've shared so far. Each entry has a link to the original tweet, if you want to respond with some feedback or question, which is always super welcome! 🚀

✍️ Adding support for the new Apple Pencil double-tap feature is super easy! All you have to do is to create a UIPencilInteraction, add it to a view, and implement one delegate method. Hopefully all pencil-compatible apps will soon adopt this.

😎 Here's a cool function that combines a value with a function to return a closure that captures that value, so that it can be called without any arguments. Super useful when working with closure-based APIs and we want to use some of our properties without having to capture self.

💉 When I'm only using a single function from a dependency, I love to inject that function as a closure, instead of having to create a protocol and inject the whole object. Makes dependency injection & testing super simple.

💥 It's cool that you can easily assign a closure as a custom NSException handler. This is super useful when building things in Playgrounds - since you can't use breakpoints - so instead of just signal SIGABRT, you'll get the full exception description if something goes wrong.

❤️ I love that in Swift, we can use the type system to make our code so much more self-documenting - one way of doing so is to use type aliases to give the primitive types that we use a more semantic meaning.

🐾 Swift's pattern matching capabilities are so powerful! Two enum cases with associated values can even be matched and handled by the same switch case - which is super useful when handling state changes with similar data.

🅰 One really nice benefit of Swift multiline string literals - even for single lines of text - is that they don't require quotes to be escaped. Perfect when working with things like HTML, or creating a custom description for an object.

🚢 Instead of using feature branches, I merge almost all of my code directly into master - and then I use feature flags to conditionally enable features when they're ready. That way I can avoid merge conflicts and keep shipping!

3️⃣ Whenever I have 3 properties or local variables that share the same prefix, I usually try to extract them into their own method or type. That way I can avoid massive types & methods, and also increase readability, without falling into a "premature optimization" trap.

👨‍🔧 Here's two extensions that I always add to the Encodable & Decodable protocols, which for me really make the Codable API nicer to use. By using type inference for decoding, a lot of boilerplate can be removed when the compiler is already able to infer the resulting type.

extensionEncodable {
funcencoded() throws-> Data {
returntryJSONEncoder().encode(self)
}
}
extensionData {
funcdecoded<T: Decodable>() throws-> T {
returntryJSONDecoder().decode(T.self, from: self)
}
}
let data =try user.encoded()
// By using a generic type in the decoded() method, the// compiler can often infer the type we want to decode// from the current context.tryuserDidLogin(data.decoded())
// And if not, we can always supply the type, still making// the call site read very nicely.let otherUser =try data.decoded() as User

📦UserDefaults is a lot more powerful than what it first might seem like. Not only can it store more complex values (like dates & dictionaries) and parse command line arguments - it also enables easy sharing of settings & lightweight data between apps in the same App Group.

let sharedDefaults =UserDefaults(suiteName: "my-app-group")!let useDarkMode = sharedDefaults.bool(forKey: "dark-mode")
// This value is put into the shared suite.sharedDefaults.set(true, forKey: "dark-mode")
// If you want to treat the shared settings as read-only (and add// local overrides on top of them), you can simply add the shared// suite to the standard UserDefaults.let combinedDefaults = UserDefaults.standard
combinedDefaults.addSuite(named: "my-app-group")
// This value is a local override, not added to the shared suite.combinedDefaults.set(true, forKey: "app-specific-override")

✅ That the compiler now automatically synthesizes Equatable conformances is such a huge upgrade for Swift! And the cool thing is that it works for all kinds of types - even for enums with associated values! Especially useful when using enums for verification in unit tests.

🆔 If you want to avoid using plain strings as identifiers (which can increase both type safety & readability), it's really easy to create a custom Identifier type that feels just like a native Swift type, thanks to protocols!

❤️ I love to structure my code using extensions in Swift. One big benefit of doing so when it comes to struct initializers, is that defining a convenience initializer doesn't remove the default one the compiler generates - best of both worlds!

🤖 Now that the Swift compiler automatically synthesizes Equatable & Hashable conformances for value types, it's easier than ever to setup model structures with nested types that are all Equatable/Hashable!

⏱ I've started using "then" as an external parameter label for completion handlers. Makes the call site read really nicely (Because I do ❤️ conversational API design) regardless of whether trailing closure syntax is used or not.

⚾️ Swift tests can throw, which is super useful in order to avoid complicated logic or force unwrapping. By making errors conform to LocalizedError, you can also get a nice error message in Xcode if there's a failure.

🎛 The awesome thing about option sets in Swift is that they can automatically either be passed as a single member or as a set. Even cooler is that you can easily define your own option sets as well, perfect for options and other non-exclusive values.

// Option sets are awesome, because you can easily pass them// both using dot syntax and array literal syntax, like when// using the UIView animation API:UIView.animate(withDuration: 0.3,
delay: 0,
options: .allowUserInteraction,
animations: animations)
UIView.animate(withDuration: 0.3,
delay: 0,
options: [.allowUserInteraction, .layoutSubviews],
animations: animations)
// The cool thing is that you can easily define your own option// sets as well, by defining a struct that has an Int rawValue,// that will be used as a bit mask.extensionCache {
structOptions: OptionSet{
staticlet saveToDisk =Options(rawValue: 1)
staticlet clearOnMemoryWarning =Options(rawValue: 1<<1)
staticlet clearDaily =Options(rawValue: 1<<2)
let rawValue:Int
}
}
// We can now use Cache.Options just like UIViewAnimationOptions:Cache(options: .saveToDisk)
Cache(options: [.saveToDisk, .clearDaily])

👨‍🍳 Combine first class functions in Swift with the fact that Dictionary elements are (Key, Value) tuples and you can build yourself some pretty awesome functional chains when iterating over a Dictionary.

// This produces a '() -> Void' closure which is a reference to the// given view's 'removeFromSuperview' method.let closure = UIView.removeFromSuperview(view)
// We can now call it just like we would any other closure, and it// will run 'view.removeFromSuperview()'closure()
// This is how running tests using the Swift Package Manager on Linux// works, you return your test functions as closures:extensionUserManagerTests {
staticvar allTests = [
("testLoggingIn", testLoggingIn),
("testLoggingOut", testLoggingOut),
("testUserPermissions", testUserPermissions)
]
}

👏 One really nice benefit of dropping suffixes from method names (and just using verbs, when possible) is that it becomes super easy to support both single and multiple arguments, and it works really well semantically.

👽 Using the AnyObject (or class) constraint on protocols is not only useful when defining delegates (or other weak references), but also when you always want instances to be mutable without copying.

// By constraining a protocol with 'AnyObject' it can only be adopted// by classes, which means all instances will always be mutable, and// that it's the original instance (not a copy) that will be mutated.protocolDataContainer: AnyObject{
var data: Data? { getset }
}
classUserSettingsManager {
privatevar settings: Settings
privatelet dataContainer: DataContainer
// Since DataContainer is a protocol, we an easily mock it in// tests if we use dependency injectioninit(settings: Settings, dataContainer: DataContainer) {
self.settings= settings
self.dataContainer= dataContainer
}
funcsaveSettings() throws {
let data =try settings.serialize()
// We can now assign properties on an instance of our protocol// because the compiler knows it's always going to be a class dataContainer.data= data
}
}

🍣 Even if you define a custom raw value for a string-based enum in Swift, the full case name will be used in string interpolation.

Super useful when using separate raw values for JSON, while still wanting to use the full case name in other contexts.

extensionBuilding {
// This enum has custom raw values that are used when decoding// a value, for example from JSON.enumKind: String{
casecastle="C"casetown="T"casebarracks="B"casegoldMine="G"casecamp="CA"caseblacksmith="BL"
}
var animation: Animation {
returnAnimation(
// When used in string interpolation, the full case name is still used.// For 'castle' this will be 'buildings/castle'.name: "buildings/\(kind)",
frameCount: frameCount,
frameDuration: frameDuration
)
}
}

📐 A really interesting side-effect of a UIView's bounds being its rect within its own coordinate system is that transforms don't affect it at all. That's why it's usually a better fit than frame when doing layout calculations of subviews.

👏 It's awesome that many UIKit APIs with completion handlers and other optional parameters import into Swift with default arguments (even though they are written in Objective-C). Getting rid of all those nil arguments is so nice!

❤️ I love the fact that optionals are enums in Swift - it makes it so easy to extend them with convenience APIs for certain types. Especially useful when doing things like data validation on optional values.

🗺 Using the where keyword can be a super nice way to quickly apply a filter in a for-loop in Swift. You can of course use map, filter and forEach, or guard, but for simple loops I think this is very expressive and nice.

✒️ Dot syntax is one of my favorite features of Swift. What's really cool is that it's not only for enums, any static method or property can be used with dot syntax - even initializers! Perfect for convenience APIs and default parameters.

functestDelegateNotRetained() {
// Assign the delegate (weak) and also retain it using a local varvar delegate: Delegate?=DelegateMock()
controller.delegate= delegate
XCTAssertNotNil(controller.delegate)
// Release the local var, which should also release the weak reference delegate =nilXCTAssertNil(controller.delegate)
}

🗝 Here's a neat little trick I use to get UserDefault key consistency in Swift (#function expands to the property name in getters/setters). Just remember to write a good suite of tests that'll guard you against bugs when changing property names.