From 7e25121e4c5f19b2665cef7a01648dbe5c25e665 Mon Sep 17 00:00:00 2001 From: Jacob Hearst Date: Wed, 7 Jan 2026 16:01:05 -0600 Subject: [PATCH 1/4] Increase minimum os to remove availabilty checks --- Package.swift | 4 +- .../ScryfallKit/Extensions/Card+helpers.swift | 6 +-- Sources/ScryfallKit/Logger.swift | 16 ------ .../EndpointRequests/CardRequests.swift | 13 +---- .../EndpointRequests/EndpointRequest.swift | 6 +-- .../Networking/NetworkService.swift | 54 ++++--------------- .../ScryfallKit/ScryfallClient+Async.swift | 1 - Sources/ScryfallKit/ScryfallClient.swift | 9 ++-- Tests/ScryfallKitTests/SmokeTests.swift | 6 +-- 9 files changed, 25 insertions(+), 90 deletions(-) delete mode 100644 Sources/ScryfallKit/Logger.swift diff --git a/Package.swift b/Package.swift index 97fe625..1c96fb7 100644 --- a/Package.swift +++ b/Package.swift @@ -1,11 +1,11 @@ -// swift-tools-version:6.0 +// swift-tools-version:6.2 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "ScryfallKit", - platforms: [.macOS(.v10_13), .iOS(.v12)], + platforms: [.macOS(.v11), .iOS(.v14), .watchOS(.v11), .visionOS(.v26)], products: [ .library( name: "ScryfallKit", diff --git a/Sources/ScryfallKit/Extensions/Card+helpers.swift b/Sources/ScryfallKit/Extensions/Card+helpers.swift index 262eb52..323068a 100644 --- a/Sources/ScryfallKit/Extensions/Card+helpers.swift +++ b/Sources/ScryfallKit/Extensions/Card+helpers.swift @@ -86,11 +86,7 @@ extension Card { } guard let uri = uris.uri(for: type) else { - if #available(iOS 14.0, macOS 11.0, *) { - Logger.main.error("No URI for image type \(type.rawValue)") - } else { - print("No URI for image type \(type)") - } + scryfallKitLogger.error("No URI for image type \(type.rawValue)") return nil } diff --git a/Sources/ScryfallKit/Logger.swift b/Sources/ScryfallKit/Logger.swift deleted file mode 100644 index d070490..0000000 --- a/Sources/ScryfallKit/Logger.swift +++ /dev/null @@ -1,16 +0,0 @@ -// -// Logger.swift -// - -import Foundation -import OSLog - -@available(macOS 11.0, *) -@available(iOS 14.0, *) -extension Logger { - static let subsystem = "dev.hearst.scryfallkit" - static let main = Logger(subsystem: subsystem, category: "ScryfallKit") - static let client = Logger(subsystem: subsystem, category: "ScryfallClient") - static let network = Logger(subsystem: subsystem, category: "Network") - static let decoder = Logger(subsystem: subsystem, category: "Decoder") -} diff --git a/Sources/ScryfallKit/Networking/EndpointRequests/CardRequests.swift b/Sources/ScryfallKit/Networking/EndpointRequests/CardRequests.swift index d48dfa3..a6b5f37 100644 --- a/Sources/ScryfallKit/Networking/EndpointRequests/CardRequests.swift +++ b/Sources/ScryfallKit/Networking/EndpointRequests/CardRequests.swift @@ -91,12 +91,7 @@ struct GetCard: EndpointRequest { default: // This guard should never trip. The only card identifier that doesn't have provider/id is the set code/collector guard let id = identifier.id else { - if #available(iOS 14.0, macOS 11.0, *) { - Logger.main.error("Provided identifier doesn't have a provider or doesn't have an id") - } else { - print("Provided identifier doesn't have a provider or doesn't have an id") - } - + scryfallKitLogger.error("Provided identifier doesn't have a provider or doesn't have an id") fatalError( "Encountered a situation that shouldn't be possible: Card identifier's id property was nil" ) @@ -124,11 +119,7 @@ struct GetCardCollection: EndpointRequest { do { body = try JSONSerialization.data(withJSONObject: requestBody) } catch { - if #available(iOS 14.0, macOS 11.0, *) { - Logger.main.error("Errored serializing dict to JSON for GetCardCollection request") - } else { - print("Errored serializing dict to JSON for GetCardCollection request") - } + scryfallKitLogger.error("Errored serializing dict to JSON for GetCardCollection request") } } } diff --git a/Sources/ScryfallKit/Networking/EndpointRequests/EndpointRequest.swift b/Sources/ScryfallKit/Networking/EndpointRequests/EndpointRequest.swift index ec90cdd..5519af5 100644 --- a/Sources/ScryfallKit/Networking/EndpointRequests/EndpointRequest.swift +++ b/Sources/ScryfallKit/Networking/EndpointRequests/EndpointRequest.swift @@ -20,11 +20,7 @@ extension EndpointRequest { } guard let url = urlComponents?.url else { - if #available(iOS 14.0, macOS 11.0, *) { - Logger.main.error("Couldn't make url") - } else { - print("Couldn't make url") - } + scryfallKitLogger.error("Couldn't make url") return nil } diff --git a/Sources/ScryfallKit/Networking/NetworkService.swift b/Sources/ScryfallKit/Networking/NetworkService.swift index 892fec0..a0ac0cd 100644 --- a/Sources/ScryfallKit/Networking/NetworkService.swift +++ b/Sources/ScryfallKit/Networking/NetworkService.swift @@ -5,52 +5,33 @@ import Foundation import OSLog -/// An enum representing the two available levels of log verbosity -public enum NetworkLogLevel: Sendable { - /// Only log when requests are made and errors - case minimal - /// Log the bodies of requests and responses - case verbose -} - protocol NetworkServiceProtocol: Sendable { func request( _ request: EndpointRequest, as type: T.Type, completion: @Sendable @escaping (Result) -> Void ) - @available(macOS 10.15.0, *, iOS 13.0.0, *) func request(_ request: EndpointRequest, as type: T.Type) async throws -> T } struct NetworkService: NetworkServiceProtocol, Sendable { - var logLevel: NetworkLogLevel + let logger: Logger - func request( + init(logger: Logger = scryfallKitLogger) { + self.logger = logger + } + + func request( _ request: EndpointRequest, as type: T.Type, completion: @Sendable @escaping (Result) -> Void ) { guard let urlRequest = request.urlRequest else { - if #available(macOS 11.0, iOS 14.0, *) { - Logger.network.error("Invalid url request") - } else { - print("Invalid url request") - } + logger.error("Invalid url request") completion(.failure(ScryfallKitError.invalidUrl)) return } - if logLevel == .verbose, let body = urlRequest.httpBody, - let JSONString = String(data: body, encoding: String.Encoding.utf8) - { - print("Sending request with body:") - if #available(macOS 11.0, iOS 14.0, *) { - Logger.network.debug("\(JSONString)") - } else { - print(JSONString) - } - } - + logger.trace("Starting request: \(urlRequest.debugDescription)") let task = URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in do { let result = try handle(dataType: type, data: data, response: response, error: error) @@ -60,12 +41,7 @@ struct NetworkService: NetworkServiceProtocol, Sendable { } } - if #available(macOS 11.0, iOS 14.0, *) { - Logger.network.debug( - "Making request to: '\(String(describing: urlRequest.url?.absoluteString))'") - } else { - print("Making request to: '\(String(describing: urlRequest.url?.absoluteString))'") - } + logger.trace("Making request to: '\(String(describing: urlRequest.url?.absoluteString))'") task.resume() } @@ -88,15 +64,8 @@ struct NetworkService: NetworkServiceProtocol, Sendable { decoder.keyDecodingStrategy = .convertFromSnakeCase if (200..<300).contains(httpStatus) { - if logLevel == .verbose { - let responseBody = String(data: content, encoding: .utf8) - if #available(macOS 11.0, iOS 14.0, *) { - Logger.network.debug("\(responseBody ?? "Couldn't represent response body as string")") - } else { - print(responseBody ?? "Couldn't represent response body as string") - } - } - + let responseBody = String(data: content, encoding: .utf8) + logger.debug("\(responseBody ?? "Couldn't represent response body as string")") return try decoder.decode(dataType, from: content) } else { let httpError = try decoder.decode(ScryfallError.self, from: content) @@ -104,7 +73,6 @@ struct NetworkService: NetworkServiceProtocol, Sendable { } } - @available(macOS 10.15.0, *, iOS 13.0.0, *) func request(_ request: EndpointRequest, as type: T.Type) async throws -> T where T: Sendable { try await withCheckedThrowingContinuation { continuation in diff --git a/Sources/ScryfallKit/ScryfallClient+Async.swift b/Sources/ScryfallKit/ScryfallClient+Async.swift index 6b69232..1234450 100644 --- a/Sources/ScryfallKit/ScryfallClient+Async.swift +++ b/Sources/ScryfallKit/ScryfallClient+Async.swift @@ -4,7 +4,6 @@ import Foundation -@available(macOS 10.15.0, *, iOS 13.0.0, *) extension ScryfallClient { /// Equivalent to ``searchCards(filters:unique:order:sortDirection:includeExtras:includeMultilingual:includeVariations:page:completion:)`` but with async/await syntax public func searchCards( diff --git a/Sources/ScryfallKit/ScryfallClient.swift b/Sources/ScryfallKit/ScryfallClient.swift index 4fd0822..9ce72ff 100644 --- a/Sources/ScryfallKit/ScryfallClient.swift +++ b/Sources/ScryfallKit/ScryfallClient.swift @@ -3,17 +3,18 @@ // import Foundation +import OSLog + +public let scryfallKitLogger = Logger(subsystem: "dev.hearst.scryfallkit", category: "ScryfallClient") /// A client for interacting with the Scryfall API public final class ScryfallClient: Sendable { - private let networkLogLevel: NetworkLogLevel let networkService: NetworkServiceProtocol /// Initialize an instance of the ScryfallClient /// - Parameter networkLogLevel: The desired logging level. See ``NetworkLogLevel`` - public init(networkLogLevel: NetworkLogLevel = .minimal) { - self.networkLogLevel = networkLogLevel - self.networkService = NetworkService(logLevel: networkLogLevel) + public init(logger: Logger = scryfallKitLogger) { + self.networkService = NetworkService(logger: logger) } /// Perform a search using an array of ``CardFieldFilter`` objects. diff --git a/Tests/ScryfallKitTests/SmokeTests.swift b/Tests/ScryfallKitTests/SmokeTests.swift index 18b3080..96e517b 100644 --- a/Tests/ScryfallKitTests/SmokeTests.swift +++ b/Tests/ScryfallKitTests/SmokeTests.swift @@ -1,17 +1,17 @@ // // SmokeTests.swift // - +import OSLog import XCTest @testable import ScryfallKit -@available(iOS 13.0.0, *) final class SmokeTests: XCTestCase { var client: ScryfallClient! override func setUp() { - self.client = ScryfallClient() + let logger = Logger(subsystem: "dev.hearst.ScryfallKitTests", category: "SmokeTests") + self.client = ScryfallClient(logger: logger) } func testLayouts() async throws { From 4626db595eaf582d02438e44f6975faf7aa32d17 Mon Sep 17 00:00:00 2001 From: Jacob Hearst Date: Wed, 7 Jan 2026 16:04:22 -0600 Subject: [PATCH 2/4] Add tvOS --- Package.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Package.swift b/Package.swift index 1c96fb7..f28ae07 100644 --- a/Package.swift +++ b/Package.swift @@ -1,11 +1,10 @@ // swift-tools-version:6.2 -// The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "ScryfallKit", - platforms: [.macOS(.v11), .iOS(.v14), .watchOS(.v11), .visionOS(.v26)], + platforms: [.macOS(.v11), .iOS(.v14), .watchOS(.v11), .visionOS(.v26), .tvOS(.v14)], products: [ .library( name: "ScryfallKit", From 367fd35ceeb19e7f37f965254e803ba8fefec5aa Mon Sep 17 00:00:00 2001 From: Jacob Hearst Date: Wed, 7 Jan 2026 16:12:46 -0600 Subject: [PATCH 3/4] Try selecting a newer xcode --- .github/workflows/build+test.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build+test.yml b/.github/workflows/build+test.yml index 9e4ded9..be8a14b 100644 --- a/.github/workflows/build+test.yml +++ b/.github/workflows/build+test.yml @@ -19,7 +19,9 @@ jobs: - uses: actions/checkout@v2 - name: Build package - run: swift build -v + run: | + xcodes select 26.1.1 + swift build -v test: runs-on: macos-latest @@ -27,7 +29,9 @@ jobs: - uses: actions/checkout@v2 - name: Smoke test package - run: swift test -v + run: | + xcodes select 26.1.1 + swift test -v - name: CINotify Email if: ${{ failure() }} From 02af63c9f86813fcd58ebae1182729ea0a5ce12e Mon Sep 17 00:00:00 2001 From: Jacob Hearst Date: Wed, 7 Jan 2026 16:17:59 -0600 Subject: [PATCH 4/4] Tidy up logging --- README.md | 2 +- Sources/ScryfallKit/Extensions/Card+helpers.swift | 1 - .../Networking/EndpointRequests/CardRequests.swift | 7 +------ .../EndpointRequests/EndpointRequest.swift | 1 - Sources/ScryfallKit/Networking/NetworkService.swift | 12 ++++++------ Sources/ScryfallKit/ScryfallClient.swift | 6 ++---- 6 files changed, 10 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index cfc2748..6ddedea 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ client.getCardByName(exact: "Doom Blade", set: "STA") { result in ``` ## Network Logging -The ScryfallClient has a configurable level of network logging with two options: minimal and verbose. Enabling verbose logging will print the HTTP body of each request and response. Minimal logging will log that a request was made (and the URL it's made to) as well as that a response was received. +The ScryfallClient will emit trace and debug logs if supplied with an `Logger` instance from the `OSLog` library. ## Contributing Contributions are always welcome, simply fork this repo, make and test your changes, and then open a pull request. I will try and review it within a reasonable amount of time. diff --git a/Sources/ScryfallKit/Extensions/Card+helpers.swift b/Sources/ScryfallKit/Extensions/Card+helpers.swift index 323068a..4023c2a 100644 --- a/Sources/ScryfallKit/Extensions/Card+helpers.swift +++ b/Sources/ScryfallKit/Extensions/Card+helpers.swift @@ -86,7 +86,6 @@ extension Card { } guard let uri = uris.uri(for: type) else { - scryfallKitLogger.error("No URI for image type \(type.rawValue)") return nil } diff --git a/Sources/ScryfallKit/Networking/EndpointRequests/CardRequests.swift b/Sources/ScryfallKit/Networking/EndpointRequests/CardRequests.swift index a6b5f37..d584aa1 100644 --- a/Sources/ScryfallKit/Networking/EndpointRequests/CardRequests.swift +++ b/Sources/ScryfallKit/Networking/EndpointRequests/CardRequests.swift @@ -91,7 +91,6 @@ struct GetCard: EndpointRequest { default: // This guard should never trip. The only card identifier that doesn't have provider/id is the set code/collector guard let id = identifier.id else { - scryfallKitLogger.error("Provided identifier doesn't have a provider or doesn't have an id") fatalError( "Encountered a situation that shouldn't be possible: Card identifier's id property was nil" ) @@ -116,10 +115,6 @@ struct GetCardCollection: EndpointRequest { let identifierJSON = identifiers.map { $0.json } let requestBody: [String: [[String: String]]] = ["identifiers": identifierJSON] - do { - body = try JSONSerialization.data(withJSONObject: requestBody) - } catch { - scryfallKitLogger.error("Errored serializing dict to JSON for GetCardCollection request") - } + self.body = try? JSONSerialization.data(withJSONObject: requestBody) } } diff --git a/Sources/ScryfallKit/Networking/EndpointRequests/EndpointRequest.swift b/Sources/ScryfallKit/Networking/EndpointRequests/EndpointRequest.swift index 5519af5..b171b7b 100644 --- a/Sources/ScryfallKit/Networking/EndpointRequests/EndpointRequest.swift +++ b/Sources/ScryfallKit/Networking/EndpointRequests/EndpointRequest.swift @@ -20,7 +20,6 @@ extension EndpointRequest { } guard let url = urlComponents?.url else { - scryfallKitLogger.error("Couldn't make url") return nil } diff --git a/Sources/ScryfallKit/Networking/NetworkService.swift b/Sources/ScryfallKit/Networking/NetworkService.swift index a0ac0cd..1e2a01a 100644 --- a/Sources/ScryfallKit/Networking/NetworkService.swift +++ b/Sources/ScryfallKit/Networking/NetworkService.swift @@ -15,9 +15,9 @@ protocol NetworkServiceProtocol: Sendable { } struct NetworkService: NetworkServiceProtocol, Sendable { - let logger: Logger + let logger: Logger? - init(logger: Logger = scryfallKitLogger) { + init(logger: Logger?) { self.logger = logger } @@ -26,12 +26,12 @@ struct NetworkService: NetworkServiceProtocol, Sendable { completion: @Sendable @escaping (Result) -> Void ) { guard let urlRequest = request.urlRequest else { - logger.error("Invalid url request") + logger?.error("Invalid url request") completion(.failure(ScryfallKitError.invalidUrl)) return } - logger.trace("Starting request: \(urlRequest.debugDescription)") + logger?.trace("Starting request: \(urlRequest.debugDescription)") let task = URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in do { let result = try handle(dataType: type, data: data, response: response, error: error) @@ -41,7 +41,7 @@ struct NetworkService: NetworkServiceProtocol, Sendable { } } - logger.trace("Making request to: '\(String(describing: urlRequest.url?.absoluteString))'") + logger?.trace("Making request to: '\(String(describing: urlRequest.url?.absoluteString))'") task.resume() } @@ -65,7 +65,7 @@ struct NetworkService: NetworkServiceProtocol, Sendable { if (200..<300).contains(httpStatus) { let responseBody = String(data: content, encoding: .utf8) - logger.debug("\(responseBody ?? "Couldn't represent response body as string")") + logger?.debug("\(responseBody ?? "Couldn't represent response body as string")") return try decoder.decode(dataType, from: content) } else { let httpError = try decoder.decode(ScryfallError.self, from: content) diff --git a/Sources/ScryfallKit/ScryfallClient.swift b/Sources/ScryfallKit/ScryfallClient.swift index 9ce72ff..3b226aa 100644 --- a/Sources/ScryfallKit/ScryfallClient.swift +++ b/Sources/ScryfallKit/ScryfallClient.swift @@ -5,15 +5,13 @@ import Foundation import OSLog -public let scryfallKitLogger = Logger(subsystem: "dev.hearst.scryfallkit", category: "ScryfallClient") - /// A client for interacting with the Scryfall API public final class ScryfallClient: Sendable { let networkService: NetworkServiceProtocol /// Initialize an instance of the ScryfallClient - /// - Parameter networkLogLevel: The desired logging level. See ``NetworkLogLevel`` - public init(logger: Logger = scryfallKitLogger) { + /// - Parameter logger: The logger to use. Pass nil to disable logging + public init(logger: Logger? = nil) { self.networkService = NetworkService(logger: logger) }