Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ jobs:
- name: Mark Workspace As Safe
# https://github.com/actions/checkout/issues/766
run: git config --global --add safe.directory ${GITHUB_WORKSPACE}
- name: Swift Version
run: |
swift --version
- name: Diagnose API Breaking Changes
run: |
swift package diagnose-api-breaking-changes origin/main --products CodableDatastore
10 changes: 8 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@ jobs:
steps:
- name: Checkout Source
uses: actions/checkout@v3
- name: Swift Version
run: |
swift --version
- name: Build
run: |
swift build --build-tests --configuration release -Xswiftc -enable-testing -Xswiftc -warnings-as-errors -Xcc -Werror
swift build --build-tests --configuration release -Xswiftc -enable-testing -Xswiftc -warnings-as-errors
- name: Run Tests
run: |
swift test --skip-build --configuration release
Expand All @@ -25,9 +28,12 @@ jobs:
steps:
- name: Checkout Source
uses: actions/checkout@v3
- name: Swift Version
run: |
swift --version
- name: Build
run: |
swift build --build-tests --configuration debug -Xswiftc -enable-testing -Xswiftc -warnings-as-errors -Xcc -Werror
swift build --build-tests --configuration debug -Xswiftc -enable-testing -Xswiftc -warnings-as-errors
- name: Run Tests
run: |
swift test --skip-build --configuration debug
12 changes: 6 additions & 6 deletions Sources/CodableDatastore/Datastore/Datastore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ extension Datastore {

/// Notify progress handlers we are evaluating for possible migrations.
for handler in warmupProgressHandlers {
handler(.evaluating)
await handler(.evaluating)
}

/// Grab an up-to-date descriptor and check the indexes against it
Expand Down Expand Up @@ -272,7 +272,7 @@ extension Datastore {

/// Notify progress handlers we are starting an entry.
for handler in warmupProgressHandlers {
handler(.working(current: index, total: persistedDescriptor.size))
await handler(.working(current: index, total: persistedDescriptor.size))
}

let instanceData = try await encoder(instance)
Expand Down Expand Up @@ -354,7 +354,7 @@ extension Datastore {
let completeProgress = Progress.complete(total: persistedDescriptor.size)

for handler in warmupProgressHandlers {
handler(completeProgress)
await handler(completeProgress)
}

warmupProgressHandlers.removeAll()
Expand Down Expand Up @@ -401,7 +401,7 @@ extension Datastore where AccessMode == ReadWrite {
else { return }

let warmUpProgress = try await self.warmupIfNeeded { progress in
progressHandler?(progress.adding(current: 0, total: descriptor.size))
await progressHandler?(progress.adding(current: 0, total: descriptor.size))
}

/// Make sure we still need to do the work, as the warm up may have made changes anyways due to incompatible types.
Expand All @@ -419,12 +419,12 @@ extension Datastore where AccessMode == ReadWrite {
/// Make sure the stored version is smaller than the one we require, otherwise stop early.
version.rawValue < minimumVersion.rawValue
else {
progressHandler?(warmUpProgress.adding(current: descriptor.size, total: descriptor.size))
await progressHandler?(warmUpProgress.adding(current: descriptor.size, total: descriptor.size))
return
}

try await self.migrate(index: index) { migrateProgress in
progressHandler?(warmUpProgress.adding(migrateProgress))
await progressHandler?(warmUpProgress.adding(migrateProgress))
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/CodableDatastore/Datastore/Progress.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import Foundation

public typealias ProgressHandler = @Sendable (_ progress: Progress) -> Void
public typealias ProgressHandler = @Sendable (_ progress: Progress) async -> Void

public enum Progress: Sendable {
case evaluating
Expand Down
14 changes: 13 additions & 1 deletion Sources/CodableDatastore/Indexes/Indexable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,17 @@ public struct AnyIndexable {

#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
/// Matching implementation from https://github.com/apple/swift/pull/64899/files
#if compiler(>=6)
extension Never: @retroactive Codable {
public init(from decoder: any Decoder) throws {
let context = DecodingError.Context(
codingPath: decoder.codingPath,
debugDescription: "Unable to decode an instance of Never.")
throw DecodingError.typeMismatch(Never.self, context)
}
public func encode(to encoder: any Encoder) throws {}
}
#else
extension Never: Codable {
public init(from decoder: any Decoder) throws {
let context = DecodingError.Context(
Expand All @@ -34,6 +45,7 @@ extension Never: Codable {
public func encode(to encoder: any Encoder) throws {}
}
#endif
#endif

/// A marker protocol for types that can be used as a ranged index.
///
Expand Down Expand Up @@ -100,7 +112,7 @@ extension Optional: RangedIndexable where Wrapped: RangedIndexable {}
// MARK: - Foundation Conformances

extension Date: RangedIndexable {}
#if canImport(Darwin)
#if canImport(Darwin) || compiler(>=6.2)
extension Decimal: RangedIndexable {}
#else
extension Decimal: RangedIndexable, @unchecked Sendable {}
Expand Down
2 changes: 1 addition & 1 deletion Sources/CodableDatastore/Persistence/AccessMode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
//

/// An AccessMode marker type.
public protocol _AccessMode {}
public protocol _AccessMode: Sendable {}

/// A marker type that indicates read-only access.
public enum ReadOnly: _AccessMode {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ actor MultiplexedAsyncSequence<Base: AsyncSequence & Sendable>: AsyncSequence wh
}
}

extension RangeReplaceableCollection {
extension RangeReplaceableCollection where Self: Sendable {
init<S: AsyncSequence>(_ sequence: S) async throws where S.Element == Element {
self = try await sequence.reduce(into: Self.init()) { @Sendable partialResult, element in
partialResult.append(element)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,6 @@ private extension DateFormatter {
}()
}

#if !canImport(Darwin)
#if !canImport(Darwin) && compiler(<6.2)
extension DateFormatter: @unchecked Sendable {}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,29 @@

import Foundation

extension ISO8601DateFormatter {
static let withMilliseconds: ISO8601DateFormatter = {
private struct GlobalDateFormatter: Sendable {
@available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *)
static let cachedFormatter = Date.ISO8601FormatStyle(includingFractionalSeconds: true)

static let parse: @Sendable (_ value: String) -> Date? = {
if #available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *) {
return { try? cachedFormatter.parse($0) }
} else {
return { ISO8601DateFormatter.withMilliseconds.date(from: $0) }
}
}()

static let format: @Sendable (_ value: Date) -> String = {
if #available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *) {
return { cachedFormatter.format($0) }
} else {
return { ISO8601DateFormatter.withMilliseconds.string(from: $0) }
}
}()
}

private extension ISO8601DateFormatter {
nonisolated(unsafe) static let withMilliseconds: ISO8601DateFormatter = {
let formatter = ISO8601DateFormatter()
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.formatOptions = [
Expand All @@ -27,7 +48,7 @@ extension JSONDecoder.DateDecodingStrategy {
static let iso8601WithMilliseconds: Self = custom { decoder in
let container = try decoder.singleValueContainer()
let string = try container.decode(String.self)
guard let date = ISO8601DateFormatter.withMilliseconds.date(from: string) else {
guard let date = GlobalDateFormatter.parse(string) else {
throw DecodingError.dataCorruptedError(in: container, debugDescription: "Invalid date: \(string)")
}
return date
Expand All @@ -37,13 +58,13 @@ extension JSONDecoder.DateDecodingStrategy {

extension JSONEncoder.DateEncodingStrategy {
static let iso8601WithMilliseconds: Self = custom { date, encoder in
let string = ISO8601DateFormatter.withMilliseconds.string(from: date)
let string = GlobalDateFormatter.format(date)
var container = encoder.singleValueContainer()
try container.encode(string)
}
}

#if compiler(>=6)
#if compiler(>=6) && compiler(<6.2)
extension ISO8601DateFormatter: @unchecked @retroactive Sendable {}
extension JSONDecoder.DateDecodingStrategy: @unchecked Sendable {}
extension JSONEncoder.DateEncodingStrategy: @unchecked Sendable {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ extension JSONDecoder {
}()
}

#if !canImport(Darwin)
#if !canImport(Darwin) && compiler(<6.2)
extension JSONEncoder: @unchecked Sendable {}
extension JSONDecoder: @unchecked Sendable {}
#endif
Loading