Skip to content

Commit 43f5498

Browse files
committed
Improve query-representable UX
Currently, the various query representable wrappers must be used a very specific way when dealing with optionals, where the optional must be on the representation type, not the type being represented: ```swift @column(as: Date.ISO8601Representation?.self) // ✅ var date: Date? @column(as: Date?.ISO8601Representation.self) // ❌ var date: Date? ``` This PR adds a few helper type aliases to address this issue, so both now compile and do the right thing: ```swift @column(as: Date.ISO8601Representation?.self) // ✅ var date: Date? @column(as: Date?.ISO8601Representation.self) // ✅ var date: Date? ``` It also renames `JSONRepresentation` to `Codable.JSONRepresentation`, deprecating the former spelling. This also allows the optionality to be placed either place: ```swift @column(as: Notes.JSONRepresentation?.self) // ✅ var notes: Notes? @column(as: Notes?.JSONRepresentation.self) // ✅ var notes: Notes? ```
1 parent 31b9cce commit 43f5498

File tree

16 files changed

+74
-44
lines changed

16 files changed

+74
-44
lines changed

Sources/StructuredQueriesCore/Documentation.docc/Articles/DefiningYourSchema.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -295,14 +295,15 @@ example, suppose the `Reminder` table had an array of notes:
295295
296296
This does not work because the `@Table` macro does not know how to encode and decode an array
297297
of strings into a value that SQLite understands. If you annotate this field with
298-
``JSONRepresentation``, then the library can encode the array of strings to a JSON string when
299-
storing data in the table, and decode the JSON array into a Swift array when decoding a row:
298+
``Swift/Decodable/JSONRepresentation``, then the library can encode the array of strings to a JSON
299+
string when storing data in the table, and decode the JSON array into a Swift array when decoding a
300+
row:
300301
301302
```swift
302303
@Table struct Reminder {
303304
let id: Int
304305
var title = ""
305-
@Column(as: JSONRepresentation<[String]>.self)
306+
@Column(as: [String].JSONRepresentation.self)
306307
var notes: [String]
307308
}
308309
```

Sources/StructuredQueriesCore/Documentation.docc/Articles/QueryCookbook.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -396,19 +396,20 @@ suspiciously like a join constraint, which should give us a hint that what we ar
396396
quite right.
397397

398398
Another way to do this is to use the `@Selection` macro described above
399-
(<doc:QueryCookbook#Custom-selections>), along with a ``JSONRepresentation`` of the collection
400-
of reminders you want to load for each list:
399+
(<doc:QueryCookbook#Custom-selections>), along with a ``Swift/Decodable/JSONRepresentation`` of the
400+
collection of reminders you want to load for each list:
401401

402402
```struct
403403
@Selection
404404
struct Row {
405405
let remindersList: RemindersList
406-
@Column(as: JSONRepresentation<[Reminder]>.self)
406+
@Column(as: [Reminder].JSONRepresentation.self)
407407
let reminders: [Reminder]
408408
}
409409
```
410410

411-
> Note: `Reminder` must conform to `Codable` to be able to use ``JSONRepresentation``.
411+
> Note: `Reminder` must conform to `Codable` to be able to use
412+
> ``Swift/Decodable/JSONRepresentation``.
412413
413414
This allows the query to serialize the associated rows into JSON, which are then deserialized into
414415
a `Row` type. To construct such a query you can use the

Sources/StructuredQueriesCore/Documentation.docc/Extensions/QueryRepresentable.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@
1616
- ``Foundation/UUID/BytesRepresentation``
1717
- ``Foundation/UUID/LowercasedRepresentation``
1818
- ``Foundation/UUID/UppercasedRepresentation``
19-
- ``JSONRepresentation``
19+
- ``Swift/Decodable/JSONRepresentation``
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// NB: Deprecated after 0.1.1:
2+
3+
@available(*, deprecated, message: "Use 'MyCodableType.JSONRepresentation', instead.")
4+
public typealias JSONRepresentation<Value: Codable> = _CodableJSONRepresentation<Value>

Sources/StructuredQueriesCore/QueryRepresentable/Codable+JSON.swift

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,6 @@
11
import Foundation
22

3-
/// A query expression representing codable JSON.
4-
///
5-
/// ```swift
6-
/// @Table
7-
/// struct Item {
8-
/// @Column(as: JSONRepresentation<[String]>.self)
9-
/// var notes: [String] = []
10-
/// }
11-
///
12-
/// Item.insert { $0.notes } values: { ["First post", "An update"] }
13-
/// // INSERT INTO "items" ("notes") VALUES ('["First post","An update"]')
14-
/// ```
15-
public struct JSONRepresentation<QueryOutput: Codable & Sendable>: QueryRepresentable {
3+
public struct _CodableJSONRepresentation<QueryOutput: Codable & Sendable>: QueryRepresentable {
164
public var queryOutput: QueryOutput
175

186
public init(queryOutput: QueryOutput) {
@@ -29,7 +17,28 @@ public struct JSONRepresentation<QueryOutput: Codable & Sendable>: QueryRepresen
2917
}
3018
}
3119

32-
extension JSONRepresentation: QueryBindable {
20+
extension Decodable where Self: Encodable {
21+
/// A query expression representing codable JSON.
22+
///
23+
/// ```swift
24+
/// @Table
25+
/// struct Item {
26+
/// @Column(as: [String].JSONRepresentation.self)
27+
/// var notes: [String] = []
28+
/// }
29+
///
30+
/// Item.insert { $0.notes } values: { ["First post", "An update"] }
31+
/// // INSERT INTO "items" ("notes") VALUES ('["First post","An update"]')
32+
/// ```
33+
public typealias JSONRepresentation = _CodableJSONRepresentation<Self>
34+
}
35+
36+
extension Optional where Wrapped: Codable {
37+
@_documentation(visibility: private)
38+
public typealias JSONRepresentation = _CodableJSONRepresentation<Wrapped>?
39+
}
40+
41+
extension _CodableJSONRepresentation: QueryBindable {
3342
public var queryBinding: QueryBinding {
3443
do {
3544
return try .text(String(decoding: jsonEncoder.encode(queryOutput), as: UTF8.self))
@@ -39,7 +48,7 @@ extension JSONRepresentation: QueryBindable {
3948
}
4049
}
4150

42-
extension JSONRepresentation: SQLiteType {
51+
extension _CodableJSONRepresentation: SQLiteType {
4352
public static var typeAffinity: SQLiteTypeAffinity {
4453
String.typeAffinity
4554
}

Sources/StructuredQueriesCore/QueryRepresentable/Date+ISO8601.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ extension Date {
2222
}
2323
}
2424

25+
extension Date? {
26+
public typealias ISO8601Representation = Date.ISO8601Representation?
27+
}
28+
2529
extension Date.ISO8601Representation: QueryBindable {
2630
public var queryBinding: QueryBinding {
2731
.text(queryOutput.iso8601String)

Sources/StructuredQueriesCore/QueryRepresentable/Date+JulianDay.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ extension Date {
2222
}
2323
}
2424

25+
extension Date? {
26+
public typealias JulianDayRepresentation = Date.JulianDayRepresentation?
27+
}
28+
2529
extension Date.JulianDayRepresentation: QueryBindable {
2630
public var queryBinding: QueryBinding {
2731
.double(2440587.5 + queryOutput.timeIntervalSince1970 / 86400)

Sources/StructuredQueriesCore/QueryRepresentable/Date+UnixTime.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ extension Date {
2222
}
2323
}
2424

25+
extension Date? {
26+
public typealias UnixTimeRepresentation = Date.UnixTimeRepresentation?
27+
}
28+
2529
extension Date.UnixTimeRepresentation: QueryBindable {
2630
public var queryBinding: QueryBinding {
2731
.int(Int64(queryOutput.timeIntervalSince1970))

Sources/StructuredQueriesCore/QueryRepresentable/UUID+Bytes.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ extension UUID {
2222
}
2323
}
2424

25+
extension UUID? {
26+
public typealias BytesRepresentation = UUID.BytesRepresentation?
27+
}
28+
2529
extension UUID.BytesRepresentation: QueryBindable {
2630
public var queryBinding: QueryBinding {
2731
.blob(withUnsafeBytes(of: queryOutput.uuid, [UInt8].init))

Sources/StructuredQueriesCore/QueryRepresentable/UUID+Lowercased.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ extension UUID {
2222
}
2323
}
2424

25+
extension UUID? {
26+
public typealias LowercasedRepresentation = UUID.LowercasedRepresentation?
27+
}
28+
2529
extension UUID.LowercasedRepresentation: QueryBindable {
2630
public var queryBinding: QueryBinding {
2731
.text(queryOutput.uuidString.lowercased())

0 commit comments

Comments
 (0)