From 388e34bd3341cb4cb20bf70ed261087a52294702 Mon Sep 17 00:00:00 2001 From: Florent Vilmart Date: Thu, 13 Oct 2016 09:44:55 -0400 Subject: [PATCH 1/4] Use the PFDecoder to properly decode objects instead of withoutData --- .../ParseLiveQuery.xcodeproj/project.pbxproj | 6 ++++ .../Internal/ClientPrivate.swift | 23 ++++----------- Sources/ParseLiveQuery/Internal/Errors.swift | 10 +++++++ .../Internal/PFDecoder_internal.h | 28 +++++++++++++++++++ .../Internal/ParseLiveQuery-Bridging-Header.h | 9 ++++++ 5 files changed, 59 insertions(+), 17 deletions(-) create mode 100644 Sources/ParseLiveQuery/Internal/PFDecoder_internal.h create mode 100644 Sources/ParseLiveQuery/Internal/ParseLiveQuery-Bridging-Header.h diff --git a/Sources/ParseLiveQuery.xcodeproj/project.pbxproj b/Sources/ParseLiveQuery.xcodeproj/project.pbxproj index 1c3bd90b..e9f42e46 100644 --- a/Sources/ParseLiveQuery.xcodeproj/project.pbxproj +++ b/Sources/ParseLiveQuery.xcodeproj/project.pbxproj @@ -34,6 +34,8 @@ /* Begin PBXFileReference section */ 0632EDD31CA1A6DB00DD3CB8 /* Parse+LiveQuery.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Parse+LiveQuery.swift"; sourceTree = ""; }; 11F6DFE2732DB0DE49976BA5 /* Pods-ParseLiveQuery OSX.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ParseLiveQuery OSX.release.xcconfig"; path = "../Pods/Target Support Files/Pods-ParseLiveQuery OSX/Pods-ParseLiveQuery OSX.release.xcconfig"; sourceTree = ""; }; + 4AEAE5701DAFC3AF005F9FFB /* PFDecoder_internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PFDecoder_internal.h; sourceTree = ""; }; + 4AEAE5731DAFC488005F9FFB /* ParseLiveQuery-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ParseLiveQuery-Bridging-Header.h"; sourceTree = ""; }; 6062D7994653A4F07D1358B9 /* Pods-ParseLiveQuery iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ParseLiveQuery iOS.debug.xcconfig"; path = "../Pods/Target Support Files/Pods-ParseLiveQuery iOS/Pods-ParseLiveQuery iOS.debug.xcconfig"; sourceTree = ""; }; 7A40A16386D0D6B38F8B2F07 /* Pods-ParseLiveQuery-iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ParseLiveQuery-iOS.release.xcconfig"; path = "../Pods/Target Support Files/Pods-ParseLiveQuery-iOS/Pods-ParseLiveQuery-iOS.release.xcconfig"; sourceTree = ""; }; 8445DD921B87567C1E6A6042 /* Pods-ParseLiveQuery OSX.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ParseLiveQuery OSX.debug.xcconfig"; path = "../Pods/Target Support Files/Pods-ParseLiveQuery OSX/Pods-ParseLiveQuery OSX.debug.xcconfig"; sourceTree = ""; }; @@ -152,6 +154,8 @@ F534A5B31BDB09CE00CBD11A /* Operation.swift */, F54D58B71C8E3446009F8D6C /* ClientPrivate.swift */, F54D58B91C8E345F009F8D6C /* BoltsHelpers.swift */, + 4AEAE5701DAFC3AF005F9FFB /* PFDecoder_internal.h */, + 4AEAE5731DAFC488005F9FFB /* ParseLiveQuery-Bridging-Header.h */, ); path = Internal; sourceTree = ""; @@ -460,6 +464,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 8.0; MACOSX_DEPLOYMENT_TARGET = 10.10; ONLY_ACTIVE_ARCH = YES; + SWIFT_OBJC_BRIDGING_HEADER = "${SWIFT_MODULE_NAME}/Internal/${SWIFT_MODULE_NAME}-Bridging-Header.h"; }; name = Debug; }; @@ -486,6 +491,7 @@ GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 8.0; MACOSX_DEPLOYMENT_TARGET = 10.10; + SWIFT_OBJC_BRIDGING_HEADER = "${SWIFT_MODULE_NAME}/Internal/${SWIFT_MODULE_NAME}-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; }; name = Release; diff --git a/Sources/ParseLiveQuery/Internal/ClientPrivate.swift b/Sources/ParseLiveQuery/Internal/ClientPrivate.swift index 2b442e2f..e9416d09 100644 --- a/Sources/ParseLiveQuery/Internal/ClientPrivate.swift +++ b/Sources/ParseLiveQuery/Internal/ClientPrivate.swift @@ -13,29 +13,18 @@ import SocketRocket import BoltsSwift private func parseObject(_ objectDictionary: [String:AnyObject]) throws -> T { - guard let parseClassName = objectDictionary["className"] as? String else { + guard let _ = objectDictionary["className"] as? String else { throw LiveQueryErrors.InvalidJSONError(json: objectDictionary, expectedKey: "parseClassName") } - guard let objectId = objectDictionary["objectId"] as? String else { + guard let _ = objectDictionary["objectId"] as? String else { throw LiveQueryErrors.InvalidJSONError(json: objectDictionary, expectedKey: "objectId") } - let parseObject = T(withoutDataWithClassName: parseClassName, objectId: objectId) - - // Map of strings to closures to determine if the key is valid. Allows for more advanced checking of - // classnames and such. - let invalidKeys: [String:(Void)->Bool] = [ - "objectId": { true }, - "parseClassName": { true }, - "sessionToken": { parseClassName == "_User" } - ] - - objectDictionary.filter { key, _ in - return !(invalidKeys[key].map { $0() } ?? false) - }.forEach { key, value in - parseObject[key] = value + guard let object = PFDecoder.object().decode(objectDictionary) as? T else { + throw LiveQueryErrors.InvalidJSONObject(json: objectDictionary, details: "cannot decode json into \(T.self)") } - return parseObject + + return object } // --------------- diff --git a/Sources/ParseLiveQuery/Internal/Errors.swift b/Sources/ParseLiveQuery/Internal/Errors.swift index 3ec35a22..d657a1f4 100644 --- a/Sources/ParseLiveQuery/Internal/Errors.swift +++ b/Sources/ParseLiveQuery/Internal/Errors.swift @@ -37,6 +37,16 @@ public struct LiveQueryErrors { public let expectedKey: String } + /** + An error that is reported when the server returns valid JSON, but it doesn't match the format we expect. + */ + public struct InvalidJSONObject: Error { + /// JSON used for matching. + public let json: [String:AnyObject] + /// Details about the error + public let details: String + } + /** An error that is reported when the live query server encounters an internal error. */ diff --git a/Sources/ParseLiveQuery/Internal/PFDecoder_internal.h b/Sources/ParseLiveQuery/Internal/PFDecoder_internal.h new file mode 100644 index 00000000..d77e0756 --- /dev/null +++ b/Sources/ParseLiveQuery/Internal/PFDecoder_internal.h @@ -0,0 +1,28 @@ +// +// PFDecoder_internal.h +// ParseLiveQuery +// +// Created by Florent Vilmart on 16-10-13. +// Copyright © 2016 Parse. All rights reserved. +// + +#ifndef PFDecoder_internal_h +#define PFDecoder_internal_h + +#import + +@interface PFDecoder: NSObject +/** + Globally available shared instance of PFDecoder. + */ ++ (nonnull PFDecoder *)objectDecoder; + +/** + Takes a complex object that was deserialized and converts encoded + dictionaries into the proper Parse types. This is the inverse of + encodeObject:allowUnsaved:allowObjects:seenObjects:. + */ +- (nullable id)decodeObject:(nullable id)object; +@end + +#endif /* PFDecoder_internal_h */ diff --git a/Sources/ParseLiveQuery/Internal/ParseLiveQuery-Bridging-Header.h b/Sources/ParseLiveQuery/Internal/ParseLiveQuery-Bridging-Header.h new file mode 100644 index 00000000..dda5c8e8 --- /dev/null +++ b/Sources/ParseLiveQuery/Internal/ParseLiveQuery-Bridging-Header.h @@ -0,0 +1,9 @@ +// +// ParseLiveQuery-Bridging-Header.h +// ParseLiveQuery +// +// Created by Florent Vilmart on 16-10-13. +// Copyright © 2016 Parse. All rights reserved. +// + +#import "PFDecoder_internal.h" From 5ea684b09548f66cde3e2209c932daa8c6aada32 Mon Sep 17 00:00:00 2001 From: Florent Vilmart Date: Thu, 13 Oct 2016 09:49:26 -0400 Subject: [PATCH 2/4] Makes Bridging-header private --- Sources/ParseLiveQuery.xcodeproj/project.pbxproj | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Sources/ParseLiveQuery.xcodeproj/project.pbxproj b/Sources/ParseLiveQuery.xcodeproj/project.pbxproj index e9f42e46..ca8715a4 100644 --- a/Sources/ParseLiveQuery.xcodeproj/project.pbxproj +++ b/Sources/ParseLiveQuery.xcodeproj/project.pbxproj @@ -11,6 +11,10 @@ 0632EDD51CA1A6DB00DD3CB8 /* Parse+LiveQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0632EDD31CA1A6DB00DD3CB8 /* Parse+LiveQuery.swift */; }; 4A819D9D1D937866009C0F61 /* ObjCCompat.swift in Sources */ = {isa = PBXBuildFile; fileRef = F54D58B51C8E33D9009F8D6C /* ObjCCompat.swift */; }; 4A819D9E1D93786A009C0F61 /* ObjCCompat.swift in Sources */ = {isa = PBXBuildFile; fileRef = F54D58B51C8E33D9009F8D6C /* ObjCCompat.swift */; }; + 4AEAE5761DAFC808005F9FFB /* PFDecoder_internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 4AEAE5701DAFC3AF005F9FFB /* PFDecoder_internal.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 4AEAE5771DAFC808005F9FFB /* ParseLiveQuery-Bridging-Header.h in Headers */ = {isa = PBXBuildFile; fileRef = 4AEAE5731DAFC488005F9FFB /* ParseLiveQuery-Bridging-Header.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 4AEAE5781DAFC809005F9FFB /* PFDecoder_internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 4AEAE5701DAFC3AF005F9FFB /* PFDecoder_internal.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 4AEAE5791DAFC809005F9FFB /* ParseLiveQuery-Bridging-Header.h in Headers */ = {isa = PBXBuildFile; fileRef = 4AEAE5731DAFC488005F9FFB /* ParseLiveQuery-Bridging-Header.h */; settings = {ATTRIBUTES = (Private, ); }; }; 629DC3BE90DA87A7857677D2 /* Pods_ParseLiveQuery_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE2643D85A7565FC20EE144C /* Pods_ParseLiveQuery_iOS.framework */; }; DE7126BDB27E5DDB1C21490A /* Pods_ParseLiveQuery_OSX.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF5A55E51D52E372CD28FF08 /* Pods_ParseLiveQuery_OSX.framework */; }; F534A5B21BDAFE0200CBD11A /* Subscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = F534A5B11BDAFE0200CBD11A /* Subscription.swift */; }; @@ -167,6 +171,8 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + 4AEAE5791DAFC809005F9FFB /* ParseLiveQuery-Bridging-Header.h in Headers */, + 4AEAE5781DAFC809005F9FFB /* PFDecoder_internal.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -174,6 +180,8 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + 4AEAE5771DAFC808005F9FFB /* ParseLiveQuery-Bridging-Header.h in Headers */, + 4AEAE5761DAFC808005F9FFB /* PFDecoder_internal.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; From 8ee9a805610ea86d4650df341a30343e687f0438 Mon Sep 17 00:00:00 2001 From: Florent Vilmart Date: Thu, 13 Oct 2016 10:00:38 -0400 Subject: [PATCH 3/4] Include headers in Podspec --- ParseLiveQuery.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ParseLiveQuery.podspec b/ParseLiveQuery.podspec index ea530019..a6e2ce63 100644 --- a/ParseLiveQuery.podspec +++ b/ParseLiveQuery.podspec @@ -13,7 +13,7 @@ Pod::Spec.new do |s| s.ios.deployment_target = '8.0' s.osx.deployment_target = '10.10' - s.source_files = 'Sources/ParseLiveQuery/**/*.swift' + s.source_files = 'Sources/ParseLiveQuery/**/*.{swift,h}' s.module_name = 'ParseLiveQuery' s.dependency 'Parse', '~> 1.14.2' From 99775ae14f2771beee7c6493df145718b26dfa7e Mon Sep 17 00:00:00 2001 From: Joe Szymanski Date: Wed, 22 Feb 2017 19:36:29 -0500 Subject: [PATCH 4/4] Add support for using Parse's native object encoding (#87) Create private headers to access the PFEncoder classes. Update the encoding of queries into dictionaries to properly use Parse's encoder for any PFObject subclasses. --- .../ParseLiveQuery.xcodeproj/project.pbxproj | 6 +++ .../Internal/PFEncoder_internal.h | 46 +++++++++++++++++++ .../Internal/ParseLiveQuery-Bridging-Header.h | 1 + .../Internal/QueryEncoder.swift | 2 + 4 files changed, 55 insertions(+) create mode 100644 Sources/ParseLiveQuery/Internal/PFEncoder_internal.h diff --git a/Sources/ParseLiveQuery.xcodeproj/project.pbxproj b/Sources/ParseLiveQuery.xcodeproj/project.pbxproj index ca8715a4..f4cdd77d 100644 --- a/Sources/ParseLiveQuery.xcodeproj/project.pbxproj +++ b/Sources/ParseLiveQuery.xcodeproj/project.pbxproj @@ -9,6 +9,8 @@ /* Begin PBXBuildFile section */ 0632EDD41CA1A6DB00DD3CB8 /* Parse+LiveQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0632EDD31CA1A6DB00DD3CB8 /* Parse+LiveQuery.swift */; }; 0632EDD51CA1A6DB00DD3CB8 /* Parse+LiveQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0632EDD31CA1A6DB00DD3CB8 /* Parse+LiveQuery.swift */; }; + 3B68E5B71DECC32300038DDD /* PFEncoder_internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B68E5B61DECC32300038DDD /* PFEncoder_internal.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 3B68E5B81DECC32300038DDD /* PFEncoder_internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B68E5B61DECC32300038DDD /* PFEncoder_internal.h */; settings = {ATTRIBUTES = (Private, ); }; }; 4A819D9D1D937866009C0F61 /* ObjCCompat.swift in Sources */ = {isa = PBXBuildFile; fileRef = F54D58B51C8E33D9009F8D6C /* ObjCCompat.swift */; }; 4A819D9E1D93786A009C0F61 /* ObjCCompat.swift in Sources */ = {isa = PBXBuildFile; fileRef = F54D58B51C8E33D9009F8D6C /* ObjCCompat.swift */; }; 4AEAE5761DAFC808005F9FFB /* PFDecoder_internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 4AEAE5701DAFC3AF005F9FFB /* PFDecoder_internal.h */; settings = {ATTRIBUTES = (Private, ); }; }; @@ -38,6 +40,7 @@ /* Begin PBXFileReference section */ 0632EDD31CA1A6DB00DD3CB8 /* Parse+LiveQuery.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Parse+LiveQuery.swift"; sourceTree = ""; }; 11F6DFE2732DB0DE49976BA5 /* Pods-ParseLiveQuery OSX.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ParseLiveQuery OSX.release.xcconfig"; path = "../Pods/Target Support Files/Pods-ParseLiveQuery OSX/Pods-ParseLiveQuery OSX.release.xcconfig"; sourceTree = ""; }; + 3B68E5B61DECC32300038DDD /* PFEncoder_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PFEncoder_internal.h; sourceTree = ""; }; 4AEAE5701DAFC3AF005F9FFB /* PFDecoder_internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PFDecoder_internal.h; sourceTree = ""; }; 4AEAE5731DAFC488005F9FFB /* ParseLiveQuery-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ParseLiveQuery-Bridging-Header.h"; sourceTree = ""; }; 6062D7994653A4F07D1358B9 /* Pods-ParseLiveQuery iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ParseLiveQuery iOS.debug.xcconfig"; path = "../Pods/Target Support Files/Pods-ParseLiveQuery iOS/Pods-ParseLiveQuery iOS.debug.xcconfig"; sourceTree = ""; }; @@ -159,6 +162,7 @@ F54D58B71C8E3446009F8D6C /* ClientPrivate.swift */, F54D58B91C8E345F009F8D6C /* BoltsHelpers.swift */, 4AEAE5701DAFC3AF005F9FFB /* PFDecoder_internal.h */, + 3B68E5B61DECC32300038DDD /* PFEncoder_internal.h */, 4AEAE5731DAFC488005F9FFB /* ParseLiveQuery-Bridging-Header.h */, ); path = Internal; @@ -173,6 +177,7 @@ files = ( 4AEAE5791DAFC809005F9FFB /* ParseLiveQuery-Bridging-Header.h in Headers */, 4AEAE5781DAFC809005F9FFB /* PFDecoder_internal.h in Headers */, + 3B68E5B81DECC32300038DDD /* PFEncoder_internal.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -182,6 +187,7 @@ files = ( 4AEAE5771DAFC808005F9FFB /* ParseLiveQuery-Bridging-Header.h in Headers */, 4AEAE5761DAFC808005F9FFB /* PFDecoder_internal.h in Headers */, + 3B68E5B71DECC32300038DDD /* PFEncoder_internal.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Sources/ParseLiveQuery/Internal/PFEncoder_internal.h b/Sources/ParseLiveQuery/Internal/PFEncoder_internal.h new file mode 100644 index 00000000..d53730be --- /dev/null +++ b/Sources/ParseLiveQuery/Internal/PFEncoder_internal.h @@ -0,0 +1,46 @@ +// +// PFEncoder_internal.h +// ParseLiveQuery +// +// Created by Joe Szymanski on 11/28/16. +// Copyright © 2016 Parse. All rights reserved. +// + +#ifndef PFEncoder_internal_h +#define PFEncoder_internal_h + +#import +#import + +@interface PFEncoder : NSObject + ++ (nonnull instancetype)objectEncoder; + +- (nullable id)encodeObject:(nullable id)object; +- (nullable id)encodeParseObject:(nullable PFObject *)object; + +@end + +/** + Encoding strategy that rejects PFObject. + */ +@interface PFNoObjectEncoder : PFEncoder + +@end + +/** + Encoding strategy that encodes PFObject to PFPointer with objectId or with localId. + */ +@interface PFPointerOrLocalIdObjectEncoder : PFEncoder + +@end + +/** + Encoding strategy that encodes PFObject to PFPointer with objectId and rejects + unsaved PFObject. + */ +@interface PFPointerObjectEncoder : PFPointerOrLocalIdObjectEncoder + +@end + +#endif /* PFEncoder_internal_h */ diff --git a/Sources/ParseLiveQuery/Internal/ParseLiveQuery-Bridging-Header.h b/Sources/ParseLiveQuery/Internal/ParseLiveQuery-Bridging-Header.h index dda5c8e8..c28d23f0 100644 --- a/Sources/ParseLiveQuery/Internal/ParseLiveQuery-Bridging-Header.h +++ b/Sources/ParseLiveQuery/Internal/ParseLiveQuery-Bridging-Header.h @@ -7,3 +7,4 @@ // #import "PFDecoder_internal.h" +#import "PFEncoder_internal.h" diff --git a/Sources/ParseLiveQuery/Internal/QueryEncoder.swift b/Sources/ParseLiveQuery/Internal/QueryEncoder.swift index 9a2b5472..ed0ccb1e 100644 --- a/Sources/ParseLiveQuery/Internal/QueryEncoder.swift +++ b/Sources/ParseLiveQuery/Internal/QueryEncoder.swift @@ -34,6 +34,8 @@ extension Dictionary where Key: ExpressibleByStringLiteral, Value: AnyObject { encodedQueryDictionary[key] = dict.encodedQueryDictionary as? Value } else if let geoPoint = val as? PFGeoPoint { encodedQueryDictionary[key] = geoPoint.encodedDictionary as? Value + } else if let object = val as? PFObject { + encodedQueryDictionary[key] = PFPointerObjectEncoder.object().encode(object) as? Value } else { encodedQueryDictionary[key] = val }