Skip to content

Commit b3169f2

Browse files
authored
fix: Improve Equatable and Hashable conformance (#148)
* fix: Improve Equatable and Hashable conformance * add changelog * revert hasSameObjectId * Improve playgrounds * Improve user login playgrounds
1 parent dc037bd commit b3169f2

38 files changed

+330
-245
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ __New features__
1414
* Add fetchAll method to array of Parse Pointer Object's ([#141](https://github.com/netreconlab/Parse-Swift/pull/141)), thanks to [Corey Baker](https://github.com/cbaker6).
1515

1616
__Fixes__
17-
* Updates to ParseUser password now persist the updated sessionToken from the server to the client Keychain ([#147](https://github.com/netreconlab/Parse-Swift/pull/147)), thanks to [Corey Baker](https://github.com/cbaker6).
17+
* Improve mutiple types conformance to Equatable and Hashable ([#148](https://github.com/netreconlab/Parse-Swift/pull/147)), thanks to [Corey Baker](https://github.com/cbaker6).
18+
* Updates to ParseUser password now persist the updated sessionToken from the server to the client Keychain ([#147](https://github.com/netreconlab/Parse-Swift/pull/148)), thanks to [Corey Baker](https://github.com/cbaker6).
1819

1920
### 5.8.2
2021
[Full Changelog](https://github.com/netreconlab/Parse-Swift/compare/5.8.1...5.8.2), [Documentation](https://swiftpackageindex.com/netreconlab/Parse-Swift/5.8.2/documentation/parseswift)

ParseSwift.playground/Pages/1 - Your first Object.xcplaygroundpage/Contents.swift

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,8 @@ score.save { result in
165165
}
166166

167167
//: This will store the second batch score to be used later.
168-
var score2ForFetchedLater: GameScore?
168+
var scoreToFetchLater: GameScore?
169+
var score2ToFetchLater: GameScore?
169170

170171
//: Saving multiple GameScores at once with batching.
171172
[score, score2].saveAll { results in
@@ -179,8 +180,10 @@ var score2ForFetchedLater: GameScore?
179180
Saved \"\(savedScore.className)\" with
180181
points \(String(describing: savedScore.points)) successfully
181182
""")
182-
if index == 1 {
183-
score2ForFetchedLater = savedScore
183+
if index == 0 {
184+
scoreToFetchLater = savedScore
185+
} else if index == 1 {
186+
score2ToFetchLater = savedScore
184187
}
185188
index += 1
186189
case .failure(let error):
@@ -203,8 +206,10 @@ var score2ForFetchedLater: GameScore?
203206
switch otherResult {
204207
case .success(let savedScore):
205208
print("Saved \"\(savedScore.className)\" with points \(savedScore.points) successfully")
206-
if index == 1 {
207-
score2ForFetchedLater = savedScore
209+
if index == 0 {
210+
scoreToFetchLater = savedScore
211+
} else if index == 1 {
212+
score2ToFetchLater = savedScore
208213
}
209214
index += 1
210215
case .failure(let error):
@@ -299,7 +304,7 @@ Task {
299304
}
300305

301306
//: Now we will fetch a ParseObject that has already been saved based on its' objectId.
302-
let scoreToFetch = GameScore(objectId: score2ForFetchedLater?.objectId)
307+
let scoreToFetch = GameScore(objectId: scoreToFetchLater?.objectId)
303308

304309
//: Asynchronous completion block fetch this GameScore based on it is objectId alone.
305310
scoreToFetch.fetch { result in
@@ -322,7 +327,7 @@ Task {
322327
}
323328

324329
//: Now we will fetch `ParseObject`'s in batch that have already been saved based on its' objectId.
325-
let score2ToFetch = GameScore(objectId: score2ForFetchedLater?.objectId)
330+
let score2ToFetch = GameScore(objectId: score2ToFetchLater?.objectId)
326331

327332
//: Asynchronous completion block fetch GameScores based on it is objectId alone.
328333
[scoreToFetch, score2ToFetch].fetchAll { result in

ParseSwift.playground/Pages/3 - User - Sign Up.xcplaygroundpage/Contents.swift

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,6 @@ import ParseSwift
1212

1313
PlaygroundPage.current.needsIndefiniteExecution = true
1414

15-
Task {
16-
do {
17-
try await initializeParse()
18-
} catch {
19-
assertionFailure("Error initializing Parse-Swift: \(error)")
20-
}
21-
}
22-
2315
struct User: ParseUser {
2416
//: These are required by `ParseObject`.
2517
var objectId: String?
@@ -52,6 +44,19 @@ struct User: ParseUser {
5244
}
5345
}
5446

47+
Task {
48+
do {
49+
try await initializeParse()
50+
} catch {
51+
assertionFailure("Error initializing Parse-Swift: \(error)")
52+
}
53+
do {
54+
try await User.logout()
55+
} catch let error {
56+
print("Error logging out: \(error)")
57+
}
58+
}
59+
5560
/*:
5661
Sign up user asynchronously - Performs work on background
5762
queue and returns to specified callbackQueue.

ParseSwift.playground/Pages/4 - User - Continued.xcplaygroundpage/Contents.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -182,8 +182,8 @@ Task {
182182
let currentUser = try? await User.current()
183183
currentUser?.fetch(includeKeys: ["gameScore"]) { result in
184184
switch result {
185-
case .success:
186-
print("Successfully fetched user with gameScore key: \(String(describing: User.current))")
185+
case .success(let user):
186+
print("Successfully fetched user with gameScore key: \(user)")
187187
case .failure(let error):
188188
print("Error fetching User: \(error)")
189189
}
@@ -196,8 +196,8 @@ Task {
196196
let currentUser = try? await User.current()
197197
currentUser?.fetch(includeKeys: ["*"]) { result in
198198
switch result {
199-
case .success:
200-
print("Successfully fetched user with all keys: \(String(describing: User.current))")
199+
case .success(let user):
200+
print("Successfully fetched user with all keys: \(user)")
201201
case .failure(let error):
202202
print("Error fetching User: \(error)")
203203
}

Sources/ParseSwift/API/API+Command.swift

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,9 @@ internal extension API {
5757
uploadProgress: ((URLSessionTask, Int64, Int64, Int64) -> Void)? = nil,
5858
stream: InputStream,
5959
completion: @escaping (ParseError?) -> Void) {
60-
guard method == .POST || method == .PUT || method == .PATCH else {
60+
guard method == .POST ||
61+
method == .PUT ||
62+
method == .PATCH else {
6163
callbackQueue.async {
6264
completion(nil)
6365
}
@@ -133,7 +135,9 @@ internal extension API {
133135
}
134136
} else {
135137
// ParseFiles are handled with a dedicated URLSession
136-
if method == .POST || method == .PUT || method == .PATCH {
138+
if method == .POST ||
139+
method == .PUT ||
140+
method == .PATCH {
137141
switch await self.prepareURLRequest(options: options,
138142
batching: batching,
139143
childObjects: childObjects,
@@ -258,7 +262,8 @@ internal extension API {
258262
Task {
259263
do {
260264
var headers = try await API.getHeaders(options: options)
261-
if method == .GET || method == .DELETE {
265+
if method == .GET ||
266+
method == .DELETE {
262267
headers.removeValue(forKey: "X-Parse-Request-Id")
263268
}
264269
let url = parseURL == nil ?
@@ -405,8 +410,9 @@ internal extension API.Command {
405410
original data: Data?,
406411
ignoringCustomObjectIdConfig: Bool,
407412
batching: Bool = false) async throws -> API.Command<V, V> where V: ParseObject {
408-
if Parse.configuration.isRequiringCustomObjectIds
409-
&& object.objectId == nil && !ignoringCustomObjectIdConfig {
413+
if Parse.configuration.isRequiringCustomObjectIds &&
414+
object.objectId == nil &&
415+
!ignoringCustomObjectIdConfig {
410416
throw ParseError(code: .missingObjectId, message: "objectId must not be nil")
411417
}
412418
if try await object.isSaved() {

Sources/ParseSwift/API/API+NonParseBodyCommand.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ internal extension API {
6767
let params = self.params?.getURLQueryItems()
6868
do {
6969
var headers = try await API.getHeaders(options: options)
70-
if method == .GET || method == .DELETE {
70+
if method == .GET ||
71+
method == .DELETE {
7172
headers.removeValue(forKey: "X-Parse-Request-Id")
7273
}
7374
let url = API.serverURL(options: options).appendingPathComponent(path.urlComponent)

Sources/ParseSwift/API/API.swift

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -203,8 +203,22 @@ public struct API {
203203
}
204204
}
205205

206+
// swiftlint:disable:next cyclomatic_complexity
206207
public static func == (lhs: API.Option, rhs: API.Option) -> Bool {
207-
lhs.hashValue == rhs.hashValue
208+
switch (lhs, rhs) {
209+
case (.usePrimaryKey, .usePrimaryKey): return true
210+
case (.removeMimeType, .removeMimeType): return true
211+
case (.sessionToken(let object1), .sessionToken(let object2)): return object1 == object2
212+
case (.installationId(let object1), .installationId(let object2)): return object1 == object2
213+
case (.mimeType(let object1), .mimeType(let object2)): return object1 == object2
214+
case (.fileSize(let object1), .fileSize(let object2)): return object1 == object2
215+
case (.metadata(let object1), .metadata(let object2)): return object1 == object2
216+
case (.tags(let object1), .tags(let object2)): return object1 == object2
217+
case (.context(let object1), .context(let object2)): return object1.isEqual(object2)
218+
case (.cachePolicy(let object1), .cachePolicy(let object2)): return object1 == object2
219+
case (.serverURL(let object1), .serverURL(let object2)): return object1 == object2
220+
default: return false
221+
}
208222
}
209223
}
210224

@@ -228,7 +242,7 @@ public struct API {
228242
headers["X-Parse-Client-Version"] = clientVersion()
229243
headers["X-Parse-Request-Id"] = Self.createUniqueRequestId()
230244

231-
options.forEach { (option) in
245+
options.forEach { option in
232246
switch option {
233247
case .usePrimaryKey:
234248
headers["X-Parse-Master-Key"] = Parse.configuration.primaryKey
@@ -272,11 +286,22 @@ public struct API {
272286
}
273287

274288
internal static func serverURL(options: API.Options) -> URL {
275-
guard let differentServerURLOption = options.first(where: { $0 == .serverURL("") }),
276-
case .serverURL(let differentServerURLString) = differentServerURLOption,
277-
let differentURL = URL(string: differentServerURLString) else {
289+
var optionURL: URL?
290+
// BAKER: Currently have to step through all options and
291+
// break to get the current URL.
292+
for option in options {
293+
switch option {
294+
case .serverURL(let url):
295+
optionURL = URL(string: url)
296+
default:
297+
continue
298+
}
299+
break
300+
}
301+
302+
guard let currentURL = optionURL else {
278303
return Parse.configuration.serverURL
279304
}
280-
return differentURL
305+
return currentURL
281306
}
282307
}

Sources/ParseSwift/Coding/AnyCodable.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,5 +110,6 @@ extension AnyCodable: ExpressibleByBooleanLiteral {}
110110
extension AnyCodable: ExpressibleByIntegerLiteral {}
111111
extension AnyCodable: ExpressibleByFloatLiteral {}
112112
extension AnyCodable: ExpressibleByStringLiteral {}
113+
extension AnyCodable: ExpressibleByStringInterpolation {}
113114
extension AnyCodable: ExpressibleByArrayLiteral {}
114115
extension AnyCodable: ExpressibleByDictionaryLiteral {}

Sources/ParseSwift/Coding/ParseEncoder.swift

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,8 @@ internal class _ParseEncoder: JSONEncoder, Encoder {
367367
}
368368
valueToEncode = pointer
369369
} else if let object = value as? Objectable {
370-
if !batching || (batching && codingPath.last?.stringValue == "body"),
370+
if !batching ||
371+
(batching && codingPath.last?.stringValue == "body"),
371372
let pointer = try? PointerType(object) {
372373
if let uniquePointer = self.uniquePointer,
373374
uniquePointer.hasSameObjectId(as: pointer) {
@@ -507,8 +508,8 @@ private struct _ParseEncoderKeyedEncodingContainer<Key: CodingKey>: KeyedEncodin
507508
guard !shouldSkipKey(key) else { return }
508509

509510
var valueToEncode: Encodable = value
510-
if ((value as? Objectable) != nil)
511-
|| ((value as? ParsePointer) != nil) {
511+
if ((value as? Objectable) != nil) ||
512+
((value as? ParsePointer) != nil) {
512513
if let replacedObject = try self.encoder.deepFindAndReplaceParseObjects(value) {
513514
valueToEncode = replacedObject
514515
}
@@ -964,18 +965,22 @@ extension _ParseEncoder {
964965
// Disambiguation between variable and function is required due to
965966
// issue tracked at: https://bugs.swift.org/browse/SR-1846
966967
let type = Swift.type(of: value)
967-
if type == Date.self || type == NSDate.self {
968+
if type == Date.self ||
969+
type == NSDate.self {
968970
// Respect Date encoding strategy
969971
return try self.box((value as! Date))
970-
} else if type == Data.self || type == NSData.self {
972+
} else if type == Data.self ||
973+
type == NSData.self {
971974
// Respect Data encoding strategy
972975
// swiftlint:disable:next force_cast
973976
return try self.box((value as! Data))
974-
} else if type == URL.self || type == NSURL.self {
977+
} else if type == URL.self ||
978+
type == NSURL.self {
975979
// Encode URLs as single strings.
976980
// swiftlint:disable:next force_cast
977981
return self.box((value as! URL).absoluteString)
978-
} else if type == Decimal.self || type == NSDecimalNumber.self {
982+
} else if type == Decimal.self ||
983+
type == NSDecimalNumber.self {
979984
// JSONSerialization can natively handle NSDecimalNumber.
980985
// swiftlint:disable:next force_cast
981986
return (value as! NSDecimalNumber)

Sources/ParseSwift/Extensions/Encodable.swift

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,25 @@ import Foundation
1010

1111
internal extension Encodable {
1212
func isEqual(_ other: Encodable?) -> Bool {
13-
guard let lhsData = try? ParseCoding.parseEncoder().encode(self,
14-
acl: nil),
13+
guard let lhsData = try? ParseCoding
14+
.parseEncoder()
15+
.encode(
16+
self,
17+
acl: nil
18+
),
1519
let lhsString = String(data: lhsData, encoding: .utf8),
1620
let other = other,
17-
let rhsData = try? ParseCoding.parseEncoder().encode(other,
18-
acl: nil),
19-
let rhsString = String(data: rhsData, encoding: .utf8) else {
20-
return false
21+
let rhsData = try? ParseCoding
22+
.parseEncoder()
23+
.encode(
24+
other,
25+
acl: nil
26+
),
27+
let rhsString = String(
28+
data: rhsData,
29+
encoding: .utf8
30+
) else {
31+
return false
2132
}
2233
return lhsString == rhsString
2334
}

Sources/ParseSwift/LiveQuery/LiveQueryConstants.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,11 @@ public enum Event<T: ParseObject>: Equatable {
4545

4646
public static func == <U>(lhs: Event<U>, rhs: Event<U>) -> Bool {
4747
switch (lhs, rhs) {
48-
case (.entered(let obj1), .entered(let obj2)): return obj1 == obj2
49-
case (.left(let obj1), .left(let obj2)): return obj1 == obj2
50-
case (.created(let obj1), .created(let obj2)): return obj1 == obj2
51-
case (.updated(let obj1), .updated(let obj2)): return obj1 == obj2
52-
case (.deleted(let obj1), .deleted(let obj2)): return obj1 == obj2
48+
case (.entered(let object1), .entered(let object2)): return object1 == object2
49+
case (.left(let object1), .left(let object2)): return object1 == object2
50+
case (.created(let object1), .created(let object2)): return object1 == object2
51+
case (.updated(let object1), .updated(let object2)): return object1 == object2
52+
case (.deleted(let object1), .deleted(let object2)): return object1 == object2
5353
default: return false
5454
}
5555
}

Sources/ParseSwift/LiveQuery/ParseLiveQuery.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -631,7 +631,8 @@ extension ParseLiveQuery {
631631
if isUserWantsToConnect {
632632
self.isDisconnectedByUser = false
633633
}
634-
if self.status == .connected || self.isDisconnectedByUser {
634+
if self.status == .connected ||
635+
self.isDisconnectedByUser {
635636
completion(nil)
636637
return
637638
}
@@ -769,8 +770,8 @@ extension ParseLiveQuery {
769770
}
770771

771772
static func == (lhs: SubscriptionRecord, rhs: SubscriptionRecord) -> Bool {
772-
lhs.messageData == rhs.messageData
773-
&& lhs.queryData == rhs.queryData
773+
lhs.messageData == rhs.messageData &&
774+
lhs.queryData == rhs.queryData
774775
}
775776
}
776777
}

Sources/ParseSwift/Objects/ParseInstallation.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,8 @@ extension ParseInstallation {
174174

175175
func endpoint(_ method: API.Method) async throws -> API.Endpoint {
176176
try await yieldIfNotInitialized()
177-
if !Parse.configuration.isRequiringCustomObjectIds || method != .POST {
177+
if !Parse.configuration.isRequiringCustomObjectIds ||
178+
method != .POST {
178179
return endpoint
179180
} else {
180181
return .installations

Sources/ParseSwift/Objects/ParseObject+async.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,8 @@ internal extension ParseObject {
312312
objectsSavedBeforeThisOne: nil,
313313
filesSavedBeforeThisOne: nil)
314314
var waitingToBeSaved = object.unsavedChildren
315-
if isShouldReturnIfChildObjectsFound && waitingToBeSaved.count > 0 {
315+
if isShouldReturnIfChildObjectsFound &&
316+
waitingToBeSaved.count > 0 {
316317
let error = ParseError(code: .otherCause,
317318
message: """
318319
When using transactions, all child ParseObjects have to originally
@@ -349,7 +350,9 @@ or disable transactions for this call.
349350
}
350351
}
351352
waitingToBeSaved = nextBatch
352-
if waitingToBeSaved.count > 0 && savableObjects.count == 0 && savableFiles.count == 0 {
353+
if waitingToBeSaved.count > 0 &&
354+
savableObjects.count == 0 &&
355+
savableFiles.count == 0 {
353356
throw ParseError(code: .otherCause,
354357
message: "Found a circular dependency in ParseObject.")
355358
}

0 commit comments

Comments
 (0)