From 34da40f20841ec230fa1bf29ccb5bbc6fd64b3aa Mon Sep 17 00:00:00 2001 From: Adam Jansch Date: Mon, 9 Feb 2026 15:48:17 +0000 Subject: [PATCH 1/5] =?UTF-8?q?Issue=20781=20=E2=80=93=20Crash=20when=20tr?= =?UTF-8?q?ying=20to=20run=20with=20Hummingbird=20library=20install=20on?= =?UTF-8?q?=20iOS=2015=20and=2016?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Lowered minimum deployment versions for iOS and Mac Catalyst to allow launch on iOS versions 15 and 16. --- Package.swift | 2 +- Package@swift-6.0.swift | 2 +- Package@swift-6.1.swift | 2 +- Sources/Hummingbird/Application.swift | 6 ++++++ .../Codable/URLEncodedForm/URLEncodedForm+Request.swift | 2 ++ .../Codable/URLEncodedForm/URLEncodedFormDecoder.swift | 5 +++++ .../Codable/URLEncodedForm/URLEncodedFormEncoder.swift | 5 +++++ .../Codable/URLEncodedForm/URLEncodedFormNode.swift | 1 + Sources/Hummingbird/HTTP/Cookie.swift | 1 + Sources/Hummingbird/HTTP/Cookies.swift | 1 + Sources/Hummingbird/HTTP/Request+Cookies.swift | 1 + Sources/Hummingbird/HTTP/Response+Cookies.swift | 2 ++ Sources/Hummingbird/Middleware/FileMiddleware.swift | 2 ++ Sources/Hummingbird/Middleware/MiddlewareGroup.swift | 1 + Sources/Hummingbird/Middleware/TracingMiddleware.swift | 2 ++ Sources/Hummingbird/Router/EndpointResponder.swift | 1 + Sources/Hummingbird/Router/RouteCollection.swift | 2 ++ Sources/Hummingbird/Router/Router+validation.swift | 1 + Sources/Hummingbird/Router/Router.swift | 1 + Sources/Hummingbird/Router/RouterGroup.swift | 1 + Sources/Hummingbird/Router/RouterMethods.swift | 1 + Sources/Hummingbird/Router/RouterResponder.swift | 1 + Sources/Hummingbird/Server/Request.swift | 1 + Sources/Hummingbird/Server/URI+decodeQuery.swift | 1 + Sources/Hummingbird/Storage/MemoryPersistDriver.swift | 1 + Sources/Hummingbird/Storage/PersistDriver.swift | 2 ++ Sources/Hummingbird/Utils/DateCache.swift | 1 + Sources/Hummingbird/Utils/HTTPHeaderDateFormatStyle.swift | 6 ++++++ Sources/HummingbirdCore/Server/HTTP/HTTPServerBuilder.swift | 1 + Sources/HummingbirdCore/Server/Server.swift | 2 ++ Sources/HummingbirdCore/Server/ServerChildChannel.swift | 1 + 31 files changed, 56 insertions(+), 3 deletions(-) diff --git a/Package.swift b/Package.swift index e377c5b1..ef89283f 100644 --- a/Package.swift +++ b/Package.swift @@ -17,7 +17,7 @@ let swiftSettings: [SwiftSetting] = [ let package = Package( name: "hummingbird", - platforms: [.macOS(.v14), .iOS(.v17), .macCatalyst(.v17), .tvOS(.v17), .visionOS(.v1)], + platforms: [.macOS(.v14), .iOS(.v15), .macCatalyst(.v15), .tvOS(.v17), .visionOS(.v1)], products: [ .library(name: "Hummingbird", targets: ["Hummingbird"]), .library(name: "HummingbirdCore", targets: ["HummingbirdCore"]), diff --git a/Package@swift-6.0.swift b/Package@swift-6.0.swift index 32ea7df3..e9ec9b09 100644 --- a/Package@swift-6.0.swift +++ b/Package@swift-6.0.swift @@ -17,7 +17,7 @@ let swiftSettings: [SwiftSetting] = [ let package = Package( name: "hummingbird", - platforms: [.macOS(.v14), .iOS(.v17), .macCatalyst(.v17), .tvOS(.v17), .visionOS(.v1)], + platforms: [.macOS(.v14), .iOS(.v15), .macCatalyst(.v15), .tvOS(.v17), .visionOS(.v1)], products: [ .library(name: "Hummingbird", targets: ["Hummingbird"]), .library(name: "HummingbirdCore", targets: ["HummingbirdCore"]), diff --git a/Package@swift-6.1.swift b/Package@swift-6.1.swift index 58645c6f..18d3aa4f 100644 --- a/Package@swift-6.1.swift +++ b/Package@swift-6.1.swift @@ -17,7 +17,7 @@ let swiftSettings: [SwiftSetting] = [ let package = Package( name: "hummingbird", - platforms: [.macOS(.v14), .iOS(.v17), .macCatalyst(.v17), .tvOS(.v17), .visionOS(.v1)], + platforms: [.macOS(.v14), .iOS(.v15), .macCatalyst(.v15), .tvOS(.v17), .visionOS(.v1)], products: [ .library(name: "Hummingbird", targets: ["Hummingbird"]), .library(name: "HummingbirdCore", targets: ["HummingbirdCore"]), diff --git a/Sources/Hummingbird/Application.swift b/Sources/Hummingbird/Application.swift index da43e737..dd5c7241 100644 --- a/Sources/Hummingbird/Application.swift +++ b/Sources/Hummingbird/Application.swift @@ -91,6 +91,12 @@ extension ApplicationProtocol { extension ApplicationProtocol { /// Construct application and run it public func run() async throws { + // `#available()` logic must be placed within this method as Service + // protocol requires `run()` method is available from iOS 13+ + guard #available(iOS 17.0, *) else { + return + } + let dateCache = DateCache() let responder = try await self.responder diff --git a/Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedForm+Request.swift b/Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedForm+Request.swift index 3b11b9c8..0c2b399a 100644 --- a/Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedForm+Request.swift +++ b/Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedForm+Request.swift @@ -6,6 +6,7 @@ // SPDX-License-Identifier: Apache-2.0 // +@available(iOS 16, *) extension URLEncodedFormEncoder: ResponseEncoder { /// Extend URLEncodedFormEncoder to support generating a ``HummingbirdCore/Response``. Sets body and header values /// - Parameters: @@ -26,6 +27,7 @@ extension URLEncodedFormEncoder: ResponseEncoder { } } +@available(iOS 16, *) extension URLEncodedFormDecoder: RequestDecoder { /// Extend URLEncodedFormDecoder to decode from ``HummingbirdCore/Request``. /// - Parameters: diff --git a/Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedFormDecoder.swift b/Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedFormDecoder.swift index be7efa2a..6f2217be 100644 --- a/Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedFormDecoder.swift +++ b/Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedFormDecoder.swift @@ -9,6 +9,7 @@ public import Foundation /// The wrapper struct for decoding URL encoded form data to Codable classes +@available(iOS 16, *) public struct URLEncodedFormDecoder: Sendable { /// The strategy to use for decoding `Date` values. public enum DateDecodingStrategy: Sendable { @@ -75,6 +76,7 @@ public struct URLEncodedFormDecoder: Sendable { } } +@available(iOS 16, *) private class _URLEncodedFormDecoder: Decoder { // MARK: Properties @@ -421,6 +423,7 @@ private class _URLEncodedFormDecoder: Decoder { } } +@available(iOS 16, *) extension _URLEncodedFormDecoder: SingleValueDecodingContainer { func decodeNil() -> Bool { (try? self.unboxNil(self.storage.topContainer)) ?? false @@ -487,6 +490,7 @@ extension _URLEncodedFormDecoder: SingleValueDecodingContainer { } } +@available(iOS 16, *) extension _URLEncodedFormDecoder { func unboxNil(_ node: URLEncodedFormNode) throws -> Bool { switch node { @@ -686,6 +690,7 @@ extension _URLEncodedFormDecoder { } } +@available(iOS 16, *) private struct URLEncodedFormDecodingStorage { /// the container stack private var containers: [URLEncodedFormNode] = [] diff --git a/Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedFormEncoder.swift b/Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedFormEncoder.swift index 2c042a55..4f6a6a94 100644 --- a/Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedFormEncoder.swift +++ b/Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedFormEncoder.swift @@ -9,6 +9,7 @@ public import Foundation /// The wrapper struct for encoding Codable classes to URL encoded form data +@available(iOS 16, *) public struct URLEncodedFormEncoder: Sendable { /// The strategy to use for encoding `Date` values. public enum DateEncodingStrategy: Sendable { @@ -80,6 +81,7 @@ public struct URLEncodedFormEncoder: Sendable { } /// Internal QueryEncoder class. Does all the heavy lifting +@available(iOS 16, *) private class _URLEncodedFormEncoder: Encoder { var codingPath: [any CodingKey] @@ -273,6 +275,7 @@ private class _URLEncodedFormEncoder: Encoder { } } +@available(iOS 16, *) extension _URLEncodedFormEncoder: SingleValueEncodingContainer { func encodeResult(_ value: URLEncodedFormNode) { self.storage.push(container: value) @@ -310,6 +313,7 @@ extension _URLEncodedFormEncoder: SingleValueEncodingContainer { } } +@available(iOS 16, *) extension _URLEncodedFormEncoder { func box(_ date: Date) throws -> URLEncodedFormNode { switch self.options.dateEncodingStrategy { @@ -355,6 +359,7 @@ extension _URLEncodedFormEncoder { } /// storage for Query Encoder. Stores a stack of QueryEncoder containers, plus leaf objects +@available(iOS 16, *) private struct URLEncodedFormEncoderStorage { /// the container stack private var containers: [URLEncodedFormNode] = [] diff --git a/Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedFormNode.swift b/Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedFormNode.swift index e15a7f33..80e5007c 100644 --- a/Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedFormNode.swift +++ b/Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedFormNode.swift @@ -71,6 +71,7 @@ extension URLEncodedFormError { } } /// Internal representation of URL encoded form data used by both encode and decode +@available(iOS 16, *) enum URLEncodedFormNode: CustomStringConvertible, Equatable { /// holds a value case leaf(NodeValue?) diff --git a/Sources/Hummingbird/HTTP/Cookie.swift b/Sources/Hummingbird/HTTP/Cookie.swift index e762f1ca..472a5878 100644 --- a/Sources/Hummingbird/HTTP/Cookie.swift +++ b/Sources/Hummingbird/HTTP/Cookie.swift @@ -13,6 +13,7 @@ public import Foundation #endif /// Structure holding a single cookie +@available(iOS 16, *) public struct Cookie: Sendable, CustomStringConvertible { public struct ValidationError: Error { enum Reason { diff --git a/Sources/Hummingbird/HTTP/Cookies.swift b/Sources/Hummingbird/HTTP/Cookies.swift index 3e06399e..55940ef0 100644 --- a/Sources/Hummingbird/HTTP/Cookies.swift +++ b/Sources/Hummingbird/HTTP/Cookies.swift @@ -9,6 +9,7 @@ /// Structure holding an array of cookies /// /// Cookies can be accessed from request via `Request.cookies`. +@available(iOS 16, *) public struct Cookies: Sendable { /// Construct cookies accessor from `Request` /// - Parameter request: request to get cookies from diff --git a/Sources/Hummingbird/HTTP/Request+Cookies.swift b/Sources/Hummingbird/HTTP/Request+Cookies.swift index 5b08dd4c..25703e3d 100644 --- a/Sources/Hummingbird/HTTP/Request+Cookies.swift +++ b/Sources/Hummingbird/HTTP/Request+Cookies.swift @@ -8,6 +8,7 @@ public import HummingbirdCore +@available(iOS 16, *) extension Request { /// access cookies from request. When accessing this for the first time the Cookies struct will be created public var cookies: Cookies { diff --git a/Sources/Hummingbird/HTTP/Response+Cookies.swift b/Sources/Hummingbird/HTTP/Response+Cookies.swift index 6c5d1ceb..1c75a738 100644 --- a/Sources/Hummingbird/HTTP/Response+Cookies.swift +++ b/Sources/Hummingbird/HTTP/Response+Cookies.swift @@ -8,6 +8,7 @@ public import HummingbirdCore +@available(iOS 16, *) extension Response { /// Set cookie on response public mutating func setCookie(_ cookie: Cookie) { @@ -15,6 +16,7 @@ extension Response { } } +@available(iOS 16, *) extension EditedResponse { /// Set cookie on reponse patch /// diff --git a/Sources/Hummingbird/Middleware/FileMiddleware.swift b/Sources/Hummingbird/Middleware/FileMiddleware.swift index 593a25fa..dd9eda49 100644 --- a/Sources/Hummingbird/Middleware/FileMiddleware.swift +++ b/Sources/Hummingbird/Middleware/FileMiddleware.swift @@ -40,6 +40,7 @@ public protocol FileMiddlewareFileAttributes { /// "if-modified-since", "if-none-match", "if-range" and 'range" headers. It will output "content-length", /// "modified-date", "eTag", "content-type", "cache-control" and "content-range" headers where /// they are relevant. +@available(iOS 16, *) public struct FileMiddleware: RouterMiddleware where Provider.FileAttributes: FileMiddlewareFileAttributes { let cacheControl: CacheControl @@ -221,6 +222,7 @@ where Provider.FileAttributes: FileMiddlewareFileAttributes { } } +@available(iOS 16, *) extension FileMiddleware { /// Whether to return data from the file or a not modified response private enum FileResult { diff --git a/Sources/Hummingbird/Middleware/MiddlewareGroup.swift b/Sources/Hummingbird/Middleware/MiddlewareGroup.swift index b74517d5..de39ce86 100644 --- a/Sources/Hummingbird/Middleware/MiddlewareGroup.swift +++ b/Sources/Hummingbird/Middleware/MiddlewareGroup.swift @@ -7,6 +7,7 @@ // /// Group of middleware that can be used to create a responder chain. Each middleware calls the next one +@available(iOS 16, *) public final class MiddlewareGroup { var middlewares: [any MiddlewareProtocol] diff --git a/Sources/Hummingbird/Middleware/TracingMiddleware.swift b/Sources/Hummingbird/Middleware/TracingMiddleware.swift index e02b5ef0..333e83f4 100644 --- a/Sources/Hummingbird/Middleware/TracingMiddleware.swift +++ b/Sources/Hummingbird/Middleware/TracingMiddleware.swift @@ -28,6 +28,7 @@ import Foundation /// Swift-Distributed-Tracing has a flexible backend, which will need to be initialized before any traces are recorded. /// /// A list of implementations is available in the swift-distributed-tracing repository's README. +@available(iOS 16, *) public struct TracingMiddleware: RouterMiddleware { private let headerNamesToRecord: Set private let queryParametersToRedact: Set @@ -196,6 +197,7 @@ public protocol RemoteAddressRequestContext: RequestContext { var remoteAddress: SocketAddress? { get } } +@available(iOS 16, *) struct RecordingHeader: Hashable { let name: HTTPField.Name let attributeName: String diff --git a/Sources/Hummingbird/Router/EndpointResponder.swift b/Sources/Hummingbird/Router/EndpointResponder.swift index 1fbc74e8..f54b5bb9 100644 --- a/Sources/Hummingbird/Router/EndpointResponder.swift +++ b/Sources/Hummingbird/Router/EndpointResponder.swift @@ -9,6 +9,7 @@ public import HTTPTypes /// Stores endpoint responders for each HTTP method +@available(iOS 16, *) @usableFromInline struct EndpointResponders: Sendable { init(path: RouterPath) { diff --git a/Sources/Hummingbird/Router/RouteCollection.swift b/Sources/Hummingbird/Router/RouteCollection.swift index beb34d5a..df3bb1d1 100644 --- a/Sources/Hummingbird/Router/RouteCollection.swift +++ b/Sources/Hummingbird/Router/RouteCollection.swift @@ -9,6 +9,7 @@ public import HTTPTypes /// Collection of routes +@available(iOS 16, *) public final class RouteCollection: RouterMethods { /// Initialize RouteCollection public init(context: Context.Type = BasicRequestContext.self) { @@ -52,6 +53,7 @@ public final class RouteCollection: RouterMethods { let middlewares: MiddlewareGroup } +@available(iOS 16, *) extension RouterMethods { /// Add route collection to router /// - Parameters diff --git a/Sources/Hummingbird/Router/Router+validation.swift b/Sources/Hummingbird/Router/Router+validation.swift index b4d30b3d..3a5580ca 100644 --- a/Sources/Hummingbird/Router/Router+validation.swift +++ b/Sources/Hummingbird/Router/Router+validation.swift @@ -14,6 +14,7 @@ import FoundationEssentials import Foundation #endif +@available(iOS 16, *) extension Router { /// Route description public struct RouteDescription: CustomStringConvertible { diff --git a/Sources/Hummingbird/Router/Router.swift b/Sources/Hummingbird/Router/Router.swift index a384fb63..2df69c47 100644 --- a/Sources/Hummingbird/Router/Router.swift +++ b/Sources/Hummingbird/Router/Router.swift @@ -36,6 +36,7 @@ public import HummingbirdCore /// Both of these match routes which start with "/user" and the next path segment being anything. /// The second version extracts the path segment out and adds it to `Request.parameters` with the /// key "id". +@available(iOS 16, *) public final class Router: RouterMethods, HTTPResponderBuilder { var trie: RouterPathTrieBuilder> public let middlewares: MiddlewareGroup diff --git a/Sources/Hummingbird/Router/RouterGroup.swift b/Sources/Hummingbird/Router/RouterGroup.swift index 2ae7e9f8..cfe34256 100644 --- a/Sources/Hummingbird/Router/RouterGroup.swift +++ b/Sources/Hummingbird/Router/RouterGroup.swift @@ -23,6 +23,7 @@ public import HummingbirdCore /// .put(":id", use: todoController.update) /// .delete(":id", use: todoController.delete) /// ``` +@available(iOS 16, *) public struct RouterGroup: RouterMethods { let path: RouterPath let parent: any RouterMethods diff --git a/Sources/Hummingbird/Router/RouterMethods.swift b/Sources/Hummingbird/Router/RouterMethods.swift index 2737f08e..03b072e6 100644 --- a/Sources/Hummingbird/Router/RouterMethods.swift +++ b/Sources/Hummingbird/Router/RouterMethods.swift @@ -34,6 +34,7 @@ public protocol RouterMethods: _HB_SendableMetatype { func add(middleware: any MiddlewareProtocol) -> Self } +@available(iOS 16, *) extension RouterMethods { /// Add path for async closure @discardableResult public func on( diff --git a/Sources/Hummingbird/Router/RouterResponder.swift b/Sources/Hummingbird/Router/RouterResponder.swift index c635fff7..77b3173f 100644 --- a/Sources/Hummingbird/Router/RouterResponder.swift +++ b/Sources/Hummingbird/Router/RouterResponder.swift @@ -8,6 +8,7 @@ import NIOCore +@available(iOS 16, *) public struct RouterResponder: HTTPResponder { @usableFromInline let trie: RouterTrie> diff --git a/Sources/Hummingbird/Server/Request.swift b/Sources/Hummingbird/Server/Request.swift index 405eef3d..83230cc0 100644 --- a/Sources/Hummingbird/Server/Request.swift +++ b/Sources/Hummingbird/Server/Request.swift @@ -58,6 +58,7 @@ extension Request { } } +@available(iOS 16, *) extension Request { /// Conditional request which will only be processed if the eTag supplied is not in the /// `If-None-Match` request header. diff --git a/Sources/Hummingbird/Server/URI+decodeQuery.swift b/Sources/Hummingbird/Server/URI+decodeQuery.swift index 8e530a61..50664d8a 100644 --- a/Sources/Hummingbird/Server/URI+decodeQuery.swift +++ b/Sources/Hummingbird/Server/URI+decodeQuery.swift @@ -9,6 +9,7 @@ public import HummingbirdCore import Logging +@available(iOS 16, *) extension URI { /// Decode request query using ``Hummingbird/URLEncodedFormDecoder``. /// - Parameters diff --git a/Sources/Hummingbird/Storage/MemoryPersistDriver.swift b/Sources/Hummingbird/Storage/MemoryPersistDriver.swift index 9401c8e1..a48bd122 100644 --- a/Sources/Hummingbird/Storage/MemoryPersistDriver.swift +++ b/Sources/Hummingbird/Storage/MemoryPersistDriver.swift @@ -12,6 +12,7 @@ import NIOCore import ServiceLifecycle /// In memory driver for persist system for storing persistent cross request key/value pairs +@available(iOS 16, *) public actor MemoryPersistDriver: PersistDriver where C.Duration == Duration { public struct Configuration: Sendable { /// amount of time between each call to tidy diff --git a/Sources/Hummingbird/Storage/PersistDriver.swift b/Sources/Hummingbird/Storage/PersistDriver.swift index 58a3c12f..82051c60 100644 --- a/Sources/Hummingbird/Storage/PersistDriver.swift +++ b/Sources/Hummingbird/Storage/PersistDriver.swift @@ -9,6 +9,7 @@ public import ServiceLifecycle /// Protocol for driver supporting persistent Key/Value pairs across requests +@available(iOS 16, *) public protocol PersistDriver: Service { /// shutdown driver func shutdown() async throws @@ -40,6 +41,7 @@ public protocol PersistDriver: Service { func remove(key: String) async throws } +@available(iOS 16, *) extension PersistDriver { /// default implemenation of shutdown() public func shutdown() async throws {} diff --git a/Sources/Hummingbird/Utils/DateCache.swift b/Sources/Hummingbird/Utils/DateCache.swift index d1404f9f..dbe66b6e 100644 --- a/Sources/Hummingbird/Utils/DateCache.swift +++ b/Sources/Hummingbird/Utils/DateCache.swift @@ -22,6 +22,7 @@ import Foundation /// /// Getting the current date formatted is an expensive operation. This creates a task that will /// update a cached version of the date in the format as detailed in RFC9110 once every second. +@available(iOS 16, *) final class DateCache: Service { final class DateContainer: AtomicReference, Sendable { let date: String diff --git a/Sources/Hummingbird/Utils/HTTPHeaderDateFormatStyle.swift b/Sources/Hummingbird/Utils/HTTPHeaderDateFormatStyle.swift index 30fd566b..585547b7 100644 --- a/Sources/Hummingbird/Utils/HTTPHeaderDateFormatStyle.swift +++ b/Sources/Hummingbird/Utils/HTTPHeaderDateFormatStyle.swift @@ -18,6 +18,7 @@ import FoundationEssentials import Foundation #endif +@available(iOS 16, *) extension Date { init?(httpHeader: String) { try? self.init(httpHeader, strategy: .rfc9110) @@ -30,6 +31,7 @@ extension Date { struct HTTPHeaderDateParsingError: Error {} +@available(iOS 16, *) struct HTTPHeaderDateFormatStyle { let calendar: Calendar @@ -40,6 +42,7 @@ struct HTTPHeaderDateFormatStyle { } } +@available(iOS 16, *) extension HTTPHeaderDateFormatStyle: ParseStrategy { func parse(_ input: String) throws -> Date { guard let components = self.components(from: input) else { @@ -275,6 +278,7 @@ let timezoneOffsetMap: [[UInt8]: Int] = [ Array("PDT".utf8): -7 * 60, ] +@available(iOS 16, *) extension HTTPHeaderDateFormatStyle: FormatStyle { //let calendar: Calendar @@ -319,10 +323,12 @@ extension HTTPHeaderDateFormatStyle: FormatStyle { ] } +@available(iOS 16, *) extension FormatStyle where Self == HTTPHeaderDateFormatStyle { static var rfc9110: Self { .init() } } +@available(iOS 16, *) extension ParseStrategy where Self == HTTPHeaderDateFormatStyle { static var rfc9110: Self { .init() } } diff --git a/Sources/HummingbirdCore/Server/HTTP/HTTPServerBuilder.swift b/Sources/HummingbirdCore/Server/HTTP/HTTPServerBuilder.swift index c98884d9..61a12d4d 100644 --- a/Sources/HummingbirdCore/Server/HTTP/HTTPServerBuilder.swift +++ b/Sources/HummingbirdCore/Server/HTTP/HTTPServerBuilder.swift @@ -32,6 +32,7 @@ public struct HTTPServerBuilder: Sendable { /// - responder: HTTP responder /// - onServerRunning: Closure to run once server is up and running /// - Returns: Server Service + @available(iOS 17, *) public func buildServer( configuration: ServerConfiguration, eventLoopGroup: any EventLoopGroup, diff --git a/Sources/HummingbirdCore/Server/Server.swift b/Sources/HummingbirdCore/Server/Server.swift index ced1fad5..3cbee590 100644 --- a/Sources/HummingbirdCore/Server/Server.swift +++ b/Sources/HummingbirdCore/Server/Server.swift @@ -18,6 +18,7 @@ import NIOTransportServices #endif /// HTTP server class +@available(iOS 17, *) public actor Server: Service { public typealias AsyncChildChannel = ChildChannel.Value public typealias AsyncServerChannel = NIOAsyncChannel @@ -361,6 +362,7 @@ extension NIOTSListenerBootstrap: ServerBootstrapProtocol { } #endif +@available(iOS 17, *) extension Server: CustomStringConvertible { public nonisolated var description: String { "Hummingbird" diff --git a/Sources/HummingbirdCore/Server/ServerChildChannel.swift b/Sources/HummingbirdCore/Server/ServerChildChannel.swift index e556eded..ed2ba007 100644 --- a/Sources/HummingbirdCore/Server/ServerChildChannel.swift +++ b/Sources/HummingbirdCore/Server/ServerChildChannel.swift @@ -34,6 +34,7 @@ public protocol ServerChildChannel: Sendable { func handle(value: Value, logger: Logger) async } +@available(iOS 17, *) extension ServerChildChannel { /// Build existential ``Server`` from existential `ServerChildChannel` /// From b94bc6e87cf233a2cea6eac22ffbe1ef11a382b4 Mon Sep 17 00:00:00 2001 From: Adam Jansch Date: Tue, 10 Feb 2026 21:53:52 +0000 Subject: [PATCH 2/5] =?UTF-8?q?=20Issue=20781=20=E2=80=93=20Crash=20when?= =?UTF-8?q?=20trying=20to=20run=20with=20Hummingbird=20library=20install?= =?UTF-8?q?=20on=20iOS=2015=20and=2016?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Included macOS (native), tvOS, and visionOS into the lower version party. --- Package.swift | 2 +- Package@swift-6.0.swift | 2 +- Package@swift-6.1.swift | 2 +- Sources/Hummingbird/Application.swift | 2 +- .../URLEncodedForm+Request.swift | 4 +- .../URLEncodedFormDecoder.swift | 10 +- .../URLEncodedFormEncoder.swift | 10 +- .../URLEncodedForm/URLEncodedFormNode.swift | 2 +- Sources/Hummingbird/HTTP/Cookie.swift | 2 +- Sources/Hummingbird/HTTP/Cookies.swift | 2 +- .../Hummingbird/HTTP/Request+Cookies.swift | 2 +- .../Hummingbird/HTTP/Response+Cookies.swift | 4 +- .../Middleware/FileMiddleware.swift | 4 +- .../Middleware/MiddlewareGroup.swift | 2 +- .../Middleware/TracingMiddleware.swift | 4 +- .../Router/EndpointResponder.swift | 2 +- .../Hummingbird/Router/RouteCollection.swift | 4 +- .../Router/Router+validation.swift | 2 +- Sources/Hummingbird/Router/Router.swift | 2 +- Sources/Hummingbird/Router/RouterGroup.swift | 2 +- .../Hummingbird/Router/RouterMethods.swift | 2 +- .../Hummingbird/Router/RouterResponder.swift | 2 +- Sources/Hummingbird/Server/Request.swift | 2 +- .../Hummingbird/Server/URI+decodeQuery.swift | 2 +- .../Storage/MemoryPersistDriver.swift | 2 +- .../Hummingbird/Storage/PersistDriver.swift | 4 +- Sources/Hummingbird/Utils/DateCache.swift | 2 +- .../Utils/HTTPHeaderDateFormatStyle.swift | 12 +- Sources/HummingbirdCore/Server/Server.swift | 6 + Sources/HummingbirdHTTP2/HTTP2Channel.swift | 2 + ...ServerConnectionManager+StateMachine.swift | 4 + .../HTTP2ServerConnectionManager.swift | 3 + .../HTTP2UpgradeChannel.swift | 1 + .../HTTPServerBuilder+http2.swift | 1 + .../HummingbirdTesting/Application+Test.swift | 1 + .../LiveTestFramework.swift | 1 + .../HummingbirdTesting/TestClient+types.swift | 1 + Sources/HummingbirdTesting/TestClient.swift | 1 + Sources/PerformanceTest/main.swift | 110 +++++++++--------- 39 files changed, 124 insertions(+), 101 deletions(-) diff --git a/Package.swift b/Package.swift index ef89283f..93d9c899 100644 --- a/Package.swift +++ b/Package.swift @@ -17,7 +17,7 @@ let swiftSettings: [SwiftSetting] = [ let package = Package( name: "hummingbird", - platforms: [.macOS(.v14), .iOS(.v15), .macCatalyst(.v15), .tvOS(.v17), .visionOS(.v1)], + platforms: [.macOS(.v11), .iOS(.v15), .macCatalyst(.v15), .tvOS(.v15), .visionOS(.v1)], products: [ .library(name: "Hummingbird", targets: ["Hummingbird"]), .library(name: "HummingbirdCore", targets: ["HummingbirdCore"]), diff --git a/Package@swift-6.0.swift b/Package@swift-6.0.swift index e9ec9b09..3d6b3bdd 100644 --- a/Package@swift-6.0.swift +++ b/Package@swift-6.0.swift @@ -17,7 +17,7 @@ let swiftSettings: [SwiftSetting] = [ let package = Package( name: "hummingbird", - platforms: [.macOS(.v14), .iOS(.v15), .macCatalyst(.v15), .tvOS(.v17), .visionOS(.v1)], + platforms: [.macOS(.v11), .iOS(.v15), .macCatalyst(.v15), .tvOS(.v15), .visionOS(.v1)], products: [ .library(name: "Hummingbird", targets: ["Hummingbird"]), .library(name: "HummingbirdCore", targets: ["HummingbirdCore"]), diff --git a/Package@swift-6.1.swift b/Package@swift-6.1.swift index 18d3aa4f..083df979 100644 --- a/Package@swift-6.1.swift +++ b/Package@swift-6.1.swift @@ -17,7 +17,7 @@ let swiftSettings: [SwiftSetting] = [ let package = Package( name: "hummingbird", - platforms: [.macOS(.v14), .iOS(.v15), .macCatalyst(.v15), .tvOS(.v17), .visionOS(.v1)], + platforms: [.macOS(.v11), .iOS(.v15), .macCatalyst(.v15), .tvOS(.v15), .visionOS(.v1)], products: [ .library(name: "Hummingbird", targets: ["Hummingbird"]), .library(name: "HummingbirdCore", targets: ["HummingbirdCore"]), diff --git a/Sources/Hummingbird/Application.swift b/Sources/Hummingbird/Application.swift index dd5c7241..2559b85f 100644 --- a/Sources/Hummingbird/Application.swift +++ b/Sources/Hummingbird/Application.swift @@ -93,7 +93,7 @@ extension ApplicationProtocol { public func run() async throws { // `#available()` logic must be placed within this method as Service // protocol requires `run()` method is available from iOS 13+ - guard #available(iOS 17.0, *) else { + guard #available(macOS 13, iOS 17, tvOS 16, *) else { return } diff --git a/Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedForm+Request.swift b/Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedForm+Request.swift index 0c2b399a..7b2df378 100644 --- a/Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedForm+Request.swift +++ b/Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedForm+Request.swift @@ -6,7 +6,7 @@ // SPDX-License-Identifier: Apache-2.0 // -@available(iOS 16, *) +@available(macOS 13, iOS 16, tvOS 16, *) extension URLEncodedFormEncoder: ResponseEncoder { /// Extend URLEncodedFormEncoder to support generating a ``HummingbirdCore/Response``. Sets body and header values /// - Parameters: @@ -27,7 +27,7 @@ extension URLEncodedFormEncoder: ResponseEncoder { } } -@available(iOS 16, *) +@available(macOS 13, iOS 16, tvOS 16, *) extension URLEncodedFormDecoder: RequestDecoder { /// Extend URLEncodedFormDecoder to decode from ``HummingbirdCore/Request``. /// - Parameters: diff --git a/Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedFormDecoder.swift b/Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedFormDecoder.swift index 6f2217be..9667b006 100644 --- a/Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedFormDecoder.swift +++ b/Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedFormDecoder.swift @@ -9,7 +9,7 @@ public import Foundation /// The wrapper struct for decoding URL encoded form data to Codable classes -@available(iOS 16, *) +@available(macOS 13, iOS 16, tvOS 16, *) public struct URLEncodedFormDecoder: Sendable { /// The strategy to use for decoding `Date` values. public enum DateDecodingStrategy: Sendable { @@ -76,7 +76,7 @@ public struct URLEncodedFormDecoder: Sendable { } } -@available(iOS 16, *) +@available(macOS 13, iOS 16, tvOS 16, *) private class _URLEncodedFormDecoder: Decoder { // MARK: Properties @@ -423,7 +423,7 @@ private class _URLEncodedFormDecoder: Decoder { } } -@available(iOS 16, *) +@available(macOS 13, iOS 16, tvOS 16, *) extension _URLEncodedFormDecoder: SingleValueDecodingContainer { func decodeNil() -> Bool { (try? self.unboxNil(self.storage.topContainer)) ?? false @@ -490,7 +490,7 @@ extension _URLEncodedFormDecoder: SingleValueDecodingContainer { } } -@available(iOS 16, *) +@available(macOS 13, iOS 16, tvOS 16, *) extension _URLEncodedFormDecoder { func unboxNil(_ node: URLEncodedFormNode) throws -> Bool { switch node { @@ -690,7 +690,7 @@ extension _URLEncodedFormDecoder { } } -@available(iOS 16, *) +@available(macOS 13, iOS 16, tvOS 16, *) private struct URLEncodedFormDecodingStorage { /// the container stack private var containers: [URLEncodedFormNode] = [] diff --git a/Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedFormEncoder.swift b/Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedFormEncoder.swift index 4f6a6a94..7c1fef7b 100644 --- a/Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedFormEncoder.swift +++ b/Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedFormEncoder.swift @@ -9,7 +9,7 @@ public import Foundation /// The wrapper struct for encoding Codable classes to URL encoded form data -@available(iOS 16, *) +@available(macOS 13, iOS 16, tvOS 16, *) public struct URLEncodedFormEncoder: Sendable { /// The strategy to use for encoding `Date` values. public enum DateEncodingStrategy: Sendable { @@ -81,7 +81,7 @@ public struct URLEncodedFormEncoder: Sendable { } /// Internal QueryEncoder class. Does all the heavy lifting -@available(iOS 16, *) +@available(macOS 13, iOS 16, tvOS 16, *) private class _URLEncodedFormEncoder: Encoder { var codingPath: [any CodingKey] @@ -275,7 +275,7 @@ private class _URLEncodedFormEncoder: Encoder { } } -@available(iOS 16, *) +@available(macOS 13, iOS 16, tvOS 16, *) extension _URLEncodedFormEncoder: SingleValueEncodingContainer { func encodeResult(_ value: URLEncodedFormNode) { self.storage.push(container: value) @@ -313,7 +313,7 @@ extension _URLEncodedFormEncoder: SingleValueEncodingContainer { } } -@available(iOS 16, *) +@available(macOS 13, iOS 16, tvOS 16, *) extension _URLEncodedFormEncoder { func box(_ date: Date) throws -> URLEncodedFormNode { switch self.options.dateEncodingStrategy { @@ -359,7 +359,7 @@ extension _URLEncodedFormEncoder { } /// storage for Query Encoder. Stores a stack of QueryEncoder containers, plus leaf objects -@available(iOS 16, *) +@available(macOS 13, iOS 16, tvOS 16, *) private struct URLEncodedFormEncoderStorage { /// the container stack private var containers: [URLEncodedFormNode] = [] diff --git a/Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedFormNode.swift b/Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedFormNode.swift index 80e5007c..46ab9e9b 100644 --- a/Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedFormNode.swift +++ b/Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedFormNode.swift @@ -71,7 +71,7 @@ extension URLEncodedFormError { } } /// Internal representation of URL encoded form data used by both encode and decode -@available(iOS 16, *) +@available(macOS 13, iOS 16, tvOS 16, *) enum URLEncodedFormNode: CustomStringConvertible, Equatable { /// holds a value case leaf(NodeValue?) diff --git a/Sources/Hummingbird/HTTP/Cookie.swift b/Sources/Hummingbird/HTTP/Cookie.swift index 472a5878..70403618 100644 --- a/Sources/Hummingbird/HTTP/Cookie.swift +++ b/Sources/Hummingbird/HTTP/Cookie.swift @@ -13,7 +13,7 @@ public import Foundation #endif /// Structure holding a single cookie -@available(iOS 16, *) +@available(macOS 13, iOS 16, tvOS 16, *) public struct Cookie: Sendable, CustomStringConvertible { public struct ValidationError: Error { enum Reason { diff --git a/Sources/Hummingbird/HTTP/Cookies.swift b/Sources/Hummingbird/HTTP/Cookies.swift index 55940ef0..a3a863ca 100644 --- a/Sources/Hummingbird/HTTP/Cookies.swift +++ b/Sources/Hummingbird/HTTP/Cookies.swift @@ -9,7 +9,7 @@ /// Structure holding an array of cookies /// /// Cookies can be accessed from request via `Request.cookies`. -@available(iOS 16, *) +@available(macOS 13, iOS 16, tvOS 16, *) public struct Cookies: Sendable { /// Construct cookies accessor from `Request` /// - Parameter request: request to get cookies from diff --git a/Sources/Hummingbird/HTTP/Request+Cookies.swift b/Sources/Hummingbird/HTTP/Request+Cookies.swift index 25703e3d..96d9be5d 100644 --- a/Sources/Hummingbird/HTTP/Request+Cookies.swift +++ b/Sources/Hummingbird/HTTP/Request+Cookies.swift @@ -8,7 +8,7 @@ public import HummingbirdCore -@available(iOS 16, *) +@available(macOS 13, iOS 16, tvOS 16, *) extension Request { /// access cookies from request. When accessing this for the first time the Cookies struct will be created public var cookies: Cookies { diff --git a/Sources/Hummingbird/HTTP/Response+Cookies.swift b/Sources/Hummingbird/HTTP/Response+Cookies.swift index 1c75a738..9a244a29 100644 --- a/Sources/Hummingbird/HTTP/Response+Cookies.swift +++ b/Sources/Hummingbird/HTTP/Response+Cookies.swift @@ -8,7 +8,7 @@ public import HummingbirdCore -@available(iOS 16, *) +@available(macOS 13, iOS 16, tvOS 16, *) extension Response { /// Set cookie on response public mutating func setCookie(_ cookie: Cookie) { @@ -16,7 +16,7 @@ extension Response { } } -@available(iOS 16, *) +@available(macOS 13, iOS 16, tvOS 16, *) extension EditedResponse { /// Set cookie on reponse patch /// diff --git a/Sources/Hummingbird/Middleware/FileMiddleware.swift b/Sources/Hummingbird/Middleware/FileMiddleware.swift index dd9eda49..d1a2b5d9 100644 --- a/Sources/Hummingbird/Middleware/FileMiddleware.swift +++ b/Sources/Hummingbird/Middleware/FileMiddleware.swift @@ -40,7 +40,7 @@ public protocol FileMiddlewareFileAttributes { /// "if-modified-since", "if-none-match", "if-range" and 'range" headers. It will output "content-length", /// "modified-date", "eTag", "content-type", "cache-control" and "content-range" headers where /// they are relevant. -@available(iOS 16, *) +@available(macOS 13, iOS 16, tvOS 16, *) public struct FileMiddleware: RouterMiddleware where Provider.FileAttributes: FileMiddlewareFileAttributes { let cacheControl: CacheControl @@ -222,7 +222,7 @@ where Provider.FileAttributes: FileMiddlewareFileAttributes { } } -@available(iOS 16, *) +@available(macOS 13, iOS 16, tvOS 16, *) extension FileMiddleware { /// Whether to return data from the file or a not modified response private enum FileResult { diff --git a/Sources/Hummingbird/Middleware/MiddlewareGroup.swift b/Sources/Hummingbird/Middleware/MiddlewareGroup.swift index de39ce86..5d7cfc03 100644 --- a/Sources/Hummingbird/Middleware/MiddlewareGroup.swift +++ b/Sources/Hummingbird/Middleware/MiddlewareGroup.swift @@ -7,7 +7,7 @@ // /// Group of middleware that can be used to create a responder chain. Each middleware calls the next one -@available(iOS 16, *) +@available(macOS 13, iOS 16, tvOS 16, *) public final class MiddlewareGroup { var middlewares: [any MiddlewareProtocol] diff --git a/Sources/Hummingbird/Middleware/TracingMiddleware.swift b/Sources/Hummingbird/Middleware/TracingMiddleware.swift index 333e83f4..6a84639a 100644 --- a/Sources/Hummingbird/Middleware/TracingMiddleware.swift +++ b/Sources/Hummingbird/Middleware/TracingMiddleware.swift @@ -28,7 +28,7 @@ import Foundation /// Swift-Distributed-Tracing has a flexible backend, which will need to be initialized before any traces are recorded. /// /// A list of implementations is available in the swift-distributed-tracing repository's README. -@available(iOS 16, *) +@available(macOS 13, iOS 16, tvOS 16, *) public struct TracingMiddleware: RouterMiddleware { private let headerNamesToRecord: Set private let queryParametersToRedact: Set @@ -197,7 +197,7 @@ public protocol RemoteAddressRequestContext: RequestContext { var remoteAddress: SocketAddress? { get } } -@available(iOS 16, *) +@available(macOS 13, iOS 16, tvOS 16, *) struct RecordingHeader: Hashable { let name: HTTPField.Name let attributeName: String diff --git a/Sources/Hummingbird/Router/EndpointResponder.swift b/Sources/Hummingbird/Router/EndpointResponder.swift index f54b5bb9..5cf5f04a 100644 --- a/Sources/Hummingbird/Router/EndpointResponder.swift +++ b/Sources/Hummingbird/Router/EndpointResponder.swift @@ -9,7 +9,7 @@ public import HTTPTypes /// Stores endpoint responders for each HTTP method -@available(iOS 16, *) +@available(macOS 13, iOS 16, tvOS 16, *) @usableFromInline struct EndpointResponders: Sendable { init(path: RouterPath) { diff --git a/Sources/Hummingbird/Router/RouteCollection.swift b/Sources/Hummingbird/Router/RouteCollection.swift index df3bb1d1..2db96d07 100644 --- a/Sources/Hummingbird/Router/RouteCollection.swift +++ b/Sources/Hummingbird/Router/RouteCollection.swift @@ -9,7 +9,7 @@ public import HTTPTypes /// Collection of routes -@available(iOS 16, *) +@available(macOS 13, iOS 16, tvOS 16, *) public final class RouteCollection: RouterMethods { /// Initialize RouteCollection public init(context: Context.Type = BasicRequestContext.self) { @@ -53,7 +53,7 @@ public final class RouteCollection: RouterMethods { let middlewares: MiddlewareGroup } -@available(iOS 16, *) +@available(macOS 13, iOS 16, tvOS 16, *) extension RouterMethods { /// Add route collection to router /// - Parameters diff --git a/Sources/Hummingbird/Router/Router+validation.swift b/Sources/Hummingbird/Router/Router+validation.swift index 3a5580ca..1c20c705 100644 --- a/Sources/Hummingbird/Router/Router+validation.swift +++ b/Sources/Hummingbird/Router/Router+validation.swift @@ -14,7 +14,7 @@ import FoundationEssentials import Foundation #endif -@available(iOS 16, *) +@available(macOS 13, iOS 16, tvOS 16, *) extension Router { /// Route description public struct RouteDescription: CustomStringConvertible { diff --git a/Sources/Hummingbird/Router/Router.swift b/Sources/Hummingbird/Router/Router.swift index 2df69c47..e1b23176 100644 --- a/Sources/Hummingbird/Router/Router.swift +++ b/Sources/Hummingbird/Router/Router.swift @@ -36,7 +36,7 @@ public import HummingbirdCore /// Both of these match routes which start with "/user" and the next path segment being anything. /// The second version extracts the path segment out and adds it to `Request.parameters` with the /// key "id". -@available(iOS 16, *) +@available(macOS 13, iOS 16, tvOS 16, *) public final class Router: RouterMethods, HTTPResponderBuilder { var trie: RouterPathTrieBuilder> public let middlewares: MiddlewareGroup diff --git a/Sources/Hummingbird/Router/RouterGroup.swift b/Sources/Hummingbird/Router/RouterGroup.swift index cfe34256..5bfae9e3 100644 --- a/Sources/Hummingbird/Router/RouterGroup.swift +++ b/Sources/Hummingbird/Router/RouterGroup.swift @@ -23,7 +23,7 @@ public import HummingbirdCore /// .put(":id", use: todoController.update) /// .delete(":id", use: todoController.delete) /// ``` -@available(iOS 16, *) +@available(macOS 13, iOS 16, tvOS 16, *) public struct RouterGroup: RouterMethods { let path: RouterPath let parent: any RouterMethods diff --git a/Sources/Hummingbird/Router/RouterMethods.swift b/Sources/Hummingbird/Router/RouterMethods.swift index 03b072e6..4780ec0e 100644 --- a/Sources/Hummingbird/Router/RouterMethods.swift +++ b/Sources/Hummingbird/Router/RouterMethods.swift @@ -34,7 +34,7 @@ public protocol RouterMethods: _HB_SendableMetatype { func add(middleware: any MiddlewareProtocol) -> Self } -@available(iOS 16, *) +@available(macOS 13, iOS 16, tvOS 16, *) extension RouterMethods { /// Add path for async closure @discardableResult public func on( diff --git a/Sources/Hummingbird/Router/RouterResponder.swift b/Sources/Hummingbird/Router/RouterResponder.swift index 77b3173f..68e65d9a 100644 --- a/Sources/Hummingbird/Router/RouterResponder.swift +++ b/Sources/Hummingbird/Router/RouterResponder.swift @@ -8,7 +8,7 @@ import NIOCore -@available(iOS 16, *) +@available(macOS 13, iOS 16, tvOS 16, *) public struct RouterResponder: HTTPResponder { @usableFromInline let trie: RouterTrie> diff --git a/Sources/Hummingbird/Server/Request.swift b/Sources/Hummingbird/Server/Request.swift index 83230cc0..388b764f 100644 --- a/Sources/Hummingbird/Server/Request.swift +++ b/Sources/Hummingbird/Server/Request.swift @@ -58,7 +58,7 @@ extension Request { } } -@available(iOS 16, *) +@available(macOS 13, iOS 16, tvOS 16, *) extension Request { /// Conditional request which will only be processed if the eTag supplied is not in the /// `If-None-Match` request header. diff --git a/Sources/Hummingbird/Server/URI+decodeQuery.swift b/Sources/Hummingbird/Server/URI+decodeQuery.swift index 50664d8a..26e1ac25 100644 --- a/Sources/Hummingbird/Server/URI+decodeQuery.swift +++ b/Sources/Hummingbird/Server/URI+decodeQuery.swift @@ -9,7 +9,7 @@ public import HummingbirdCore import Logging -@available(iOS 16, *) +@available(macOS 13, iOS 16, tvOS 16, *) extension URI { /// Decode request query using ``Hummingbird/URLEncodedFormDecoder``. /// - Parameters diff --git a/Sources/Hummingbird/Storage/MemoryPersistDriver.swift b/Sources/Hummingbird/Storage/MemoryPersistDriver.swift index a48bd122..e49d521a 100644 --- a/Sources/Hummingbird/Storage/MemoryPersistDriver.swift +++ b/Sources/Hummingbird/Storage/MemoryPersistDriver.swift @@ -12,7 +12,7 @@ import NIOCore import ServiceLifecycle /// In memory driver for persist system for storing persistent cross request key/value pairs -@available(iOS 16, *) +@available(macOS 13, iOS 16, tvOS 16, *) public actor MemoryPersistDriver: PersistDriver where C.Duration == Duration { public struct Configuration: Sendable { /// amount of time between each call to tidy diff --git a/Sources/Hummingbird/Storage/PersistDriver.swift b/Sources/Hummingbird/Storage/PersistDriver.swift index 82051c60..fc5b7cf4 100644 --- a/Sources/Hummingbird/Storage/PersistDriver.swift +++ b/Sources/Hummingbird/Storage/PersistDriver.swift @@ -9,7 +9,7 @@ public import ServiceLifecycle /// Protocol for driver supporting persistent Key/Value pairs across requests -@available(iOS 16, *) +@available(macOS 13, iOS 16, tvOS 16, *) public protocol PersistDriver: Service { /// shutdown driver func shutdown() async throws @@ -41,7 +41,7 @@ public protocol PersistDriver: Service { func remove(key: String) async throws } -@available(iOS 16, *) +@available(macOS 13, iOS 16, tvOS 16, *) extension PersistDriver { /// default implemenation of shutdown() public func shutdown() async throws {} diff --git a/Sources/Hummingbird/Utils/DateCache.swift b/Sources/Hummingbird/Utils/DateCache.swift index dbe66b6e..b8c4176a 100644 --- a/Sources/Hummingbird/Utils/DateCache.swift +++ b/Sources/Hummingbird/Utils/DateCache.swift @@ -22,7 +22,7 @@ import Foundation /// /// Getting the current date formatted is an expensive operation. This creates a task that will /// update a cached version of the date in the format as detailed in RFC9110 once every second. -@available(iOS 16, *) +@available(macOS 13, iOS 16, tvOS 16, *) final class DateCache: Service { final class DateContainer: AtomicReference, Sendable { let date: String diff --git a/Sources/Hummingbird/Utils/HTTPHeaderDateFormatStyle.swift b/Sources/Hummingbird/Utils/HTTPHeaderDateFormatStyle.swift index 585547b7..6c770dac 100644 --- a/Sources/Hummingbird/Utils/HTTPHeaderDateFormatStyle.swift +++ b/Sources/Hummingbird/Utils/HTTPHeaderDateFormatStyle.swift @@ -18,7 +18,7 @@ import FoundationEssentials import Foundation #endif -@available(iOS 16, *) +@available(macOS 13, iOS 16, tvOS 16, *) extension Date { init?(httpHeader: String) { try? self.init(httpHeader, strategy: .rfc9110) @@ -31,7 +31,7 @@ extension Date { struct HTTPHeaderDateParsingError: Error {} -@available(iOS 16, *) +@available(macOS 13, iOS 16, tvOS 16, *) struct HTTPHeaderDateFormatStyle { let calendar: Calendar @@ -42,7 +42,7 @@ struct HTTPHeaderDateFormatStyle { } } -@available(iOS 16, *) +@available(macOS 13, iOS 16, tvOS 16, *) extension HTTPHeaderDateFormatStyle: ParseStrategy { func parse(_ input: String) throws -> Date { guard let components = self.components(from: input) else { @@ -278,7 +278,7 @@ let timezoneOffsetMap: [[UInt8]: Int] = [ Array("PDT".utf8): -7 * 60, ] -@available(iOS 16, *) +@available(macOS 13, iOS 16, tvOS 16, *) extension HTTPHeaderDateFormatStyle: FormatStyle { //let calendar: Calendar @@ -323,12 +323,12 @@ extension HTTPHeaderDateFormatStyle: FormatStyle { ] } -@available(iOS 16, *) +@available(macOS 13, iOS 16, tvOS 16, *) extension FormatStyle where Self == HTTPHeaderDateFormatStyle { static var rfc9110: Self { .init() } } -@available(iOS 16, *) +@available(macOS 13, iOS 16, tvOS 16, *) extension ParseStrategy where Self == HTTPHeaderDateFormatStyle { static var rfc9110: Self { .init() } } diff --git a/Sources/HummingbirdCore/Server/Server.swift b/Sources/HummingbirdCore/Server/Server.swift index 3cbee590..5b2620c4 100644 --- a/Sources/HummingbirdCore/Server/Server.swift +++ b/Sources/HummingbirdCore/Server/Server.swift @@ -90,6 +90,12 @@ public actor Server: Service { } public func run() async throws { + // `#available()` logic must be placed within this method as Service + // protocol requires `run()` method is available from iOS 13+ + guard #available(macOS 14, iOS 15, tvOS 17, *) else { + return + } + switch self.state { case .initial(let childChannelSetup, let configuration, let onServerRunning): self.state = .starting diff --git a/Sources/HummingbirdHTTP2/HTTP2Channel.swift b/Sources/HummingbirdHTTP2/HTTP2Channel.swift index 711c5ca7..c22cab23 100644 --- a/Sources/HummingbirdHTTP2/HTTP2Channel.swift +++ b/Sources/HummingbirdHTTP2/HTTP2Channel.swift @@ -15,6 +15,7 @@ import NIOHTTPTypesHTTP2 import NIOSSL /// HTTP2 configuration +@available(macOS 13, iOS 16, tvOS 16, *) public struct HTTP2ChannelConfiguration: Sendable { /// Idle timeout, how long connection is kept idle before closing public var idleTimeout: Duration? @@ -44,6 +45,7 @@ public struct HTTP2ChannelConfiguration: Sendable { } /// Child channel for processing HTTP2 +@available(macOS 14, iOS 17, tvOS 17, *) public struct HTTP2Channel: ServerChildChannel { public typealias Configuration = HTTP2ChannelConfiguration typealias HTTP2Connection = NIOHTTP2Handler.AsyncStreamMultiplexer diff --git a/Sources/HummingbirdHTTP2/HTTP2ServerConnectionManager+StateMachine.swift b/Sources/HummingbirdHTTP2/HTTP2ServerConnectionManager+StateMachine.swift index def8b863..b4578b94 100644 --- a/Sources/HummingbirdHTTP2/HTTP2ServerConnectionManager+StateMachine.swift +++ b/Sources/HummingbirdHTTP2/HTTP2ServerConnectionManager+StateMachine.swift @@ -9,6 +9,7 @@ import NIOCore import NIOHTTP2 +@available(macOS 13, iOS 16, tvOS 16, *) extension HTTP2ServerConnectionManager { struct StateMachine: ~Copyable { var state: State @@ -196,6 +197,7 @@ extension HTTP2ServerConnectionManager { } } +@available(macOS 13, iOS 16, tvOS 16, *) extension HTTP2ServerConnectionManager.StateMachine { enum State: ~Copyable { struct ActiveState { @@ -232,6 +234,7 @@ extension HTTP2ServerConnectionManager.StateMachine { } } +@available(macOS 13, iOS 16, tvOS 16, *) extension HTTP2ServerConnectionManager.StateMachine { struct Keepalive { /// Allow the client to send keep alive pings when there are no active calls. @@ -303,6 +306,7 @@ extension HTTP2ServerConnectionManager.StateMachine { } } +@available(macOS 13, iOS 16, tvOS 16, *) extension HTTP2ServerConnectionManager.StateMachine { // case active(ActiveState) // case closing(ClosingState) diff --git a/Sources/HummingbirdHTTP2/HTTP2ServerConnectionManager.swift b/Sources/HummingbirdHTTP2/HTTP2ServerConnectionManager.swift index d867d2f8..cd2753e0 100644 --- a/Sources/HummingbirdHTTP2/HTTP2ServerConnectionManager.swift +++ b/Sources/HummingbirdHTTP2/HTTP2ServerConnectionManager.swift @@ -12,6 +12,7 @@ import NIOHTTP2 /// HTTP2 server connection manager /// /// This is heavily based off the ServerConnectionManagementHandler from https://github.com/grpc/grpc-swift-nio-transport +@available(macOS 13, iOS 16, tvOS 16, *) final class HTTP2ServerConnectionManager: ChannelDuplexHandler { package typealias InboundIn = HTTP2Frame package typealias InboundOut = HTTP2Frame @@ -225,6 +226,7 @@ final class HTTP2ServerConnectionManager: ChannelDuplexHandler { } } +@available(macOS 13, iOS 16, tvOS 16, *) extension HTTP2ServerConnectionManager { struct LoopBoundHandler: @unchecked Sendable { let handler: HTTP2ServerConnectionManager @@ -240,6 +242,7 @@ extension HTTP2ServerConnectionManager { } } +@available(macOS 13, iOS 16, tvOS 16, *) extension HTTP2ServerConnectionManager { /// Stream delegate struct HTTP2StreamDelegate: NIOHTTP2StreamDelegate, @unchecked Sendable { diff --git a/Sources/HummingbirdHTTP2/HTTP2UpgradeChannel.swift b/Sources/HummingbirdHTTP2/HTTP2UpgradeChannel.swift index 49d05681..c2151e28 100644 --- a/Sources/HummingbirdHTTP2/HTTP2UpgradeChannel.swift +++ b/Sources/HummingbirdHTTP2/HTTP2UpgradeChannel.swift @@ -17,6 +17,7 @@ import NIOSSL import NIOTLS /// Child channel for processing HTTP1 with the option of upgrading to HTTP2 via ALPN +@available(macOS 14, iOS 17, tvOS 17, *) public struct HTTP2UpgradeChannel: HTTPChannelHandler { public typealias Configuration = HTTP2ChannelConfiguration typealias HTTP1Connection = HTTP1Channel.Value diff --git a/Sources/HummingbirdHTTP2/HTTPServerBuilder+http2.swift b/Sources/HummingbirdHTTP2/HTTPServerBuilder+http2.swift index 2bc1d0f2..c42c3030 100644 --- a/Sources/HummingbirdHTTP2/HTTPServerBuilder+http2.swift +++ b/Sources/HummingbirdHTTP2/HTTPServerBuilder+http2.swift @@ -10,6 +10,7 @@ import HummingbirdCore import NIOCore import NIOSSL +@available(macOS 14, iOS 17, tvOS 17, *) extension HTTPServerBuilder { /// Build HTTP channel with HTTP2 upgrade /// diff --git a/Sources/HummingbirdTesting/Application+Test.swift b/Sources/HummingbirdTesting/Application+Test.swift index e9fb7462..11cce692 100644 --- a/Sources/HummingbirdTesting/Application+Test.swift +++ b/Sources/HummingbirdTesting/Application+Test.swift @@ -36,6 +36,7 @@ public struct TestingSetup { } /// Extends `ApplicationProtocol` to support testing of applications +@available(macOS 13, iOS 16, tvOS 16, *) extension ApplicationProtocol { // MARK: Initialization diff --git a/Sources/HummingbirdTesting/LiveTestFramework.swift b/Sources/HummingbirdTesting/LiveTestFramework.swift index dacf3d64..74fbbecc 100644 --- a/Sources/HummingbirdTesting/LiveTestFramework.swift +++ b/Sources/HummingbirdTesting/LiveTestFramework.swift @@ -16,6 +16,7 @@ import ServiceLifecycle import UnixSignals /// Test using a live server +@available(macOS 13, iOS 16, tvOS 16, *) final class LiveTestFramework: ApplicationTestFramework { struct Client: TestClientProtocol { let client: TestClient diff --git a/Sources/HummingbirdTesting/TestClient+types.swift b/Sources/HummingbirdTesting/TestClient+types.swift index 1d015cfa..74478970 100644 --- a/Sources/HummingbirdTesting/TestClient+types.swift +++ b/Sources/HummingbirdTesting/TestClient+types.swift @@ -10,6 +10,7 @@ public import HTTPTypes public import NIOCore /// HTTP client types +@available(macOS 13, iOS 16, tvOS 16, *) extension TestClient { public enum Error: Swift.Error { case invalidURL diff --git a/Sources/HummingbirdTesting/TestClient.swift b/Sources/HummingbirdTesting/TestClient.swift index 9e896b64..0a4aa4df 100644 --- a/Sources/HummingbirdTesting/TestClient.swift +++ b/Sources/HummingbirdTesting/TestClient.swift @@ -18,6 +18,7 @@ public import NIOSSL /// /// This HTTP client is used for internal testing of Hummingbird and is also /// the client used by `.live` testing framework. +@available(macOS 13, iOS 16, tvOS 16, *) public struct TestClient: Sendable { public let channelPromise: EventLoopPromise let eventLoopGroup: any EventLoopGroup diff --git a/Sources/PerformanceTest/main.swift b/Sources/PerformanceTest/main.swift index 35cb80a2..3dbeb33d 100644 --- a/Sources/PerformanceTest/main.swift +++ b/Sources/PerformanceTest/main.swift @@ -11,58 +11,60 @@ import Logging import NIOCore import NIOPosix -// get environment -let env = Environment() -let hostname = env.get("SERVER_HOSTNAME") ?? "127.0.0.1" -let port = env.get("SERVER_PORT", as: Int.self) ?? 8080 - -// create app -let elg = MultiThreadedEventLoopGroup(numberOfThreads: 4) -var router = Router() -router.addMiddleware { - FileMiddleware() -} - -// number of raw requests -// ./wrk -c 128 -d 15s -t 8 http://localhost:8080 -router.get { _, _ in - "Hello, world" -} - -// request with a body -// ./wrk -c 128 -d 15s -t 8 -s scripts/post.lua http://localhost:8080 -router.post { request, _ in - Response(status: .ok, body: .init(asyncSequence: request.body)) -} - -struct Object: ResponseEncodable { - let message: String +if #available(macOS 13, iOS 16, tvOS 16, *) { + // get environment + let env = Environment() + let hostname = env.get("SERVER_HOSTNAME") ?? "127.0.0.1" + let port = env.get("SERVER_PORT", as: Int.self) ?? 8080 + + // create app + let elg = MultiThreadedEventLoopGroup(numberOfThreads: 4) + var router = Router() + router.addMiddleware { + FileMiddleware() + } + + // number of raw requests + // ./wrk -c 128 -d 15s -t 8 http://localhost:8080 + router.get { _, _ in + "Hello, world" + } + + // request with a body + // ./wrk -c 128 -d 15s -t 8 -s scripts/post.lua http://localhost:8080 + router.post { request, _ in + Response(status: .ok, body: .init(asyncSequence: request.body)) + } + + struct Object: ResponseEncodable { + let message: String + } + + // return JSON + // ./wrk -c 128 -d 15s -t 8 http://localhost:8080/json + router.get("json") { _, _ in + Object(message: "Hello, world") + } + + // return JSON + // ./wrk -c 128 -d 15s -t 8 http://localhost:8080/json + router.get("wait") { _, _ in + try await Task.sleep(for: .seconds(8)) + return "I waited" + } + + var app = Application( + responder: router.buildResponder(), + configuration: .init( + address: .hostname(hostname, port: port), + serverName: "Hummingbird" + ), + eventLoopGroupProvider: .shared(elg) + ) + app.logger.logLevel = .debug + + // configure app + + // run app + try await app.runService() } - -// return JSON -// ./wrk -c 128 -d 15s -t 8 http://localhost:8080/json -router.get("json") { _, _ in - Object(message: "Hello, world") -} - -// return JSON -// ./wrk -c 128 -d 15s -t 8 http://localhost:8080/json -router.get("wait") { _, _ in - try await Task.sleep(for: .seconds(8)) - return "I waited" -} - -var app = Application( - responder: router.buildResponder(), - configuration: .init( - address: .hostname(hostname, port: port), - serverName: "Hummingbird" - ), - eventLoopGroupProvider: .shared(elg) -) -app.logger.logLevel = .debug - -// configure app - -// run app -try await app.runService() From ebe3a99faa83886b8e827b3531a84b1f95d40b89 Mon Sep 17 00:00:00 2001 From: Adam Jansch Date: Thu, 19 Feb 2026 16:35:13 +0000 Subject: [PATCH 3/5] =?UTF-8?q?Issue=20781=20=E2=80=93=20Crash=20when=20tr?= =?UTF-8?q?ying=20to=20run=20with=20Hummingbird=20library=20install=20on?= =?UTF-8?q?=20iOS=2015=20and=2016?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Code review changes – applied version restrictions to ApplicationProtocol and Application types. --- Sources/Hummingbird/Application.swift | 12 ++++++------ .../Server/HTTP/HTTPServerBuilder.swift | 2 +- Sources/HummingbirdCore/Server/Server.swift | 11 +++-------- .../HummingbirdCore/Server/ServerChildChannel.swift | 2 +- 4 files changed, 11 insertions(+), 16 deletions(-) diff --git a/Sources/Hummingbird/Application.swift b/Sources/Hummingbird/Application.swift index 2559b85f..be8382e6 100644 --- a/Sources/Hummingbird/Application.swift +++ b/Sources/Hummingbird/Application.swift @@ -41,6 +41,7 @@ public enum EventLoopGroupProvider { } /// Protocol for an Application. Brings all the components of Hummingbird together +@available(macOS 14, iOS 17, tvOS 17, *) public protocol ApplicationProtocol: Service where Context: InitializableFromSource { /// Responder that generates a response from a requests and context associatedtype Responder: HTTPResponder @@ -67,11 +68,13 @@ public protocol ApplicationProtocol: Service where Context: InitializableFromSou var processesRunBeforeServerStart: [@Sendable () async throws -> Void] { get } } +@available(macOS 14, iOS 17, tvOS 17, *) extension ApplicationProtocol { /// Server channel setup public var server: HTTPServerBuilder { .http1() } } +@available(macOS 14, iOS 17, tvOS 17, *) extension ApplicationProtocol { /// Default event loop group used by application public var eventLoopGroup: any EventLoopGroup { MultiThreadedEventLoopGroup.singleton } @@ -88,15 +91,10 @@ extension ApplicationProtocol { } /// Conform to `Service` from `ServiceLifecycle`. +@available(macOS 14, iOS 17, tvOS 17, *) extension ApplicationProtocol { /// Construct application and run it public func run() async throws { - // `#available()` logic must be placed within this method as Service - // protocol requires `run()` method is available from iOS 13+ - guard #available(macOS 13, iOS 17, tvOS 16, *) else { - return - } - let dateCache = DateCache() let responder = try await self.responder @@ -181,6 +179,7 @@ extension ApplicationProtocol { /// try await app.runService() /// ``` /// Editing the application setup after calling `runService` will produce undefined behaviour. +@available(macOS 14, iOS 17, tvOS 17, *) public struct Application: ApplicationProtocol where Responder.Context: InitializableFromSource { // MARK: Member variables @@ -309,6 +308,7 @@ where Responder.Context: InitializableFromSource: Service { public typealias AsyncChildChannel = ChildChannel.Value public typealias AsyncServerChannel = NIOAsyncChannel @@ -89,13 +89,8 @@ public actor Server: Service { self.logger = logger } + @available(macOS 14, iOS 17, tvOS 17, *) public func run() async throws { - // `#available()` logic must be placed within this method as Service - // protocol requires `run()` method is available from iOS 13+ - guard #available(macOS 14, iOS 15, tvOS 17, *) else { - return - } - switch self.state { case .initial(let childChannelSetup, let configuration, let onServerRunning): self.state = .starting @@ -368,7 +363,7 @@ extension NIOTSListenerBootstrap: ServerBootstrapProtocol { } #endif -@available(iOS 17, *) +@available(macOS 14, iOS 17, tvOS 17, *) extension Server: CustomStringConvertible { public nonisolated var description: String { "Hummingbird" diff --git a/Sources/HummingbirdCore/Server/ServerChildChannel.swift b/Sources/HummingbirdCore/Server/ServerChildChannel.swift index ed2ba007..9b9ffaab 100644 --- a/Sources/HummingbirdCore/Server/ServerChildChannel.swift +++ b/Sources/HummingbirdCore/Server/ServerChildChannel.swift @@ -34,7 +34,7 @@ public protocol ServerChildChannel: Sendable { func handle(value: Value, logger: Logger) async } -@available(iOS 17, *) +@available(macOS 14, iOS 17, tvOS 17, *) extension ServerChildChannel { /// Build existential ``Server`` from existential `ServerChildChannel` /// From 7541291edfd8ae217de7eb00b1ec26b8fe7df898 Mon Sep 17 00:00:00 2001 From: Adam Jansch Date: Tue, 24 Feb 2026 13:02:17 +0000 Subject: [PATCH 4/5] Issue 781 - Crash when trying to run with Hummingbird library install on iOS 15 and 16 Applied correct version restrictions across subpackages. --- .../URLEncodedForm/URLEncodedForm+Request.swift | 4 ++-- .../URLEncodedForm/URLEncodedFormDecoder.swift | 10 +++++----- .../URLEncodedForm/URLEncodedFormEncoder.swift | 10 +++++----- .../Codable/URLEncodedForm/URLEncodedFormNode.swift | 2 +- Sources/Hummingbird/HTTP/Cookie.swift | 2 +- Sources/Hummingbird/HTTP/Cookies.swift | 2 +- Sources/Hummingbird/HTTP/Request+Cookies.swift | 2 +- Sources/Hummingbird/HTTP/Response+Cookies.swift | 4 ++-- Sources/Hummingbird/Middleware/FileMiddleware.swift | 4 ++-- Sources/Hummingbird/Middleware/MiddlewareGroup.swift | 2 +- .../Hummingbird/Middleware/TracingMiddleware.swift | 4 ++-- Sources/Hummingbird/Router/EndpointResponder.swift | 2 +- Sources/Hummingbird/Router/RouteCollection.swift | 4 ++-- Sources/Hummingbird/Router/Router+validation.swift | 2 +- Sources/Hummingbird/Router/Router.swift | 2 +- Sources/Hummingbird/Router/RouterGroup.swift | 2 +- Sources/Hummingbird/Router/RouterMethods.swift | 2 +- Sources/Hummingbird/Router/RouterResponder.swift | 2 +- Sources/Hummingbird/Server/Request.swift | 2 +- Sources/Hummingbird/Server/URI+decodeQuery.swift | 2 +- .../Hummingbird/Storage/MemoryPersistDriver.swift | 2 +- Sources/Hummingbird/Storage/PersistDriver.swift | 4 ++-- Sources/Hummingbird/Utils/DateCache.swift | 2 +- .../Utils/HTTPHeaderDateFormatStyle.swift | 12 ++++++------ Sources/HummingbirdHTTP2/HTTP2Channel.swift | 2 +- .../HTTP2ServerConnectionManager+StateMachine.swift | 8 ++++---- .../HTTP2ServerConnectionManager.swift | 6 +++--- Sources/HummingbirdTesting/Application+Test.swift | 2 +- .../AsyncHTTPClientTestFramework.swift | 1 + Sources/HummingbirdTesting/LiveTestFramework.swift | 2 +- Sources/HummingbirdTesting/RouterTestFramework.swift | 1 + Sources/HummingbirdTesting/TestApplication.swift | 1 + Sources/HummingbirdTesting/TestClient+types.swift | 2 +- Sources/HummingbirdTesting/TestClient.swift | 2 +- Sources/PerformanceTest/main.swift | 4 +++- 35 files changed, 61 insertions(+), 56 deletions(-) diff --git a/Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedForm+Request.swift b/Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedForm+Request.swift index 7b2df378..b74eb0c4 100644 --- a/Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedForm+Request.swift +++ b/Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedForm+Request.swift @@ -6,7 +6,7 @@ // SPDX-License-Identifier: Apache-2.0 // -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) extension URLEncodedFormEncoder: ResponseEncoder { /// Extend URLEncodedFormEncoder to support generating a ``HummingbirdCore/Response``. Sets body and header values /// - Parameters: @@ -27,7 +27,7 @@ extension URLEncodedFormEncoder: ResponseEncoder { } } -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) extension URLEncodedFormDecoder: RequestDecoder { /// Extend URLEncodedFormDecoder to decode from ``HummingbirdCore/Request``. /// - Parameters: diff --git a/Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedFormDecoder.swift b/Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedFormDecoder.swift index 9667b006..1593b50e 100644 --- a/Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedFormDecoder.swift +++ b/Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedFormDecoder.swift @@ -9,7 +9,7 @@ public import Foundation /// The wrapper struct for decoding URL encoded form data to Codable classes -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) public struct URLEncodedFormDecoder: Sendable { /// The strategy to use for decoding `Date` values. public enum DateDecodingStrategy: Sendable { @@ -76,7 +76,7 @@ public struct URLEncodedFormDecoder: Sendable { } } -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) private class _URLEncodedFormDecoder: Decoder { // MARK: Properties @@ -423,7 +423,7 @@ private class _URLEncodedFormDecoder: Decoder { } } -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) extension _URLEncodedFormDecoder: SingleValueDecodingContainer { func decodeNil() -> Bool { (try? self.unboxNil(self.storage.topContainer)) ?? false @@ -490,7 +490,7 @@ extension _URLEncodedFormDecoder: SingleValueDecodingContainer { } } -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) extension _URLEncodedFormDecoder { func unboxNil(_ node: URLEncodedFormNode) throws -> Bool { switch node { @@ -690,7 +690,7 @@ extension _URLEncodedFormDecoder { } } -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) private struct URLEncodedFormDecodingStorage { /// the container stack private var containers: [URLEncodedFormNode] = [] diff --git a/Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedFormEncoder.swift b/Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedFormEncoder.swift index 7c1fef7b..cceeff3b 100644 --- a/Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedFormEncoder.swift +++ b/Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedFormEncoder.swift @@ -9,7 +9,7 @@ public import Foundation /// The wrapper struct for encoding Codable classes to URL encoded form data -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) public struct URLEncodedFormEncoder: Sendable { /// The strategy to use for encoding `Date` values. public enum DateEncodingStrategy: Sendable { @@ -81,7 +81,7 @@ public struct URLEncodedFormEncoder: Sendable { } /// Internal QueryEncoder class. Does all the heavy lifting -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) private class _URLEncodedFormEncoder: Encoder { var codingPath: [any CodingKey] @@ -275,7 +275,7 @@ private class _URLEncodedFormEncoder: Encoder { } } -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) extension _URLEncodedFormEncoder: SingleValueEncodingContainer { func encodeResult(_ value: URLEncodedFormNode) { self.storage.push(container: value) @@ -313,7 +313,7 @@ extension _URLEncodedFormEncoder: SingleValueEncodingContainer { } } -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) extension _URLEncodedFormEncoder { func box(_ date: Date) throws -> URLEncodedFormNode { switch self.options.dateEncodingStrategy { @@ -359,7 +359,7 @@ extension _URLEncodedFormEncoder { } /// storage for Query Encoder. Stores a stack of QueryEncoder containers, plus leaf objects -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) private struct URLEncodedFormEncoderStorage { /// the container stack private var containers: [URLEncodedFormNode] = [] diff --git a/Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedFormNode.swift b/Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedFormNode.swift index 46ab9e9b..56319bac 100644 --- a/Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedFormNode.swift +++ b/Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedFormNode.swift @@ -71,7 +71,7 @@ extension URLEncodedFormError { } } /// Internal representation of URL encoded form data used by both encode and decode -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) enum URLEncodedFormNode: CustomStringConvertible, Equatable { /// holds a value case leaf(NodeValue?) diff --git a/Sources/Hummingbird/HTTP/Cookie.swift b/Sources/Hummingbird/HTTP/Cookie.swift index 70403618..d0093503 100644 --- a/Sources/Hummingbird/HTTP/Cookie.swift +++ b/Sources/Hummingbird/HTTP/Cookie.swift @@ -13,7 +13,7 @@ public import Foundation #endif /// Structure holding a single cookie -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) public struct Cookie: Sendable, CustomStringConvertible { public struct ValidationError: Error { enum Reason { diff --git a/Sources/Hummingbird/HTTP/Cookies.swift b/Sources/Hummingbird/HTTP/Cookies.swift index a3a863ca..f7d73ad7 100644 --- a/Sources/Hummingbird/HTTP/Cookies.swift +++ b/Sources/Hummingbird/HTTP/Cookies.swift @@ -9,7 +9,7 @@ /// Structure holding an array of cookies /// /// Cookies can be accessed from request via `Request.cookies`. -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) public struct Cookies: Sendable { /// Construct cookies accessor from `Request` /// - Parameter request: request to get cookies from diff --git a/Sources/Hummingbird/HTTP/Request+Cookies.swift b/Sources/Hummingbird/HTTP/Request+Cookies.swift index 96d9be5d..5eb82449 100644 --- a/Sources/Hummingbird/HTTP/Request+Cookies.swift +++ b/Sources/Hummingbird/HTTP/Request+Cookies.swift @@ -8,7 +8,7 @@ public import HummingbirdCore -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) extension Request { /// access cookies from request. When accessing this for the first time the Cookies struct will be created public var cookies: Cookies { diff --git a/Sources/Hummingbird/HTTP/Response+Cookies.swift b/Sources/Hummingbird/HTTP/Response+Cookies.swift index 9a244a29..f087576c 100644 --- a/Sources/Hummingbird/HTTP/Response+Cookies.swift +++ b/Sources/Hummingbird/HTTP/Response+Cookies.swift @@ -8,7 +8,7 @@ public import HummingbirdCore -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) extension Response { /// Set cookie on response public mutating func setCookie(_ cookie: Cookie) { @@ -16,7 +16,7 @@ extension Response { } } -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) extension EditedResponse { /// Set cookie on reponse patch /// diff --git a/Sources/Hummingbird/Middleware/FileMiddleware.swift b/Sources/Hummingbird/Middleware/FileMiddleware.swift index d1a2b5d9..815fefdd 100644 --- a/Sources/Hummingbird/Middleware/FileMiddleware.swift +++ b/Sources/Hummingbird/Middleware/FileMiddleware.swift @@ -40,7 +40,7 @@ public protocol FileMiddlewareFileAttributes { /// "if-modified-since", "if-none-match", "if-range" and 'range" headers. It will output "content-length", /// "modified-date", "eTag", "content-type", "cache-control" and "content-range" headers where /// they are relevant. -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) public struct FileMiddleware: RouterMiddleware where Provider.FileAttributes: FileMiddlewareFileAttributes { let cacheControl: CacheControl @@ -222,7 +222,7 @@ where Provider.FileAttributes: FileMiddlewareFileAttributes { } } -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) extension FileMiddleware { /// Whether to return data from the file or a not modified response private enum FileResult { diff --git a/Sources/Hummingbird/Middleware/MiddlewareGroup.swift b/Sources/Hummingbird/Middleware/MiddlewareGroup.swift index 5d7cfc03..fbd6d8a2 100644 --- a/Sources/Hummingbird/Middleware/MiddlewareGroup.swift +++ b/Sources/Hummingbird/Middleware/MiddlewareGroup.swift @@ -7,7 +7,7 @@ // /// Group of middleware that can be used to create a responder chain. Each middleware calls the next one -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) public final class MiddlewareGroup { var middlewares: [any MiddlewareProtocol] diff --git a/Sources/Hummingbird/Middleware/TracingMiddleware.swift b/Sources/Hummingbird/Middleware/TracingMiddleware.swift index 6a84639a..ea85e76f 100644 --- a/Sources/Hummingbird/Middleware/TracingMiddleware.swift +++ b/Sources/Hummingbird/Middleware/TracingMiddleware.swift @@ -28,7 +28,7 @@ import Foundation /// Swift-Distributed-Tracing has a flexible backend, which will need to be initialized before any traces are recorded. /// /// A list of implementations is available in the swift-distributed-tracing repository's README. -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) public struct TracingMiddleware: RouterMiddleware { private let headerNamesToRecord: Set private let queryParametersToRedact: Set @@ -197,7 +197,7 @@ public protocol RemoteAddressRequestContext: RequestContext { var remoteAddress: SocketAddress? { get } } -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) struct RecordingHeader: Hashable { let name: HTTPField.Name let attributeName: String diff --git a/Sources/Hummingbird/Router/EndpointResponder.swift b/Sources/Hummingbird/Router/EndpointResponder.swift index 5cf5f04a..8cdacdc3 100644 --- a/Sources/Hummingbird/Router/EndpointResponder.swift +++ b/Sources/Hummingbird/Router/EndpointResponder.swift @@ -9,7 +9,7 @@ public import HTTPTypes /// Stores endpoint responders for each HTTP method -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) @usableFromInline struct EndpointResponders: Sendable { init(path: RouterPath) { diff --git a/Sources/Hummingbird/Router/RouteCollection.swift b/Sources/Hummingbird/Router/RouteCollection.swift index 2db96d07..46fb98e7 100644 --- a/Sources/Hummingbird/Router/RouteCollection.swift +++ b/Sources/Hummingbird/Router/RouteCollection.swift @@ -9,7 +9,7 @@ public import HTTPTypes /// Collection of routes -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) public final class RouteCollection: RouterMethods { /// Initialize RouteCollection public init(context: Context.Type = BasicRequestContext.self) { @@ -53,7 +53,7 @@ public final class RouteCollection: RouterMethods { let middlewares: MiddlewareGroup } -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) extension RouterMethods { /// Add route collection to router /// - Parameters diff --git a/Sources/Hummingbird/Router/Router+validation.swift b/Sources/Hummingbird/Router/Router+validation.swift index 1c20c705..5ed85202 100644 --- a/Sources/Hummingbird/Router/Router+validation.swift +++ b/Sources/Hummingbird/Router/Router+validation.swift @@ -14,7 +14,7 @@ import FoundationEssentials import Foundation #endif -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) extension Router { /// Route description public struct RouteDescription: CustomStringConvertible { diff --git a/Sources/Hummingbird/Router/Router.swift b/Sources/Hummingbird/Router/Router.swift index e1b23176..0c13b412 100644 --- a/Sources/Hummingbird/Router/Router.swift +++ b/Sources/Hummingbird/Router/Router.swift @@ -36,7 +36,7 @@ public import HummingbirdCore /// Both of these match routes which start with "/user" and the next path segment being anything. /// The second version extracts the path segment out and adds it to `Request.parameters` with the /// key "id". -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) public final class Router: RouterMethods, HTTPResponderBuilder { var trie: RouterPathTrieBuilder> public let middlewares: MiddlewareGroup diff --git a/Sources/Hummingbird/Router/RouterGroup.swift b/Sources/Hummingbird/Router/RouterGroup.swift index 5bfae9e3..a7c045ad 100644 --- a/Sources/Hummingbird/Router/RouterGroup.swift +++ b/Sources/Hummingbird/Router/RouterGroup.swift @@ -23,7 +23,7 @@ public import HummingbirdCore /// .put(":id", use: todoController.update) /// .delete(":id", use: todoController.delete) /// ``` -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) public struct RouterGroup: RouterMethods { let path: RouterPath let parent: any RouterMethods diff --git a/Sources/Hummingbird/Router/RouterMethods.swift b/Sources/Hummingbird/Router/RouterMethods.swift index 4780ec0e..147adb0e 100644 --- a/Sources/Hummingbird/Router/RouterMethods.swift +++ b/Sources/Hummingbird/Router/RouterMethods.swift @@ -34,7 +34,7 @@ public protocol RouterMethods: _HB_SendableMetatype { func add(middleware: any MiddlewareProtocol) -> Self } -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) extension RouterMethods { /// Add path for async closure @discardableResult public func on( diff --git a/Sources/Hummingbird/Router/RouterResponder.swift b/Sources/Hummingbird/Router/RouterResponder.swift index 68e65d9a..eb3a26c9 100644 --- a/Sources/Hummingbird/Router/RouterResponder.swift +++ b/Sources/Hummingbird/Router/RouterResponder.swift @@ -8,7 +8,7 @@ import NIOCore -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) public struct RouterResponder: HTTPResponder { @usableFromInline let trie: RouterTrie> diff --git a/Sources/Hummingbird/Server/Request.swift b/Sources/Hummingbird/Server/Request.swift index 388b764f..c0efceb8 100644 --- a/Sources/Hummingbird/Server/Request.swift +++ b/Sources/Hummingbird/Server/Request.swift @@ -58,7 +58,7 @@ extension Request { } } -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) extension Request { /// Conditional request which will only be processed if the eTag supplied is not in the /// `If-None-Match` request header. diff --git a/Sources/Hummingbird/Server/URI+decodeQuery.swift b/Sources/Hummingbird/Server/URI+decodeQuery.swift index 26e1ac25..da934177 100644 --- a/Sources/Hummingbird/Server/URI+decodeQuery.swift +++ b/Sources/Hummingbird/Server/URI+decodeQuery.swift @@ -9,7 +9,7 @@ public import HummingbirdCore import Logging -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) extension URI { /// Decode request query using ``Hummingbird/URLEncodedFormDecoder``. /// - Parameters diff --git a/Sources/Hummingbird/Storage/MemoryPersistDriver.swift b/Sources/Hummingbird/Storage/MemoryPersistDriver.swift index e49d521a..759d889c 100644 --- a/Sources/Hummingbird/Storage/MemoryPersistDriver.swift +++ b/Sources/Hummingbird/Storage/MemoryPersistDriver.swift @@ -12,7 +12,7 @@ import NIOCore import ServiceLifecycle /// In memory driver for persist system for storing persistent cross request key/value pairs -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) public actor MemoryPersistDriver: PersistDriver where C.Duration == Duration { public struct Configuration: Sendable { /// amount of time between each call to tidy diff --git a/Sources/Hummingbird/Storage/PersistDriver.swift b/Sources/Hummingbird/Storage/PersistDriver.swift index fc5b7cf4..641c0188 100644 --- a/Sources/Hummingbird/Storage/PersistDriver.swift +++ b/Sources/Hummingbird/Storage/PersistDriver.swift @@ -9,7 +9,7 @@ public import ServiceLifecycle /// Protocol for driver supporting persistent Key/Value pairs across requests -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) public protocol PersistDriver: Service { /// shutdown driver func shutdown() async throws @@ -41,7 +41,7 @@ public protocol PersistDriver: Service { func remove(key: String) async throws } -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) extension PersistDriver { /// default implemenation of shutdown() public func shutdown() async throws {} diff --git a/Sources/Hummingbird/Utils/DateCache.swift b/Sources/Hummingbird/Utils/DateCache.swift index b8c4176a..d6a161ea 100644 --- a/Sources/Hummingbird/Utils/DateCache.swift +++ b/Sources/Hummingbird/Utils/DateCache.swift @@ -22,7 +22,7 @@ import Foundation /// /// Getting the current date formatted is an expensive operation. This creates a task that will /// update a cached version of the date in the format as detailed in RFC9110 once every second. -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) final class DateCache: Service { final class DateContainer: AtomicReference, Sendable { let date: String diff --git a/Sources/Hummingbird/Utils/HTTPHeaderDateFormatStyle.swift b/Sources/Hummingbird/Utils/HTTPHeaderDateFormatStyle.swift index 6c770dac..2a759c77 100644 --- a/Sources/Hummingbird/Utils/HTTPHeaderDateFormatStyle.swift +++ b/Sources/Hummingbird/Utils/HTTPHeaderDateFormatStyle.swift @@ -18,7 +18,7 @@ import FoundationEssentials import Foundation #endif -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) extension Date { init?(httpHeader: String) { try? self.init(httpHeader, strategy: .rfc9110) @@ -31,7 +31,7 @@ extension Date { struct HTTPHeaderDateParsingError: Error {} -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) struct HTTPHeaderDateFormatStyle { let calendar: Calendar @@ -42,7 +42,7 @@ struct HTTPHeaderDateFormatStyle { } } -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) extension HTTPHeaderDateFormatStyle: ParseStrategy { func parse(_ input: String) throws -> Date { guard let components = self.components(from: input) else { @@ -278,7 +278,7 @@ let timezoneOffsetMap: [[UInt8]: Int] = [ Array("PDT".utf8): -7 * 60, ] -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) extension HTTPHeaderDateFormatStyle: FormatStyle { //let calendar: Calendar @@ -323,12 +323,12 @@ extension HTTPHeaderDateFormatStyle: FormatStyle { ] } -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) extension FormatStyle where Self == HTTPHeaderDateFormatStyle { static var rfc9110: Self { .init() } } -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) extension ParseStrategy where Self == HTTPHeaderDateFormatStyle { static var rfc9110: Self { .init() } } diff --git a/Sources/HummingbirdHTTP2/HTTP2Channel.swift b/Sources/HummingbirdHTTP2/HTTP2Channel.swift index c22cab23..27234559 100644 --- a/Sources/HummingbirdHTTP2/HTTP2Channel.swift +++ b/Sources/HummingbirdHTTP2/HTTP2Channel.swift @@ -15,7 +15,7 @@ import NIOHTTPTypesHTTP2 import NIOSSL /// HTTP2 configuration -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) public struct HTTP2ChannelConfiguration: Sendable { /// Idle timeout, how long connection is kept idle before closing public var idleTimeout: Duration? diff --git a/Sources/HummingbirdHTTP2/HTTP2ServerConnectionManager+StateMachine.swift b/Sources/HummingbirdHTTP2/HTTP2ServerConnectionManager+StateMachine.swift index b4578b94..be6350df 100644 --- a/Sources/HummingbirdHTTP2/HTTP2ServerConnectionManager+StateMachine.swift +++ b/Sources/HummingbirdHTTP2/HTTP2ServerConnectionManager+StateMachine.swift @@ -9,7 +9,7 @@ import NIOCore import NIOHTTP2 -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) extension HTTP2ServerConnectionManager { struct StateMachine: ~Copyable { var state: State @@ -197,7 +197,7 @@ extension HTTP2ServerConnectionManager { } } -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) extension HTTP2ServerConnectionManager.StateMachine { enum State: ~Copyable { struct ActiveState { @@ -234,7 +234,7 @@ extension HTTP2ServerConnectionManager.StateMachine { } } -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) extension HTTP2ServerConnectionManager.StateMachine { struct Keepalive { /// Allow the client to send keep alive pings when there are no active calls. @@ -306,7 +306,7 @@ extension HTTP2ServerConnectionManager.StateMachine { } } -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) extension HTTP2ServerConnectionManager.StateMachine { // case active(ActiveState) // case closing(ClosingState) diff --git a/Sources/HummingbirdHTTP2/HTTP2ServerConnectionManager.swift b/Sources/HummingbirdHTTP2/HTTP2ServerConnectionManager.swift index cd2753e0..807e790a 100644 --- a/Sources/HummingbirdHTTP2/HTTP2ServerConnectionManager.swift +++ b/Sources/HummingbirdHTTP2/HTTP2ServerConnectionManager.swift @@ -12,7 +12,7 @@ import NIOHTTP2 /// HTTP2 server connection manager /// /// This is heavily based off the ServerConnectionManagementHandler from https://github.com/grpc/grpc-swift-nio-transport -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) final class HTTP2ServerConnectionManager: ChannelDuplexHandler { package typealias InboundIn = HTTP2Frame package typealias InboundOut = HTTP2Frame @@ -226,7 +226,7 @@ final class HTTP2ServerConnectionManager: ChannelDuplexHandler { } } -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) extension HTTP2ServerConnectionManager { struct LoopBoundHandler: @unchecked Sendable { let handler: HTTP2ServerConnectionManager @@ -242,7 +242,7 @@ extension HTTP2ServerConnectionManager { } } -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) extension HTTP2ServerConnectionManager { /// Stream delegate struct HTTP2StreamDelegate: NIOHTTP2StreamDelegate, @unchecked Sendable { diff --git a/Sources/HummingbirdTesting/Application+Test.swift b/Sources/HummingbirdTesting/Application+Test.swift index 11cce692..b6f9f9c8 100644 --- a/Sources/HummingbirdTesting/Application+Test.swift +++ b/Sources/HummingbirdTesting/Application+Test.swift @@ -36,7 +36,7 @@ public struct TestingSetup { } /// Extends `ApplicationProtocol` to support testing of applications -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) extension ApplicationProtocol { // MARK: Initialization diff --git a/Sources/HummingbirdTesting/AsyncHTTPClientTestFramework.swift b/Sources/HummingbirdTesting/AsyncHTTPClientTestFramework.swift index 3388a653..02667f27 100644 --- a/Sources/HummingbirdTesting/AsyncHTTPClientTestFramework.swift +++ b/Sources/HummingbirdTesting/AsyncHTTPClientTestFramework.swift @@ -19,6 +19,7 @@ import ServiceLifecycle import UnixSignals /// Test using a live server and AsyncHTTPClient as a client +@available(macOS 14, iOS 17, tvOS 17, *) final class AsyncHTTPClientTestFramework: ApplicationTestFramework { struct Client: TestClientProtocol { let client: HTTPClient diff --git a/Sources/HummingbirdTesting/LiveTestFramework.swift b/Sources/HummingbirdTesting/LiveTestFramework.swift index 74fbbecc..d9c657d4 100644 --- a/Sources/HummingbirdTesting/LiveTestFramework.swift +++ b/Sources/HummingbirdTesting/LiveTestFramework.swift @@ -16,7 +16,7 @@ import ServiceLifecycle import UnixSignals /// Test using a live server -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) final class LiveTestFramework: ApplicationTestFramework { struct Client: TestClientProtocol { let client: TestClient diff --git a/Sources/HummingbirdTesting/RouterTestFramework.swift b/Sources/HummingbirdTesting/RouterTestFramework.swift index a028fea5..feb709b6 100644 --- a/Sources/HummingbirdTesting/RouterTestFramework.swift +++ b/Sources/HummingbirdTesting/RouterTestFramework.swift @@ -20,6 +20,7 @@ import ServiceLifecycle import UnixSignals /// Test sending requests directly to router. This does not setup a live server +@available(macOS 14, iOS 17, tvOS 17, *) struct RouterTestFramework: ApplicationTestFramework where Responder.Context: InitializableFromSource { let responder: Responder let makeContext: @Sendable (Logger) -> Responder.Context diff --git a/Sources/HummingbirdTesting/TestApplication.swift b/Sources/HummingbirdTesting/TestApplication.swift index 0d812210..b2f47bbe 100644 --- a/Sources/HummingbirdTesting/TestApplication.swift +++ b/Sources/HummingbirdTesting/TestApplication.swift @@ -16,6 +16,7 @@ import ServiceLifecycle /// TestApplication used to wrap Application being tested. /// /// This is needed to override the `onServerRunning` function +@available(macOS 14, iOS 17, tvOS 17, *) internal struct TestApplication: ApplicationProtocol, Service { typealias Responder = BaseApp.Responder diff --git a/Sources/HummingbirdTesting/TestClient+types.swift b/Sources/HummingbirdTesting/TestClient+types.swift index 74478970..904c9308 100644 --- a/Sources/HummingbirdTesting/TestClient+types.swift +++ b/Sources/HummingbirdTesting/TestClient+types.swift @@ -10,7 +10,7 @@ public import HTTPTypes public import NIOCore /// HTTP client types -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) extension TestClient { public enum Error: Swift.Error { case invalidURL diff --git a/Sources/HummingbirdTesting/TestClient.swift b/Sources/HummingbirdTesting/TestClient.swift index 0a4aa4df..cb079555 100644 --- a/Sources/HummingbirdTesting/TestClient.swift +++ b/Sources/HummingbirdTesting/TestClient.swift @@ -18,7 +18,7 @@ public import NIOSSL /// /// This HTTP client is used for internal testing of Hummingbird and is also /// the client used by `.live` testing framework. -@available(macOS 13, iOS 16, tvOS 16, *) +@available(macOS 14, iOS 17, tvOS 17, *) public struct TestClient: Sendable { public let channelPromise: EventLoopPromise let eventLoopGroup: any EventLoopGroup diff --git a/Sources/PerformanceTest/main.swift b/Sources/PerformanceTest/main.swift index 3dbeb33d..29dff1ab 100644 --- a/Sources/PerformanceTest/main.swift +++ b/Sources/PerformanceTest/main.swift @@ -11,7 +11,7 @@ import Logging import NIOCore import NIOPosix -if #available(macOS 13, iOS 16, tvOS 16, *) { +if #available(macOS 14, iOS 17, tvOS 17, *) { // get environment let env = Environment() let hostname = env.get("SERVER_HOSTNAME") ?? "127.0.0.1" @@ -67,4 +67,6 @@ if #available(macOS 13, iOS 16, tvOS 16, *) { // run app try await app.runService() +} else { + fatalError("Hummingbird requires macOS 14, iOS 17 or tvOS 17") } From 658c26c1606ca70ef22e7b9617a0717b67433e61 Mon Sep 17 00:00:00 2001 From: Adam Jansch Date: Tue, 24 Feb 2026 13:04:12 +0000 Subject: [PATCH 5/5] Issue 781 - Crash when trying to run with Hummingbird library install on iOS 15 and 16 Run swift format on code. --- Sources/PerformanceTest/main.swift | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Sources/PerformanceTest/main.swift b/Sources/PerformanceTest/main.swift index 29dff1ab..6dac7c57 100644 --- a/Sources/PerformanceTest/main.swift +++ b/Sources/PerformanceTest/main.swift @@ -16,43 +16,43 @@ if #available(macOS 14, iOS 17, tvOS 17, *) { let env = Environment() let hostname = env.get("SERVER_HOSTNAME") ?? "127.0.0.1" let port = env.get("SERVER_PORT", as: Int.self) ?? 8080 - + // create app let elg = MultiThreadedEventLoopGroup(numberOfThreads: 4) var router = Router() router.addMiddleware { FileMiddleware() } - + // number of raw requests // ./wrk -c 128 -d 15s -t 8 http://localhost:8080 router.get { _, _ in "Hello, world" } - + // request with a body // ./wrk -c 128 -d 15s -t 8 -s scripts/post.lua http://localhost:8080 router.post { request, _ in Response(status: .ok, body: .init(asyncSequence: request.body)) } - + struct Object: ResponseEncodable { let message: String } - + // return JSON // ./wrk -c 128 -d 15s -t 8 http://localhost:8080/json router.get("json") { _, _ in Object(message: "Hello, world") } - + // return JSON // ./wrk -c 128 -d 15s -t 8 http://localhost:8080/json router.get("wait") { _, _ in try await Task.sleep(for: .seconds(8)) return "I waited" } - + var app = Application( responder: router.buildResponder(), configuration: .init( @@ -62,9 +62,9 @@ if #available(macOS 14, iOS 17, tvOS 17, *) { eventLoopGroupProvider: .shared(elg) ) app.logger.logLevel = .debug - + // configure app - + // run app try await app.runService() } else {