Notes on Swift

Combine

Combine schedulers .receive and .subscribe

Combine allows for publishers to specify the scheduler used when either receiving from an upstream publisher (in the case of operators), or when sending to a downstream subscriber. This is critical when working with a subscriber that updates UI elements, as that should always be called on the main thread.

1
2
3
4
somePublisher
.subscribe(on: DispatchQueue.global()) // to subscribe on background thread
.receive(on: RunLoop.main) // but receive results on main thread as we need it for some UI updates
...

Future with Deferred

If you want your Future to act more like Rx’s Single by having it defer its execution until it receives a subscriber, and having the work execute every time you subscribe you can wrap your Future in a Deferred publisher. Let’s expand the previous example a bit to demonstrate this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func createFuture() -> AnyPublisher<Int, Never> {
return Deferred {
Future { promise in
print("Closure executed")
promise(.success(42))
}
}.eraseToAnyPublisher()
}

let future = createFuture() // nothing happens yet

let sub1 = future.sink(receiveValue: { value in
print("sub1: \(value)")
}) // the Future executes because it has a subscriber

let sub2 = future.sink(receiveValue: { value in
print("sub2: \(value)")
}) // the Future executes again because it received another subscriber

source: https://www.donnywals.com/using-promises-and-futures-in-combine/

SwiftUI

Aligning with .alignmentGuide

To make for example one text align to the left and one to the right within a container, you can use alignmentGuide as below.

1
2
3
4
5
6
7
8
VStack(alignment: .leading) {
Text("Hello, world!")
.alignmentGuide(.leading) { d in d[.trailing] }
Text("This is a longer line of text")
}
.background(Color.red)
.frame(width: 400, height: 400)
.background(Color.blue)

SwiftUI alignmentGuide example

source: https://www.hackingwithswift.com/books/ios-swiftui/alignment-and-alignment-guides

Initialize @State from initializer

SwiftUI doesn’t allow you to change @State in the initializer but you can initialize it.

Remove the default value and use _valueName to set @State directly instead of going through the property wrapper accessor.

1
2
3
4
5
@State var fullText: String // No default value of ""

init(letter: String) {
_fullText = State(initialValue: list[letter]!)
}

source: https://stackoverflow.com/a/58137096

Async code. DispatchQueue

Use DispatchQueue to print something after 1 second

1
2
3
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
print("Timer fired!")
}

Timer

Basic usage of scheduled timer

1
2
3
4
5
6
7
8
9
10
var runCount = 0

Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
print("Timer fired!")
runCount += 1

if runCount == 3 {
timer.invalidate()
}
}

Example of using Timer within SwiftUI’s ObservableObject

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class TimeCounter: ObservableObject {
@Published var time = 0

lazy var timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in self.time += 1 }
init() { timer.fire() }
}

struct ContentView: View {
@StateObject var timeCounter = TimeCounter()

var body: some View {
Text("\(timeCounter.time)")
}
}

Swift language quirks

Self vs self

When used with a capital S, Self refers to the type that conform to the protocol, e.g. String or Int. When used with a lowercase S, self refers to the value inside that type, e.g. “hello” or 556.

1
2
3
4
5
extension BinaryInteger {
func squared() -> Self {
return self * self
}
}

class func vs static func functions

Protocols use the class keyword, but it doesn’t exclude structs from implementing the protocol, they just use static instead. Class was chosen for protocols so there wouldn’t have to be a third keyword to represent static or class. That’s the main difference but some other differences are that class functions are dynamically dispatched and can be overridden by subclasses.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class ClassA {
class func func1() -> String {
return "func1"
}

static func func2() -> String {
return "func2"
}

/* same as above
final class func func2() -> String {
return "func2"
}
*/
}

class ClassB : ClassA {
override class func func1() -> String {
return "func1 in ClassB"
}

// ERROR: Class method overrides a 'final` class method
override static func func2() -> String {
return "func2 in ClassB"
}
}