#29 Foundation’s partition(by:) is useful if we want to divide a collection into two parts and operate on both sub-collections. Example could be partitioning index paths and calling UITableView reloadRows for one part and reconfigureRows for the other part.
var indexPaths = changingIndexPaths
let reconfiguredFirstIndex = indexPaths.partition(by: canReconfigure(_:))
let reloadedIndexPaths = indexPaths[0..<reconfiguredFirstIndex] // to UITableView reloadRows
let reconfiguredIndexPaths = indexPaths[reconfiguredFirstIndex...] // to UITableView reconfigureRows
#28 When dealing with shared instances, sometimes it makes sense to introduce static subscript for skipping typing .shared
everywhere.
@dynamicMemberLookup
final class DependencyContainer {
static let shared = DependencyContainer()
let database: Database
// …
}
extension DependencyContainer {
static subscript<T>(dynamicMember keyPath: KeyPath<DependencyContainer, T>) -> T {
return shared[keyPath: keyPath]
}
}
let before = DependencyContainer.shared.database
let after = DependencyContainer.database
#27 Using enums over structs for namespacing static constants. Nicer, since we can’t create an instance of AppConstants
nor AppConstants.URLs
.
enum AppConstants {
enum URLs {
static let api = URL(string: "https://example.com/api/data")!
// …
}
// …
}
#26 @autoclosure enables wrapping function parameters automatically with a closure, which means we can just pass a value, not a closure, to the function. Useful for custom logging functions where we might want to skip running the closure all together, depending on the build settings.
func debugLog( _ message: @autoclosure () -> String) {
#if DEBUG
print(message())
#endif
}
debugLog("greeting")
#25 Destructuring a tuple is a short way to extract values from tuples in Swift
let timestamps = (start: Date.distantPast, end: Date.distantFuture)
// Tuple destructuring
let (start, end) = timestamps
print(start, end)
// vs more verbose
let start2 = timestamps.start
let end2 = timestamps.end
print(start2, end2)
#24 In addition to functions and properties, we can add init methods to protocols in Swift. In that case, the conforming non-final class needs to use the “required” modifier. This ensures that the init is explicitly implemented by the class or its subclasses.
#23 Swift 5.9 added sleep(for:tolerance:) to the Clock protocol, which makes it simpler to add delays. Before, we needed to use an instant.
#22 If a function in Swift has multiple defer statements, then these are run in a reverse order. Something to keep in mind if the order happens to be important.
#21 We can opt in to upcoming Swift features with -enable-upcoming-feature
Swift build flag. For example, in Swift 5.8 we can opt in to a new #file
behaviour where it equals to the module_name/file_name
instead of the full path. Useful when implementing custom logging functions.
#20 Swift 5.8 implements @backDeployed
attribute for adding functionality to older API versions.
#19 When using switch for enums with associated values, and we do not want to use associated values, then we can only write out the case name .error
(instead of .error(_)
).
#18 Adding custom initializer in an extension does not override the auto-generated initializer for structs in Swift.
#17 Additional concurrency checks can be enabled for async-await Swift code by adding -Xfrontend -warn-concurrency -enable-actor-data-race-checks
to Other Swift Flags in Xcode
#16 Swift 5.7 introduced a new ContinuousClock API which also provides a nice way to measure time of async functions.
#15 Async property getters (but not setters) in Swift are written by adding the async keyword next to the get.
#14 Actor is a reference type which only allows one operation on their state at a time. Meaning, we can write code modifying multiple properties without worrying that these properties are read while we are still updating them.
#13 Optional pattern matching in for loop only loops over non-nil values.
#12 Swift 5.7 introduced if let shorthands. No more if let variable = variable
and instead just if let variable
.
#11 I have found the Foundation provided merge function for dictionaries a bit confusing to read. This is a bit shorter version I have preferred to use instead:
#10 #unavailable() allows switching API usage based on the OS version.
#9 Swift.org has API design guidelines page. My go-to place when I am stuck with naming something.
#8 Dictionary has a subscript with default value. Useful if accessing the dictionary should return a fallback value when the dictionary key is not present.
#7 Comparing boolean conditions with switch instead of if-elseif-elseif-else. Additionally, the compiler makes sure that all the possible cases are handled.
#6 Add a function with default argument values in protocol extension. In protocol extension, 'self'
points at the type implementing the protocol function.
#5 HTTPURLResponse
has a localizedString(forStatusCode:) function for returning localized strings for HTTP status codes.
#4 Use dump()
for printing out all the properties of a type. Especially useful when the type has a custom description which reveals only some information.
#3 Avoiding default in switch statements which will trigger a compiler error after adding a new case and forces reviewing all the related code.
#2 Converting integer to Data and back. Useful when working with raw bytes.
#1 Avoid optional when converting data to utf8 string with init(decoding:as:)
.