Blog posts about programming languages, Swift, Go and more by me.

Swift, VS Code and you


Editors like Visual Studio Code live from a wide range of extensions and customization. In contrast there are IDEs like Xcode and AppCode, which have everything set up and are ready to go. In order to provide a rich set of features, they cannot not offer the same level of flexibility. Which editor you might want to use is a highly personal decision.

Disclaimer: I am the maintainer of the extensions Maintained Swift Development Environment, sourcekite, SwiftLint, SwiftFormat, apple-swift-format.

Read more ⟶

Debugging Swift in VS Code the old way


Running and debugging your targets in Visual Studio Code is not prepared by default. Especially for us Swift developers this might come unexpected, especially in comparison to Xcode. In VS Code we require extensions and configs for this purpose.

Update from 2022: the Swift Server Work Group released their own official VS Code extension which dramatically improves the debugging user experience. Here is the new, updated blog post.

Within this blog post, we will set up debugging for a Swift Package Manager project. As a bonus we will also prepare debugging your unit tests.

Read more ⟶

InferIt: a Constraint Solving Package Manager


The initial idea behind InferIt was to create some mixture of a constraint solver and a dependency manager: you would just tell it what to install and it would gather as much information as possible to install it.

The goal is to fulfill a requirement. InferIt would then try to resolve all variables by trying to fulfill several requirements. If a requirement has been met, the value can be propagated.

Read more ⟶

ReactifSwift: async function composition


Async operators debounce, throttle or delay that functional reactive programming libraries as RxSwift and ReactiveSwift provide are super useful and expressive. Though their biggest benefit lays within composability.

This playground tries to achieve the same using plain functions with little help of a global scheduler for a greater testing experience. For better composability it relies on Overture in version 0.2.0.

Originally written at 2018-06-10

Example


var count = 0
let countChars = with({ count += $0 }, pipe(
    map(get(\String.count)),
    throttle(time: 100)
))

let time = TestingScheduler()
ReactifCurrent.scheduler = time
countChars("A")
time.tick(50)
countChars("B")
time.tick(100)
countChars("Abc")
count // 4

Implementation

// import Overture
import Foundation

typealias Unary<A> = (A) -> Void

protocol Invalidatable {
    func invalidate()
}
extension Timer: Invalidatable {}

protocol Scheduler {
    func now() -> Date
    func delay(_ time: TimeInterval, _ f: @escaping () -> Void) -> Invalidatable
}

final class TestingScheduler: Scheduler {
    private var delayed: [FakeTimer] = []
    private var currentInterval: TimeInterval = 0

    init() {}

    private final class FakeTimer: Invalidatable {
        let date: Date
        var fire: (() -> Void)?

        init(date: Date, fire: @escaping () -> Void) {
            self.fire = fire
            self.date = date
        }

        var isValid: Bool {
            return fire != nil
        }

        func tick(_ now: Date) {
            if let fire = fire, now >= self.date {
                fire()
            }
        }

        func invalidate() {
            self.fire = nil
        }
    }

    func tick(_ interval: TimeInterval) {
        self.currentInterval += interval
        let currently = now()
        self.delayed.forEach { $0.tick(currently) }
        self.delayed = self.delayed.filter { $0.isValid }
    }

    func now() -> Date {
        return Date(timeIntervalSince1970: currentInterval)
    }

    func delay(_ interval: TimeInterval, _ f: @escaping () -> Void) -> Invalidatable {
        let timer = FakeTimer(date: now().addingTimeInterval(interval), fire: f)
        delayed.append(timer)
        return timer
    }
}

final class TimeScheduler: Scheduler {
    init() {}

    func now() -> Date {
        return Date()
    }

    func delay(_ interval: TimeInterval, _ f: @escaping () -> Void) -> Invalidatable {
        return Timer(timeInterval: interval, repeats: false) { _ in
            f()
        }
    }
}

struct ReactifRuntimeContext {
    var scheduler: Scheduler = TimeScheduler()
}

var ReactifCurrent = ReactifRuntimeContext()

struct Sink {
    typealias Completion = () -> Void
    let call: (@escaping (@escaping Completion) -> Void) -> Bool
    init(_ call: @escaping (@escaping (@escaping Completion) -> Void) -> Bool) {
        self.call = call
    }
}

extension Sink {
    static func lock(name: String? = nil) -> Sink {
        let lock = NSLock()
        lock.name = name
        return Sink { f in
            if lock.try() {
                f(lock.unlock)
                return true
            } else {
                return false
            }
        }
    }

    static func recursiveLock(name: String? = nil) -> Sink {
        let lock = NSRecursiveLock()
        lock.name = name
        return Sink { f in
            if lock.try() {
                f(lock.unlock)
                return true
            } else {
                return false
            }
        }
    }

    static func synchronous() -> Sink {
        return Sink { f in
            f({})
            return true
        }
    }
}

func filter(_ includes: @escaping () -> Bool) -> (Sink) -> Sink {
    return { (sink: Sink) in
        Sink { (f: @escaping Unary<Sink.Completion>) in
            includes() && sink.call(f)
        }
    }
}

func reschedule(_ transform: @escaping (@escaping Unary<Sink.Completion>, @escaping Sink.Completion) -> Void) -> (Sink) -> Sink {
    return { (sink: Sink) in
        Sink { (f: @escaping Unary<Sink.Completion>) in
            sink.call { complete in
                transform(f, complete)
            }
        }
    }
}

func delay(time: TimeInterval) -> (Sink) -> Sink {
    return reschedule { f, completion in
        ReactifCurrent.scheduler.delay(time) {
            f(completion)
        }
    }
}

func extend(time: TimeInterval) -> (Sink) -> Sink {
    return reschedule { f, completion in
        f {
            ReactifCurrent.scheduler.delay(time, completion)
        }
    }
}

func debounce(time: TimeInterval) -> (Sink) -> Sink {
    var currentAttempt: Invalidatable?
    return reschedule { f, completion in
        currentAttempt?.invalidate()
        currentAttempt = ReactifCurrent.scheduler.delay(time) {
            f(completion)
        }
    }
}

func throttle(time: TimeInterval) -> (Sink) -> Sink {
    var lastInvocation: Date?
    return filter {
        let now = ReactifCurrent.scheduler.now()
        defer { lastInvocation = now }
        if let lastInvocation = lastInvocation, now.timeIntervalSince(lastInvocation) < time {
            return false
        } else {
            return true
        }
    }
}

func schedule<A>(_ factory: @escaping @autoclosure () -> Sink) -> (@escaping Unary<A>) -> Unary<A> {
    return { f in
        let sink = factory()
        return { a in sink.call { f(a);$0() } }
    }
}

// TODO: throttle using time after completion instead of starting time
func throttle<A>(time: TimeInterval) -> (@escaping Unary<A>) -> Unary<A> {
    return schedule(with(.synchronous(), throttle(time: time)))
}

func exhaustA<A>(ending time: TimeInterval? = nil) -> (@escaping Unary<A>) -> Unary<A> {
    return schedule(with(.recursiveLock(), extend(time: 10)))
}
func exhaust<A>(ending time: TimeInterval? = nil) -> (@escaping Unary<A>) -> Unary<A> {
    return { f in
        var lock = NSLock()
        return { a in
            if lock.try() {
                f(a)
                if let time = time {
                    ReactifCurrent.scheduler.delay(time, lock.unlock)
                } else {
                    lock.unlock()
                }
            }
        }
    }
}

func map<A, B>(_ transform: @escaping (A) -> B) -> (@escaping Unary<B>) -> Unary<A> {
    return { f in pipe(transform, f) }
}

func filter<A>(_ include: @escaping (A) -> Bool) -> (@escaping Unary<A>) -> Unary<A> {
    return { f in
        return { a in
            if include(a) {
                f(a)
            }
        }
    }
}

func debounce<A>(time: TimeInterval) -> (@escaping Unary<A>) -> Unary<A> {
    return { f in
        var currentAttempt: Invalidatable?
        return { a in
            currentAttempt?.invalidate()
            currentAttempt = ReactifCurrent.scheduler.delay(time) {
                f(a)
            }
        }
    }
}

Conclusion

I think this is a quite cool idea for 240 lines of code. If just a few functions are required and using a FRP library would be too much just for a few functions, this might be a lightweight, but valuable alternative.

Read more ⟶

Swifducks: simple Redux store with multiple reducers


A simple example implementation of the Redux pattern with multiple reducers and listeners.

The name is derived from Swift + Redux = 🏎🦆

Originally written at 2018-06-10

Definitions

public final class Store<State, Action> {
    public typealias Reducer = (Action, inout State) -> Void

    public private(set) var state: State
    private var reducers: [Reducer] = []
    private var callbacks: [Weak<Listener<State>>] = []

    public init(initial state: State) {
        self.state = state
    }

    public func select(_ changes: @escaping (State) -> Void) -> Any {
        let subscription = Listener(on: changes)
        callbacks.append(Weak(subscription))
        return subscription
    }

    private func reduce(with reducer: @escaping Reducer) {
        reducers.append(reducer)
    }

    public func dispatch(_ action: Action) {
        state = reducers.reduce(into: state) { intermediate, reducer in
            reducer(action, &intermediate)
        }
        callbacks = callbacks
            .compactMap { $0.value }
            .map(Weak.init)
        callbacks.forEach { $0.value?.onChange(state) }
    }
}

public extension Store {
    convenience init(initial state: State, reducer: @escaping Reducer) {
        self.init(initial: state)
        reduce(with: reducer)
    }
}

internal struct Weak<A: AnyObject> {
    weak var value: A?

    init(_ value: A?) {
        self.value = value
    }
}

internal final class Listener<State> {
    var onChange: (State) -> Void

    init(on change: @escaping (State) -> Void) {
        onChange = change
    }
}

Example Usage

enum IntAction {
    case increase
    case decrease
}

let root = Store<Int, IntAction>(initial: 0) { action, state in
    switch action {
    case .increase:
        state += 1
    case .decrease:
        state -= 1
    }
}

var sideEffect = -1
var listener: Any? = root.select {
    sideEffect = $0
}

root.dispatch(.increase)
root.state // will be 1
sideEffect // will be 1
listener = nil
root.dispatch(.decrease)
root.state // will be 0
sideEffect // will be 1

Conclusion

Without explicit support for modularity, supporting multiple reducers as above is probably not needed. Instead some composability functions should be used.

Read more ⟶

Archery 0.3.0 released


Archery is about doing something with your project’s metadata. The new version 0.3.0 puts everything on steroids and allows you to script your metadata. A detailed overview of all changes can be found on GitHub.

Archerfile Loaders

At first you will notice the new option to load additional contents into your Archerfile an incredibly open field of new possibilities. The most obvious use case is to collect metadata from multiple configuration files. At a second look you can even script the generation of your metadata.

Read more ⟶

SDE 2.7.0 released


Today I released the new 2.7.0 update to SDE for VS Code and the companion project sourcekite has been updated, too.

The new sourcekite 0.5.0 now supports Swift 5, but drops support for Swift 3. If you still need support for Swift 3.1, I also tagged 0.4.2.

Since 2.6.0, SDE already supported Apple‘s official sourcekit-lsp by using the "sde.languageServerMode": "langserver" and the swift.languageServerPath setting. As announced in Apples SourceKit-LSP and SDE Roadmap SDE 2.7.0 now explicitly mirrors official SourceKit-LSP settings like sourcekit-lsp.serverPath and sourcekit-lsp.toolchainPath. These settings will only be respected when explicitly setting "sde.languageServerMode": "sourcekit-lsp".

Read more ⟶

Apple’s SourceKit LSP and SDE Roadmap


Apple recently announced to develop a language server for Swift and C-family languages. Or said more clearly: Apple started development to support every editor implementing the language server protocol like VS Code, Sublime Text, Jet Brains‘ IDEs and Atom.

Later they published the source code in GitHub including support for VS Code and Sublime Text. It will work on Linux but is currently limited to Swift snapshots and the VS Code extension hasn’t been published yet.

Read more ⟶

Thoughts on: Elegant Objects


Writing things down is part of my learning process. These thoughts came up while reading through Yegor Bugayenko’s book Elegant Objects about a more declarative and less procedural approach of object oriented programming. This is more a personal document than a book review or summary and as I already knew some topics, I do not mention several chapters or details, I might explain concepts different than the original author and many examples from Java, Ruby or C++ cannot be applied to Swift as they would already solve the issue.

Read more ⟶

ArgumentOverture


A Swift Playground aiming to provide some functional helpers to parse arguments for command line tools. It uses Overture and is build for high composability, flexibility and little impact on your project’s freedom to evolve.

A central use case was Archery’s: only actually interpreted arguments shall be consumed. Any others shall be collected (remaining) or should prevent execution (exhaust), depending on the current command.

First of all an example usage.

// Experiment
do {
    let (isVerbose, whoToGreet, language, _) = try with(["-v", "hi", "Some string", "--language", "en"], chain(
        flag("verbose", "v"),
        positional("Name"),
        argument("language", "l"),
        exhaust
    ))
} catch {
    print("Command failed:", error)
}

The implementation of the micro-library itself.

Read more ⟶