1560830836
I’ve always been about the bottom line. Uninterested in pseudo intellectual concepts, fancy terminology and hype. Instead, I always reach for the tools and technologies that help me ship code as soon as possible. This approach was initially productive — specifically when I was building smaller “proof of concept” applications.
Unfortunately, this approach did not scale. As I progressed as a developer I started feeling the law of diminishing return on my productivity. Setting up a project, and reaching basic functionality was fast. But the real problems started creeping up when my applications started growing in complexity. I found that as a project’s lifecycle advanced I was writing complex code. Code that I had written become harder to reason about. In order to understand it, I had to be extremely concentrated.
I had this itching feeling that a better, cleaner approach to developing software had to exist. I had heard whispers about functional programming, and how it allows developers to write more concise and elegant code. I was unknowingly exposed to functional paradigms and patterns for the first time while working with React and Redux. They both incorporated some of the principles, and I liked them. I read about FP — to my initial dismay I saw its paradigms were based on abstract mathematical concepts and that it was very prevalent in academia. Being that my goal is to ship products as fast as possible, this seemed like a counterintuitive approach to what I was trying to achieve. After 4 years in engineering school, I was pretty set on the opinion that academia only tackled theoretical problems, and was unlikely to ever help me in my day-to-day of building things.
🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔
But FP kept haunting me. Elegant solutions and paradigms were sprinkled online in all my favorite open source projects, blog posts and tutorials. I put my skeptecism aside and started delving into FP.
Although the concepts involve new jargon, and include a steep learning curve, I was amazed and really excited about this “new approach”. This series of articles shares my learning experience, and aims at extracting and summarizing the pearls of FP which enable a cleaner, more concise development experience. I will attempt to build an intuitive understanding of the patterns I discuss and frame the problems and the provided solutions as simply as possible, overstepping unnecessarily complex definitions. Learning FP has a reputation for being a bit daunting, but by breaking down the concepts into smaller bits, we will make the ideas easier to digest.
The main difference in FP in comparison to other programming paradigms is a declarative approach (FP) versus an imperative one. Before we dive into formal definitions, let’s explore the differences by looking at an example.
// triple the value of every element in a given array const triple = (arr) => { let results = [] for (let i = 0; i < arr.length; i++){ results.push(arr[i] * 3) } return results }// sum all the elements in a given array
const sum = (arr) => {
let result = 0
for (let i = 0; i < arr.length; i++){
result += arr[i]
}
return result
}
Imperative functions in the wild
Does this code seem evil? It should! What are the similarities between the methods above?
Let’s rewrite this snippet of code, but in a declarative manner.
// triple the value of every item in a given array
const triple = (arr) => arr.map((currentItem) => currentItem * 3)// sum all the elements in a given array
const sum = (arr) => arr.reduce((prev, current) => prev + current, 0)
.map? .reduce? What is this black magic?
First off, I promise that given the same input, these two methods produce the same output every single time.
A quick aside on the declarative snippet -
.map()
is a method accessible from every array in JS. The .map()
method creates a new array with the results of calling a provided function on every element in the calling array.
.reduce()
is a method that applies a function against an accumulator and each element in the array (from left to right) to reduce it to a single value.
Don’t fret about these just yet. We’re going to explore these handy array-native methods in depths in upcoming posts. But it is clear that the declarative snippet is more concise than the imperative one. It’s also a lot easier to read. Instead of instructing the program on which indexes I want it to access etc, I am simply supplying an expression to .map() and** .reduce()**
(an anonymous function in our case) which tells the program what I want it do to every element in the array.
This declarative approach is going to serve us well across the board by:
2. Composing shorter, expressive and concise code. After all, the less code we write the less we have to debug.
Most importantly, these tools and paradigms are going to help us achieve our (my) ultimate goal of shipping products faster. Check out the next post, where we discuss functions in JS, why they are special and how their characteristics enable functional programming.
Before we get started, there’s something you need to know … If you’ve ever programmed in JS you’ve probably used FP patterns before! These patterns and paradigms have been there all along, we just haven’t been able to see them properly. We are going to start from the familiar and explore new territory. Things may get a bit … well … strange. But fear not! Together we will survive!
In Javascript, functions are first class objects. Like I mentioned earlier, we don’t like cryptic terminology, so let’s explain. According to the Mozilla developer glossary:
A programming language is said to have First-class functions when functions in that language are treated like any other variable. For example, in such a language, a function can be passed as an argument to other functions, can be returned by another function and can be assigned as a value to a variable.
In the following example we will declare a const and assign it an anonymous arrow functions.
After the initial assignment constFunction is a constant with a value of a function. We verify that by logging the constFunction variable in the Chrome inspector. Because constFunction is a function we can also invoke it.
Now that we understand that variables can hold functions, let’s demonstrate a function as a value of a key in an object. This should be familiar for anyone who has done any object oriented programming before.
const functionAsObjectProperty = {
print: (value) => console.log(value)
};functionAsObjectProperty.print(“mic check”); // “mic check”
When functions are first class objects we can pass them as data to an array, just like any other data type. Let’s use the Chrome console and check this out.
Now that we’ve warmed up, let’s get to the interesting stuff :) JS developers see functions that accept other functions as arguments on a daily basis. If you’re coming from a language that doesn’t support FP this should seem a bit weird 😳😳😳😳😳😳😳 Let’s acquaint ourselves with this concept by looking at some examples.
An asynchronous function that accepts a callback function.
const jsonfile = require(‘jsonfile’)const file = ‘/tmp/data.json’
const obj = {name: ‘JP’}const errorLoggerFunction = (err) => console.error(err);
jsonfile.writeFile(file, obj, errorLoggerFunction)
errorLoggerFunction is defined as a function with the ES6 arrow syntax
We’re using the jsonfile npm module in this example for the _writeFile m_ethod. The third parameter that writeFile is expecting is a function. When the jsonfile.writeFile method executes it will either succeed or fail. If it fails it will execute the errorLoggerFunction. Alternatively, we could have gone for a more terse syntax, and dropped the named function:
const jsonfile = require(‘jsonfile’)const file = ‘/tmp/data.json’
const obj = {name: ‘JP’}jsonfile.writeFile(file, obj, (err) => console.error(err))
It’s an anonymous function because we didn’t name it
setTimeout
const timeout = () => {
setTimeout(() => alert(“WoW”), 1000);
}
Classic callback example
This example shows the built in asynchronous setTimeout method which accepts 2 arguments. Let’s formalize this a little bit and explain the setTimeout function in functional programming terms.
Let’s start by reading the signature of the function. We can observe that the number of arguments that setTimeout takes is two. In functional programming the number of arguments a function takes is called its Arity, from words like unary, binary, ternary etc. So we can say that setTimeout is of arity 2, or equivalently say that is a binary function.
The arguments that setTimeout expects is a function and a time interval to wait before executing the given function. Hmmm … another function that accepts a function as input?
In functional programming this is so common that these types of functions even have a name! They are called higher order functions.
A higher order function is a function that takes a function as an argument, or returns a function.
There you go. Now you can drop this term low key in any random conversation at work / with friends and sound like a boss! 😂😂😂
const add = (x,y) => x + y;
const subtract = (x,y) => x - y;
const multiply = (x,y) => x * y;const arrayOfFunctions = [add, subtract, multiply];
arrayOfFunctions.forEach(calculationFunction => console.log(calculationFunction(1,1))); // 2 0 1
On line 5 we are declaring an array of functions. We are then using the forEach method to iterate over the array. forEach is a natively supported ES6+ function, that accepts a function to execute on every item in the array. Therefore, forEach is also a higher order function!
Our forEach accepts an anonymous function as input. forEach will iterate over the array and implicitly access the current item in the array and call it getCalculation. It is worth noting that forEach implicitly accesses array elements, in comparison to how we would have accessed the current element if we had used a regular for loop — ie. arrayOfFunctions[i]. Every item in our array is a function, therefore we invoke getCalculation with the arguments that it is expecting.
Fantastic. This example illustrates that functions in functional programming can be passed into arrays (lists) just like any other data type. Functions can go anywhere!
Now let’s build our own higher order function!
const addWrapper = () => (x,y) => x + y;const add = addWrapper();
const sum1 = add (1,2); // 3
// Or we could do it like this
const sum2 = addWrapper()(4,4); // 8
view raw
functionThatReturnsFunction.js hosted with ❤ by GitHub
The addWrapper function returns a simple addition function when called. By invoking the result of the addWrapper function and supplying it two arguments, we have access to the anonymous addition function.
We could get even crazier with our level of indirection and write a function that returns a function, that in turn returns its own function!
const bankStatement =
name =>
location =>
balance =>
Hello ${name}! Welcome to the bank of ${location}. Your current balance is ${balance}
;const statementExpectingLocation = bankStatement(“Omer”);
const statementExpectingBalance = statementExpectingLocation(“NYC”);
const bankStatementMsg = statementExpectingBalance(“100 million”); // wishful thinking?console.log(bankStatementMsg); // Hello Omer! Welcome to the bank of NYC. Your current balance is 100 million
// We could also call the function with all the arguments up front
const msg = bankStatement(“Jeff Bezos”)(“Silicon Valley”)(“97.7 billion”);
console.log(msg); // Hello Jeff Bezos! Welcome to the bank of Silicon Valley. Your current balance is 97.7 billion
I hope you like curry!
This is a very powerful pattern in functional programming. We will explore it in depth in the coming posts when we talk about currying and partial applications.
First class functions are the cornerstones of any functional programming language. The main point that you should take away from our discussion about first class functions is that functions can be assigned as constants, variables, placed as array elements and even set as values of keys on an object. Additionally, (and most importantly ?!) functions can be returned to and from functions —** just like any other data type!**
Motivation
So many of our bugs are rooted in IO related, data mutation, side effect bearing code. These creep up all over our code base — from things like accepting user inputs, receiving an unexpected response via an http call, or writing to the file system. Unfortunately, this is a harsh reality that we should grow accustomed to dealing with. Or is it?
What if I told you, that we could minimize the parts of our code which executed the critical / volatile bits of our program? We could enforce (by convention) that the large majority of our code base would be pure, and limit IO related / side effect bearing code to a specific part of our codebase. This would make our debugging process a lot easier, more coherent and easier to reason about.
So, what is this mythical pure function? A pure function has two main characteristics:

const add = (x, y) => x + y // A pure function
add is a pure function because it’s output is solely dependent on the arguments it receives. Therefore, given the same values, it will always produce the same output.
How about this one?
const magicLetter = ‘*’
const createMagicPhrase = (phrase) =>${magicLetter}abra${phrase}
Something about this one is fishy…. The createMagicPhrase function is dependent on a value which is external to its scope. Therefore, it is not pure!
const fetchLoginToken = externalAPI.getUserToken
Is fetchLoginToken a pure function? Does it return the same value every single time? Absolutely not! Sometimes it will work — sometimes the server will be down and we will get a 500 error — and at some point in the future the API may change so that this call is no longer valid! So, because the function is non-deterministic, we can safely say that it is an impure function.
const calculateBill = (sumOfCart, tax) => sumOfCart * tax
Is calculateBill pure? Definitely :) It exhibits the two necessary characteristics:
The Mostly Adequate Guide states that side effects include, but are not limited to:
Readability -> Side effects make our code harder to read. Since a non pure function is not deterministic it may return several different values for a given input. We end up writing code that needs to account for the different possibilities. Let’s look at another http based example:
async function getUserToken(id) {
const token = await getTokenFromServer(id);
return token;
}
This snippet can fail in so many different ways. What if the id passed to the getTokenFromServer was invalid? What if the server crashed and returned an error, instead of the expected token? There are a lot of contingencies that need to be planned for, and forgetting one (or several!) of them is very easy.
Additionally, a pure function is easier to read, as it requires no context. It receives all of its needed parameters up front, and does not talk / tamper with the state of the application.
Testability -> Because pure functions are deterministic by nature, writing unit tests for them is a lot simpler. Either your function works or it doesn’t 😁
Parallel Code -> Since pure functions only depend on their input, and will not cause side effects, they are great for scenarios where parallel threads run and use shared memory.
Modularity and Reusability -> Think of pure functions as little units of logic. Because they only depend on the input you feed them, you can easily reuse functions between different parts of your codebase or different projects altogether.
Referential Transparency -> This one sounds so complicated 🙄🙄 When I first read the title I wanted a coffee break! Simply put, referential transparency means that a function call could be replaced by its output value, without changing the overall behavior of our program. This is mostly useful as a framework of thought when creating pure functions.
It’s important to note that although pure functions offer a ton of benefits, it’s not realistic to only have pure functions in our applications. After all, if we did our application would have no side effects, thus not produce any observable effects to the outside world. That would be pretty boring 😥😥😥. Instead we will try to encapsulate all of our side effects to specific parts of our codebase. That way, assuming we have written unit tests for our pure functions and know they are working, if something breaks in our app, it will be a lot easier to track down.
Let’s conclude our discussion by converting the following non pure function to pure. This is a contrived example, but demonstrates how we can easily refactor unpure code to pure.
let a = 4;
let b = 5;
let c = 6;
const updateTwoVars = (a) => {
b++;
c = a * b;
}updateTwoVars(a);
console.log(b,c); // b = 6, c = 24
Let’s start by reviewing why this function is unpure. Our function is unpure because it depends on a and b, which are external to its scope. In addition, it is also directly mutating (changing) the values of the variables. The quickest way to refactor this function is
let a = 4;
let b = 5;
let c = 6;
const updateTwoVars = (a, b, c) => [b++, a * b];const updateRes = updateTwoVars(a,b,c);
b = updateRes[0]
c = updateRes[1]
We’ve covered a lot of the benefits of transitioning our code base to include more pure functions. It makes our code easier to reason about, test, and most importantly more predictable. Remember, pure functions are not about completely ridding our code base of side effects. It’s about constraining them to a definitive location and eliminating as much of them as possible. This approach will justify itself many times over, when your programs start growing in size and complexity.
Currying is when we call a function with fewer arguments than it expects. In turn, the invoked function returns a function that takes the remaining arguments.
const magicPhrase =
(magicWord) =>
(muggleWord) =>
magicWord + muggleWord
We could then invoke this function with the following pattern
Call it maaagic
Writing functions that return functions, that in turn return some output (possibly another function!) can get quite cumbersome. Luckily we have functional JS helper libraries like Ramda and lodash which provide us with utility methods such as curry. The curry utility wraps normally declared functions and transforms them into a series of one-argument functions. So we could convert the previous code to:
import _ from “lodash”const magicPhrase = _.curry((magicWord, muggleWord) => magicWord + muggleWord)
const muggleWordAccepter = magicPhrase("Abra kedabra ")
muggleWordAccepter(“dishwasher”)
Another example would be a revamped implementation of our favorite add function
import _ from “lodash”const addFunction = _.curry((a, b) => a + b)
const addOne = add(1)
addTen(1)
So we are essentially, “pre loading” the add function with the first variable. Our function has the ability to remember the first value passed thanks to JS closure.
2. We use these functions as clean, testable units of logic to compose the more logically complex parts of our programs.
3. With currying, any function that works on single elements can be converted into a function that works on arrays (lists), simply by wrapping it with map.
const getObjectId = (obj) => obj.id // works on single objectconst arrayOfObjects = [{id: 1}, {id: 2}, {id: 3}, {id: 4}]
const arrayOfIDs = arrayOfObjects.map(getObjectId)
BAM! Our function that worked on single elements can work on arrays!
The only real way to get familiar with these concepts is to practice :) Let’s get to it. We shall start with one more example of converting a function that operates on a single element to a function that operates on an array.
const getFirstTwoLettersOfWord = (word) => word.substring(0,2)// We can convert it, by wrapping it in the map method
[“aabb”, “bbcc”, “ccdd”, “ddee”].map(getFirstTwoLettersOfWord)
The next example comes out of the amazing Mostly Adequate guide, with a small ES6 refactors :)
Let’s refactor the max function so that it won’t reference any arguments.
arr = [2,4,6,8,9]// LEAVE BE:
const getMax = (x, y) => {
return x >= y ? x : y;
};// REFACTOR THIS ONE:
const max = (arr) => {
return arr.reduce((acc, x) => {
return getMax(acc, x);
}, -Infinity);
};const max = arr.reduce(getMax, -Infinity)
Let’s wrap the native JS slice method so that it functional and curried.
import _ from “lodash”const arr = [“barney”, “fred”, “dave”]
arr.slice(0, 2) // [“barney”, “fred”]
const slice = _.curry((start, end, arr) => arr.slice(start, end));
const sliceWithSetIndexes = slice(0,2)sliceWithSetIndexes(arr) // [“barney”, “fred”]
We’ve seen several examples where we curry JS functions. Currying refers to the process of transforming a function with multiple arity (arguments accepted) into the same function with less arity. It utilizes JS closure to remember the arguments used in the previous invocations. Currying twists functions around so that they can work more naturally together. Its biggest advantage is that it easily allows for function composition, which we will explore in depth in the next post!
☞ 10 JavaScript array methods you should know
☞ Introducing TensorFlow.js: Machine Learning in Javascript
☞ Machine Learning in JavaScript with TensorFlow.js
☞ 5 ways to build real-time apps with JavaScript
☞ Full Stack Developers: Everything You Need to Know
☞ 5 Javascript (ES6+) features that you should be using in 2019
☞ The Complete JavaScript Course 2019: Build Real Projects!
☞ JavaScript: Understanding the Weird Parts
☞ Vue JS 2 - The Complete Guide (incl. Vue Router & Vuex)
☞ The Full JavaScript & ES6 Tutorial - (including ES7 & React)
Originally published by Omer Goldberg at https://hackernoon.com
#javascript #web-development
1666245660
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! 🚀
⚠️ This list is no longer being updated. For my latest Swift tips, checkout the "Tips" section on Swift by Sundell.
Also make sure to check out all of my other Swift content:
🚀 Here are some quick tips to make async tests faster & more stable:
// BEFORE:
class MentionDetectorTests: XCTestCase {
func testDetectingMention() {
let detector = MentionDetector()
let string = "This test was written by @johnsundell."
detector.detectMentions(in: string) { mentions in
XCTAssertEqual(mentions, ["johnsundell"])
}
sleep(2)
}
}
// AFTER:
class MentionDetectorTests: XCTestCase {
func testDetectingMention() {
let detector = MentionDetector()
let string = "This test was written by @johnsundell."
var mentions: [String]?
let expectation = self.expectation(description: #function)
detector.detectMentions(in: string) {
mentions = $0
expectation.fulfill()
}
waitForExpectations(timeout: 10)
XCTAssertEqual(mentions, ["johnsundell"])
}
}
For more on async testing, check out "Unit testing asynchronous Swift code".
✍️ 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.
let interaction = UIPencilInteraction()
interaction.delegate = self
view.addInteraction(interaction)
extension ViewController: UIPencilInteractionDelegate {
func pencilInteractionDidTap(_ interaction: UIPencilInteraction) {
// Handle pencil double-tap
}
}
For more on using this and other iPad Pro features, check out "Building iPad Pro features in Swift".
😎 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
.
func combine<A, B>(_ value: A, with closure: @escaping (A) -> B) -> () -> B {
return { closure(value) }
}
// BEFORE:
class ProductViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
buyButton.handler = { [weak self] in
guard let self = self else {
return
}
self.productManager.startCheckout(for: self.product)
}
}
}
// AFTER:
class ProductViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
buyButton.handler = combine(product, with: productManager.startCheckout)
}
}
💉 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.
final class ArticleLoader {
typealias Networking = (Endpoint) -> Future<Data>
private let networking: Networking
init(networking: @escaping Networking = URLSession.shared.load) {
self.networking = networking
}
func loadLatest() -> Future<[Article]> {
return networking(.latestArticles).decode()
}
}
For more on this technique, check out "Simple Swift dependency injection with functions".
💥 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.
NSSetUncaughtExceptionHandler { exception in
print(exception)
}
❤️ 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.
extension List.Item {
// Using type aliases, we can give semantic meaning to the
// primitive types that we use, without having to introduce
// wrapper types.
typealias Index = Int
}
extension List {
enum Mutation {
// Our enum cases now become a lot more self-documenting,
// without having to add additional parameter labels to
// explain them.
case add(Item, Item.Index)
case update(Item, Item.Index)
case remove(Item.Index)
}
}
For more on self-documenting code, check out "Writing self-documenting Swift code".
🤯 A little late night prototyping session reveals that protocol constraints can not only be applied to extensions - they can also be added to protocol definitions!
This is awesome, since it lets us easily define specialized protocols based on more generic ones.
protocol Component {
associatedtype Container
func add(to container: Container)
}
// Protocols that inherit from other protocols can include
// constraints to further specialize them.
protocol ViewComponent: Component where Container == UIView {
associatedtype View: UIView
var view: View { get }
}
extension ViewComponent {
func add(to container: UIView) {
container.addSubview(view)
}
}
For more on specializing protocols, check out "Specializing protocols in Swift".
📦 Here's a super handy extension on Swift's Optional
type, which gives us a really nice API for easily unwrapping an optional, or throwing an error in case the value turned out to be nil
:
extension Optional {
func orThrow(_ errorExpression: @autoclosure () -> Error) throws -> Wrapped {
switch self {
case .some(let value):
return value
case .none:
throw errorExpression()
}
}
}
let file = try loadFile(at: path).orThrow(MissingFileError())
For more ways that optionals can be extended, check out "Extending optionals in Swift".
👩🔬 Testing code that uses static APIs can be really tricky, but there's a way that it can often be done - using Swift's first class function capabilities!
Instead of accessing that static API directly, we can inject the function we want to use, which enables us to mock it!
// BEFORE
class FriendsLoader {
func loadFriends(then handler: @escaping (Result<[Friend]>) -> Void) {
Networking.loadData(from: .friends) { result in
...
}
}
}
// AFTER
class FriendsLoader {
typealias Handler<T> = (Result<T>) -> Void
typealias DataLoadingFunction = (Endpoint, @escaping Handler<Data>) -> Void
func loadFriends(using dataLoading: DataLoadingFunction = Networking.loadData,
then handler: @escaping Handler<[Friend]>) {
dataLoading(.friends) { result in
...
}
}
}
// MOCKING IN TESTS
let dataLoading: FriendsLoader.DataLoadingFunction = { _, handler in
handler(.success(mockData))
}
friendsLoader.loadFriends(using: dataLoading) { result in
...
}
🐾 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.
enum DownloadState {
case inProgress(progress: Double)
case paused(progress: Double)
case cancelled
case finished(Data)
}
func downloadStateDidChange(to state: DownloadState) {
switch state {
case .inProgress(let progress), .paused(let progress):
updateProgressView(with: progress)
case .cancelled:
showCancelledMessage()
case .finished(let data):
process(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.
let html = highlighter.highlight("Array<String>")
XCTAssertEqual(html, """
<span class="type">Array</span><<span class="type">String</span>>
""")
💎 While it's very common in functional programming, the reduce
function might be a bit of a hidden gem in Swift. It provides a super useful way to transform a sequence into a single value.
extension Sequence where Element: Equatable {
func numberOfOccurrences(of target: Element) -> Int {
return reduce(0) { result, element in
guard element == target else {
return result
}
return result + 1
}
}
}
You can read more about transforming collections in "Transforming collections in Swift".
📦 When I use Codable in Swift, I want to avoid manual implementations as much as possible, even when there's a mismatch between my code structure and the JSON I'm decoding.
One way that can often be achieved is to use private data containers combined with computed properties.
struct User: Codable {
let name: String
let age: Int
var homeTown: String { return originPlace.name }
private let originPlace: Place
}
private extension User {
struct Place: Codable {
let name: String
}
}
extension User {
struct Container: Codable {
let user: User
}
}
🚢 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!
extension ListViewController {
func addSearchIfNeeded() {
// Rather than having to keep maintaining a separate
// feature branch for a new feature, we can use a flag
// to conditionally turn it on.
guard FeatureFlags.searchEnabled else {
return
}
let resultsVC = SearchResultsViewController()
let searchVC = UISearchController(
searchResultsController: resultsVC
)
searchVC.searchResultsUpdater = resultsVC
navigationItem.searchController = searchVC
}
}
You can read more about feature flags in "Feature flags in Swift".
💾 Here I'm using tuples to create a lightweight hierarchy for my data, giving me a nice structure without having to introduce any additional types.
struct CodeSegment {
var tokens: (
previous: String?,
current: String
)
var delimiters: (
previous: Character?
next: Character?
)
}
handle(segment.tokens.current)
You can read more about tuples in "Using tuples as lightweight types in Swift"
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.
Before
public func generate() throws {
let contentFolder = try folder.subfolder(named: "content")
let articleFolder = try contentFolder.subfolder(named: "posts")
let articleProcessor = ContentProcessor(folder: articleFolder)
let articles = try articleProcessor.process()
...
}
After
public func generate() throws {
let contentFolder = try folder.subfolder(named: "content")
let articles = try processArticles(in: contentFolder)
...
}
private func processArticles(in folder: Folder) throws -> [ContentItem] {
let folder = try folder.subfolder(named: "posts")
let processor = ContentProcessor(folder: folder)
return try processor.process()
}
👨🔧 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.
extension Encodable {
func encoded() throws -> Data {
return try JSONEncoder().encode(self)
}
}
extension Data {
func decoded<T: Decodable>() throws -> T {
return try JSONDecoder().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.
try userDidLogin(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")
🎨 By overriding layerClass
you can tell UIKit what CALayer
class to use for a UIView
's backing layer. That way you can reduce the amount of layers, and don't have to do any manual layout.
final class GradientView: UIView {
override class var layerClass: AnyClass { return CAGradientLayer.self }
var colors: (start: UIColor, end: UIColor)? {
didSet { updateLayer() }
}
private func updateLayer() {
let layer = self.layer as! CAGradientLayer
layer.colors = colors.map { [$0.start.cgColor, $0.end.cgColor] }
}
}
✅ 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.
struct Article: Equatable {
let title: String
let text: String
}
struct User: Equatable {
let name: String
let age: Int
}
extension Navigator {
enum Destination: Equatable {
case profile(User)
case article(Article)
}
}
func testNavigatingToArticle() {
let article = Article(title: "Title", text: "Text")
controller.select(article)
XCTAssertEqual(navigator.destinations, [.article(article)])
}
🤝 Associated types can have defaults in Swift - which is super useful for types that are not easily inferred (for example when they're not used for a specific instance method or property).
protocol Identifiable {
associatedtype RawIdentifier: Codable = String
var id: Identifier<Self> { get }
}
struct User: Identifiable {
let id: Identifier<User>
let name: String
}
struct Group: Identifiable {
typealias RawIdentifier = Int
let id: Identifier<Group>
let name: String
}
🆔 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!
More on this topic in "Type-safe identifiers in Swift".
struct Identifier: Hashable {
let string: String
}
extension Identifier: ExpressibleByStringLiteral {
init(stringLiteral value: String) {
string = value
}
}
extension Identifier: CustomStringConvertible {
var description: String {
return string
}
}
extension Identifier: Codable {
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
string = try container.decode(String.self)
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(string)
}
}
struct Article: Codable {
let id: Identifier
let title: String
}
let article = Article(id: "my-article", title: "Hello world!")
🙌 A really cool thing about using tuples to model the internal state of a Swift type, is that you can unwrap an optional tuple's members directly into local variables.
Very useful in order to group multiple optional values together for easy unwrapping & handling.
class ImageTransformer {
private var queue = [(image: UIImage, transform: Transform)]()
private func processNext() {
// When unwrapping an optional tuple, you can assign the members
// directly to local variables.
guard let (image, transform) = queue.first else {
return
}
let context = Context()
context.draw(image)
context.apply(transform)
...
}
}
❤️ 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!
struct Article {
let date: Date
var title: String
var text: String
var comments: [Comment]
}
extension Article {
init(title: String, text: String) {
self.init(date: Date(), title: title, text: text, comments: [])
}
}
let articleA = Article(title: "Best Cupcake Recipe", text: "...")
let articleB = Article(
date: Date(),
title: "Best Cupcake Recipe",
text: "...",
comments: [
Comment(user: currentUser, text: "Yep, can confirm!")
]
)
🏈 A big benefit of using throwing functions for synchronous Swift APIs is that the caller can decide whether they want to treat the return value as optional (try?
) or required (try
).
func loadFile(named name: String) throws -> File {
guard let url = urlForFile(named: name) else {
throw File.Error.missing
}
do {
let data = try Data(contentsOf: url)
return File(url: url, data: data)
} catch {
throw File.Error.invalidData(error)
}
}
let requiredFile = try loadFile(named: "AppConfig.json")
let optionalFile = try? loadFile(named: "UserSettings.json")
🐝 Types that are nested in generics automatically inherit their parent's generic types - which is super useful when defining accessory types (for things like states or outcomes).
struct Task<Input, Output> {
typealias Closure = (Input) throws -> Output
let closure: Closure
}
extension Task {
enum Result {
case success(Output)
case failure(Error)
}
}
🤖 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
!
typealias Value = Hashable & Codable
struct User: Value {
var name: String
var age: Int
var lastLoginDate: Date?
var settings: Settings
}
extension User {
struct Settings: Value {
var itemsPerPage: Int
var theme: Theme
}
}
extension User.Settings {
enum Theme: String, Value {
case light
case dark
}
}
You can read more about using nested types in Swift here.
🎉 Swift 4.1 is here! One of the key features it brings is conditional conformances, which lets you have a type only conform to a protocol under certain constraints.
protocol UnboxTransformable {
associatedtype RawValue
static func transform(_ value: RawValue) throws -> Self?
}
extension Array: UnboxTransformable where Element: UnboxTransformable {
typealias RawValue = [Element.RawValue]
static func transform(_ value: RawValue) throws -> [Element]? {
return try value.compactMap(Element.transform)
}
}
I also have an article with lots of more info on conditional conformances here. Paul Hudson also has a great overview of all Swift 4.1 features here.
🕵️♀️ A cool thing about Swift type aliases is that they can be generic! Combine that with tuples and you can easily define simple generic types.
typealias Pair<T> = (T, T)
extension Game {
func calculateScore(for players: Pair<Player>) -> Int {
...
}
}
You can read more about using tuples as lightweight types here.
☑️ A really cool "hidden" feature of UserDefaults is that it contains any arguments that were passed to the app at launch!
Super useful both in Swift command line tools & scripts, but also to temporarily override a value when debugging iOS apps.
let defaults = UserDefaults.standard
let query = defaults.string(forKey: "query")
let resultCount = defaults.integer(forKey: "results")
👏 Swift's &
operator is awesome! Not only can you use it to compose protocols, you can compose other types too! Very useful if you want to hide concrete types & implementation details.
protocol LoadableFromURL {
func load(from url: URL)
}
class ContentViewController: UIViewController, LoadableFromURL {
func load(from url: URL) {
...
}
}
class ViewControllerFactory {
func makeContentViewController() -> UIViewController & LoadableFromURL {
return ContentViewController()
}
}
🤗 When capturing values in mocks, using an array (instead of just a single value) makes it easy to verify that only a certain number of values were passed.
Perfect for protecting against "over-calling" something.
class UserManagerTests: XCTestCase {
func testObserversCalledWhenUserFirstLogsIn() {
let manager = UserManager()
let observer = ObserverMock()
manager.addObserver(observer)
// First login, observers should be notified
let user = User(id: 123, name: "John")
manager.userDidLogin(user)
XCTAssertEqual(observer.users, [user])
// If the same user logs in again, observers shouldn't be notified
manager.userDidLogin(user)
XCTAssertEqual(observer.users, [user])
}
}
private extension UserManagerTests {
class ObserverMock: UserManagerObserver {
private(set) var users = [User]()
func userDidChange(to user: User) {
users.append(user)
}
}
}
👋 When writing tests, you don't always need to create mocks - you can create stubs using real instances of things like errors, URLs & UserDefaults.
Here's how to do that for some common tasks/object types in Swift:
// Create errors using NSError (#function can be used to reference the name of the test)
let error = NSError(domain: #function, code: 1, userInfo: nil)
// Create non-optional URLs using file paths
let url = URL(fileURLWithPath: "Some/URL")
// Reference the test bundle using Bundle(for:)
let bundle = Bundle(for: type(of: self))
// Create an explicit UserDefaults object (instead of having to use a mock)
let userDefaults = UserDefaults(suiteName: #function)
// Create queues to control/await concurrent operations
let queue = DispatchQueue(label: #function)
For when you actually do need mocking, check out "Mocking in Swift".
⏱ 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.
protocol DataLoader {
// Adding type aliases to protocols can be a great way to
// reduce verbosity for parameter types.
typealias Handler = (Result<Data>) -> Void
associatedtype Endpoint
func loadData(from endpoint: Endpoint, then handler: @escaping Handler)
}
loader.loadData(from: .messages) { result in
...
}
loader.loadData(from: .messages, then: { result in
...
})
😴 Combining lazily evaluated sequences with builder pattern-like properties can lead to some pretty sweet APIs for configurable sequences in Swift.
Also useful for queries & other things you "build up" and then execute.
// Extension adding builder pattern-like properties that return
// a new sequence value with the given configuration applied
extension FileSequence {
var recursive: FileSequence {
var sequence = self
sequence.isRecursive = true
return sequence
}
var includingHidden: FileSequence {
var sequence = self
sequence.includeHidden = true
return sequence
}
}
// BEFORE
let files = folder.makeFileSequence(recursive: true, includeHidden: true)
// AFTER
let files = folder.files.recursive.includingHidden
Want an intro to lazy sequences? Check out "Swift sequences: The art of being lazy".
My top 3 tips for faster & more stable UI tests:
📱 Reset the app's state at the beginning of every test.
🆔 Use accessibility identifiers instead of UI strings.
⏱ Use expectations instead of waiting time.
func testOpeningArticle() {
// Launch the app with an argument that tells it to reset its state
let app = XCUIApplication()
app.launchArguments.append("--uitesting")
app.launch()
// Check that the app is displaying an activity indicator
let activityIndicator = app.activityIndicator.element
XCTAssertTrue(activityIndicator.exists)
// Wait for the loading indicator to disappear = content is ready
expectation(for: NSPredicate(format: "exists == 0"),
evaluatedWith: activityIndicator)
// Use a generous timeout in case the network is slow
waitForExpectations(timeout: 10)
// Tap the cell for the first article
app.tables.cells["Article.0"].tap()
// Assert that a label with the accessibility identifier "Article.Title" exists
let label = app.staticTexts["Article.Title"]
XCTAssertTrue(label.exists)
}
📋 It's super easy to access the contents of the clipboard from a Swift script. A big benefit of Swift scripting is being able to use Cocoa's powerful APIs for Mac apps.
import Cocoa
let clipboard = NSPasteboard.general.string(forType: .string)
🎯 Using Swift tuples for view state can be a super nice way to group multiple properties together and render them reactively using the layout system.
By using a tuple we don't have to either introduce a new type or make our view model-aware.
class TextView: UIView {
var state: (title: String?, text: String?) {
// By telling UIKit that our view needs layout and binding our
// state in layoutSubviews, we can react to state changes without
// doing unnecessary layout work.
didSet { setNeedsLayout() }
}
private let titleLabel = UILabel()
private let textLabel = UILabel()
override func layoutSubviews() {
super.layoutSubviews()
titleLabel.text = state.title
textLabel.text = state.text
...
}
}
⚾️ 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.
class ImageCacheTests: XCTestCase {
func testCachingAndLoadingImage() throws {
let bundle = Bundle(for: type(of: self))
let cache = ImageCache(bundle: bundle)
// Bonus tip: You can easily load images from your test
// bundle using this UIImage initializer
let image = try require(UIImage(named: "sample", in: bundle, compatibleWith: nil))
try cache.cache(image, forKey: "key")
let cachedImage = try cache.image(forKey: "key")
XCTAssertEqual(image, cachedImage)
}
}
enum ImageCacheError {
case emptyKey
case dataConversionFailed
}
// When using throwing tests, making your errors conform to
// LocalizedError will render a much nicer error message in
// Xcode (per default only the error code is shown).
extension ImageCacheError: LocalizedError {
var errorDescription: String? {
switch self {
case .emptyKey:
return "An empty key was given"
case .dataConversionFailed:
return "Failed to convert the given image to Data"
}
}
}
For more information, and the implementation of the require
method used above, check out "Avoiding force unwrapping in Swift unit tests".
✍️ Unlike static
properties, class
properties can be overridden by subclasses (however, they can't be stored, only computed).
class TableViewCell: UITableViewCell {
class var preferredHeight: CGFloat { return 60 }
}
class TallTableViewCell: TableViewCell {
override class var preferredHeight: CGFloat { return 100 }
}
👨🎨 Creating extensions with static factory methods can be a great alternative to subclassing in Swift, especially for things like setting up UIViews, CALayers or other kinds of styling.
It also lets you remove a lot of styling & setup from your view controllers.
extension UILabel {
static func makeForTitle() -> UILabel {
let label = UILabel()
label.font = .boldSystemFont(ofSize: 24)
label.textColor = .darkGray
label.adjustsFontSizeToFitWidth = true
label.minimumScaleFactor = 0.75
return label
}
static func makeForText() -> UILabel {
let label = UILabel()
label.font = .systemFont(ofSize: 16)
label.textColor = .black
label.numberOfLines = 0
return label
}
}
class ArticleViewController: UIViewController {
lazy var titleLabel = UILabel.makeForTitle()
lazy var textLabel = UILabel.makeForText()
}
🧒 An awesome thing about child view controllers is that they're automatically resized to match their parent, making them a super nice solution for things like loading & error views.
class ListViewController: UIViewController {
func loadItems() {
let loadingViewController = LoadingViewController()
add(loadingViewController)
dataLoader.loadItems { [weak self] result in
loadingViewController.remove()
self?.handle(result)
}
}
}
For more about child view controller (including the add
and remove
methods used above), check out "Using child view controllers as plugins in Swift".
🤐 Using the zip function in Swift you can easily combine two sequences. Super useful when using two sequences to do some work, since zip takes care of all the bounds-checking.
func render(titles: [String]) {
for (label, text) in zip(titleLabels, titles) {
print(text)
label.text = text
}
}
🎛 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.
extension Cache {
struct Options: OptionSet {
static let saveToDisk = Options(rawValue: 1)
static let clearOnMemoryWarning = Options(rawValue: 1 << 1)
static let clearDaily = Options(rawValue: 1 << 2)
let rawValue: Int
}
}
// We can now use Cache.Options just like UIViewAnimationOptions:
Cache(options: .saveToDisk)
Cache(options: [.saveToDisk, .clearDaily])
🙌 Using the where
clause when designing protocol-oriented APIs in Swift can let your implementations (or others' if it's open source) have a lot more freedom, especially when it comes to collections.
See "Using generic type constraints in Swift 4" for more info.
public protocol PathFinderMap {
associatedtype Node
// Using the 'where' clause for associated types, we can
// ensure that a type meets certain requirements (in this
// case that it's a sequence with Node elements).
associatedtype NodeSequence: Sequence where NodeSequence.Element == Node
// Instead of using a concrete type (like [Node]) here, we
// give implementors of this protocol more freedom while
// still meeting our requirements. For example, one
// implementation might use Set<Node>.
func neighbors(of node: Node) -> NodeSequence
}
👨🍳 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.
func makeActor(at coordinate: Coordinate, for building: Building) -> Actor {
let actor = Actor()
actor.position = coordinate.point
actor.animation = building.animation
return actor
}
func render(_ buildings: [Coordinate : Building]) {
buildings.map(makeActor).forEach(add)
}
😎 In Swift, you can call any instance method as a static function and it will return a closure representing that method. This is how running tests using SPM on Linux works.
More about this topic in my blog post "First class functions in Swift".
// 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:
extension UserManagerTests {
static var 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.
extension UIView {
func add(_ subviews: UIView...) {
subviews.forEach(addSubview)
}
}
view.add(button)
view.add(label)
// By dropping the "Subview" suffix from the method name, both
// single and multiple arguments work really well semantically.
view.add(button, label)
👽 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.
protocol DataContainer: AnyObject {
var data: Data? { get set }
}
class UserSettingsManager {
private var settings: Settings
private let dataContainer: DataContainer
// Since DataContainer is a protocol, we an easily mock it in
// tests if we use dependency injection
init(settings: Settings, dataContainer: DataContainer) {
self.settings = settings
self.dataContainer = dataContainer
}
func saveSettings() 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.
extension Building {
// This enum has custom raw values that are used when decoding
// a value, for example from JSON.
enum Kind: String {
case castle = "C"
case town = "T"
case barracks = "B"
case goldMine = "G"
case camp = "CA"
case blacksmith = "BL"
}
var animation: Animation {
return Animation(
// 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
)
}
}
👨🔬 Continuing to experiment with expressive ways of comparing a value with a list of candidates in Swift. Adding an extension on Equatable is probably my favorite approach so far.
extension Equatable {
func isAny(of candidates: Self...) -> Bool {
return candidates.contains(self)
}
}
let isHorizontal = direction.isAny(of: .left, .right)
See tip 35 for my previous experiment.
📐 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.
let view = UIView()
view.frame.size = CGSize(width: 100, height: 100)
view.transform = CGAffineTransform(scaleX: 2, y: 2)
print(view.frame) // (-50.0, -50.0, 200.0, 200.0)
print(view.bounds) // (0.0, 0.0, 100.0, 100.0)
👏 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!
// BEFORE: All parameters are specified, just like in Objective-C
viewController.present(modalViewController, animated: true, completion: nil)
modalViewController.dismiss(animated: true, completion: nil)
viewController.transition(from: loadingViewController,
to: contentViewController,
duration: 0.3,
options: [],
animations: animations,
completion: nil)
// AFTER: Since many UIKit APIs with completion handlers and other
// optional parameters import into Swift with default arguments,
// we can make our calls shorter
viewController.present(modalViewController, animated: true)
modalViewController.dismiss(animated: true)
viewController.transition(from: loadingViewController,
to: contentViewController,
duration: 0.3,
animations: animations)
✂️ Avoiding Massive View Controllers is all about finding the right levels of abstraction and splitting things up.
My personal rule of thumb is that as soon as I have 3 methods or properties that have the same prefix, I break them out into their own type.
// BEFORE
class LoginViewController: UIViewController {
private lazy var signUpLabel = UILabel()
private lazy var signUpImageView = UIImageView()
private lazy var signUpButton = UIButton()
}
// AFTER
class LoginViewController: UIViewController {
private lazy var signUpView = SignUpView()
}
class SignUpView: UIView {
private lazy var label = UILabel()
private lazy var imageView = UIImageView()
private lazy var button = UIButton()
}
❤️ 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.
func validateTextFields() -> Bool {
guard !usernameTextField.text.isNilOrEmpty else {
return false
}
...
return true
}
// Since all optionals are actual enum values in Swift, we can easily
// extend them for certain types, to add our own convenience APIs
extension Optional where Wrapped == String {
var isNilOrEmpty: Bool {
switch self {
case let string?:
return string.isEmpty
case nil:
return true
}
}
}
// Since strings are now Collections in Swift 4, you can even
// add this property to all optional collections:
extension Optional where Wrapped: Collection {
var isNilOrEmpty: Bool {
switch self {
case let collection?:
return collection.isEmpty
case nil:
return true
}
}
}
🗺 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.
func archiveMarkedPosts() {
for post in posts where post.isMarked {
archive(post)
}
}
func healAllies() {
for player in players where player.isAllied(to: currentPlayer) {
player.heal()
}
}
👻 Variable shadowing can be super useful in Swift, especially when you want to create a local copy of a parameter value in order to use it as state within a closure.
init(repeatMode: RepeatMode, closure: @escaping () -> UpdateOutcome) {
// Shadow the argument with a local, mutable copy
var repeatMode = repeatMode
self.closure = {
// With shadowing, there's no risk of accidentially
// referring to the immutable version
switch repeatMode {
case .forever:
break
case .times(let count):
guard count > 0 else {
return .finished
}
// We can now capture the mutable version and use
// it for state in a closure
repeatMode = .times(count - 1)
}
return closure()
}
}
✒️ 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.
public enum RepeatMode {
case times(Int)
case forever
}
public extension RepeatMode {
static var never: RepeatMode {
return .times(0)
}
static var once: RepeatMode {
return .times(1)
}
}
view.perform(animation, repeated: .once)
// To make default parameters more compact, you can even use init with dot syntax
class ImageLoader {
init(cache: Cache = .init(), decoder: ImageDecoder = .init()) {
...
}
}
🚀 One really cool aspect of Swift having first class functions is that you can pass any function (or even initializer) as a closure, and even call it with a tuple containing its parameters!
// This function lets us treat any "normal" function or method as
// a closure and run it with a tuple that contains its parameters
func call<Input, Output>(_ function: (Input) -> Output, with input: Input) -> Output {
return function(input)
}
class ViewFactory {
func makeHeaderView() -> HeaderView {
// We can now pass an initializer as a closure, and a tuple
// containing its parameters
return call(HeaderView.init, with: loadTextStyles())
}
private func loadTextStyles() -> (font: UIFont, color: UIColor) {
return (theme.font, theme.textColor)
}
}
class HeaderView {
init(font: UIFont, textColor: UIColor) {
...
}
}
💉 If you've been struggling to test code that uses static APIs, here's a technique you can use to enable static dependency injection without having to modify any call sites:
// Before: Almost impossible to test due to the use of singletons
class Analytics {
static func log(_ event: Event) {
Database.shared.save(event)
let dictionary = event.serialize()
NetworkManager.shared.post(dictionary, to: eventURL)
}
}
// After: Much easier to test, since we can inject mocks as arguments
class Analytics {
static func log(_ event: Event,
database: Database = .shared,
networkManager: NetworkManager = .shared) {
database.save(event)
let dictionary = event.serialize()
networkManager.post(dictionary, to: eventURL)
}
}
🎉 In Swift 4, type inference works for lazy properties and you don't need to explicitly refer to self
!
// Swift 3
class PurchaseView: UIView {
private lazy var buyButton: UIButton = self.makeBuyButton()
private func makeBuyButton() -> UIButton {
let button = UIButton()
button.setTitle("Buy", for: .normal)
button.setTitleColor(.blue, for: .normal)
return button
}
}
// Swift 4
class PurchaseView: UIView {
private lazy var buyButton = makeBuyButton()
private func makeBuyButton() -> UIButton {
let button = UIButton()
button.setTitle("Buy", for: .normal)
button.setTitleColor(.blue, for: .normal)
return button
}
}
😎 You can turn any Swift Error
into an NSError
, which is super useful when pattern matching with a code 👍. Also, switching on optionals is pretty cool!
let task = urlSession.dataTask(with: url) { data, _, error in
switch error {
case .some(let error as NSError) where error.code == NSURLErrorNotConnectedToInternet:
presenter.showOfflineView()
case .some(let error):
presenter.showGenericErrorView()
case .none:
presenter.renderContent(from: data)
}
}
task.resume()
Also make sure to check out Kostas Kremizas' tip about how you can pattern match directly against a member of URLError
.
🖥 Here's an easy way to make iOS model code that uses UIImage
macOS compatible - like me and Gui Rambo discussed on the Swift by Sundell Podcast.
// Either put this in a separate file that you only include in your macOS target or wrap the code in #if os(macOS) / #endif
import Cocoa
// Step 1: Typealias UIImage to NSImage
typealias UIImage = NSImage
// Step 2: You might want to add these APIs that UIImage has but NSImage doesn't.
extension NSImage {
var cgImage: CGImage? {
var proposedRect = CGRect(origin: .zero, size: size)
return cgImage(forProposedRect: &proposedRect,
context: nil,
hints: nil)
}
convenience init?(named name: String) {
self.init(named: Name(name))
}
}
// Step 3: Profit - you can now make your model code that uses UIImage cross-platform!
struct User {
let name: String
let profileImage: UIImage
}
🤖 You can easily define a protocol-oriented API that can only be mutated internally, by using an internal protocol that extends a public one.
// Declare a public protocol that acts as your immutable API
public protocol ModelHolder {
associatedtype Model
var model: Model { get }
}
// Declare an extended, internal protocol that provides a mutable API
internal protocol MutableModelHolder: ModelHolder {
var model: Model { get set }
}
// You can now implement the requirements using 'public internal(set)'
public class UserHolder: MutableModelHolder {
public internal(set) var model: User
internal init(model: User) {
self.model = model
}
}
🎛 You can switch on a set using array literals as cases in Swift! Can be really useful to avoid many if
/else if
statements.
class RoadTile: Tile {
var connectedDirections = Set<Direction>()
func render() {
switch connectedDirections {
case [.up, .down]:
image = UIImage(named: "road-vertical")
case [.left, .right]:
image = UIImage(named: "road-horizontal")
default:
image = UIImage(named: "road")
}
}
}
🌍 When caching localized content in an app, it's a good idea to add the current locale to all keys, to prevent bugs when switching languages.
func cache(_ content: Content, forKey key: String) throws {
let data = try wrap(content) as Data
let key = localize(key: key)
try storage.store(data, forKey: key)
}
func loadCachedContent(forKey key: String) -> Content? {
let key = localize(key: key)
let data = storage.loadData(forKey: key)
return data.flatMap { try? unbox(data: $0) }
}
private func localize(key: String) -> String {
return key + "-" + Bundle.main.preferredLocalizations[0]
}
🚳 Here's an easy way to setup a test to avoid accidental retain cycles with object relationships (like weak delegates & observers) in Swift:
func testDelegateNotRetained() {
// Assign the delegate (weak) and also retain it using a local var
var delegate: Delegate? = DelegateMock()
controller.delegate = delegate
XCTAssertNotNil(controller.delegate)
// Release the local var, which should also release the weak reference
delegate = nil
XCTAssertNil(controller.delegate)
}
👨🔬 Playing around with an expressive way to check if a value matches any of a list of candidates in Swift:
// Instead of multiple conditions like this:
if string == "One" || string == "Two" || string == "Three" {
}
// You can now do:
if string == any(of: "One", "Two", "Three") {
}
You can find a gist with the implementation here.
👪 APIs in a Swift extension automatically inherit its access control level, making it a neat way to organize public, internal & private APIs.
public extension Animation {
init(textureNamed textureName: String) {
frames = [Texture(name: textureName)]
}
init(texturesNamed textureNames: [String], frameDuration: TimeInterval = 1) {
frames = textureNames.map(Texture.init)
self.frameDuration = frameDuration
}
init(image: Image) {
frames = [Texture(image: image)]
}
}
internal extension Animation {
func loadFrameImages() -> [Image] {
return frames.map { $0.loadImageIfNeeded() }
}
}
🗺 Using map
you can transform an optional value into an optional Result
type by simply passing in the enum case.
enum Result<Value> {
case value(Value)
case error(Error)
}
class Promise<Value> {
private var result: Result<Value>?
init(value: Value? = nil) {
result = value.map(Result.value)
}
}
👌 It's so nice that you can assign directly to self
in struct
initializers in Swift. Very useful when adding conformance to protocols.
extension Bool: AnswerConvertible {
public init(input: String) throws {
switch input.lowercased() {
case "y", "yes", "👍":
self = true
default:
self = false
}
}
}
☎️ Defining Swift closures as inline functions enables you to recursively call them, which is super useful in things like custom sequences.
class Database {
func records(matching query: Query) -> AnySequence<Record> {
var recordIterator = loadRecords().makeIterator()
func iterate() -> Record? {
guard let nextRecord = recordIterator.next() else {
return nil
}
guard nextRecord.matches(query) else {
// Since the closure is an inline function, it can be recursively called,
// in this case in order to advance to the next item.
return iterate()
}
return nextRecord
}
// AnySequence/AnyIterator are part of the standard library and provide an easy way
// to define custom sequences using closures.
return AnySequence { AnyIterator(iterate) }
}
}
Rob Napier points out that using the above might cause crashes if used on a large databaset, since Swift has no guaranteed Tail Call Optimization (TCO).
Slava Pestov also points out that another benefit of inline functions vs closures is that they can have their own generic parameter list.
🏖 Using lazy properties in Swift, you can pass self
to required Objective-C dependencies without having to use force-unwrapped optionals.
class DataLoader: NSObject {
lazy var urlSession: URLSession = self.makeURLSession()
private func makeURLSession() -> URLSession {
return URLSession(configuration: .default, delegate: self, delegateQueue: .main)
}
}
class Renderer {
lazy var displayLink: CADisplayLink = self.makeDisplayLink()
private func makeDisplayLink() -> CADisplayLink {
return CADisplayLink(target: self, selector: #selector(screenDidRefresh))
}
}
👓 If you have a property in Swift that needs to be weak
or lazy
, you can still make it readonly by using private(set)
.
class Node {
private(set) weak var parent: Node?
private(set) lazy var children = [Node]()
func add(child: Node) {
children.append(child)
child.parent = self
}
}
🌏 Tired of using URL(string: "url")!
for static URLs? Make URL
conform to ExpressibleByStringLiteral
and you can now simply use "url"
instead.
extension URL: ExpressibleByStringLiteral {
// By using 'StaticString' we disable string interpolation, for safety
public init(stringLiteral value: StaticString) {
self = URL(string: "\(value)").require(hint: "Invalid URL string literal: \(value)")
}
}
// We can now define URLs using static string literals 🎉
let url: URL = "https://www.swiftbysundell.com"
let task = URLSession.shared.dataTask(with: "https://www.swiftbysundell.com")
// In Swift 3 or earlier, you also have to implement 2 additional initializers
extension URL {
public init(extendedGraphemeClusterLiteral value: StaticString) {
self.init(stringLiteral: value)
}
public init(unicodeScalarLiteral value: StaticString) {
self.init(stringLiteral: value)
}
}
To find the extension that adds the require()
method on Optional
that I use above, check out Require.
✚ I'm always careful with operator overloading, but for manipulating things like sizes, points & frames I find them super useful.
extension CGSize {
static func *(lhs: CGSize, rhs: CGFloat) -> CGSize {
return CGSize(width: lhs.width * rhs, height: lhs.height * rhs)
}
}
button.frame.size = image.size * 2
If you like the above idea, check out CGOperators, which contains math operator overloads for all Core Graphics' vector types.
🔗 You can use closure types in generic constraints in Swift. Enables nice APIs for handling sequences of closures.
extension Sequence where Element == () -> Void {
func callAll() {
forEach { $0() }
}
}
extension Sequence where Element == () -> String {
func joinedResults(separator: String) -> String {
return map { $0() }.joined(separator: separator)
}
}
callbacks.callAll()
let names = nameProviders.joinedResults(separator: ", ")
(If you're using Swift 3, you have to change Element
to Iterator.Element
)
🎉 Using associated enum values is a super nice way to encapsulate mutually exclusive state info (and avoiding state-specific optionals).
// BEFORE: Lots of state-specific, optional properties
class Player {
var isWaitingForMatchMaking: Bool
var invitingUser: User?
var numberOfLives: Int
var playerDefeatedBy: Player?
var roundDefeatedIn: Int?
}
// AFTER: All state-specific information is encapsulated in enum cases
class Player {
enum State {
case waitingForMatchMaking
case waitingForInviteResponse(from: User)
case active(numberOfLives: Int)
case defeated(by: Player, roundNumber: Int)
}
var state: State
}
👍 I really like using enums for all async result types, even boolean ones. Self-documenting, and makes the call site a lot nicer to read too!
protocol PushNotificationService {
// Before
func enablePushNotifications(completionHandler: @escaping (Bool) -> Void)
// After
func enablePushNotifications(completionHandler: @escaping (PushNotificationStatus) -> Void)
}
enum PushNotificationStatus {
case enabled
case disabled
}
service.enablePushNotifications { status in
if status == .enabled {
enableNotificationsButton.removeFromSuperview()
}
}
🏃 Want to work on your async code in a Swift Playground? Just set needsIndefiniteExecution
to true to keep it running:
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
let greeting = "Hello after 3 seconds"
print(greeting)
}
To stop the playground from executing, simply call PlaygroundPage.current.finishExecution()
.
💦 Avoid memory leaks when accidentially refering to self
in closures by overriding it locally with a weak reference:
Swift >= 4.2
dataLoader.loadData(from: url) { [weak self] result in
guard let self = self else {
return
}
self.cache(result)
...
Swift < 4.2
dataLoader.loadData(from: url) { [weak self] result in
guard let `self` = self else {
return
}
self.cache(result)
...
Note that the reason the above currently works is because of a compiler bug (which I hope gets turned into a properly supported feature soon).
🕓 Using dispatch work items you can easily cancel a delayed asynchronous GCD task if you no longer need it:
let workItem = DispatchWorkItem {
// Your async code goes in here
}
// Execute the work item after 1 second
DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: workItem)
// You can cancel the work item if you no longer need it
workItem.cancel()
➕ While working on a new Swift developer tool (to be open sourced soon 😉), I came up with a pretty neat way of organizing its sequence of operations, by combining their functions into a closure:
internal func +<A, B, C>(lhs: @escaping (A) throws -> B,
rhs: @escaping (B) throws -> C) -> (A) throws -> C {
return { try rhs(lhs($0)) }
}
public func run() throws {
try (determineTarget + build + analyze + output)()
}
If you're familiar with the functional programming world, you might know the above technique as the pipe operator (thanks to Alexey Demedreckiy for pointing this out!)
🗺 Using map()
and flatMap()
on optionals you can chain multiple operations without having to use lengthy if lets
or guards
:
// BEFORE
guard let string = argument(at: 1) else {
return
}
guard let url = URL(string: string) else {
return
}
handle(url)
// AFTER
argument(at: 1).flatMap(URL.init).map(handle)
🚀 Using self-executing closures is a great way to encapsulate lazy property initialization:
class StoreViewController: UIViewController {
private lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
let view = UICollectionView(frame: self.view.bounds, collectionViewLayout: layout)
view.delegate = self
view.dataSource = self
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(collectionView)
}
}
⚡️ You can speed up your Swift package tests using the --parallel
flag. For Marathon, the tests execute 3 times faster that way!
swift test --parallel
🛠 Struggling with mocking UserDefaults
in a test? The good news is: you don't need mocking - just create a real instance:
class LoginTests: XCTestCase {
private var userDefaults: UserDefaults!
private var manager: LoginManager!
override func setUp() {
super.setup()
userDefaults = UserDefaults(suiteName: #file)
userDefaults.removePersistentDomain(forName: #file)
manager = LoginManager(userDefaults: userDefaults)
}
}
👍 Using variadic parameters in Swift, you can create some really nice APIs that take a list of objects without having to use an array:
extension Canvas {
func add(_ shapes: Shape...) {
shapes.forEach(add)
}
}
let circle = Circle(center: CGPoint(x: 5, y: 5), radius: 5)
let lineA = Line(start: .zero, end: CGPoint(x: 10, y: 10))
let lineB = Line(start: CGPoint(x: 0, y: 10), end: CGPoint(x: 10, y: 0))
let canvas = Canvas()
canvas.add(circle, lineA, lineB)
canvas.render()
😮 Just like you can refer to a Swift function as a closure, you can do the same thing with enum cases with associated values:
enum UnboxPath {
case key(String)
case keyPath(String)
}
struct UserSchema {
static let name = key("name")
static let age = key("age")
static let posts = key("posts")
private static let key = UnboxPath.key
}
📈 The ===
operator lets you check if two objects are the same instance. Very useful when verifying that an array contains an instance in a test:
protocol InstanceEquatable: class, Equatable {}
extension InstanceEquatable {
static func ==(lhs: Self, rhs: Self) -> Bool {
return lhs === rhs
}
}
extension Enemy: InstanceEquatable {}
func testDestroyingEnemy() {
player.attack(enemy)
XCTAssertTrue(player.destroyedEnemies.contains(enemy))
}
😎 Cool thing about Swift initializers: you can call them using dot syntax and pass them as closures! Perfect for mocking dates in tests.
class Logger {
private let storage: LogStorage
private let dateProvider: () -> Date
init(storage: LogStorage = .init(), dateProvider: @escaping () -> Date = Date.init) {
self.storage = storage
self.dateProvider = dateProvider
}
func log(event: Event) {
storage.store(event: event, date: dateProvider())
}
}
📱 Most of my UI testing logic is now categories on XCUIApplication
. Makes the test cases really easy to read:
func testLoggingInAndOut() {
XCTAssertFalse(app.userIsLoggedIn)
app.launch()
app.login()
XCTAssertTrue(app.userIsLoggedIn)
app.logout()
XCTAssertFalse(app.userIsLoggedIn)
}
func testDisplayingCategories() {
XCTAssertFalse(app.isDisplayingCategories)
app.launch()
app.login()
app.goToCategories()
XCTAssertTrue(app.isDisplayingCategories)
}
🙂 It’s a good idea to avoid “default” cases when switching on Swift enums - it’ll “force you” to update your logic when a new case is added:
enum State {
case loggedIn
case loggedOut
case onboarding
}
func handle(_ state: State) {
switch state {
case .loggedIn:
showMainUI()
case .loggedOut:
showLoginUI()
// Compiler error: Switch must be exhaustive
}
}
💂 It's really cool that you can use Swift's 'guard' statement to exit out of pretty much any scope, not only return from functions:
// You can use the 'guard' statement to...
for string in strings {
// ...continue an iteration
guard shouldProcess(string) else {
continue
}
// ...or break it
guard !shouldBreak(for: string) else {
break
}
// ...or return
guard !shouldReturn(for: string) else {
return
}
// ..or throw an error
guard string.isValid else {
throw StringError.invalid(string)
}
// ...or exit the program
guard !shouldExit(for: string) else {
exit(1)
}
}
❤️ Love how you can pass functions & operators as closures in Swift. For example, it makes the syntax for sorting arrays really nice!
let array = [3, 9, 1, 4, 6, 2]
let sorted = array.sorted(by: <)
🗝 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.
extension UserDefaults {
var onboardingCompleted: Bool {
get { return bool(forKey: #function) }
set { set(newValue, forKey: #function) }
}
}
📛 Want to use a name already taken by the standard library for a nested type? No problem - just use Swift.
to disambiguate:
extension Command {
enum Error: Swift.Error {
case missing
case invalid(String)
}
}
📦 Playing around with using Wrap to implement Equatable
for any type, primarily for testing:
protocol AutoEquatable: Equatable {}
extension AutoEquatable {
static func ==(lhs: Self, rhs: Self) -> Bool {
let lhsData = try! wrap(lhs) as Data
let rhsData = try! wrap(rhs) as Data
return lhsData == rhsData
}
}
📏 One thing that I find really useful in Swift is to use typealiases to reduce the length of method signatures in generic types:
public class PathFinder<Object: PathFinderObject> {
public typealias Map = Object.Map
public typealias Node = Map.Node
public typealias Path = PathFinderPath<Object>
public static func possiblePaths(for object: Object, at rootNode: Node, on map: Map) -> Path.Sequence {
return .init(object: object, rootNode: rootNode, map: map)
}
}
📖 You can reference either the external or internal parameter label when writing Swift docs - and they get parsed the same:
// EITHER:
class Foo {
/**
* - parameter string: A string
*/
func bar(with string: String) {}
}
// OR:
class Foo {
/**
* - parameter with: A string
*/
func bar(with string: String) {}
}
👍 Finding more and more uses for auto closures in Swift. Can enable some pretty nice APIs:
extension Dictionary {
mutating func value(for key: Key, orAdd valueClosure: @autoclosure () -> Value) -> Value {
if let value = self[key] {
return value
}
let value = valueClosure()
self[key] = value
return value
}
}
🚀 I’ve started to become a really big fan of nested types in Swift. Love the additional namespacing it gives you!
public struct Map {
public struct Model {
public let size: Size
public let theme: Theme
public var terrain: [Position : Terrain.Model]
public var units: [Position : Unit.Model]
public var buildings: [Position : Building.Model]
}
public enum Direction {
case up
case right
case down
case left
}
public struct Position {
public var x: Int
public var y: Int
}
public enum Size: String {
case small = "S"
case medium = "M"
case large = "L"
case extraLarge = "XL"
}
}
Author: JohnSundell
Source Code: https://github.com/JohnSundell/SwiftTips
License: MIT license
1661592007
⚠️ This list is no longer being updated. For my latest Swift tips, checkout the "Tips" section on Swift by Sundell.
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! 🚀
Also make sure to check out all of my other Swift content:
🚀 Here are some quick tips to make async tests faster & more stable:
// BEFORE:
class MentionDetectorTests: XCTestCase {
func testDetectingMention() {
let detector = MentionDetector()
let string = "This test was written by @johnsundell."
detector.detectMentions(in: string) { mentions in
XCTAssertEqual(mentions, ["johnsundell"])
}
sleep(2)
}
}
// AFTER:
class MentionDetectorTests: XCTestCase {
func testDetectingMention() {
let detector = MentionDetector()
let string = "This test was written by @johnsundell."
var mentions: [String]?
let expectation = self.expectation(description: #function)
detector.detectMentions(in: string) {
mentions = $0
expectation.fulfill()
}
waitForExpectations(timeout: 10)
XCTAssertEqual(mentions, ["johnsundell"])
}
}
For more on async testing, check out "Unit testing asynchronous Swift code".
✍️ 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.
let interaction = UIPencilInteraction()
interaction.delegate = self
view.addInteraction(interaction)
extension ViewController: UIPencilInteractionDelegate {
func pencilInteractionDidTap(_ interaction: UIPencilInteraction) {
// Handle pencil double-tap
}
}
For more on using this and other iPad Pro features, check out "Building iPad Pro features in Swift".
😎 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
.
func combine<A, B>(_ value: A, with closure: @escaping (A) -> B) -> () -> B {
return { closure(value) }
}
// BEFORE:
class ProductViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
buyButton.handler = { [weak self] in
guard let self = self else {
return
}
self.productManager.startCheckout(for: self.product)
}
}
}
// AFTER:
class ProductViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
buyButton.handler = combine(product, with: productManager.startCheckout)
}
}
💉 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.
final class ArticleLoader {
typealias Networking = (Endpoint) -> Future<Data>
private let networking: Networking
init(networking: @escaping Networking = URLSession.shared.load) {
self.networking = networking
}
func loadLatest() -> Future<[Article]> {
return networking(.latestArticles).decode()
}
}
For more on this technique, check out "Simple Swift dependency injection with functions".
💥 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.
NSSetUncaughtExceptionHandler { exception in
print(exception)
}
❤️ 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.
extension List.Item {
// Using type aliases, we can give semantic meaning to the
// primitive types that we use, without having to introduce
// wrapper types.
typealias Index = Int
}
extension List {
enum Mutation {
// Our enum cases now become a lot more self-documenting,
// without having to add additional parameter labels to
// explain them.
case add(Item, Item.Index)
case update(Item, Item.Index)
case remove(Item.Index)
}
}
For more on self-documenting code, check out "Writing self-documenting Swift code".
🤯 A little late night prototyping session reveals that protocol constraints can not only be applied to extensions - they can also be added to protocol definitions!
This is awesome, since it lets us easily define specialized protocols based on more generic ones.
protocol Component {
associatedtype Container
func add(to container: Container)
}
// Protocols that inherit from other protocols can include
// constraints to further specialize them.
protocol ViewComponent: Component where Container == UIView {
associatedtype View: UIView
var view: View { get }
}
extension ViewComponent {
func add(to container: UIView) {
container.addSubview(view)
}
}
For more on specializing protocols, check out "Specializing protocols in Swift".
📦 Here's a super handy extension on Swift's Optional
type, which gives us a really nice API for easily unwrapping an optional, or throwing an error in case the value turned out to be nil
:
extension Optional {
func orThrow(_ errorExpression: @autoclosure () -> Error) throws -> Wrapped {
switch self {
case .some(let value):
return value
case .none:
throw errorExpression()
}
}
}
let file = try loadFile(at: path).orThrow(MissingFileError())
For more ways that optionals can be extended, check out "Extending optionals in Swift".
👩🔬 Testing code that uses static APIs can be really tricky, but there's a way that it can often be done - using Swift's first class function capabilities!
Instead of accessing that static API directly, we can inject the function we want to use, which enables us to mock it!
// BEFORE
class FriendsLoader {
func loadFriends(then handler: @escaping (Result<[Friend]>) -> Void) {
Networking.loadData(from: .friends) { result in
...
}
}
}
// AFTER
class FriendsLoader {
typealias Handler<T> = (Result<T>) -> Void
typealias DataLoadingFunction = (Endpoint, @escaping Handler<Data>) -> Void
func loadFriends(using dataLoading: DataLoadingFunction = Networking.loadData,
then handler: @escaping Handler<[Friend]>) {
dataLoading(.friends) { result in
...
}
}
}
// MOCKING IN TESTS
let dataLoading: FriendsLoader.DataLoadingFunction = { _, handler in
handler(.success(mockData))
}
friendsLoader.loadFriends(using: dataLoading) { result in
...
}
🐾 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.
enum DownloadState {
case inProgress(progress: Double)
case paused(progress: Double)
case cancelled
case finished(Data)
}
func downloadStateDidChange(to state: DownloadState) {
switch state {
case .inProgress(let progress), .paused(let progress):
updateProgressView(with: progress)
case .cancelled:
showCancelledMessage()
case .finished(let data):
process(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.
let html = highlighter.highlight("Array<String>")
XCTAssertEqual(html, """
<span class="type">Array</span><<span class="type">String</span>>
""")
💎 While it's very common in functional programming, the reduce
function might be a bit of a hidden gem in Swift. It provides a super useful way to transform a sequence into a single value.
extension Sequence where Element: Equatable {
func numberOfOccurrences(of target: Element) -> Int {
return reduce(0) { result, element in
guard element == target else {
return result
}
return result + 1
}
}
}
You can read more about transforming collections in "Transforming collections in Swift".
📦 When I use Codable in Swift, I want to avoid manual implementations as much as possible, even when there's a mismatch between my code structure and the JSON I'm decoding.
One way that can often be achieved is to use private data containers combined with computed properties.
struct User: Codable {
let name: String
let age: Int
var homeTown: String { return originPlace.name }
private let originPlace: Place
}
private extension User {
struct Place: Codable {
let name: String
}
}
extension User {
struct Container: Codable {
let user: User
}
}
🚢 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!
extension ListViewController {
func addSearchIfNeeded() {
// Rather than having to keep maintaining a separate
// feature branch for a new feature, we can use a flag
// to conditionally turn it on.
guard FeatureFlags.searchEnabled else {
return
}
let resultsVC = SearchResultsViewController()
let searchVC = UISearchController(
searchResultsController: resultsVC
)
searchVC.searchResultsUpdater = resultsVC
navigationItem.searchController = searchVC
}
}
You can read more about feature flags in "Feature flags in Swift".
💾 Here I'm using tuples to create a lightweight hierarchy for my data, giving me a nice structure without having to introduce any additional types.
struct CodeSegment {
var tokens: (
previous: String?,
current: String
)
var delimiters: (
previous: Character?
next: Character?
)
}
handle(segment.tokens.current)
You can read more about tuples in "Using tuples as lightweight types in Swift"
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.
Before
public func generate() throws {
let contentFolder = try folder.subfolder(named: "content")
let articleFolder = try contentFolder.subfolder(named: "posts")
let articleProcessor = ContentProcessor(folder: articleFolder)
let articles = try articleProcessor.process()
...
}
After
public func generate() throws {
let contentFolder = try folder.subfolder(named: "content")
let articles = try processArticles(in: contentFolder)
...
}
private func processArticles(in folder: Folder) throws -> [ContentItem] {
let folder = try folder.subfolder(named: "posts")
let processor = ContentProcessor(folder: folder)
return try processor.process()
}
👨🔧 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.
extension Encodable {
func encoded() throws -> Data {
return try JSONEncoder().encode(self)
}
}
extension Data {
func decoded<T: Decodable>() throws -> T {
return try JSONDecoder().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.
try userDidLogin(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")
🎨 By overriding layerClass
you can tell UIKit what CALayer
class to use for a UIView
's backing layer. That way you can reduce the amount of layers, and don't have to do any manual layout.
final class GradientView: UIView {
override class var layerClass: AnyClass { return CAGradientLayer.self }
var colors: (start: UIColor, end: UIColor)? {
didSet { updateLayer() }
}
private func updateLayer() {
let layer = self.layer as! CAGradientLayer
layer.colors = colors.map { [$0.start.cgColor, $0.end.cgColor] }
}
}
✅ 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.
struct Article: Equatable {
let title: String
let text: String
}
struct User: Equatable {
let name: String
let age: Int
}
extension Navigator {
enum Destination: Equatable {
case profile(User)
case article(Article)
}
}
func testNavigatingToArticle() {
let article = Article(title: "Title", text: "Text")
controller.select(article)
XCTAssertEqual(navigator.destinations, [.article(article)])
}
🤝 Associated types can have defaults in Swift - which is super useful for types that are not easily inferred (for example when they're not used for a specific instance method or property).
protocol Identifiable {
associatedtype RawIdentifier: Codable = String
var id: Identifier<Self> { get }
}
struct User: Identifiable {
let id: Identifier<User>
let name: String
}
struct Group: Identifiable {
typealias RawIdentifier = Int
let id: Identifier<Group>
let name: String
}
🆔 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!
More on this topic in "Type-safe identifiers in Swift".
struct Identifier: Hashable {
let string: String
}
extension Identifier: ExpressibleByStringLiteral {
init(stringLiteral value: String) {
string = value
}
}
extension Identifier: CustomStringConvertible {
var description: String {
return string
}
}
extension Identifier: Codable {
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
string = try container.decode(String.self)
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(string)
}
}
struct Article: Codable {
let id: Identifier
let title: String
}
let article = Article(id: "my-article", title: "Hello world!")
🙌 A really cool thing about using tuples to model the internal state of a Swift type, is that you can unwrap an optional tuple's members directly into local variables.
Very useful in order to group multiple optional values together for easy unwrapping & handling.
class ImageTransformer {
private var queue = [(image: UIImage, transform: Transform)]()
private func processNext() {
// When unwrapping an optional tuple, you can assign the members
// directly to local variables.
guard let (image, transform) = queue.first else {
return
}
let context = Context()
context.draw(image)
context.apply(transform)
...
}
}
❤️ 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!
struct Article {
let date: Date
var title: String
var text: String
var comments: [Comment]
}
extension Article {
init(title: String, text: String) {
self.init(date: Date(), title: title, text: text, comments: [])
}
}
let articleA = Article(title: "Best Cupcake Recipe", text: "...")
let articleB = Article(
date: Date(),
title: "Best Cupcake Recipe",
text: "...",
comments: [
Comment(user: currentUser, text: "Yep, can confirm!")
]
)
🏈 A big benefit of using throwing functions for synchronous Swift APIs is that the caller can decide whether they want to treat the return value as optional (try?
) or required (try
).
func loadFile(named name: String) throws -> File {
guard let url = urlForFile(named: name) else {
throw File.Error.missing
}
do {
let data = try Data(contentsOf: url)
return File(url: url, data: data)
} catch {
throw File.Error.invalidData(error)
}
}
let requiredFile = try loadFile(named: "AppConfig.json")
let optionalFile = try? loadFile(named: "UserSettings.json")
🐝 Types that are nested in generics automatically inherit their parent's generic types - which is super useful when defining accessory types (for things like states or outcomes).
struct Task<Input, Output> {
typealias Closure = (Input) throws -> Output
let closure: Closure
}
extension Task {
enum Result {
case success(Output)
case failure(Error)
}
}
🤖 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
!
typealias Value = Hashable & Codable
struct User: Value {
var name: String
var age: Int
var lastLoginDate: Date?
var settings: Settings
}
extension User {
struct Settings: Value {
var itemsPerPage: Int
var theme: Theme
}
}
extension User.Settings {
enum Theme: String, Value {
case light
case dark
}
}
You can read more about using nested types in Swift here.
🎉 Swift 4.1 is here! One of the key features it brings is conditional conformances, which lets you have a type only conform to a protocol under certain constraints.
protocol UnboxTransformable {
associatedtype RawValue
static func transform(_ value: RawValue) throws -> Self?
}
extension Array: UnboxTransformable where Element: UnboxTransformable {
typealias RawValue = [Element.RawValue]
static func transform(_ value: RawValue) throws -> [Element]? {
return try value.compactMap(Element.transform)
}
}
I also have an article with lots of more info on conditional conformances here. Paul Hudson also has a great overview of all Swift 4.1 features here.
🕵️♀️ A cool thing about Swift type aliases is that they can be generic! Combine that with tuples and you can easily define simple generic types.
typealias Pair<T> = (T, T)
extension Game {
func calculateScore(for players: Pair<Player>) -> Int {
...
}
}
You can read more about using tuples as lightweight types here.
☑️ A really cool "hidden" feature of UserDefaults is that it contains any arguments that were passed to the app at launch!
Super useful both in Swift command line tools & scripts, but also to temporarily override a value when debugging iOS apps.
let defaults = UserDefaults.standard
let query = defaults.string(forKey: "query")
let resultCount = defaults.integer(forKey: "results")
👏 Swift's &
operator is awesome! Not only can you use it to compose protocols, you can compose other types too! Very useful if you want to hide concrete types & implementation details.
protocol LoadableFromURL {
func load(from url: URL)
}
class ContentViewController: UIViewController, LoadableFromURL {
func load(from url: URL) {
...
}
}
class ViewControllerFactory {
func makeContentViewController() -> UIViewController & LoadableFromURL {
return ContentViewController()
}
}
🤗 When capturing values in mocks, using an array (instead of just a single value) makes it easy to verify that only a certain number of values were passed.
Perfect for protecting against "over-calling" something.
class UserManagerTests: XCTestCase {
func testObserversCalledWhenUserFirstLogsIn() {
let manager = UserManager()
let observer = ObserverMock()
manager.addObserver(observer)
// First login, observers should be notified
let user = User(id: 123, name: "John")
manager.userDidLogin(user)
XCTAssertEqual(observer.users, [user])
// If the same user logs in again, observers shouldn't be notified
manager.userDidLogin(user)
XCTAssertEqual(observer.users, [user])
}
}
private extension UserManagerTests {
class ObserverMock: UserManagerObserver {
private(set) var users = [User]()
func userDidChange(to user: User) {
users.append(user)
}
}
}
👋 When writing tests, you don't always need to create mocks - you can create stubs using real instances of things like errors, URLs & UserDefaults.
Here's how to do that for some common tasks/object types in Swift:
// Create errors using NSError (#function can be used to reference the name of the test)
let error = NSError(domain: #function, code: 1, userInfo: nil)
// Create non-optional URLs using file paths
let url = URL(fileURLWithPath: "Some/URL")
// Reference the test bundle using Bundle(for:)
let bundle = Bundle(for: type(of: self))
// Create an explicit UserDefaults object (instead of having to use a mock)
let userDefaults = UserDefaults(suiteName: #function)
// Create queues to control/await concurrent operations
let queue = DispatchQueue(label: #function)
For when you actually do need mocking, check out "Mocking in Swift".
⏱ 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.
protocol DataLoader {
// Adding type aliases to protocols can be a great way to
// reduce verbosity for parameter types.
typealias Handler = (Result<Data>) -> Void
associatedtype Endpoint
func loadData(from endpoint: Endpoint, then handler: @escaping Handler)
}
loader.loadData(from: .messages) { result in
...
}
loader.loadData(from: .messages, then: { result in
...
})
😴 Combining lazily evaluated sequences with builder pattern-like properties can lead to some pretty sweet APIs for configurable sequences in Swift.
Also useful for queries & other things you "build up" and then execute.
// Extension adding builder pattern-like properties that return
// a new sequence value with the given configuration applied
extension FileSequence {
var recursive: FileSequence {
var sequence = self
sequence.isRecursive = true
return sequence
}
var includingHidden: FileSequence {
var sequence = self
sequence.includeHidden = true
return sequence
}
}
// BEFORE
let files = folder.makeFileSequence(recursive: true, includeHidden: true)
// AFTER
let files = folder.files.recursive.includingHidden
Want an intro to lazy sequences? Check out "Swift sequences: The art of being lazy".
My top 3 tips for faster & more stable UI tests:
📱 Reset the app's state at the beginning of every test.
🆔 Use accessibility identifiers instead of UI strings.
⏱ Use expectations instead of waiting time.
func testOpeningArticle() {
// Launch the app with an argument that tells it to reset its state
let app = XCUIApplication()
app.launchArguments.append("--uitesting")
app.launch()
// Check that the app is displaying an activity indicator
let activityIndicator = app.activityIndicator.element
XCTAssertTrue(activityIndicator.exists)
// Wait for the loading indicator to disappear = content is ready
expectation(for: NSPredicate(format: "exists == 0"),
evaluatedWith: activityIndicator)
// Use a generous timeout in case the network is slow
waitForExpectations(timeout: 10)
// Tap the cell for the first article
app.tables.cells["Article.0"].tap()
// Assert that a label with the accessibility identifier "Article.Title" exists
let label = app.staticTexts["Article.Title"]
XCTAssertTrue(label.exists)
}
📋 It's super easy to access the contents of the clipboard from a Swift script. A big benefit of Swift scripting is being able to use Cocoa's powerful APIs for Mac apps.
import Cocoa
let clipboard = NSPasteboard.general.string(forType: .string)
🎯 Using Swift tuples for view state can be a super nice way to group multiple properties together and render them reactively using the layout system.
By using a tuple we don't have to either introduce a new type or make our view model-aware.
class TextView: UIView {
var state: (title: String?, text: String?) {
// By telling UIKit that our view needs layout and binding our
// state in layoutSubviews, we can react to state changes without
// doing unnecessary layout work.
didSet { setNeedsLayout() }
}
private let titleLabel = UILabel()
private let textLabel = UILabel()
override func layoutSubviews() {
super.layoutSubviews()
titleLabel.text = state.title
textLabel.text = state.text
...
}
}
⚾️ 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.
class ImageCacheTests: XCTestCase {
func testCachingAndLoadingImage() throws {
let bundle = Bundle(for: type(of: self))
let cache = ImageCache(bundle: bundle)
// Bonus tip: You can easily load images from your test
// bundle using this UIImage initializer
let image = try require(UIImage(named: "sample", in: bundle, compatibleWith: nil))
try cache.cache(image, forKey: "key")
let cachedImage = try cache.image(forKey: "key")
XCTAssertEqual(image, cachedImage)
}
}
enum ImageCacheError {
case emptyKey
case dataConversionFailed
}
// When using throwing tests, making your errors conform to
// LocalizedError will render a much nicer error message in
// Xcode (per default only the error code is shown).
extension ImageCacheError: LocalizedError {
var errorDescription: String? {
switch self {
case .emptyKey:
return "An empty key was given"
case .dataConversionFailed:
return "Failed to convert the given image to Data"
}
}
}
For more information, and the implementation of the require
method used above, check out "Avoiding force unwrapping in Swift unit tests".
✍️ Unlike static
properties, class
properties can be overridden by subclasses (however, they can't be stored, only computed).
class TableViewCell: UITableViewCell {
class var preferredHeight: CGFloat { return 60 }
}
class TallTableViewCell: TableViewCell {
override class var preferredHeight: CGFloat { return 100 }
}
👨🎨 Creating extensions with static factory methods can be a great alternative to subclassing in Swift, especially for things like setting up UIViews, CALayers or other kinds of styling.
It also lets you remove a lot of styling & setup from your view controllers.
extension UILabel {
static func makeForTitle() -> UILabel {
let label = UILabel()
label.font = .boldSystemFont(ofSize: 24)
label.textColor = .darkGray
label.adjustsFontSizeToFitWidth = true
label.minimumScaleFactor = 0.75
return label
}
static func makeForText() -> UILabel {
let label = UILabel()
label.font = .systemFont(ofSize: 16)
label.textColor = .black
label.numberOfLines = 0
return label
}
}
class ArticleViewController: UIViewController {
lazy var titleLabel = UILabel.makeForTitle()
lazy var textLabel = UILabel.makeForText()
}
🧒 An awesome thing about child view controllers is that they're automatically resized to match their parent, making them a super nice solution for things like loading & error views.
class ListViewController: UIViewController {
func loadItems() {
let loadingViewController = LoadingViewController()
add(loadingViewController)
dataLoader.loadItems { [weak self] result in
loadingViewController.remove()
self?.handle(result)
}
}
}
For more about child view controller (including the add
and remove
methods used above), check out "Using child view controllers as plugins in Swift".
🤐 Using the zip function in Swift you can easily combine two sequences. Super useful when using two sequences to do some work, since zip takes care of all the bounds-checking.
func render(titles: [String]) {
for (label, text) in zip(titleLabels, titles) {
print(text)
label.text = text
}
}
🎛 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.
extension Cache {
struct Options: OptionSet {
static let saveToDisk = Options(rawValue: 1)
static let clearOnMemoryWarning = Options(rawValue: 1 << 1)
static let clearDaily = Options(rawValue: 1 << 2)
let rawValue: Int
}
}
// We can now use Cache.Options just like UIViewAnimationOptions:
Cache(options: .saveToDisk)
Cache(options: [.saveToDisk, .clearDaily])
🙌 Using the where
clause when designing protocol-oriented APIs in Swift can let your implementations (or others' if it's open source) have a lot more freedom, especially when it comes to collections.
See "Using generic type constraints in Swift 4" for more info.
public protocol PathFinderMap {
associatedtype Node
// Using the 'where' clause for associated types, we can
// ensure that a type meets certain requirements (in this
// case that it's a sequence with Node elements).
associatedtype NodeSequence: Sequence where NodeSequence.Element == Node
// Instead of using a concrete type (like [Node]) here, we
// give implementors of this protocol more freedom while
// still meeting our requirements. For example, one
// implementation might use Set<Node>.
func neighbors(of node: Node) -> NodeSequence
}
👨🍳 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.
func makeActor(at coordinate: Coordinate, for building: Building) -> Actor {
let actor = Actor()
actor.position = coordinate.point
actor.animation = building.animation
return actor
}
func render(_ buildings: [Coordinate : Building]) {
buildings.map(makeActor).forEach(add)
}
😎 In Swift, you can call any instance method as a static function and it will return a closure representing that method. This is how running tests using SPM on Linux works.
More about this topic in my blog post "First class functions in Swift".
// 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:
extension UserManagerTests {
static var 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.
extension UIView {
func add(_ subviews: UIView...) {
subviews.forEach(addSubview)
}
}
view.add(button)
view.add(label)
// By dropping the "Subview" suffix from the method name, both
// single and multiple arguments work really well semantically.
view.add(button, label)
👽 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.
protocol DataContainer: AnyObject {
var data: Data? { get set }
}
class UserSettingsManager {
private var settings: Settings
private let dataContainer: DataContainer
// Since DataContainer is a protocol, we an easily mock it in
// tests if we use dependency injection
init(settings: Settings, dataContainer: DataContainer) {
self.settings = settings
self.dataContainer = dataContainer
}
func saveSettings() 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.
extension Building {
// This enum has custom raw values that are used when decoding
// a value, for example from JSON.
enum Kind: String {
case castle = "C"
case town = "T"
case barracks = "B"
case goldMine = "G"
case camp = "CA"
case blacksmith = "BL"
}
var animation: Animation {
return Animation(
// 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
)
}
}
👨🔬 Continuing to experiment with expressive ways of comparing a value with a list of candidates in Swift. Adding an extension on Equatable is probably my favorite approach so far.
extension Equatable {
func isAny(of candidates: Self...) -> Bool {
return candidates.contains(self)
}
}
let isHorizontal = direction.isAny(of: .left, .right)
See tip #35 for my previous experiment.
📐 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.
let view = UIView()
view.frame.size = CGSize(width: 100, height: 100)
view.transform = CGAffineTransform(scaleX: 2, y: 2)
print(view.frame) // (-50.0, -50.0, 200.0, 200.0)
print(view.bounds) // (0.0, 0.0, 100.0, 100.0)
👏 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!
// BEFORE: All parameters are specified, just like in Objective-C
viewController.present(modalViewController, animated: true, completion: nil)
modalViewController.dismiss(animated: true, completion: nil)
viewController.transition(from: loadingViewController,
to: contentViewController,
duration: 0.3,
options: [],
animations: animations,
completion: nil)
// AFTER: Since many UIKit APIs with completion handlers and other
// optional parameters import into Swift with default arguments,
// we can make our calls shorter
viewController.present(modalViewController, animated: true)
modalViewController.dismiss(animated: true)
viewController.transition(from: loadingViewController,
to: contentViewController,
duration: 0.3,
animations: animations)
✂️ Avoiding Massive View Controllers is all about finding the right levels of abstraction and splitting things up.
My personal rule of thumb is that as soon as I have 3 methods or properties that have the same prefix, I break them out into their own type.
// BEFORE
class LoginViewController: UIViewController {
private lazy var signUpLabel = UILabel()
private lazy var signUpImageView = UIImageView()
private lazy var signUpButton = UIButton()
}
// AFTER
class LoginViewController: UIViewController {
private lazy var signUpView = SignUpView()
}
class SignUpView: UIView {
private lazy var label = UILabel()
private lazy var imageView = UIImageView()
private lazy var button = UIButton()
}
❤️ 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.
func validateTextFields() -> Bool {
guard !usernameTextField.text.isNilOrEmpty else {
return false
}
...
return true
}
// Since all optionals are actual enum values in Swift, we can easily
// extend them for certain types, to add our own convenience APIs
extension Optional where Wrapped == String {
var isNilOrEmpty: Bool {
switch self {
case let string?:
return string.isEmpty
case nil:
return true
}
}
}
// Since strings are now Collections in Swift 4, you can even
// add this property to all optional collections:
extension Optional where Wrapped: Collection {
var isNilOrEmpty: Bool {
switch self {
case let collection?:
return collection.isEmpty
case nil:
return true
}
}
}
🗺 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.
func archiveMarkedPosts() {
for post in posts where post.isMarked {
archive(post)
}
}
func healAllies() {
for player in players where player.isAllied(to: currentPlayer) {
player.heal()
}
}
👻 Variable shadowing can be super useful in Swift, especially when you want to create a local copy of a parameter value in order to use it as state within a closure.
init(repeatMode: RepeatMode, closure: @escaping () -> UpdateOutcome) {
// Shadow the argument with a local, mutable copy
var repeatMode = repeatMode
self.closure = {
// With shadowing, there's no risk of accidentially
// referring to the immutable version
switch repeatMode {
case .forever:
break
case .times(let count):
guard count > 0 else {
return .finished
}
// We can now capture the mutable version and use
// it for state in a closure
repeatMode = .times(count - 1)
}
return closure()
}
}
✒️ 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.
public enum RepeatMode {
case times(Int)
case forever
}
public extension RepeatMode {
static var never: RepeatMode {
return .times(0)
}
static var once: RepeatMode {
return .times(1)
}
}
view.perform(animation, repeated: .once)
// To make default parameters more compact, you can even use init with dot syntax
class ImageLoader {
init(cache: Cache = .init(), decoder: ImageDecoder = .init()) {
...
}
}
🚀 One really cool aspect of Swift having first class functions is that you can pass any function (or even initializer) as a closure, and even call it with a tuple containing its parameters!
// This function lets us treat any "normal" function or method as
// a closure and run it with a tuple that contains its parameters
func call<Input, Output>(_ function: (Input) -> Output, with input: Input) -> Output {
return function(input)
}
class ViewFactory {
func makeHeaderView() -> HeaderView {
// We can now pass an initializer as a closure, and a tuple
// containing its parameters
return call(HeaderView.init, with: loadTextStyles())
}
private func loadTextStyles() -> (font: UIFont, color: UIColor) {
return (theme.font, theme.textColor)
}
}
class HeaderView {
init(font: UIFont, textColor: UIColor) {
...
}
}
💉 If you've been struggling to test code that uses static APIs, here's a technique you can use to enable static dependency injection without having to modify any call sites:
// Before: Almost impossible to test due to the use of singletons
class Analytics {
static func log(_ event: Event) {
Database.shared.save(event)
let dictionary = event.serialize()
NetworkManager.shared.post(dictionary, to: eventURL)
}
}
// After: Much easier to test, since we can inject mocks as arguments
class Analytics {
static func log(_ event: Event,
database: Database = .shared,
networkManager: NetworkManager = .shared) {
database.save(event)
let dictionary = event.serialize()
networkManager.post(dictionary, to: eventURL)
}
}
🎉 In Swift 4, type inference works for lazy properties and you don't need to explicitly refer to self
!
// Swift 3
class PurchaseView: UIView {
private lazy var buyButton: UIButton = self.makeBuyButton()
private func makeBuyButton() -> UIButton {
let button = UIButton()
button.setTitle("Buy", for: .normal)
button.setTitleColor(.blue, for: .normal)
return button
}
}
// Swift 4
class PurchaseView: UIView {
private lazy var buyButton = makeBuyButton()
private func makeBuyButton() -> UIButton {
let button = UIButton()
button.setTitle("Buy", for: .normal)
button.setTitleColor(.blue, for: .normal)
return button
}
}
😎 You can turn any Swift Error
into an NSError
, which is super useful when pattern matching with a code 👍. Also, switching on optionals is pretty cool!
let task = urlSession.dataTask(with: url) { data, _, error in
switch error {
case .some(let error as NSError) where error.code == NSURLErrorNotConnectedToInternet:
presenter.showOfflineView()
case .some(let error):
presenter.showGenericErrorView()
case .none:
presenter.renderContent(from: data)
}
}
task.resume()
Also make sure to check out Kostas Kremizas' tip about how you can pattern match directly against a member of URLError
.
🖥 Here's an easy way to make iOS model code that uses UIImage
macOS compatible - like me and Gui Rambo discussed on the Swift by Sundell Podcast.
// Either put this in a separate file that you only include in your macOS target or wrap the code in #if os(macOS) / #endif
import Cocoa
// Step 1: Typealias UIImage to NSImage
typealias UIImage = NSImage
// Step 2: You might want to add these APIs that UIImage has but NSImage doesn't.
extension NSImage {
var cgImage: CGImage? {
var proposedRect = CGRect(origin: .zero, size: size)
return cgImage(forProposedRect: &proposedRect,
context: nil,
hints: nil)
}
convenience init?(named name: String) {
self.init(named: Name(name))
}
}
// Step 3: Profit - you can now make your model code that uses UIImage cross-platform!
struct User {
let name: String
let profileImage: UIImage
}
🤖 You can easily define a protocol-oriented API that can only be mutated internally, by using an internal protocol that extends a public one.
// Declare a public protocol that acts as your immutable API
public protocol ModelHolder {
associatedtype Model
var model: Model { get }
}
// Declare an extended, internal protocol that provides a mutable API
internal protocol MutableModelHolder: ModelHolder {
var model: Model { get set }
}
// You can now implement the requirements using 'public internal(set)'
public class UserHolder: MutableModelHolder {
public internal(set) var model: User
internal init(model: User) {
self.model = model
}
}
🎛 You can switch on a set using array literals as cases in Swift! Can be really useful to avoid many if
/else if
statements.
class RoadTile: Tile {
var connectedDirections = Set<Direction>()
func render() {
switch connectedDirections {
case [.up, .down]:
image = UIImage(named: "road-vertical")
case [.left, .right]:
image = UIImage(named: "road-horizontal")
default:
image = UIImage(named: "road")
}
}
}
🌍 When caching localized content in an app, it's a good idea to add the current locale to all keys, to prevent bugs when switching languages.
func cache(_ content: Content, forKey key: String) throws {
let data = try wrap(content) as Data
let key = localize(key: key)
try storage.store(data, forKey: key)
}
func loadCachedContent(forKey key: String) -> Content? {
let key = localize(key: key)
let data = storage.loadData(forKey: key)
return data.flatMap { try? unbox(data: $0) }
}
private func localize(key: String) -> String {
return key + "-" + Bundle.main.preferredLocalizations[0]
}
🚳 Here's an easy way to setup a test to avoid accidental retain cycles with object relationships (like weak delegates & observers) in Swift:
func testDelegateNotRetained() {
// Assign the delegate (weak) and also retain it using a local var
var delegate: Delegate? = DelegateMock()
controller.delegate = delegate
XCTAssertNotNil(controller.delegate)
// Release the local var, which should also release the weak reference
delegate = nil
XCTAssertNil(controller.delegate)
}
👨🔬 Playing around with an expressive way to check if a value matches any of a list of candidates in Swift:
// Instead of multiple conditions like this:
if string == "One" || string == "Two" || string == "Three" {
}
// You can now do:
if string == any(of: "One", "Two", "Three") {
}
You can find a gist with the implementation here.
👪 APIs in a Swift extension automatically inherit its access control level, making it a neat way to organize public, internal & private APIs.
public extension Animation {
init(textureNamed textureName: String) {
frames = [Texture(name: textureName)]
}
init(texturesNamed textureNames: [String], frameDuration: TimeInterval = 1) {
frames = textureNames.map(Texture.init)
self.frameDuration = frameDuration
}
init(image: Image) {
frames = [Texture(image: image)]
}
}
internal extension Animation {
func loadFrameImages() -> [Image] {
return frames.map { $0.loadImageIfNeeded() }
}
}
🗺 Using map
you can transform an optional value into an optional Result
type by simply passing in the enum case.
enum Result<Value> {
case value(Value)
case error(Error)
}
class Promise<Value> {
private var result: Result<Value>?
init(value: Value? = nil) {
result = value.map(Result.value)
}
}
👌 It's so nice that you can assign directly to self
in struct
initializers in Swift. Very useful when adding conformance to protocols.
extension Bool: AnswerConvertible {
public init(input: String) throws {
switch input.lowercased() {
case "y", "yes", "👍":
self = true
default:
self = false
}
}
}
☎️ Defining Swift closures as inline functions enables you to recursively call them, which is super useful in things like custom sequences.
class Database {
func records(matching query: Query) -> AnySequence<Record> {
var recordIterator = loadRecords().makeIterator()
func iterate() -> Record? {
guard let nextRecord = recordIterator.next() else {
return nil
}
guard nextRecord.matches(query) else {
// Since the closure is an inline function, it can be recursively called,
// in this case in order to advance to the next item.
return iterate()
}
return nextRecord
}
// AnySequence/AnyIterator are part of the standard library and provide an easy way
// to define custom sequences using closures.
return AnySequence { AnyIterator(iterate) }
}
}
Rob Napier points out that using the above might cause crashes if used on a large databaset, since Swift has no guaranteed Tail Call Optimization (TCO).
Slava Pestov also points out that another benefit of inline functions vs closures is that they can have their own generic parameter list.
🏖 Using lazy properties in Swift, you can pass self
to required Objective-C dependencies without having to use force-unwrapped optionals.
class DataLoader: NSObject {
lazy var urlSession: URLSession = self.makeURLSession()
private func makeURLSession() -> URLSession {
return URLSession(configuration: .default, delegate: self, delegateQueue: .main)
}
}
class Renderer {
lazy var displayLink: CADisplayLink = self.makeDisplayLink()
private func makeDisplayLink() -> CADisplayLink {
return CADisplayLink(target: self, selector: #selector(screenDidRefresh))
}
}
👓 If you have a property in Swift that needs to be weak
or lazy
, you can still make it readonly by using private(set)
.
class Node {
private(set) weak var parent: Node?
private(set) lazy var children = [Node]()
func add(child: Node) {
children.append(child)
child.parent = self
}
}
🌏 Tired of using URL(string: "url")!
for static URLs? Make URL
conform to ExpressibleByStringLiteral
and you can now simply use "url"
instead.
extension URL: ExpressibleByStringLiteral {
// By using 'StaticString' we disable string interpolation, for safety
public init(stringLiteral value: StaticString) {
self = URL(string: "\(value)").require(hint: "Invalid URL string literal: \(value)")
}
}
// We can now define URLs using static string literals 🎉
let url: URL = "https://www.swiftbysundell.com"
let task = URLSession.shared.dataTask(with: "https://www.swiftbysundell.com")
// In Swift 3 or earlier, you also have to implement 2 additional initializers
extension URL {
public init(extendedGraphemeClusterLiteral value: StaticString) {
self.init(stringLiteral: value)
}
public init(unicodeScalarLiteral value: StaticString) {
self.init(stringLiteral: value)
}
}
To find the extension that adds the require()
method on Optional
that I use above, check out Require.
✚ I'm always careful with operator overloading, but for manipulating things like sizes, points & frames I find them super useful.
extension CGSize {
static func *(lhs: CGSize, rhs: CGFloat) -> CGSize {
return CGSize(width: lhs.width * rhs, height: lhs.height * rhs)
}
}
button.frame.size = image.size * 2
If you like the above idea, check out CGOperators, which contains math operator overloads for all Core Graphics' vector types.
🔗 You can use closure types in generic constraints in Swift. Enables nice APIs for handling sequences of closures.
extension Sequence where Element == () -> Void {
func callAll() {
forEach { $0() }
}
}
extension Sequence where Element == () -> String {
func joinedResults(separator: String) -> String {
return map { $0() }.joined(separator: separator)
}
}
callbacks.callAll()
let names = nameProviders.joinedResults(separator: ", ")
(If you're using Swift 3, you have to change Element
to Iterator.Element
)
🎉 Using associated enum values is a super nice way to encapsulate mutually exclusive state info (and avoiding state-specific optionals).
// BEFORE: Lots of state-specific, optional properties
class Player {
var isWaitingForMatchMaking: Bool
var invitingUser: User?
var numberOfLives: Int
var playerDefeatedBy: Player?
var roundDefeatedIn: Int?
}
// AFTER: All state-specific information is encapsulated in enum cases
class Player {
enum State {
case waitingForMatchMaking
case waitingForInviteResponse(from: User)
case active(numberOfLives: Int)
case defeated(by: Player, roundNumber: Int)
}
var state: State
}
👍 I really like using enums for all async result types, even boolean ones. Self-documenting, and makes the call site a lot nicer to read too!
protocol PushNotificationService {
// Before
func enablePushNotifications(completionHandler: @escaping (Bool) -> Void)
// After
func enablePushNotifications(completionHandler: @escaping (PushNotificationStatus) -> Void)
}
enum PushNotificationStatus {
case enabled
case disabled
}
service.enablePushNotifications { status in
if status == .enabled {
enableNotificationsButton.removeFromSuperview()
}
}
🏃 Want to work on your async code in a Swift Playground? Just set needsIndefiniteExecution
to true to keep it running:
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
let greeting = "Hello after 3 seconds"
print(greeting)
}
To stop the playground from executing, simply call PlaygroundPage.current.finishExecution()
.
💦 Avoid memory leaks when accidentially refering to self
in closures by overriding it locally with a weak reference:
Swift >= 4.2
dataLoader.loadData(from: url) { [weak self] result in
guard let self = self else {
return
}
self.cache(result)
...
Swift < 4.2
dataLoader.loadData(from: url) { [weak self] result in
guard let `self` = self else {
return
}
self.cache(result)
...
Note that the reason the above currently works is because of a compiler bug (which I hope gets turned into a properly supported feature soon).
🕓 Using dispatch work items you can easily cancel a delayed asynchronous GCD task if you no longer need it:
let workItem = DispatchWorkItem {
// Your async code goes in here
}
// Execute the work item after 1 second
DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: workItem)
// You can cancel the work item if you no longer need it
workItem.cancel()
➕ While working on a new Swift developer tool (to be open sourced soon 😉), I came up with a pretty neat way of organizing its sequence of operations, by combining their functions into a closure:
internal func +<A, B, C>(lhs: @escaping (A) throws -> B,
rhs: @escaping (B) throws -> C) -> (A) throws -> C {
return { try rhs(lhs($0)) }
}
public func run() throws {
try (determineTarget + build + analyze + output)()
}
If you're familiar with the functional programming world, you might know the above technique as the pipe operator (thanks to Alexey Demedreckiy for pointing this out!)
🗺 Using map()
and flatMap()
on optionals you can chain multiple operations without having to use lengthy if lets
or guards
:
// BEFORE
guard let string = argument(at: 1) else {
return
}
guard let url = URL(string: string) else {
return
}
handle(url)
// AFTER
argument(at: 1).flatMap(URL.init).map(handle)
🚀 Using self-executing closures is a great way to encapsulate lazy property initialization:
class StoreViewController: UIViewController {
private lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
let view = UICollectionView(frame: self.view.bounds, collectionViewLayout: layout)
view.delegate = self
view.dataSource = self
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(collectionView)
}
}
⚡️ You can speed up your Swift package tests using the --parallel
flag. For Marathon, the tests execute 3 times faster that way!
swift test --parallel
🛠 Struggling with mocking UserDefaults
in a test? The good news is: you don't need mocking - just create a real instance:
class LoginTests: XCTestCase {
private var userDefaults: UserDefaults!
private var manager: LoginManager!
override func setUp() {
super.setup()
userDefaults = UserDefaults(suiteName: #file)
userDefaults.removePersistentDomain(forName: #file)
manager = LoginManager(userDefaults: userDefaults)
}
}
👍 Using variadic parameters in Swift, you can create some really nice APIs that take a list of objects without having to use an array:
extension Canvas {
func add(_ shapes: Shape...) {
shapes.forEach(add)
}
}
let circle = Circle(center: CGPoint(x: 5, y: 5), radius: 5)
let lineA = Line(start: .zero, end: CGPoint(x: 10, y: 10))
let lineB = Line(start: CGPoint(x: 0, y: 10), end: CGPoint(x: 10, y: 0))
let canvas = Canvas()
canvas.add(circle, lineA, lineB)
canvas.render()
😮 Just like you can refer to a Swift function as a closure, you can do the same thing with enum cases with associated values:
enum UnboxPath {
case key(String)
case keyPath(String)
}
struct UserSchema {
static let name = key("name")
static let age = key("age")
static let posts = key("posts")
private static let key = UnboxPath.key
}
📈 The ===
operator lets you check if two objects are the same instance. Very useful when verifying that an array contains an instance in a test:
protocol InstanceEquatable: class, Equatable {}
extension InstanceEquatable {
static func ==(lhs: Self, rhs: Self) -> Bool {
return lhs === rhs
}
}
extension Enemy: InstanceEquatable {}
func testDestroyingEnemy() {
player.attack(enemy)
XCTAssertTrue(player.destroyedEnemies.contains(enemy))
}
😎 Cool thing about Swift initializers: you can call them using dot syntax and pass them as closures! Perfect for mocking dates in tests.
class Logger {
private let storage: LogStorage
private let dateProvider: () -> Date
init(storage: LogStorage = .init(), dateProvider: @escaping () -> Date = Date.init) {
self.storage = storage
self.dateProvider = dateProvider
}
func log(event: Event) {
storage.store(event: event, date: dateProvider())
}
}
📱 Most of my UI testing logic is now categories on XCUIApplication
. Makes the test cases really easy to read:
func testLoggingInAndOut() {
XCTAssertFalse(app.userIsLoggedIn)
app.launch()
app.login()
XCTAssertTrue(app.userIsLoggedIn)
app.logout()
XCTAssertFalse(app.userIsLoggedIn)
}
func testDisplayingCategories() {
XCTAssertFalse(app.isDisplayingCategories)
app.launch()
app.login()
app.goToCategories()
XCTAssertTrue(app.isDisplayingCategories)
}
🙂 It’s a good idea to avoid “default” cases when switching on Swift enums - it’ll “force you” to update your logic when a new case is added:
enum State {
case loggedIn
case loggedOut
case onboarding
}
func handle(_ state: State) {
switch state {
case .loggedIn:
showMainUI()
case .loggedOut:
showLoginUI()
// Compiler error: Switch must be exhaustive
}
}
💂 It's really cool that you can use Swift's 'guard' statement to exit out of pretty much any scope, not only return from functions:
// You can use the 'guard' statement to...
for string in strings {
// ...continue an iteration
guard shouldProcess(string) else {
continue
}
// ...or break it
guard !shouldBreak(for: string) else {
break
}
// ...or return
guard !shouldReturn(for: string) else {
return
}
// ..or throw an error
guard string.isValid else {
throw StringError.invalid(string)
}
// ...or exit the program
guard !shouldExit(for: string) else {
exit(1)
}
}
❤️ Love how you can pass functions & operators as closures in Swift. For example, it makes the syntax for sorting arrays really nice!
let array = [3, 9, 1, 4, 6, 2]
let sorted = array.sorted(by: <)
🗝 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.
extension UserDefaults {
var onboardingCompleted: Bool {
get { return bool(forKey: #function) }
set { set(newValue, forKey: #function) }
}
}
📛 Want to use a name already taken by the standard library for a nested type? No problem - just use Swift.
to disambiguate:
extension Command {
enum Error: Swift.Error {
case missing
case invalid(String)
}
}
📦 Playing around with using Wrap to implement Equatable
for any type, primarily for testing:
protocol AutoEquatable: Equatable {}
extension AutoEquatable {
static func ==(lhs: Self, rhs: Self) -> Bool {
let lhsData = try! wrap(lhs) as Data
let rhsData = try! wrap(rhs) as Data
return lhsData == rhsData
}
}
📏 One thing that I find really useful in Swift is to use typealiases to reduce the length of method signatures in generic types:
public class PathFinder<Object: PathFinderObject> {
public typealias Map = Object.Map
public typealias Node = Map.Node
public typealias Path = PathFinderPath<Object>
public static func possiblePaths(for object: Object, at rootNode: Node, on map: Map) -> Path.Sequence {
return .init(object: object, rootNode: rootNode, map: map)
}
}
📖 You can reference either the external or internal parameter label when writing Swift docs - and they get parsed the same:
// EITHER:
class Foo {
/**
* - parameter string: A string
*/
func bar(with string: String) {}
}
// OR:
class Foo {
/**
* - parameter with: A string
*/
func bar(with string: String) {}
}
👍 Finding more and more uses for auto closures in Swift. Can enable some pretty nice APIs:
extension Dictionary {
mutating func value(for key: Key, orAdd valueClosure: @autoclosure () -> Value) -> Value {
if let value = self[key] {
return value
}
let value = valueClosure()
self[key] = value
return value
}
}
🚀 I’ve started to become a really big fan of nested types in Swift. Love the additional namespacing it gives you!
public struct Map {
public struct Model {
public let size: Size
public let theme: Theme
public var terrain: [Position : Terrain.Model]
public var units: [Position : Unit.Model]
public var buildings: [Position : Building.Model]
}
public enum Direction {
case up
case right
case down
case left
}
public struct Position {
public var x: Int
public var y: Int
}
public enum Size: String {
case small = "S"
case medium = "M"
case large = "L"
case extraLarge = "XL"
}
}
Author: JohnSundell
Source code: https://github.com/JohnSundell/SwiftTips
License: MIT license
#swift
1596848280
In this post, I will explain why declarative code is better than imperative code.
Then I will list some techniques to convert imperative JavaScript to a declarative one in common situations, defining key terms along the way.
First, let’s define what declarative and imperative mean.
Declarative code is one that highlights the intent of what it’s doing.
It favors the “what” over the “how”.
In other words, the exact implementations actually doing the work (aka the “how”) are hidden in order to convey what that work actually is (aka the “what”).
At the opposite, imperative code is one that favors the “how” over the “what”.
Let’s see an example:
The snippet below perform two things: it computes the square of x
, then check if the result is even or not.
// imperative way
const x = 5;
const xSquared = x * x;
let isEven;
if (xSquared % 2 === 0) {
isEven = true;
} else {
isEven = false;
}
view raw
block1.js hosted with ❤ by GitHub
Here, we can see that we finally get isEven
after several steps that we must follow in order.
These steps describe “how” we arrive to know if the square of x
is even, but that’s not obvious.
If you take a non-programmer and show him this, he might have a hard time deciphering it.
Now let’s see another snippet where I introduce a magic isSquareEven
function that performs the two same things than the previous one.
#functional-programming #javascript #javascript-tips #programming #declarative-programming #function
1605017502
Other then the syntactical differences. The main difference is the way the this keyword behaves? In an arrow function, the this keyword remains the same throughout the life-cycle of the function and is always bound to the value of this in the closest non-arrow parent function. Arrow functions can never be constructor functions so they can never be invoked with the new keyword. And they can never have duplicate named parameters like a regular function not using strict mode.
this.name = "Bob";const person = {
name: “Jon”,<span style="color: #008000">// Regular function</span> func1: <span style="color: #0000ff">function</span> () { console.log(<span style="color: #0000ff">this</span>); }, <span style="color: #008000">// Arrow function</span> func2: () => { console.log(<span style="color: #0000ff">this</span>); }
}
person.func1(); // Call the Regular function
// Output: {name:“Jon”, func1:[Function: func1], func2:[Function: func2]}person.func2(); // Call the Arrow function
// Output: {name:“Bob”}
const person = (name) => console.log("Your name is " + name); const bob = new person("Bob"); // Uncaught TypeError: person is not a constructor
#arrow functions #javascript #regular functions #arrow functions vs normal functions #difference between functions and arrow functions
1603176407
Nowadays, all my code is based on the use of arrow functions. If you are still not using them yourself, then don’t be ashamed of who you are. That’s your parent’s job. Instead, find about all the benefits that you can get by using arrow functions like the cool kids.
This is an example of arrow function and the same code written traditionally:
const arrowFunction = (arg1, arg2) => arg1 + arg 2;
const traditionalFunction = function(arg1, arg2) {
return arg1 + arg2;
};
You may notice that the code is shorter and that there is an arrow. Everything before the arrow is arguments of the function and everything after the arrow is always returned as the result of the function.
If you need a function that contains multiple statements you can still do this:
const arrowFunction = (arg1, arg2) => {
const result = arg1 + arg2;
return result;
};
#javascript #js #functional-javascript #functional-programming #javascript-tips