Last active
October 13, 2017 21:38
-
-
Save Ben-G/8c9b7800d86d4dd8f92d8cf100d8f630 to your computer and use it in GitHub Desktop.
Keypath Based Joins in Swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//: Playground - noun: a place where people can play | |
import Cocoa | |
struct IssueAssignee { | |
let issueUid: String | |
let assigneeUid: String | |
} | |
struct IssueFollower { | |
let issueUid: String | |
let followerUid: String | |
} | |
struct IssueModel { | |
let uid: String | |
let title: String | |
var assignees: [IssueAssignee] = [] | |
var followers: [IssueFollower] = [] | |
static let joins: [AnyRelationship<IssueModel>] = [ | |
OneToMany(\IssueModel.assignees, \IssueAssignee.issueUid, \IssueModel.uid), | |
OneToMany(\IssueModel.followers, \IssueFollower.issueUid, \IssueModel.uid) | |
] | |
} | |
class AnyRelationship<Base> { | |
func query(withModel model: inout Base) { | |
fatalError("Needs to be overriden!") | |
} | |
} | |
final class OneToMany<One, Many, SharedKey: Equatable>: AnyRelationship<One> { | |
let oneKeyPath: WritableKeyPath<One, [Many]> | |
let manyKeyPath: KeyPath<Many, SharedKey> | |
let joinKeyPath: KeyPath<One, SharedKey> | |
init(_ oneKeyPath: WritableKeyPath<One, [Many]>, _ manyKeyPath: KeyPath<Many, SharedKey>, _ joinKeyPath: KeyPath<One, SharedKey>) { | |
self.oneKeyPath = oneKeyPath | |
self.manyKeyPath = manyKeyPath | |
self.joinKeyPath = joinKeyPath | |
} | |
override func query(withModel model: inout One) { | |
// Force casts here simulate generic storage that allows us to get values by metatype. | |
// For this playground we only support hardcoded `IssueAssignee` type, but that doesn't change | |
// anything about overall API. | |
if Many.self == IssueAssignee.self { | |
model[keyPath: self.oneKeyPath] = assignees.filter { ($0 as! Many)[keyPath: self.manyKeyPath] == model[keyPath: self.joinKeyPath] } as! [Many] | |
} else if Many.self == IssueFollower.self { | |
model[keyPath: self.oneKeyPath] = followers.filter { ($0 as! Many)[keyPath: self.manyKeyPath] == model[keyPath: self.joinKeyPath] } as! [Many] | |
} else { | |
fatalError() | |
} | |
} | |
} | |
var issue = IssueModel(uid: "1", title: "ABC", assignees: [], followers: []) | |
let assignees = [ | |
IssueAssignee(issueUid: "1", assigneeUid: "Assigned to Issue 1"), | |
IssueAssignee(issueUid: "2", assigneeUid: "Assigned to Issue 2"), | |
IssueAssignee(issueUid: "1", assigneeUid: "Also assigned to issue 1!!!!") | |
] | |
let followers = [ | |
IssueFollower(issueUid: "1", followerUid: "Following Issue 1"), | |
IssueFollower(issueUid: "2", followerUid: "Following Issue 2"), | |
IssueFollower(issueUid: "1", followerUid: "Also following issue 1!!!!") | |
] | |
for join in IssueModel.joins { | |
join.query(withModel: &issue) | |
} | |
print(issue) | |
// IssueModel(uid: "1", title: "ABC", assignees: [__lldb_expr_1.IssueAssignee(issueUid: "1", assigneeUid: "Assigned to Issue 1"), __lldb_expr_1.IssueAssignee(issueUid: "1", assigneeUid: "Also assigned to issue 1!!!!")], followers: [__lldb_expr_1.IssueFollower(issueUid: "1", followerUid: "Following Issue 1"), __lldb_expr_1.IssueFollower(issueUid: "1", followerUid: "Also following issue 1!!!!")]) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment