Skip to content

Commit

Permalink
feat(HelperCoders): added base64 data decoding/encoding helpers
Browse files Browse the repository at this point in the history
  • Loading branch information
soumyamahunt committed Nov 2, 2023
1 parent ae9ed44 commit bee7cc4
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 0 deletions.
64 changes: 64 additions & 0 deletions Sources/HelperCoders/Base64Coder.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import Foundation
import MetaCodable

/// An ``/MetaCodable/HelperCoder`` that helps decoding/encoding
/// base64 data.
///
/// This type can be used to decode/encode base64 data
/// string and convert to `Data` type.
public struct Base64Coder: HelperCoder {
/// The options to use when decoding data.
private let decodeOptions: Data.Base64DecodingOptions
/// The options to use when encoding data.
private let encodeOptions: Data.Base64EncodingOptions

/// Creates a new instance of ``/MetaCodable/HelperCoder`` that decodes/encodes
/// base64 data.
///
/// - Parameters:
/// - decodeOptions: The options to use when decoding data.
/// - encodeOptions: The options to use when encoding data.
public init(
decodeOptions: Data.Base64DecodingOptions = [],
encodeOptions: Data.Base64EncodingOptions = []
) {
self.decodeOptions = decodeOptions
self.encodeOptions = encodeOptions
}

/// Decodes base64 data with provided decoding options
/// from the given `decoder`.
///
/// The data is decoded from a base64 string representation.
///
/// - Parameter decoder: The decoder to read data from.
/// - Returns: The data decoded.
///
/// - Throws: `DecodingError.typeMismatch` if the decoded string
/// isn't a valid base64 representation.
public func decode(from decoder: Decoder) throws -> Data {
let base64Str = try String(from: decoder)
guard let data = Data(base64Encoded: base64Str, options: decodeOptions)
else {
let errDesc = "Invalid base64 string \"\(base64Str)\""
throw DecodingError.typeMismatch(
Data.self,
.init(codingPath: decoder.codingPath, debugDescription: errDesc)
)
}
return data
}

/// Encodes base64 data with provided encoding options
/// to the given `decoder`.
///
/// The data is encoded as a base64 string representation.
///
/// - Parameters:
/// - value: The data to encode.
/// - encoder: The encoder to write data to.
public func encode(_ value: Data, to encoder: Encoder) throws {
let base64Str = value.base64EncodedString(options: encodeOptions)
try base64Str.encode(to: encoder)
}
}
5 changes: 5 additions & 0 deletions Sources/HelperCoders/HelperCoders.docc/HelperCoders.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Level up ``/MetaCodable``'s generated implementations with helpers assisting com

- Decoding basic data type (i.e `Bool`, `Int`, `String`) from any other basic data types (i.e `Bool`, `Int`, `String`).
- Custom `Date` decoding/encoding approach, i.e. converting from UNIX timestamp, text formatted date etc.
- Custom `Data` decoding/encoding approach, i.e. converting from base64 text etc.

## Installation

Expand Down Expand Up @@ -48,3 +49,7 @@ Level up ``/MetaCodable``'s generated implementations with helpers assisting com
- ``DateCoder``
- ``ISO8601DateCoder``
- ``DateFormatConverter``

### Data

- ``Base64Coder``
38 changes: 38 additions & 0 deletions Tests/MetaCodableTests/HelperCoders/DataCoderTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import HelperCoders
import MetaCodable
import XCTest

final class DataCoderTests: XCTestCase {
func testDecoding() throws {
let jsonStr = """
{
"data": "SGVsbG8h"
}
"""
let json = try XCTUnwrap(jsonStr.data(using: .utf8))
let model = try JSONDecoder().decode(Model.self, from: json)
XCTAssertEqual(String(data: model.data, encoding: .utf8), "Hello!")
let encoded = try JSONEncoder().encode(model)
let newModel = try JSONDecoder().decode(Model.self, from: encoded)
XCTAssertEqual(newModel, model)
}

func testInvalidDataDecoding() throws {
let jsonStr = """
{
"data": "invalid data"
}
"""
let json = try XCTUnwrap(jsonStr.data(using: .utf8))
do {
let _ = try JSONDecoder().decode(Model.self, from: json)
XCTFail("Invalid Base64 conversion")
} catch {}
}
}

@Codable
fileprivate struct Model: Equatable {
@CodedBy(Base64Coder())
let data: Data
}

0 comments on commit bee7cc4

Please sign in to comment.