From 08b312e2efe701940dbc492cbb0b955b215287aa Mon Sep 17 00:00:00 2001 From: Richard Piazza Date: Wed, 26 Aug 2026 15:15:09 -0500 Subject: [PATCH 01/10] Cleanup Foundation Imports --- Package.resolved | 6 ++-- Package.swift | 2 +- .../CoreData/CoreDataLogProvider.swift | 4 +-- Sources/Occurrence/CoreData/LogModel.swift | 4 +-- .../CoreData/Logger.Filter+Predicate.swift | 4 +-- .../Occurrence/CoreData/ManagedEntry.swift | 4 +-- .../Extensions/Array+Occurrence.swift | 1 - .../Extensions/CocoaError+Occurrence.swift | 4 +++ .../Extensions/DecodingError+Occurrence.swift | 4 +++ .../Extensions/EncodingError+Occurrence.swift | 4 +++ .../Extensions/FileManager+Occurrence.swift | 4 +++ .../Occurrence/Extensions/Logger+Entry.swift | 4 +++ .../Occurrence/Extensions/Logger+Filter.swift | 4 +++ .../Extensions/Logger+Occurrence.swift | 4 +++ .../Extensions/Logger.Level+Occurrence.swift | 4 +++ .../Logger.Message+Occurrence.swift | 1 - .../Logger.Metadata+Occurrence.swift | 4 +++ .../Logger.MetadataValue+Occurrence.swift | 1 - .../Extensions/URLError+Occurrence.swift | 3 +- Sources/Occurrence/LazyLogger.swift | 1 - Sources/Occurrence/LogProvider.swift | 1 - Sources/Occurrence/LogStreamer.swift | 1 - Sources/Occurrence/LoggableError.swift | 4 +++ Sources/Occurrence/Occurrence.swift | 28 ++++++++----------- .../SQLite/Logger.Filter+Where.swift | 1 - .../SQLite/SQLiteEntry+Mapping.swift | 4 +++ Sources/Occurrence/SQLite/SQLiteEntry.swift | 4 +++ .../Occurrence/SQLite/SQLiteLogProvider.swift | 4 +++ .../Streamer/OccurrenceLogStreamer.swift | 4 +++ .../SwiftUI/PreviewLogProvider.swift | 4 +++ Tests/OccurrenceTests/LogProviderTests.swift | 7 +++++ .../OccurrenceTests/LoggableErrorTests.swift | 4 +++ .../OccurrenceTests/LoggerMetadataTests.swift | 4 +++ Tests/OccurrenceTests/RedactionTests.swift | 4 +++ 34 files changed, 105 insertions(+), 36 deletions(-) diff --git a/Package.resolved b/Package.resolved index 534d0c6..41e32e6 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "48c9daa9bc6b9b0058632d48d5d17f18202b68c77a6fdce81dd258d2321ca03c", + "originHash" : "bda727af496f33ba05f6c8424a8dd2dbd27ee363794fc499fb819c3e97dbfd8a", "pins" : [ { "identity" : "sqlite.swift", @@ -24,8 +24,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-log.git", "state" : { - "revision" : "2778fd4e5a12a8aaa30a3ee8285f4ce54c5f3181", - "version" : "1.9.1" + "revision" : "3ffafb9722d5d918c614feb496c8789a3b59d222", + "version" : "1.15.0" } }, { diff --git a/Package.swift b/Package.swift index ad6df6f..a9716f3 100644 --- a/Package.swift +++ b/Package.swift @@ -22,7 +22,7 @@ let package = Package( dependencies: [ // Dependencies declare other packages that this package depends on. // .package(url: /* package url */, from: "1.0.0"), - .package(url: "https://github.com/apple/swift-log.git", from: "1.9.1"), + .package(url: "https://github.com/apple/swift-log.git", from: "1.15.0"), .package(url: "https://github.com/richardpiazza/Statement.git", from: "0.8.1"), .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.16.0"), .package(url: "https://github.com/swhitty/swift-mutex.git", from: "0.0.6"), diff --git a/Sources/Occurrence/CoreData/CoreDataLogProvider.swift b/Sources/Occurrence/CoreData/CoreDataLogProvider.swift index 4225a8e..7a5fa5a 100644 --- a/Sources/Occurrence/CoreData/CoreDataLogProvider.swift +++ b/Sources/Occurrence/CoreData/CoreDataLogProvider.swift @@ -1,8 +1,8 @@ +#if canImport(CoreData) +@preconcurrency import CoreData import Foundation import Logging import Mutex -#if canImport(CoreData) -@preconcurrency import CoreData final class CoreDataLogProvider: LogProvider { diff --git a/Sources/Occurrence/CoreData/LogModel.swift b/Sources/Occurrence/CoreData/LogModel.swift index 3086da1..b228f61 100644 --- a/Sources/Occurrence/CoreData/LogModel.swift +++ b/Sources/Occurrence/CoreData/LogModel.swift @@ -1,7 +1,7 @@ -import Foundation -import Mutex #if canImport(CoreData) import CoreData +import Foundation +import Mutex enum LogModel { case version_1_0_0 diff --git a/Sources/Occurrence/CoreData/Logger.Filter+Predicate.swift b/Sources/Occurrence/CoreData/Logger.Filter+Predicate.swift index 665880f..25ff567 100644 --- a/Sources/Occurrence/CoreData/Logger.Filter+Predicate.swift +++ b/Sources/Occurrence/CoreData/Logger.Filter+Predicate.swift @@ -1,7 +1,7 @@ -import Foundation -import Logging #if canImport(CoreData) import CoreData +import Foundation +import Logging extension Logger.Filter { var predicate: NSPredicate { diff --git a/Sources/Occurrence/CoreData/ManagedEntry.swift b/Sources/Occurrence/CoreData/ManagedEntry.swift index 85eb94d..2723e7e 100644 --- a/Sources/Occurrence/CoreData/ManagedEntry.swift +++ b/Sources/Occurrence/CoreData/ManagedEntry.swift @@ -1,7 +1,7 @@ -import Foundation -import Logging #if canImport(CoreData) import CoreData +import Foundation +import Logging @objc(ManagedEntry) class ManagedEntry: NSManagedObject { diff --git a/Sources/Occurrence/Extensions/Array+Occurrence.swift b/Sources/Occurrence/Extensions/Array+Occurrence.swift index f4b3e7a..85d5b92 100644 --- a/Sources/Occurrence/Extensions/Array+Occurrence.swift +++ b/Sources/Occurrence/Extensions/Array+Occurrence.swift @@ -1,4 +1,3 @@ -import Foundation import Logging extension [Any] { diff --git a/Sources/Occurrence/Extensions/CocoaError+Occurrence.swift b/Sources/Occurrence/Extensions/CocoaError+Occurrence.swift index 170fbd9..23f364c 100644 --- a/Sources/Occurrence/Extensions/CocoaError+Occurrence.swift +++ b/Sources/Occurrence/Extensions/CocoaError+Occurrence.swift @@ -1,3 +1,7 @@ +#if canImport(FoundationEssentials) +import FoundationEssentials +#else import Foundation +#endif extension CocoaError: LoggableError {} diff --git a/Sources/Occurrence/Extensions/DecodingError+Occurrence.swift b/Sources/Occurrence/Extensions/DecodingError+Occurrence.swift index 0f15106..1f25014 100644 --- a/Sources/Occurrence/Extensions/DecodingError+Occurrence.swift +++ b/Sources/Occurrence/Extensions/DecodingError+Occurrence.swift @@ -1,4 +1,8 @@ +#if canImport(FoundationEssentials) +import FoundationEssentials +#else import Foundation +#endif import Logging #if hasFeature(RetroactiveAttribute) diff --git a/Sources/Occurrence/Extensions/EncodingError+Occurrence.swift b/Sources/Occurrence/Extensions/EncodingError+Occurrence.swift index 7983033..c1e69ce 100644 --- a/Sources/Occurrence/Extensions/EncodingError+Occurrence.swift +++ b/Sources/Occurrence/Extensions/EncodingError+Occurrence.swift @@ -1,4 +1,8 @@ +#if canImport(FoundationEssentials) +import FoundationEssentials +#else import Foundation +#endif import Logging #if hasFeature(RetroactiveAttribute) diff --git a/Sources/Occurrence/Extensions/FileManager+Occurrence.swift b/Sources/Occurrence/Extensions/FileManager+Occurrence.swift index 60ddf2b..370b7da 100644 --- a/Sources/Occurrence/Extensions/FileManager+Occurrence.swift +++ b/Sources/Occurrence/Extensions/FileManager+Occurrence.swift @@ -1,4 +1,8 @@ +#if canImport(FoundationEssentials) +import FoundationEssentials +#else import Foundation +#endif extension FileManager { /// Locates (and creates) the **Occurrence** package directory. diff --git a/Sources/Occurrence/Extensions/Logger+Entry.swift b/Sources/Occurrence/Extensions/Logger+Entry.swift index 73bb579..68772e2 100644 --- a/Sources/Occurrence/Extensions/Logger+Entry.swift +++ b/Sources/Occurrence/Extensions/Logger+Entry.swift @@ -1,4 +1,8 @@ +#if canImport(FoundationEssentials) +import FoundationEssentials +#else import Foundation +#endif import Logging public extension Logger { diff --git a/Sources/Occurrence/Extensions/Logger+Filter.swift b/Sources/Occurrence/Extensions/Logger+Filter.swift index dad0eb1..0a1f2e6 100644 --- a/Sources/Occurrence/Extensions/Logger+Filter.swift +++ b/Sources/Occurrence/Extensions/Logger+Filter.swift @@ -1,4 +1,8 @@ +#if canImport(FoundationEssentials) +import FoundationEssentials +#else import Foundation +#endif import Logging public extension Logger { diff --git a/Sources/Occurrence/Extensions/Logger+Occurrence.swift b/Sources/Occurrence/Extensions/Logger+Occurrence.swift index 4f67b43..f251491 100644 --- a/Sources/Occurrence/Extensions/Logger+Occurrence.swift +++ b/Sources/Occurrence/Extensions/Logger+Occurrence.swift @@ -1,4 +1,8 @@ +#if canImport(FoundationEssentials) +import FoundationEssentials +#else import Foundation +#endif import Logging public extension Logger { diff --git a/Sources/Occurrence/Extensions/Logger.Level+Occurrence.swift b/Sources/Occurrence/Extensions/Logger.Level+Occurrence.swift index de162ef..eaf6b17 100644 --- a/Sources/Occurrence/Extensions/Logger.Level+Occurrence.swift +++ b/Sources/Occurrence/Extensions/Logger.Level+Occurrence.swift @@ -1,4 +1,8 @@ +#if canImport(FoundationEssentials) +import FoundationEssentials +#else import Foundation +#endif import Logging public extension Logger.Level { diff --git a/Sources/Occurrence/Extensions/Logger.Message+Occurrence.swift b/Sources/Occurrence/Extensions/Logger.Message+Occurrence.swift index 87f4591..8d75ea2 100644 --- a/Sources/Occurrence/Extensions/Logger.Message+Occurrence.swift +++ b/Sources/Occurrence/Extensions/Logger.Message+Occurrence.swift @@ -1,4 +1,3 @@ -import Foundation import Logging #if hasFeature(RetroactiveAttribute) diff --git a/Sources/Occurrence/Extensions/Logger.Metadata+Occurrence.swift b/Sources/Occurrence/Extensions/Logger.Metadata+Occurrence.swift index 2a705b7..fd002e2 100644 --- a/Sources/Occurrence/Extensions/Logger.Metadata+Occurrence.swift +++ b/Sources/Occurrence/Extensions/Logger.Metadata+Occurrence.swift @@ -1,4 +1,8 @@ +#if canImport(FoundationEssentials) +import FoundationEssentials +#else import Foundation +#endif import Logging public extension Logger.Metadata { diff --git a/Sources/Occurrence/Extensions/Logger.MetadataValue+Occurrence.swift b/Sources/Occurrence/Extensions/Logger.MetadataValue+Occurrence.swift index 7ec6e57..91264d4 100644 --- a/Sources/Occurrence/Extensions/Logger.MetadataValue+Occurrence.swift +++ b/Sources/Occurrence/Extensions/Logger.MetadataValue+Occurrence.swift @@ -1,4 +1,3 @@ -import Foundation import Logging #if hasFeature(RetroactiveAttribute) diff --git a/Sources/Occurrence/Extensions/URLError+Occurrence.swift b/Sources/Occurrence/Extensions/URLError+Occurrence.swift index a8953b4..7d7eb19 100644 --- a/Sources/Occurrence/Extensions/URLError+Occurrence.swift +++ b/Sources/Occurrence/Extensions/URLError+Occurrence.swift @@ -1,6 +1,7 @@ -import Foundation #if canImport(FoundationNetworking) import FoundationNetworking +#else +import Foundation #endif extension URLError: LoggableError {} diff --git a/Sources/Occurrence/LazyLogger.swift b/Sources/Occurrence/LazyLogger.swift index 9dcb41c..7bd7f34 100644 --- a/Sources/Occurrence/LazyLogger.swift +++ b/Sources/Occurrence/LazyLogger.swift @@ -1,4 +1,3 @@ -import Foundation import Logging /// Provides an inline way to reference a lazy-loaded instance of `Logger`. diff --git a/Sources/Occurrence/LogProvider.swift b/Sources/Occurrence/LogProvider.swift index 840abba..5f304b8 100644 --- a/Sources/Occurrence/LogProvider.swift +++ b/Sources/Occurrence/LogProvider.swift @@ -1,4 +1,3 @@ -import Foundation import Logging /// A source of logging data. diff --git a/Sources/Occurrence/LogStreamer.swift b/Sources/Occurrence/LogStreamer.swift index 3d2a448..c0b9251 100644 --- a/Sources/Occurrence/LogStreamer.swift +++ b/Sources/Occurrence/LogStreamer.swift @@ -1,4 +1,3 @@ -import Foundation import Logging public protocol LogStreamer: Sendable { diff --git a/Sources/Occurrence/LoggableError.swift b/Sources/Occurrence/LoggableError.swift index d93ee1f..e8692a9 100644 --- a/Sources/Occurrence/LoggableError.swift +++ b/Sources/Occurrence/LoggableError.swift @@ -1,4 +1,8 @@ +#if canImport(FoundationEssentials) +import FoundationEssentials +#else import Foundation +#endif import Logging /// An `Error` type which can produced `Logger.Metadata` suitable for logging. diff --git a/Sources/Occurrence/Occurrence.swift b/Sources/Occurrence/Occurrence.swift index e5d2688..5e9c2c0 100644 --- a/Sources/Occurrence/Occurrence.swift +++ b/Sources/Occurrence/Occurrence.swift @@ -1,4 +1,8 @@ +#if canImport(FoundationEssentials) +import FoundationEssentials +#else import Foundation +#endif import Logging import Mutex @@ -73,16 +77,8 @@ public struct Occurrence: LogHandler { } } - public func log( - level: Logger.Level, - message: Logger.Message, - metadata: Logger.Metadata?, - source: String, - file: String, - function: String, - line: UInt, - ) { - let joinedMetadata: Logger.Metadata? = switch (metadata, metadataProvider?.get()) { + public func log(event: LogEvent) { + let joinedMetadata: Logger.Metadata? = switch (event.metadata, metadataProvider?.get()) { case (.some(let instance), .some(let context)): instance.merging(context, uniquingKeysWith: { instanceValue, _ in instanceValue }) case (.some(let instance), .none): @@ -95,13 +91,13 @@ public struct Occurrence: LogHandler { let entry = Logger.Entry( subsystem: Logger.Subsystem(stringLiteral: label), - level: level, - message: message, + level: event.level, + message: event.message, metadata: joinedMetadata, - source: source, - file: file, - function: function, - line: line, + source: event.source, + file: event.file, + function: event.function, + line: event.line, ) if Self.configuration.outputToConsole { diff --git a/Sources/Occurrence/SQLite/Logger.Filter+Where.swift b/Sources/Occurrence/SQLite/Logger.Filter+Where.swift index da51aa9..2f3032a 100644 --- a/Sources/Occurrence/SQLite/Logger.Filter+Where.swift +++ b/Sources/Occurrence/SQLite/Logger.Filter+Where.swift @@ -1,4 +1,3 @@ -import Foundation import Logging import Statement import StatementSQLite diff --git a/Sources/Occurrence/SQLite/SQLiteEntry+Mapping.swift b/Sources/Occurrence/SQLite/SQLiteEntry+Mapping.swift index c8313ac..9253681 100644 --- a/Sources/Occurrence/SQLite/SQLiteEntry+Mapping.swift +++ b/Sources/Occurrence/SQLite/SQLiteEntry+Mapping.swift @@ -1,4 +1,8 @@ +#if canImport(FoundationEssentials) +import FoundationEssentials +#else import Foundation +#endif import Logging private let encoder = JSONEncoder() diff --git a/Sources/Occurrence/SQLite/SQLiteEntry.swift b/Sources/Occurrence/SQLite/SQLiteEntry.swift index 5d970ee..71c0196 100644 --- a/Sources/Occurrence/SQLite/SQLiteEntry.swift +++ b/Sources/Occurrence/SQLite/SQLiteEntry.swift @@ -1,4 +1,8 @@ +#if canImport(FoundationEssentials) +import FoundationEssentials +#else import Foundation +#endif import Logging import Statement diff --git a/Sources/Occurrence/SQLite/SQLiteLogProvider.swift b/Sources/Occurrence/SQLite/SQLiteLogProvider.swift index 5ac7ecf..f6bf5c5 100644 --- a/Sources/Occurrence/SQLite/SQLiteLogProvider.swift +++ b/Sources/Occurrence/SQLite/SQLiteLogProvider.swift @@ -1,4 +1,8 @@ +#if canImport(FoundationEssentials) +import FoundationEssentials +#else import Foundation +#endif import Logging @preconcurrency import SQLite import Statement diff --git a/Sources/Occurrence/Streamer/OccurrenceLogStreamer.swift b/Sources/Occurrence/Streamer/OccurrenceLogStreamer.swift index 940a571..17d6a72 100644 --- a/Sources/Occurrence/Streamer/OccurrenceLogStreamer.swift +++ b/Sources/Occurrence/Streamer/OccurrenceLogStreamer.swift @@ -1,4 +1,8 @@ +#if canImport(FoundationEssentials) +import FoundationEssentials +#else import Foundation +#endif import Logging import Mutex diff --git a/Sources/Occurrence/SwiftUI/PreviewLogProvider.swift b/Sources/Occurrence/SwiftUI/PreviewLogProvider.swift index 3e01cc4..b982e15 100644 --- a/Sources/Occurrence/SwiftUI/PreviewLogProvider.swift +++ b/Sources/Occurrence/SwiftUI/PreviewLogProvider.swift @@ -1,4 +1,8 @@ +#if canImport(FoundationEssentials) +import FoundationEssentials +#else import Foundation +#endif import Logging struct PreviewLogProvider: LogProvider { diff --git a/Tests/OccurrenceTests/LogProviderTests.swift b/Tests/OccurrenceTests/LogProviderTests.swift index a8e509c..9efbfed 100644 --- a/Tests/OccurrenceTests/LogProviderTests.swift +++ b/Tests/OccurrenceTests/LogProviderTests.swift @@ -1,7 +1,14 @@ #if canImport(CoreData) import CoreData #endif +#if canImport(FoundationEssentials) +import FoundationEssentials +#else import Foundation +#endif +#if canImport(FoundationNetworking) +import FoundationNetworking +#endif import Logging @testable import Occurrence import Testing diff --git a/Tests/OccurrenceTests/LoggableErrorTests.swift b/Tests/OccurrenceTests/LoggableErrorTests.swift index 0797db6..f8139b0 100644 --- a/Tests/OccurrenceTests/LoggableErrorTests.swift +++ b/Tests/OccurrenceTests/LoggableErrorTests.swift @@ -1,4 +1,8 @@ +#if canImport(FoundationEssentials) +import FoundationEssentials +#else import Foundation +#endif #if canImport(FoundationNetworking) import FoundationNetworking #endif diff --git a/Tests/OccurrenceTests/LoggerMetadataTests.swift b/Tests/OccurrenceTests/LoggerMetadataTests.swift index f575833..aa63af8 100644 --- a/Tests/OccurrenceTests/LoggerMetadataTests.swift +++ b/Tests/OccurrenceTests/LoggerMetadataTests.swift @@ -1,4 +1,8 @@ +#if canImport(FoundationEssentials) +import FoundationEssentials +#else import Foundation +#endif import Logging @testable import Occurrence import Testing diff --git a/Tests/OccurrenceTests/RedactionTests.swift b/Tests/OccurrenceTests/RedactionTests.swift index 81d1892..8e0aa02 100644 --- a/Tests/OccurrenceTests/RedactionTests.swift +++ b/Tests/OccurrenceTests/RedactionTests.swift @@ -1,4 +1,8 @@ +#if canImport(FoundationEssentials) +import FoundationEssentials +#else import Foundation +#endif @testable import Occurrence import Testing From 4b7ff8861ae129b4bd842bf7ef94472728a40644 Mon Sep 17 00:00:00 2001 From: Richard Piazza Date: Wed, 26 Aug 2026 15:17:46 -0500 Subject: [PATCH 02/10] Updated format rules --- .swiftformat | 1 + 1 file changed, 1 insertion(+) diff --git a/.swiftformat b/.swiftformat index 321250d..f47882e 100644 --- a/.swiftformat +++ b/.swiftformat @@ -11,6 +11,7 @@ --disable blankLinesAtStartOfScope --disable hoistPatternLet --disable redundantType +--disable swiftTestingTestCaseNames --disable unusedArguments --disable wrapArguments --disable wrapPropertyBodies From 58d9e78887d5673db4ebf5b75b7b0b179a45e1af Mon Sep 17 00:00:00 2001 From: Richard Piazza Date: Wed, 26 Aug 2026 15:29:22 -0500 Subject: [PATCH 03/10] Android Test Action --- .github/workflows/pull_request.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index d7db51c..5fb5349 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -84,3 +84,15 @@ jobs: env: DESTINATION: "generic/platform=${{ matrix.platform }}" run: set -o pipefail && xcodebuild build -scheme "$SCHEME" -destination "$DESTINATION" | xcbeautify + + Android: + strategy: + fail-fast: false + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v6 + - name: Build + uses: skiptools/swift-android-action@v2 + with: + free-disk-space: true From 24d2ea76d4d69b48a8fe6a6f76a44dffc9425249 Mon Sep 17 00:00:00 2001 From: Richard Piazza Date: Wed, 26 Aug 2026 15:33:51 -0500 Subject: [PATCH 04/10] Localized Description --- Sources/Occurrence/Extensions/DecodingError+Occurrence.swift | 4 ---- Sources/Occurrence/Extensions/EncodingError+Occurrence.swift | 4 ---- Sources/Occurrence/Occurrence.swift | 4 ---- Tests/OccurrenceTests/LogProviderTests.swift | 4 ---- 4 files changed, 16 deletions(-) diff --git a/Sources/Occurrence/Extensions/DecodingError+Occurrence.swift b/Sources/Occurrence/Extensions/DecodingError+Occurrence.swift index 1f25014..0f15106 100644 --- a/Sources/Occurrence/Extensions/DecodingError+Occurrence.swift +++ b/Sources/Occurrence/Extensions/DecodingError+Occurrence.swift @@ -1,8 +1,4 @@ -#if canImport(FoundationEssentials) -import FoundationEssentials -#else import Foundation -#endif import Logging #if hasFeature(RetroactiveAttribute) diff --git a/Sources/Occurrence/Extensions/EncodingError+Occurrence.swift b/Sources/Occurrence/Extensions/EncodingError+Occurrence.swift index c1e69ce..7983033 100644 --- a/Sources/Occurrence/Extensions/EncodingError+Occurrence.swift +++ b/Sources/Occurrence/Extensions/EncodingError+Occurrence.swift @@ -1,8 +1,4 @@ -#if canImport(FoundationEssentials) -import FoundationEssentials -#else import Foundation -#endif import Logging #if hasFeature(RetroactiveAttribute) diff --git a/Sources/Occurrence/Occurrence.swift b/Sources/Occurrence/Occurrence.swift index 5e9c2c0..98fce28 100644 --- a/Sources/Occurrence/Occurrence.swift +++ b/Sources/Occurrence/Occurrence.swift @@ -1,8 +1,4 @@ -#if canImport(FoundationEssentials) -import FoundationEssentials -#else import Foundation -#endif import Logging import Mutex diff --git a/Tests/OccurrenceTests/LogProviderTests.swift b/Tests/OccurrenceTests/LogProviderTests.swift index 9efbfed..6431d90 100644 --- a/Tests/OccurrenceTests/LogProviderTests.swift +++ b/Tests/OccurrenceTests/LogProviderTests.swift @@ -1,11 +1,7 @@ #if canImport(CoreData) import CoreData #endif -#if canImport(FoundationEssentials) -import FoundationEssentials -#else import Foundation -#endif #if canImport(FoundationNetworking) import FoundationNetworking #endif From 7339e57c95dceb6534381fafa3a5455072a6afd2 Mon Sep 17 00:00:00 2001 From: Richard Piazza Date: Wed, 26 Aug 2026 16:39:18 -0500 Subject: [PATCH 05/10] Additional localized reductions --- Sources/Occurrence/Extensions/Logger+Entry.swift | 3 +++ Sources/Occurrence/LoggableError.swift | 3 --- Sources/Occurrence/SwiftUI/LogEntryView.swift | 2 +- Tests/OccurrenceTests/LogProviderTests.swift | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Sources/Occurrence/Extensions/Logger+Entry.swift b/Sources/Occurrence/Extensions/Logger+Entry.swift index 68772e2..a012538 100644 --- a/Sources/Occurrence/Extensions/Logger+Entry.swift +++ b/Sources/Occurrence/Extensions/Logger+Entry.swift @@ -8,12 +8,15 @@ import Logging public extension Logger { struct Entry: Codable, Sendable { + #if canImport(ObjectiveC) + @available(*, deprecated) public static let gmtDateFormatter: DateFormatter = { let formatter = DateFormatter() formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" formatter.timeZone = TimeZone(secondsFromGMT: 0) return formatter }() + #endif public let date: Date public let subsystem: Subsystem diff --git a/Sources/Occurrence/LoggableError.swift b/Sources/Occurrence/LoggableError.swift index e8692a9..30f3d24 100644 --- a/Sources/Occurrence/LoggableError.swift +++ b/Sources/Occurrence/LoggableError.swift @@ -59,10 +59,7 @@ public extension LoggableError { if let localizedError = self as? any LocalizedError { if let errorDescription = localizedError.errorDescription { meta[.localizedDescription] = .string(errorDescription) - } else { - meta[.localizedDescription] = .string(localizedError.localizedDescription) } - if let failureReason = localizedError.failureReason { meta[.localizedFailureReason] = .string(failureReason) } diff --git a/Sources/Occurrence/SwiftUI/LogEntryView.swift b/Sources/Occurrence/SwiftUI/LogEntryView.swift index f3e329e..052caf5 100644 --- a/Sources/Occurrence/SwiftUI/LogEntryView.swift +++ b/Sources/Occurrence/SwiftUI/LogEntryView.swift @@ -23,7 +23,7 @@ struct LogEntryView: View { HStack { Text(entry.level.fancyDescription) - Text(Logger.Entry.gmtDateFormatter.string(from: entry.date)) + Text(entry.date, format: .iso8601) } .font(.system(size: 10, weight: .semibold, design: .monospaced)) diff --git a/Tests/OccurrenceTests/LogProviderTests.swift b/Tests/OccurrenceTests/LogProviderTests.swift index 6431d90..d7a8a5e 100644 --- a/Tests/OccurrenceTests/LogProviderTests.swift +++ b/Tests/OccurrenceTests/LogProviderTests.swift @@ -86,7 +86,7 @@ final class LogProviderTests { } } } catch { - print(error.localizedDescription) + print(String(describing: error)) } } From d970f4f55b541c127fde0ccbaf5d6db51c2c6e42 Mon Sep 17 00:00:00 2001 From: Richard Piazza Date: Thu, 27 Aug 2026 07:51:52 -0500 Subject: [PATCH 06/10] Reverted Foundation Essentials --- .../Extensions/CocoaError+Occurrence.swift | 4 ---- .../Extensions/FileManager+Occurrence.swift | 4 ---- .../Occurrence/Extensions/Logger+Entry.swift | 17 +++++++++++----- .../Occurrence/Extensions/Logger+Filter.swift | 4 ---- .../Extensions/Logger+Occurrence.swift | 4 ---- .../Extensions/Logger.Level+Occurrence.swift | 4 ---- .../Logger.Metadata+Occurrence.swift | 4 ---- Sources/Occurrence/LoggableError.swift | 4 ---- .../SQLite/SQLiteEntry+Mapping.swift | 4 ---- Sources/Occurrence/SQLite/SQLiteEntry.swift | 4 ---- .../Occurrence/SQLite/SQLiteLogProvider.swift | 4 ---- .../Streamer/OccurrenceLogStreamer.swift | 4 ---- .../SwiftUI/PreviewLogProvider.swift | 4 ---- .../OccurrenceTests/LoggableErrorTests.swift | 20 ++----------------- .../OccurrenceTests/LoggerMetadataTests.swift | 4 ---- Tests/OccurrenceTests/RedactionTests.swift | 4 ---- 16 files changed, 14 insertions(+), 79 deletions(-) diff --git a/Sources/Occurrence/Extensions/CocoaError+Occurrence.swift b/Sources/Occurrence/Extensions/CocoaError+Occurrence.swift index 23f364c..170fbd9 100644 --- a/Sources/Occurrence/Extensions/CocoaError+Occurrence.swift +++ b/Sources/Occurrence/Extensions/CocoaError+Occurrence.swift @@ -1,7 +1,3 @@ -#if canImport(FoundationEssentials) -import FoundationEssentials -#else import Foundation -#endif extension CocoaError: LoggableError {} diff --git a/Sources/Occurrence/Extensions/FileManager+Occurrence.swift b/Sources/Occurrence/Extensions/FileManager+Occurrence.swift index 370b7da..60ddf2b 100644 --- a/Sources/Occurrence/Extensions/FileManager+Occurrence.swift +++ b/Sources/Occurrence/Extensions/FileManager+Occurrence.swift @@ -1,8 +1,4 @@ -#if canImport(FoundationEssentials) -import FoundationEssentials -#else import Foundation -#endif extension FileManager { /// Locates (and creates) the **Occurrence** package directory. diff --git a/Sources/Occurrence/Extensions/Logger+Entry.swift b/Sources/Occurrence/Extensions/Logger+Entry.swift index a012538..5f5d727 100644 --- a/Sources/Occurrence/Extensions/Logger+Entry.swift +++ b/Sources/Occurrence/Extensions/Logger+Entry.swift @@ -1,8 +1,4 @@ -#if canImport(FoundationEssentials) -import FoundationEssentials -#else import Foundation -#endif import Logging public extension Logger { @@ -78,8 +74,19 @@ public extension Logger { } extension Logger.Entry: CustomStringConvertible { + private var formatStyle: Date.ISO8601FormatStyle { + .iso8601 + .month() + .day() + .year() + .dateSeparator(.dash) + .time(includingFractionalSeconds: true) + .timeSeparator(.colon) + .timeZone(separator: .omitted) + } + public var description: String { - let _date = Self.gmtDateFormatter.string(from: date) + let _date = date.formatted(formatStyle) let sourceFile = [source, fileName].filter { !$0.isEmpty }.joined(separator: " ") let output = "[\(_date) \(level.fancyDescription) | \(subsystem) | \(sourceFile) | \(function) \(line)] \(message)" if let metadata { diff --git a/Sources/Occurrence/Extensions/Logger+Filter.swift b/Sources/Occurrence/Extensions/Logger+Filter.swift index 0a1f2e6..dad0eb1 100644 --- a/Sources/Occurrence/Extensions/Logger+Filter.swift +++ b/Sources/Occurrence/Extensions/Logger+Filter.swift @@ -1,8 +1,4 @@ -#if canImport(FoundationEssentials) -import FoundationEssentials -#else import Foundation -#endif import Logging public extension Logger { diff --git a/Sources/Occurrence/Extensions/Logger+Occurrence.swift b/Sources/Occurrence/Extensions/Logger+Occurrence.swift index f251491..4f67b43 100644 --- a/Sources/Occurrence/Extensions/Logger+Occurrence.swift +++ b/Sources/Occurrence/Extensions/Logger+Occurrence.swift @@ -1,8 +1,4 @@ -#if canImport(FoundationEssentials) -import FoundationEssentials -#else import Foundation -#endif import Logging public extension Logger { diff --git a/Sources/Occurrence/Extensions/Logger.Level+Occurrence.swift b/Sources/Occurrence/Extensions/Logger.Level+Occurrence.swift index eaf6b17..de162ef 100644 --- a/Sources/Occurrence/Extensions/Logger.Level+Occurrence.swift +++ b/Sources/Occurrence/Extensions/Logger.Level+Occurrence.swift @@ -1,8 +1,4 @@ -#if canImport(FoundationEssentials) -import FoundationEssentials -#else import Foundation -#endif import Logging public extension Logger.Level { diff --git a/Sources/Occurrence/Extensions/Logger.Metadata+Occurrence.swift b/Sources/Occurrence/Extensions/Logger.Metadata+Occurrence.swift index fd002e2..2a705b7 100644 --- a/Sources/Occurrence/Extensions/Logger.Metadata+Occurrence.swift +++ b/Sources/Occurrence/Extensions/Logger.Metadata+Occurrence.swift @@ -1,8 +1,4 @@ -#if canImport(FoundationEssentials) -import FoundationEssentials -#else import Foundation -#endif import Logging public extension Logger.Metadata { diff --git a/Sources/Occurrence/LoggableError.swift b/Sources/Occurrence/LoggableError.swift index 30f3d24..5c428ac 100644 --- a/Sources/Occurrence/LoggableError.swift +++ b/Sources/Occurrence/LoggableError.swift @@ -1,8 +1,4 @@ -#if canImport(FoundationEssentials) -import FoundationEssentials -#else import Foundation -#endif import Logging /// An `Error` type which can produced `Logger.Metadata` suitable for logging. diff --git a/Sources/Occurrence/SQLite/SQLiteEntry+Mapping.swift b/Sources/Occurrence/SQLite/SQLiteEntry+Mapping.swift index 9253681..c8313ac 100644 --- a/Sources/Occurrence/SQLite/SQLiteEntry+Mapping.swift +++ b/Sources/Occurrence/SQLite/SQLiteEntry+Mapping.swift @@ -1,8 +1,4 @@ -#if canImport(FoundationEssentials) -import FoundationEssentials -#else import Foundation -#endif import Logging private let encoder = JSONEncoder() diff --git a/Sources/Occurrence/SQLite/SQLiteEntry.swift b/Sources/Occurrence/SQLite/SQLiteEntry.swift index 71c0196..5d970ee 100644 --- a/Sources/Occurrence/SQLite/SQLiteEntry.swift +++ b/Sources/Occurrence/SQLite/SQLiteEntry.swift @@ -1,8 +1,4 @@ -#if canImport(FoundationEssentials) -import FoundationEssentials -#else import Foundation -#endif import Logging import Statement diff --git a/Sources/Occurrence/SQLite/SQLiteLogProvider.swift b/Sources/Occurrence/SQLite/SQLiteLogProvider.swift index f6bf5c5..5ac7ecf 100644 --- a/Sources/Occurrence/SQLite/SQLiteLogProvider.swift +++ b/Sources/Occurrence/SQLite/SQLiteLogProvider.swift @@ -1,8 +1,4 @@ -#if canImport(FoundationEssentials) -import FoundationEssentials -#else import Foundation -#endif import Logging @preconcurrency import SQLite import Statement diff --git a/Sources/Occurrence/Streamer/OccurrenceLogStreamer.swift b/Sources/Occurrence/Streamer/OccurrenceLogStreamer.swift index 17d6a72..940a571 100644 --- a/Sources/Occurrence/Streamer/OccurrenceLogStreamer.swift +++ b/Sources/Occurrence/Streamer/OccurrenceLogStreamer.swift @@ -1,8 +1,4 @@ -#if canImport(FoundationEssentials) -import FoundationEssentials -#else import Foundation -#endif import Logging import Mutex diff --git a/Sources/Occurrence/SwiftUI/PreviewLogProvider.swift b/Sources/Occurrence/SwiftUI/PreviewLogProvider.swift index b982e15..3e01cc4 100644 --- a/Sources/Occurrence/SwiftUI/PreviewLogProvider.swift +++ b/Sources/Occurrence/SwiftUI/PreviewLogProvider.swift @@ -1,8 +1,4 @@ -#if canImport(FoundationEssentials) -import FoundationEssentials -#else import Foundation -#endif import Logging struct PreviewLogProvider: LogProvider { diff --git a/Tests/OccurrenceTests/LoggableErrorTests.swift b/Tests/OccurrenceTests/LoggableErrorTests.swift index f8139b0..c3c2bfe 100644 --- a/Tests/OccurrenceTests/LoggableErrorTests.swift +++ b/Tests/OccurrenceTests/LoggableErrorTests.swift @@ -1,8 +1,4 @@ -#if canImport(FoundationEssentials) -import FoundationEssentials -#else import Foundation -#endif #if canImport(FoundationNetworking) import FoundationNetworking #endif @@ -99,23 +95,17 @@ struct LoggableErrorTests { ) let error = EncodingError.invalidValue(FileRef(), context) let metadata = error.metadata - #expect(metadata.count == 4) + #expect(metadata.count == 3) let domain = try #require(metadata[.domain]?.stringValue) let code = try #require(metadata[.code]?.intValue) let userInfo = try #require(metadata[.userInfo]?.dictionaryValue) let description = try #require(userInfo[.description]?.stringValue) - let localizedDescription = try #require(metadata[.localizedDescription]?.stringValue) #expect(domain == "SwiftEncodingErrorDomain") #expect(code == 0) #expect(userInfo.count == 1) #expect(description == #"Encoding (Invalid Value) - Value: FileRef(description: "Some File"), Context: Bad data. ["description", "file"]"#) - #if os(Linux) - #expect(localizedDescription == "The operation could not be completed. (SwiftEncodingErrorDomain error 0.)") - #else - #expect(localizedDescription == "The data couldn’t be written because it isn’t in the correct format.") - #endif } @Test func decodingError() throws { @@ -125,22 +115,16 @@ struct LoggableErrorTests { ) let error = DecodingError.typeMismatch(Int.self, context) let metadata = error.metadata - #expect(metadata.count == 4) + #expect(metadata.count == 3) let domain = try #require(metadata[.domain]?.stringValue) let code = try #require(metadata[.code]?.intValue) let userInfo = try #require(metadata[.userInfo]?.dictionaryValue) let description = try #require(userInfo[.description]?.stringValue) - let localizedDescription = try #require(metadata[.localizedDescription]?.stringValue) #expect(domain == "SwiftDecodingErrorDomain") #expect(code == 0) #expect(userInfo.count == 1) #expect(description == #"Decoding (Type Mismatch) - Type: Int, Context: Unexpected type. ["code"]"#) - #if os(Linux) - #expect(localizedDescription == "The operation could not be completed. (SwiftDecodingErrorDomain error 0.)") - #else - #expect(localizedDescription == "The data couldn’t be read because it isn’t in the correct format.") - #endif } } diff --git a/Tests/OccurrenceTests/LoggerMetadataTests.swift b/Tests/OccurrenceTests/LoggerMetadataTests.swift index aa63af8..f575833 100644 --- a/Tests/OccurrenceTests/LoggerMetadataTests.swift +++ b/Tests/OccurrenceTests/LoggerMetadataTests.swift @@ -1,8 +1,4 @@ -#if canImport(FoundationEssentials) -import FoundationEssentials -#else import Foundation -#endif import Logging @testable import Occurrence import Testing diff --git a/Tests/OccurrenceTests/RedactionTests.swift b/Tests/OccurrenceTests/RedactionTests.swift index 8e0aa02..81d1892 100644 --- a/Tests/OccurrenceTests/RedactionTests.swift +++ b/Tests/OccurrenceTests/RedactionTests.swift @@ -1,8 +1,4 @@ -#if canImport(FoundationEssentials) -import FoundationEssentials -#else import Foundation -#endif @testable import Occurrence import Testing From 1cac6d701e8f0168dfe8eb385dfa765d6a0bc087 Mon Sep 17 00:00:00 2001 From: Richard Piazza Date: Thu, 27 Aug 2026 07:54:53 -0500 Subject: [PATCH 07/10] URLError imports --- Sources/Occurrence/Extensions/Logger+Entry.swift | 3 +-- Sources/Occurrence/Extensions/URLError+Occurrence.swift | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Sources/Occurrence/Extensions/Logger+Entry.swift b/Sources/Occurrence/Extensions/Logger+Entry.swift index 5f5d727..6c33c52 100644 --- a/Sources/Occurrence/Extensions/Logger+Entry.swift +++ b/Sources/Occurrence/Extensions/Logger+Entry.swift @@ -86,9 +86,8 @@ extension Logger.Entry: CustomStringConvertible { } public var description: String { - let _date = date.formatted(formatStyle) let sourceFile = [source, fileName].filter { !$0.isEmpty }.joined(separator: " ") - let output = "[\(_date) \(level.fancyDescription) | \(subsystem) | \(sourceFile) | \(function) \(line)] \(message)" + let output = "[\(date.formatted(formatStyle)) \(level.fancyDescription) | \(subsystem) | \(sourceFile) | \(function) \(line)] \(message)" if let metadata { let sortedMetadata = metadata.sorted(by: { $0.key < $1.key }) let values = sortedMetadata.map { "\($0.key): \($0.value)" }.joined(separator: ", ") diff --git a/Sources/Occurrence/Extensions/URLError+Occurrence.swift b/Sources/Occurrence/Extensions/URLError+Occurrence.swift index 7d7eb19..a8953b4 100644 --- a/Sources/Occurrence/Extensions/URLError+Occurrence.swift +++ b/Sources/Occurrence/Extensions/URLError+Occurrence.swift @@ -1,7 +1,6 @@ +import Foundation #if canImport(FoundationNetworking) import FoundationNetworking -#else -import Foundation #endif extension URLError: LoggableError {} From 2f676e3285a9ee53900feec128b5d102e56e9523 Mon Sep 17 00:00:00 2001 From: Richard Piazza Date: Thu, 27 Aug 2026 07:59:13 -0500 Subject: [PATCH 08/10] Public format style for UI usage --- .../Occurrence/Extensions/Logger+Entry.swift | 25 ++++++++++--------- Sources/Occurrence/SwiftUI/LogEntryView.swift | 2 +- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/Sources/Occurrence/Extensions/Logger+Entry.swift b/Sources/Occurrence/Extensions/Logger+Entry.swift index 6c33c52..0f73540 100644 --- a/Sources/Occurrence/Extensions/Logger+Entry.swift +++ b/Sources/Occurrence/Extensions/Logger+Entry.swift @@ -74,20 +74,9 @@ public extension Logger { } extension Logger.Entry: CustomStringConvertible { - private var formatStyle: Date.ISO8601FormatStyle { - .iso8601 - .month() - .day() - .year() - .dateSeparator(.dash) - .time(includingFractionalSeconds: true) - .timeSeparator(.colon) - .timeZone(separator: .omitted) - } - public var description: String { let sourceFile = [source, fileName].filter { !$0.isEmpty }.joined(separator: " ") - let output = "[\(date.formatted(formatStyle)) \(level.fancyDescription) | \(subsystem) | \(sourceFile) | \(function) \(line)] \(message)" + let output = "[\(date.formatted(Self.formatStyle)) \(level.fancyDescription) | \(subsystem) | \(sourceFile) | \(function) \(line)] \(message)" if let metadata { let sortedMetadata = metadata.sorted(by: { $0.key < $1.key }) let values = sortedMetadata.map { "\($0.key): \($0.value)" }.joined(separator: ", ") @@ -99,6 +88,18 @@ extension Logger.Entry: CustomStringConvertible { } public extension Logger.Entry { + /// Format style that uses: `yyyy-MM-dd'T'HH:mm:ss.SSS'Z'` + static var formatStyle: Date.ISO8601FormatStyle { + .iso8601 + .month() + .day() + .year() + .dateSeparator(.dash) + .time(includingFractionalSeconds: true) + .timeSeparator(.colon) + .timeZone(separator: .omitted) + } + /// Attempts to extract only the last path component of the `file` var fileName: String { URL(fileURLWithPath: file).lastPathComponent diff --git a/Sources/Occurrence/SwiftUI/LogEntryView.swift b/Sources/Occurrence/SwiftUI/LogEntryView.swift index 052caf5..ea4d28b 100644 --- a/Sources/Occurrence/SwiftUI/LogEntryView.swift +++ b/Sources/Occurrence/SwiftUI/LogEntryView.swift @@ -23,7 +23,7 @@ struct LogEntryView: View { HStack { Text(entry.level.fancyDescription) - Text(entry.date, format: .iso8601) + Text(entry.date, format: Logger.Entry.formatStyle) } .font(.system(size: 10, weight: .semibold, design: .monospaced)) From 3aa4a889035c194ef94b7eadeb66158024664531 Mon Sep 17 00:00:00 2001 From: Richard Piazza Date: Thu, 27 Aug 2026 08:45:04 -0500 Subject: [PATCH 09/10] Disabled some tests on Android --- .github/workflows/pull_request.yml | 2 +- Tests/OccurrenceTests/LogProviderTests.swift | 2 ++ Tests/OccurrenceTests/OccurrenceTests.swift | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 5fb5349..a6042cf 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -92,7 +92,7 @@ jobs: steps: - name: Checkout uses: actions/checkout@v6 - - name: Build + - name: Build & Test uses: skiptools/swift-android-action@v2 with: free-disk-space: true diff --git a/Tests/OccurrenceTests/LogProviderTests.swift b/Tests/OccurrenceTests/LogProviderTests.swift index d7a8a5e..c565f40 100644 --- a/Tests/OccurrenceTests/LogProviderTests.swift +++ b/Tests/OccurrenceTests/LogProviderTests.swift @@ -1,3 +1,4 @@ +#if !canImport(Android) #if canImport(CoreData) import CoreData #endif @@ -401,3 +402,4 @@ final class LogProviderTests { #expect(entries == ["fourteen", "thirteen"]) } } +#endif diff --git a/Tests/OccurrenceTests/OccurrenceTests.swift b/Tests/OccurrenceTests/OccurrenceTests.swift index ef6097f..2642030 100644 --- a/Tests/OccurrenceTests/OccurrenceTests.swift +++ b/Tests/OccurrenceTests/OccurrenceTests.swift @@ -1,3 +1,4 @@ +#if !canImport(Android) import Logging @testable import Occurrence import Testing @@ -76,3 +77,4 @@ final class OccurrenceTests { #expect(description == output) } } +#endif From ed37ac03d294d64e371319a512ed62bfb6008217 Mon Sep 17 00:00:00 2001 From: Richard Piazza Date: Thu, 27 Aug 2026 08:57:12 -0500 Subject: [PATCH 10/10] Janky test with line numbers --- Tests/OccurrenceTests/OccurrenceTests.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/OccurrenceTests/OccurrenceTests.swift b/Tests/OccurrenceTests/OccurrenceTests.swift index 2642030..d341208 100644 --- a/Tests/OccurrenceTests/OccurrenceTests.swift +++ b/Tests/OccurrenceTests/OccurrenceTests.swift @@ -48,7 +48,7 @@ final class OccurrenceTests { description.replaceSubrange(first ... last, with: "") let output = """ - [🔎 INFO | com.richardpiazza.occurrence | OccurrenceTests OccurrenceTests.swift | dictionaryConvenience() 39] Dictionary { context: XCTestCase, label: count, value: } + [🔎 INFO | com.richardpiazza.occurrence | OccurrenceTests OccurrenceTests.swift | dictionaryConvenience() 40] Dictionary { context: XCTestCase, label: count, value: } """ #expect(description == output) @@ -71,7 +71,7 @@ final class OccurrenceTests { description.replaceSubrange(first ... last, with: "") let output = """ - [🔎 INFO | com.richardpiazza.occurrence | OccurrenceTests OccurrenceTests.swift | encodableConvenience() 62] Encodable { context: XCTestCase, id: 123, name: } + [🔎 INFO | com.richardpiazza.occurrence | OccurrenceTests OccurrenceTests.swift | encodableConvenience() 63] Encodable { context: XCTestCase, id: 123, name: } """ #expect(description == output)