Lately, I’ve started creating a curated list of interesting Swift articles and learnings to keep them in lazy brain memory aka Notion and being able to review it at a later point. I think this the best picks can be helpful so I’ll just share them here. I’ll go with Swift Bytes for this series.

TLDR: For this month it’s mainly syntax related things.

1. Better map’s

map(\.{var}), filter(\.{var}), compactMap(\.{var})

Filter, map, compactMap etc can make use of KeyPath’s which makes the code much more readable and saves a bunch of characters. This is also commonly used in SwiftUI when setting the id for ForEach.

struct Task {
	var name: String
	var completed: Bool
	var priority: Int

	var isHighPriority: Bool {
		priority >= 5
	}
}

let tasks = [
	Task(name: "Walk the dog", completed: true, priority: 5),
	Task(name: "Do taxes", completed: false, priority: 2),
	Task(name: "Drink some matcha", completed: true, priority: 1)
]

let completedNames = tasks
	.filter(\.completed)
	.filter(\.isHighPriority)
	.map(\.name)
print(completedNames)

// output
// ["Walk the dog"]

2. Better Optionals

extension String? {}

There is a handy way to extend Optionals which is often worked around with the ternary operator eg like let test = task.name ?? "New Task" or other workarounds for nil or empty values. This can be more easily solved via extensions.

// alternative -> extension Optional where Wrapped == String
extension String? {
	var isNilOrEmpty: Bool { return self == nil || self == "" }
    var unwrapEmpty: String { return self ?? "" }
}

var testString: String? = nil
print(testString.isNilOrEmpty)
print(testString.unwrapEmpty)

var test2String: String? = "ocha"
print(test2String.isNilOrEmpty)
print(test2String.unwrapEmpty)

// outputs
// true
// 
// false
// ocha

3. Better loops

for {var} in {list} where {condition}

Regular for loops will normally iterate over each element whereas with the where keyword there is an easier / cleaner way to iterate over a subset.

let numbers = [1, 2, 5, 10, 100, 200, 500, 1000]

// instead of doing this (or similar)
for num in numbers.filter({ $0 < 101 }) {
	print(num)
}

// can be achieved like this
for num in numbers where (num <= 10) {
	print(num)
}

// output
// 1
// 2
// 5
// 10
// 100

It’s also possible to concat conditions with && operator

let names = ["Swift", "Kotlin", "Golang", "Java", "Javascript"]
for name in names where (name.starts(with: "J") && (name.suffix(1) ?? "") == "a") {
	print(name)
}

// output
// ["Java"]

4. Improved Keypaths

Relating to 1. Better map’s we can even improve the KeyPath’s by extending the functionality with eg an not or equal operator.

prefix func !<T>(keyPath: KeyPath<T, Bool>) -> (T) -> Bool {
    return { !$0[keyPath: keyPath] }
}

let fullLengthArticles = articles.filter(!\.isActive)

func ==<T, V: Equatable>(lhs: KeyPath<T, V>, rhs: V) -> (T) -> Bool {
    return { $0[keyPath: lhs] == rhs }
}

let fullLengthArticles = articles.filter(\.category == .fullLength)

- cheers & stay motivated

Sources