Skip to content

Commit fa1e07f

Browse files
authored
refactor: improve layouts of live query subscription files (#273)
* WIP * improve live query subscription files * add more publisher docs * lint
1 parent 72eb7c5 commit fa1e07f

File tree

8 files changed

+115
-84
lines changed

8 files changed

+115
-84
lines changed

.codecov.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ coverage:
66
status:
77
patch:
88
default:
9-
target: auto
9+
target: 36
1010
changes: false
1111
project:
1212
default:

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/2.2.0...main)
66
* _Contributing to this repo? Add info about your change here to be included in the next release_
77

8-
### 2.1.0
8+
### 2.2.0
99
[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/2.1.0...2.2.0)
1010

1111
__Improvements__

ParseSwift.xcodeproj/project.pbxproj

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,10 @@
293293
705D950925BE4C08003EF6F8 /* SubscriptionCallback.swift in Sources */ = {isa = PBXBuildFile; fileRef = 705D950725BE4C08003EF6F8 /* SubscriptionCallback.swift */; };
294294
705D950A25BE4C08003EF6F8 /* SubscriptionCallback.swift in Sources */ = {isa = PBXBuildFile; fileRef = 705D950725BE4C08003EF6F8 /* SubscriptionCallback.swift */; };
295295
705D950B25BE4C08003EF6F8 /* SubscriptionCallback.swift in Sources */ = {isa = PBXBuildFile; fileRef = 705D950725BE4C08003EF6F8 /* SubscriptionCallback.swift */; };
296+
7064369B273313D5007C6461 /* LiveQueryConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7064369A273313D5007C6461 /* LiveQueryConstants.swift */; };
297+
7064369C273313D5007C6461 /* LiveQueryConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7064369A273313D5007C6461 /* LiveQueryConstants.swift */; };
298+
7064369D273313D5007C6461 /* LiveQueryConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7064369A273313D5007C6461 /* LiveQueryConstants.swift */; };
299+
7064369E273313D5007C6461 /* LiveQueryConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7064369A273313D5007C6461 /* LiveQueryConstants.swift */; };
296300
70647E9C259E3A9A004C1004 /* ParseType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70647E9B259E3A9A004C1004 /* ParseType.swift */; };
297301
70647E9D259E3A9A004C1004 /* ParseType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70647E9B259E3A9A004C1004 /* ParseType.swift */; };
298302
70647E9E259E3A9A004C1004 /* ParseType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70647E9B259E3A9A004C1004 /* ParseType.swift */; };
@@ -878,6 +882,7 @@
878882
705A99F8259807F900B3547F /* ParseFileManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseFileManagerTests.swift; sourceTree = "<group>"; };
879883
705A9A2E25991C1400B3547F /* Fileable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Fileable.swift; sourceTree = "<group>"; };
880884
705D950725BE4C08003EF6F8 /* SubscriptionCallback.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionCallback.swift; sourceTree = "<group>"; };
885+
7064369A273313D5007C6461 /* LiveQueryConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveQueryConstants.swift; sourceTree = "<group>"; };
881886
70647E9B259E3A9A004C1004 /* ParseType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseType.swift; sourceTree = "<group>"; };
882887
70732C592606CCAD000CAB81 /* ParseObjectCustomObjectIdTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseObjectCustomObjectIdTests.swift; sourceTree = "<group>"; };
883888
707A3BF025B0A4F0000D215C /* ParseAuthentication.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseAuthentication.swift; sourceTree = "<group>"; };
@@ -1379,6 +1384,7 @@
13791384
70510AAA259EE23700FEA700 /* LiveQuery */ = {
13801385
isa = PBXGroup;
13811386
children = (
1387+
7064369A273313D5007C6461 /* LiveQueryConstants.swift */,
13821388
70510AAB259EE25E00FEA700 /* LiveQuerySocket.swift */,
13831389
7003959425A10DFC0052CB31 /* Messages.swift */,
13841390
700395A225A119430052CB31 /* Operations.swift */,
@@ -2087,6 +2093,7 @@
20872093
70C550A025B4A9F600B5DBC2 /* RemoveRelation.swift in Sources */,
20882094
F97B463B24D9C74400F4A88B /* API+Command.swift in Sources */,
20892095
F97B464624D9C78B00F4A88B /* ParseOperation.swift in Sources */,
2096+
7064369B273313D5007C6461 /* LiveQueryConstants.swift in Sources */,
20902097
89899CCF2603CE3A002E2043 /* ParseFacebook.swift in Sources */,
20912098
7028373926BD8C89007688C9 /* ParseUser+async.swift in Sources */,
20922099
705A9A2F25991C1400B3547F /* Fileable.swift in Sources */,
@@ -2300,6 +2307,7 @@
23002307
70C550A125B4A9F600B5DBC2 /* RemoveRelation.swift in Sources */,
23012308
F97B463C24D9C74400F4A88B /* API+Command.swift in Sources */,
23022309
F97B464724D9C78B00F4A88B /* ParseOperation.swift in Sources */,
2310+
7064369C273313D5007C6461 /* LiveQueryConstants.swift in Sources */,
23032311
89899CD02603CE3A002E2043 /* ParseFacebook.swift in Sources */,
23042312
7028373A26BD8C89007688C9 /* ParseUser+async.swift in Sources */,
23052313
705A9A3025991C1400B3547F /* Fileable.swift in Sources */,
@@ -2609,6 +2617,7 @@
26092617
70C550A325B4A9F600B5DBC2 /* RemoveRelation.swift in Sources */,
26102618
F97B460D24D9C6F200F4A88B /* Fetchable.swift in Sources */,
26112619
F97B45ED24D9C6F200F4A88B /* ParseGeoPoint.swift in Sources */,
2620+
7064369E273313D5007C6461 /* LiveQueryConstants.swift in Sources */,
26122621
89899CD22603CE3A002E2043 /* ParseFacebook.swift in Sources */,
26132622
7028373C26BD8C89007688C9 /* ParseUser+async.swift in Sources */,
26142623
705A9A3225991C1400B3547F /* Fileable.swift in Sources */,
@@ -2735,6 +2744,7 @@
27352744
70C550A225B4A9F600B5DBC2 /* RemoveRelation.swift in Sources */,
27362745
F97B460C24D9C6F200F4A88B /* Fetchable.swift in Sources */,
27372746
F97B45EC24D9C6F200F4A88B /* ParseGeoPoint.swift in Sources */,
2747+
7064369D273313D5007C6461 /* LiveQueryConstants.swift in Sources */,
27382748
89899CD12603CE3A002E2043 /* ParseFacebook.swift in Sources */,
27392749
7028373B26BD8C89007688C9 /* ParseUser+async.swift in Sources */,
27402750
705A9A3125991C1400B3547F /* Fileable.swift in Sources */,
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
//
2+
// LiveQueryConstants.swift
3+
// ParseSwift
4+
//
5+
// Created by Corey Baker on 11/3/21.
6+
// Copyright © 2021 Parse Community. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
/**
12+
Represents an update on a specific object from the `ParseLiveQuery` Server.
13+
- Entered: The object has been updated, and is now included in the query.
14+
- Left: The object has been updated, and is no longer included in the query.
15+
- Created: The object has been created, and is a part of the query.
16+
- Updated: The object has been updated, and is still a part of the query.
17+
- Deleted: The object has been deleted, and is no longer included in the query.
18+
*/
19+
public enum Event<T: ParseObject> {
20+
/// The object has been updated, and is now included in the query.
21+
case entered(T)
22+
23+
/// The object has been updated, and is no longer included in the query.
24+
case left(T)
25+
26+
/// The object has been created, and is a part of the query.
27+
case created(T)
28+
29+
/// The object has been updated, and is still a part of the query.
30+
case updated(T)
31+
32+
/// The object has been deleted, and is no longer included in the query.
33+
case deleted(T)
34+
35+
init?(event: EventResponse<T>) {
36+
switch event.op {
37+
case .enter: self = .entered(event.object)
38+
case .leave: self = .left(event.object)
39+
case .create: self = .created(event.object)
40+
case .update: self = .updated(event.object)
41+
case .delete: self = .deleted(event.object)
42+
default: fatalError()
43+
}
44+
}
45+
}
46+
47+
internal func == <T>(lhs: Event<T>, rhs: Event<T>) -> Bool {
48+
switch (lhs, rhs) {
49+
case (.entered(let obj1), .entered(let obj2)): return obj1 == obj2
50+
case (.left(let obj1), .left(let obj2)): return obj1 == obj2
51+
case (.created(let obj1), .created(let obj2)): return obj1 == obj2
52+
case (.updated(let obj1), .updated(let obj2)): return obj1 == obj2
53+
case (.deleted(let obj1), .deleted(let obj2)): return obj1 == obj2
54+
default: return false
55+
}
56+
}

Sources/ParseSwift/LiveQuery/Subscription.swift

Lines changed: 8 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -7,60 +7,15 @@
77
//
88
//
99

10+
#if canImport(Combine)
1011
import Foundation
1112

12-
/**
13-
Represents an update on a specific object from the `ParseLiveQuery` Server.
14-
- Entered: The object has been updated, and is now included in the query.
15-
- Left: The object has been updated, and is no longer included in the query.
16-
- Created: The object has been created, and is a part of the query.
17-
- Updated: The object has been updated, and is still a part of the query.
18-
- Deleted: The object has been deleted, and is no longer included in the query.
19-
*/
20-
public enum Event<T: ParseObject> {
21-
/// The object has been updated, and is now included in the query.
22-
case entered(T)
23-
24-
/// The object has been updated, and is no longer included in the query.
25-
case left(T)
26-
27-
/// The object has been created, and is a part of the query.
28-
case created(T)
29-
30-
/// The object has been updated, and is still a part of the query.
31-
case updated(T)
32-
33-
/// The object has been deleted, and is no longer included in the query.
34-
case deleted(T)
35-
36-
init?(event: EventResponse<T>) {
37-
switch event.op {
38-
case .enter: self = .entered(event.object)
39-
case .leave: self = .left(event.object)
40-
case .create: self = .created(event.object)
41-
case .update: self = .updated(event.object)
42-
case .delete: self = .deleted(event.object)
43-
default: fatalError()
44-
}
45-
}
46-
}
47-
48-
private func == <T>(lhs: Event<T>, rhs: Event<T>) -> Bool {
49-
switch (lhs, rhs) {
50-
case (.entered(let obj1), .entered(let obj2)): return obj1 == obj2
51-
case (.left(let obj1), .left(let obj2)): return obj1 == obj2
52-
case (.created(let obj1), .created(let obj2)): return obj1 == obj2
53-
case (.updated(let obj1), .updated(let obj2)): return obj1 == obj2
54-
case (.deleted(let obj1), .deleted(let obj2)): return obj1 == obj2
55-
default: return false
56-
}
57-
}
58-
59-
#if canImport(Combine)
6013
/**
6114
A default implementation of the `QuerySubscribable` protocol. Suitable for `ObjectObserved`
6215
as the subscription can be used as a SwiftUI publisher. Meaning it can serve
63-
indepedently as a ViewModel in MVVM.
16+
indepedently as a ViewModel in MVVM. Also can be used as a Combine publisher. See Apple's
17+
[documentation](https://developer.apple.com/documentation/combine/observableobject)
18+
for more details.
6419
*/
6520
open class Subscription<T: ParseObject>: QueryViewModel<T>, QuerySubscribable {
6621

@@ -112,7 +67,11 @@ open class Subscription<T: ParseObject>: QueryViewModel<T>, QuerySubscribable {
11267
self.event = nil
11368
self.unsubscribed = nil
11469
}
70+
}
11571

72+
// MARK: QuerySubscribable
73+
74+
extension Subscription {
11675
open func didReceive(_ eventData: Data) throws {
11776
// Need to decode the event with respect to the `ParseObject`.
11877
let eventMessage = try ParseCoding.jsonDecoder().decode(EventResponse<T>.self, from: eventData)
@@ -131,30 +90,3 @@ open class Subscription<T: ParseObject>: QueryViewModel<T>, QuerySubscribable {
13190
}
13291
}
13392
#endif
134-
135-
extension SubscriptionCallback {
136-
137-
/**
138-
Register a callback for when an event occurs of a specific type
139-
Example:
140-
subscription.handle(Event.Created) { query, object in
141-
// Called whenever an object is creaated
142-
}
143-
- parameter eventType: The event type to handle. You should pass one of the enum cases in `Event`.
144-
- parameter handler: The callback to register.
145-
- returns: The same subscription, for easy chaining.
146-
*/
147-
@discardableResult public func handle(_ eventType: @escaping (T) -> Event<T>,
148-
_ handler: @escaping (Query<T>, T) -> Void) -> SubscriptionCallback {
149-
return handleEvent { query, event in
150-
switch event {
151-
case .entered(let obj) where eventType(obj) == event: handler(query, obj)
152-
case .left(let obj) where eventType(obj) == event: handler(query, obj)
153-
case .created(let obj) where eventType(obj) == event: handler(query, obj)
154-
case .updated(let obj) where eventType(obj) == event: handler(query, obj)
155-
case .deleted(let obj) where eventType(obj) == event: handler(query, obj)
156-
default: return
157-
}
158-
}
159-
}
160-
}

Sources/ParseSwift/LiveQuery/SubscriptionCallback.swift

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@ open class SubscriptionCallback<T: ParseObject>: QuerySubscribable {
1515

1616
public var query: Query<T>
1717
public typealias Object = T
18-
fileprivate var eventHandlers: [(Query<T>, Event<T>) -> Void] = []
19-
fileprivate var subscribeHandlers: [(Query<T>, Bool) -> Void] = []
20-
fileprivate var unsubscribeHandlers: [(Query<T>) -> Void] = []
18+
fileprivate var eventHandlers = [(Query<T>, Event<T>) -> Void]()
19+
fileprivate var subscribeHandlers = [(Query<T>, Bool) -> Void]()
20+
fileprivate var unsubscribeHandlers = [(Query<T>) -> Void]()
2121

2222
/**
2323
Creates a new subscription that can be used to handle updates.
2424
*/
25-
required public init(query: Query<T>) {
25+
public required init(query: Query<T>) {
2626
self.query = query
2727
}
2828

@@ -58,6 +58,35 @@ open class SubscriptionCallback<T: ParseObject>: QuerySubscribable {
5858
return self
5959
}
6060

61+
/**
62+
Register a callback for when an event occurs of a specific type
63+
Example:
64+
subscription.handle(Event.Created) { query, object in
65+
// Called whenever an object is creaated
66+
}
67+
- parameter eventType: The event type to handle. You should pass one of the enum cases in `Event`.
68+
- parameter handler: The callback to register.
69+
- returns: The same subscription, for easy chaining.
70+
*/
71+
@discardableResult public func handle(_ eventType: @escaping (T) -> Event<T>,
72+
_ handler: @escaping (Query<T>, T) -> Void) -> SubscriptionCallback {
73+
return handleEvent { query, event in
74+
switch event {
75+
case .entered(let obj) where eventType(obj) == event: handler(query, obj)
76+
case .left(let obj) where eventType(obj) == event: handler(query, obj)
77+
case .created(let obj) where eventType(obj) == event: handler(query, obj)
78+
case .updated(let obj) where eventType(obj) == event: handler(query, obj)
79+
case .deleted(let obj) where eventType(obj) == event: handler(query, obj)
80+
default: return
81+
}
82+
}
83+
}
84+
85+
}
86+
87+
// MARK: QuerySubscribable
88+
89+
extension SubscriptionCallback {
6190
open func didReceive(_ eventData: Data) throws {
6291
// Need to decode the event with respect to the `ParseObject`.
6392
let eventMessage = try ParseCoding.jsonDecoder().decode(EventResponse<T>.self, from: eventData)

Sources/ParseSwift/Types/CloudViewModel.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ import Foundation
1010

1111
/**
1212
A default implementation of the `CloudCodeObservable` protocol. Suitable for `ObjectObserved`
13-
and can be used as a SwiftUI view model.
13+
and can be used as a SwiftUI view model. Also can be used as a Combine publisher. See Apple's
14+
[documentation](https://developer.apple.com/documentation/combine/observableobject)
15+
for more details.
1416
*/
1517
open class CloudViewModel<T: ParseCloud>: CloudObservable {
1618

Sources/ParseSwift/Types/QueryViewModel.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ import Foundation
1111

1212
/**
1313
A default implementation of the `QueryObservable` protocol. Suitable for `ObjectObserved`
14-
and can be used as a SwiftUI view model.
14+
and can be used as a SwiftUI view model. Also can be used as a Combine publisher. See Apple's
15+
[documentation](https://developer.apple.com/documentation/combine/observableobject)
16+
for more details.
1517
*/
1618
open class QueryViewModel<T: ParseObject>: QueryObservable {
1719

0 commit comments

Comments
 (0)