Skip to content

Commit 5de5544

Browse files
committed
✨ Add dynamic sorting capabilities
1 parent b66c9fc commit 5de5544

File tree

11 files changed

+129
-49
lines changed

11 files changed

+129
-49
lines changed

Sources/Model/Calendar.swift

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

3-
struct PlanCalendar: Codable, KeyPathAccessible {
3+
struct PlanCalendar: Codable, ReverseCodable {
44
let id: String
55
let type: String
66
let label: String
@@ -13,8 +13,20 @@ struct PlanCalendar: Codable, KeyPathAccessible {
1313
color: "#FFFFFF"
1414
)
1515

16-
static func codingKey(for key: String) -> CodingKey? {
17-
return CodingKeys(stringValue: key)
16+
enum CodingKeys: String, CodingKey, CaseIterable {
17+
case id
18+
case type
19+
case label
20+
case color
21+
}
22+
23+
static func reverseCodingKeys() -> [String: String] {
24+
return [
25+
CodingKeys.id.rawValue: "id",
26+
CodingKeys.type.rawValue: "type",
27+
CodingKeys.label.rawValue: "label",
28+
CodingKeys.color.rawValue: "color",
29+
]
1830
}
1931
}
2032

Sources/Model/Event.swift

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import EventKit
22
import Foundation
33

4-
struct Event: Codable, KeyPathAccessible {
4+
struct Event: Codable, ReverseCodable {
55
// An EKEvent has three different identifiers
66
// 1. calendarItemIdentifier (via EKCalendarItem)
77
// 2. calendarItemExternalIdentifier (via EKCalendarItem)
@@ -36,7 +36,7 @@ struct Event: Codable, KeyPathAccessible {
3636
let services: [Service: String]
3737
let tags: [String]
3838

39-
enum CodingKeys: String, CodingKey {
39+
enum CodingKeys: String, CodingKey, CaseIterable {
4040
case id
4141
case calendar
4242
case title
@@ -47,8 +47,17 @@ struct Event: Codable, KeyPathAccessible {
4747
case tags
4848
}
4949

50-
static func codingKey(for key: String) -> CodingKey? {
51-
return CodingKeys(stringValue: key)
50+
static func reverseCodingKeys() -> [String: String] {
51+
return [
52+
CodingKeys.id.rawValue: "id",
53+
CodingKeys.calendar.rawValue: "calendar",
54+
CodingKeys.title.rawValue: "title",
55+
CodingKeys.schedule.rawValue: "schedule",
56+
CodingKeys.location.rawValue: "location",
57+
CodingKeys.meeting.rawValue: "meeting",
58+
CodingKeys.services.rawValue: "services",
59+
CodingKeys.tags.rawValue: "tags",
60+
]
5261
}
5362
}
5463

Sources/Model/KeyPathAccessible.swift

Lines changed: 0 additions & 3 deletions
This file was deleted.

Sources/Model/Meeting.swift

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
11
import EventKit
22
import Foundation
33

4-
struct Meeting: Codable, KeyPathAccessible {
4+
struct Meeting: Codable, ReverseCodable {
55
let organizer: String
66
let attendees: [String]
77

8-
enum CodingKeys: String, CodingKey {
8+
enum CodingKeys: String, CodingKey, CaseIterable {
99
case organizer
1010
case attendees
1111
}
1212

13-
static func codingKey(for key: String) -> CodingKey? {
14-
return CodingKeys(stringValue: key)
13+
static func reverseCodingKeys() -> [String: String] {
14+
return [
15+
CodingKeys.organizer.rawValue: "organizer",
16+
CodingKeys.attendees.rawValue: "attendees",
17+
]
1518
}
1619
}
1720

Sources/Model/Object.swift

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
enum Object {
2+
enum PathError: Error {
3+
case notComparable
4+
case notDecodable
5+
case notFound
6+
}
7+
8+
static func valueForKeyPath(_ object: Any, _ keyPath: String) throws -> Any? {
9+
let keys = keyPath.split(separator: ".").map { String($0) }
10+
11+
var currentObject: Any = object
12+
for key in keys {
13+
let propertyName = try? originalPropertyName(for: key, in: currentObject)
14+
15+
let mirror = Mirror(reflecting: currentObject)
16+
if let child = mirror.children.first(where: { $0.label == propertyName }) {
17+
currentObject = child.value
18+
} else {
19+
throw PathError.notFound
20+
}
21+
}
22+
23+
guard let comparableObject = currentObject as? any Comparable else {
24+
throw PathError.notComparable
25+
}
26+
27+
return comparableObject
28+
}
29+
30+
static func originalPropertyName(for wanted: String, in object: Any) throws -> String? {
31+
guard let reverseCodable = object as? ReverseCodable else {
32+
throw PathError.notDecodable
33+
}
34+
35+
let candidates = type(of: reverseCodable).reverseCodingKeys()
36+
37+
for case let (coded, original) in candidates where coded == wanted {
38+
return original
39+
}
40+
41+
throw PathError.notFound
42+
}
43+
}

Sources/Model/ReverseCodable.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
protocol ReverseCodable {
2+
static func reverseCodingKeys() -> [String: String]
3+
}

Sources/Model/Schedule.swift

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

3-
struct Schedule: Codable, KeyPathAccessible {
3+
struct Schedule: Codable, ReverseCodable {
44
let start: Temporal
55
let end: Temporal
66
let allDay: Bool
@@ -17,13 +17,17 @@ struct Schedule: Codable, KeyPathAccessible {
1717
allDay = isAllDay
1818
}
1919

20-
enum CodingKeys: String, CodingKey {
20+
enum CodingKeys: String, CodingKey, CaseIterable {
2121
case start
2222
case end
2323
case allDay = "all_day"
2424
}
2525

26-
static func codingKey(for key: String) -> CodingKey? {
27-
return CodingKeys(stringValue: key)
26+
static func reverseCodingKeys() -> [String: String] {
27+
return [
28+
CodingKeys.start.rawValue: "start",
29+
CodingKeys.end.rawValue: "end",
30+
CodingKeys.allDay.rawValue: "allDay",
31+
]
2832
}
2933
}

Sources/Model/Sorting.swift

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,24 +17,6 @@ class Sorting {
1717
}
1818
return events
1919
}
20-
21-
static func valueForKeyPath(_ object: Any, _ keyPath: String) -> Any? {
22-
let keys = keyPath.split(separator: ".").map { String($0) }
23-
var currentObject: Any = object
24-
25-
for key in keys {
26-
let mirror = Mirror(reflecting: currentObject)
27-
28-
// Try to find the property in the current object
29-
if let child = mirror.children.first(where: { $0.label == key }) {
30-
currentObject = child.value
31-
} else {
32-
return nil // Key not found
33-
}
34-
}
35-
36-
return currentObject
37-
}
3820
}
3921

4022
enum Field: String, ExpressibleByArgument {

Sources/Model/Temporal.swift

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

3-
struct Temporal: Codable, KeyPathAccessible {
3+
struct Temporal: Codable, ReverseCodable {
44
let at: Date
55
let inMinutes: Int
66

@@ -9,7 +9,10 @@ struct Temporal: Codable, KeyPathAccessible {
99
case inMinutes = "in"
1010
}
1111

12-
static func codingKey(for key: String) -> CodingKey? {
13-
return CodingKeys(stringValue: key)
12+
static func reverseCodingKeys() -> [String: String] {
13+
return [
14+
CodingKeys.at.rawValue: "at",
15+
CodingKeys.inMinutes.rawValue: "inMinutes",
16+
]
1417
}
1518
}

Sources/Model/Title.swift

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
struct Title: Codable {
1+
struct Title: Codable, ReverseCodable {
22
let full: String
33
let label: String
44
let icon: String
@@ -9,6 +9,20 @@ struct Title: Codable {
99
label = legend.description
1010
icon = legend.icon
1111
}
12+
13+
enum CodingKeys: String, CodingKey, CaseIterable {
14+
case full
15+
case label
16+
case icon
17+
}
18+
19+
static func reverseCodingKeys() -> [String: String] {
20+
return [
21+
CodingKeys.full.rawValue: "full",
22+
CodingKeys.label.rawValue: "label",
23+
CodingKeys.icon.rawValue: "icon",
24+
]
25+
}
1226
}
1327

1428
struct Legend: Codable, Equatable {

0 commit comments

Comments
 (0)