Vapor: 4.0.0-alpha.3.2 Release

Release date:
October 1, 2019
Previous version:
4.0.0-alpha.3.1.1 (released August 29, 2019)
Magnitude:
156 Diff Delta
Contributors:
1 total committer
Data confidence:
Commits:

Top Contributors in 4.0.0-alpha.3.2

tanner0101

Directory Browser for 4.0.0-alpha.3.2

We haven't yet finished calculating and confirming the files and directories changed in this release. Please check back soon.

Release Notes Published

  • Added onStop future to Application.Running. (#2061)

Application.runCommands() was renamed to Application.start() and no longer blocks until complete. Application.run() now calls Application.start() and waits for Application.running's onStop future to complete.

Note: This change makes it easier to test Application since you can boot and start without needing a background thread.

  • Added new Services.global method for registering application-wide singletons. (#2062)

With this addition, there are now three ways to register a service:

  • register: This factory will be called each time the service is made.
  • singleton: This factory will be called only once per container.
  • global: This factory will be called only once per application.

Note that global services will be shared across event loops and must be thread-safe.

Below is an example usage of Services.global for creating an application-wide memory cache.

final class MemoryCache {
    var storage: [String: String]
    var lock: Lock

    init() {
        self.storage = [:]
        self.lock = .init()
    }

    func get(_ key: String) -> String? {
        self.lock.lock()
        defer { self.lock.unlock() }
        return self.storage[key]
    }

    func set(_ key: String, to value: String?) {
        self.lock.lock()
        defer { self.lock.unlock() }
        self.storage[key] = value
    }
}

The service can be registered globally:

public func configure(_ s: inout Services) {
    ...
    s.global(MemoryCache.self) { _ in
        return .init()
    }
}

The same instance of MemoryCache is now shared across containers:

public func routes(_ r: Routes, _ c: Container) throws {
    ...
    let cache = try c.make(MemoryCache.self)
    r.get("cache", "get", ":key") { req -> String in
        guard let key = req.parameters.get("key") else {
            throw Abort(.internalServerError)
        }
        return "\(key) = \(cache.get(key) ?? "nil")"
    }
    r.get("cache", "set", ":key", ":value") { req -> String in
        guard let key = req.parameters.get("key") else {
            throw Abort(.internalServerError)
        }
        guard let value = req.parameters.get("value") else {
            throw Abort(.internalServerError)
        }
        cache.set(key, to: value)
        return "\(key) = \(value)"
    }
}

This results in the cached values being accessible from across event loops.