Skip to content

Commit

Permalink
Add TopLevelEncoder and TopLevelDecoder protocols (#15)
Browse files Browse the repository at this point in the history
# Add TopLevelEncoder and TopLevelDecoder protocols

## ♻️ Current situation & Problem
Swift doesn't provide protocols to refer to arbitrary encoders and
decoders. A popular approach is to rely on the
[`TopLevelEncoder`](https://developer.apple.com/documentation/combine/toplevelencoder)
and
[`TopLevelDecoder`](https://developer.apple.com/documentation/combine/topleveldecoder)
protocols exposed by the Combine framework, though it's generally a good
idea to avoid relying on Combine. Further, these protocols became
outdated with iOS 17 as they didn't add the new methods for the
[`EncodableWithConfiguration`](https://developer.apple.com/documentation/foundation/encodablewithconfiguration)
and
[`DecodableWithConfiguration`](https://developer.apple.com/documentation/foundation/decodablewithconfiguration)
protocols.

This PR introduces custom `TopLevelEncoder` and `TopLevelDecoder`
protocols in SpeziFoundation, that adds protocol conformance to encode
and decode both `Codable` and `CodableWithConfiguration` types. [While
there have been attempts to write a proposal to include such protocols
in the
stdlib](https://forums.swift.org/t/should-regex-be-sendable/69529/16),
there hasn't been any activity since 4 years.

## ⚙️ Release Notes 
* Added `TopLevelEncoder` protocol.
* Added `TopLevelDecoder` protocol.

## 📚 Documentation
Added documentation for all new types and methods and added new category
in the root article.


## ✅ Testing
This PR just added new types and conformances.


## 📝 Code of Conduct & Contributing Guidelines 

By submitting creating this pull request, you agree to follow our [Code
of
Conduct](https://github.com/StanfordSpezi/.github/blob/main/CODE_OF_CONDUCT.md)
and [Contributing
Guidelines](https://github.com/StanfordSpezi/.github/blob/main/CONTRIBUTING.md):
- [x] I agree to follow the [Code of
Conduct](https://github.com/StanfordSpezi/.github/blob/main/CODE_OF_CONDUCT.md)
and [Contributing
Guidelines](https://github.com/StanfordSpezi/.github/blob/main/CONTRIBUTING.md).
  • Loading branch information
Supereg authored Aug 27, 2024
1 parent d3a6757 commit 17bd0e0
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 2 deletions.
57 changes: 57 additions & 0 deletions Sources/SpeziFoundation/Misc/TopLevelDecoder.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//
// This source file is part of the Stanford Spezi open-source project
//
// SPDX-FileCopyrightText: 2024 Stanford University and the project authors (see CONTRIBUTORS.md)
//
// SPDX-License-Identifier: MIT
//

import Foundation


/// A type that supports decoding.
public protocol TopLevelDecoder {
/// The input type.
associatedtype Input


/// Decode a instance for the specified type.
/// - Parameters:
/// - type: The type you want to decode.
/// - input: The input you want to decode from.
/// - Returns: Returns the decoded instance.
/// - Throws: Throws errors occurred while attempting to decode input.
func decode<T: Decodable>(_ type: T.Type, from input: Input) throws -> T

/// Decode a instance for the specified type that requires additional configuration.
/// - Parameters:
/// - type: The type you want to decode.
/// - input: The input you want to decode from.
/// - configuration: The configuration that provides additional information for decoding.
/// - Returns: Returns the decoded instance.
/// - Throws: Throws errors occurred while attempting to decode input.
func decode<T: DecodableWithConfiguration>(
_ type: T.Type,
from input: Input,
configuration: T.DecodingConfiguration
) throws -> T

/// Decode a instance for the specified type that requires additional configuration.
/// - Parameters:
/// - type: The type you want to decode.
/// - input: The input you want to decode from.
/// - configuration: A type that provides additional information for decoding.
/// - Returns: Returns the decoded instance.
/// - Throws: Throws errors occurred while attempting to decode input.
func decode<T: DecodableWithConfiguration, C: DecodingConfigurationProviding>(
_ type: T.Type,
from input: Input,
configuration: C.Type
) throws -> T where T.DecodingConfiguration == C.DecodingConfiguration
}


extension JSONDecoder: TopLevelDecoder {}


extension PropertyListDecoder: TopLevelDecoder {}
47 changes: 47 additions & 0 deletions Sources/SpeziFoundation/Misc/TopLevelEncoder.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//
// This source file is part of the Stanford Spezi open-source project
//
// SPDX-FileCopyrightText: 2024 Stanford University and the project authors (see CONTRIBUTORS.md)
//
// SPDX-License-Identifier: MIT
//

import Foundation


/// A type that supports encoding.
public protocol TopLevelEncoder {
/// The output type.
associatedtype Output

/// Encode a value.
/// - Parameter value: The value to encode.
/// - Returns: The encoded instance.
/// - Throws: Throws errors occurred while attempting to encode the value.
func encode<T: Encodable>(_ value: T) throws -> Output

/// Encode a value.
/// - Parameters:
/// - value: The value to encode.
/// - configuration: The configuration that provides additional information for encoding.
/// - Returns: The encoded instance.
/// - Throws: Throws errors occurred while attempting to encode the value.
func encode<T: EncodableWithConfiguration>(_ value: T, configuration: T.EncodingConfiguration) throws -> Output

/// Encode a value.
/// - Parameters:
/// - value: The value to encode.
/// - configuration: A type that provides additional information for encoding.
/// - Returns: The encoded instance.
/// - Throws: Throws errors occurred while attempting to encode the value.
func encode<T: EncodableWithConfiguration, C: EncodingConfigurationProviding>(
_ value: T,
configuration: C.Type
) throws -> Output where T.EncodingConfiguration == C.EncodingConfiguration
}


extension JSONEncoder: TopLevelEncoder {}


extension PropertyListEncoder: TopLevelEncoder {}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ Spezi Foundation provides a base layer of functionality useful in many applicati

- ``AsyncSemaphore``

### Encoders and Decoders
- ``TopLevelEncoder``
- ``TopLevelDecoder``

### Timeout

- ``TimeoutError``
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ final class SendableSharedRepositoryTests: XCTestCase {
}

@MainActor
override func setUp() {
override func setUp() async throws {
self.repository = .init()
computedValue = 3
optionalComputedValue = nil
Expand Down
2 changes: 1 addition & 1 deletion Tests/SpeziFoundationTests/SharedRepositoryTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ final class SharedRepositoryTests: XCTestCase {
}

@MainActor
override func setUp() {
override func setUp() async throws {
self.repository = .init()
computedValue = 3
optionalComputedValue = nil
Expand Down

0 comments on commit 17bd0e0

Please sign in to comment.