Skip to content

Commit 95a547d

Browse files
committed
feat: Attempt to retreive full objects on server during POST
1 parent e4d22f7 commit 95a547d

File tree

7 files changed

+197
-14
lines changed

7 files changed

+197
-14
lines changed

CHANGELOG.md

+8-1
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,16 @@
22
# Parse-Swift Changelog
33

44
### main
5-
[Full Changelog](https://github.com/netreconlab/Parse-Swift/compare/5.12.1...main), [Documentation](https://swiftpackageindex.com/netreconlab/Parse-Swift/main/documentation/parseswift)
5+
[Full Changelog](https://github.com/netreconlab/Parse-Swift/compare/5.12.2...main), [Documentation](https://swiftpackageindex.com/netreconlab/Parse-Swift/main/documentation/parseswift)
66
* _Contributing to this repo? Add info about your change here to be included in the next release_
77

8+
### 5.12.2
9+
[Full Changelog](https://github.com/netreconlab/Parse-Swift/compare/5.12.1...5.12.2), [Documentation](https://swiftpackageindex.com/netreconlab/Parse-Swift/5.12.2/documentation/parseswift)
10+
11+
__Fixes__
12+
* When creating objects, if the server does not return the correct full object, attempt to retrieve it before returning to call-site. ([#197](https://github.com/netreconlab/Parse-Swift/pull/197)), thanks to [Corey Baker](https://github.com/cbaker6).
13+
* Fixes an issue when using `mergeable` on `ParseInstallation`'s' and properties that are automatically computed such as `appVersion` are not sent to the server. ([#197](https://github.com/netreconlab/Parse-Swift/pull/197)), thanks to [Corey Baker](https://github.com/cbaker6).
14+
815
### 5.12.1
916
[Full Changelog](https://github.com/netreconlab/Parse-Swift/compare/5.12.0...5.12.1), [Documentation](https://swiftpackageindex.com/netreconlab/Parse-Swift/5.12.1/documentation/parseswift)
1017

Sources/ParseSwift/API/API+Command.swift

+21-1
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,27 @@ internal extension API.Command {
436436
}
437437
let updatedObject = object
438438
let mapper = { @Sendable (data) -> V in
439-
try ParseCoding.jsonDecoder().decode(CreateResponse.self, from: data).apply(to: updatedObject)
439+
do {
440+
// Try to decode CreateResponse, if that doesn't work try Pointer
441+
let savedObject = try ParseCoding.jsonDecoder().decode(
442+
CreateResponse.self,
443+
from: data
444+
).apply(
445+
to: updatedObject
446+
)
447+
return savedObject
448+
} catch let originalError {
449+
do {
450+
let pointer = try ParseCoding.jsonDecoder().decode(
451+
Pointer<V>.self,
452+
from: data
453+
)
454+
let fetchedObject = try await pointer.fetch()
455+
return fetchedObject
456+
} catch {
457+
throw originalError
458+
}
459+
}
440460
}
441461
return API.Command<V, V>(method: .POST,
442462
path: try await object.endpoint(.POST),

Sources/ParseSwift/Objects/ParseInstallation.swift

+25-6
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,25 @@ public extension ParseInstallation {
9595
"_Installation"
9696
}
9797

98+
var mergeable: Self {
99+
guard isSaved,
100+
originalData == nil else {
101+
return self
102+
}
103+
var object = Self()
104+
object.objectId = objectId
105+
object.createdAt = createdAt
106+
object.badge = badge
107+
object.timeZone = timeZone
108+
object.appName = appName
109+
object.appIdentifier = appIdentifier
110+
object.appVersion = appVersion
111+
object.parseVersion = parseVersion
112+
object.localeIdentifier = localeIdentifier
113+
object.originalData = try? ParseCoding.jsonEncoder().encode(self)
114+
return object
115+
}
116+
98117
var endpoint: API.Endpoint {
99118
if let objectId = objectId {
100119
return .installation(objectId: objectId)
@@ -418,7 +437,7 @@ extension ParseInstallation {
418437
return
419438
}
420439
#if !os(Linux) && !os(Android) && !os(Windows)
421-
#if TARGET_OS_MACCATALYST
440+
#if targetEnvironment(macCatalyst)
422441
// If using an Xcode new enough to know about Mac Catalyst:
423442
// Mac Catalyst Apps use a prefix to the bundle ID. This should not be transmitted
424443
// to Parse Server. Catalyst apps should look like iOS apps otherwise
@@ -754,18 +773,18 @@ extension ParseInstallation {
754773
let savedObject = try ParseCoding.jsonDecoder().decode(
755774
CreateResponse.self,
756775
from: data
757-
).apply(to: updatedObject)
758-
776+
).apply(
777+
to: updatedObject
778+
)
759779
return savedObject
760780
} catch let originalError {
761781
do {
762782
let pointer = try ParseCoding.jsonDecoder().decode(
763783
Pointer<Self>.self,
764784
from: data
765785
)
766-
var objectToUpdate = updatedObject
767-
objectToUpdate.objectId = pointer.objectId
768-
return objectToUpdate
786+
let fetchedObject = try await pointer.fetch()
787+
return fetchedObject
769788
} catch {
770789
throw originalError
771790
}

Sources/ParseSwift/Objects/ParseUser.swift

+5-5
Original file line numberDiff line numberDiff line change
@@ -1223,18 +1223,18 @@ extension ParseUser {
12231223
let savedObject = try ParseCoding.jsonDecoder().decode(
12241224
CreateResponse.self,
12251225
from: data
1226-
).apply(to: updatedUser)
1227-
1226+
).apply(
1227+
to: updatedUser
1228+
)
12281229
return savedObject
12291230
} catch let originalError {
12301231
do {
12311232
let pointer = try ParseCoding.jsonDecoder().decode(
12321233
Pointer<Self>.self,
12331234
from: data
12341235
)
1235-
var objectToUpdate = updatedUser
1236-
objectToUpdate.objectId = pointer.objectId
1237-
return objectToUpdate
1236+
let fetchedObject = try await pointer.fetch()
1237+
return fetchedObject
12381238
} catch {
12391239
throw originalError
12401240
}

Sources/ParseSwift/ParseConstants.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import Foundation
1010

1111
enum ParseConstants {
1212
static let sdk = "swift"
13-
static let version = "5.12.1"
13+
static let version = "5.12.2"
1414
static let fileManagementDirectory = "parse/"
1515
static let fileManagementPrivateDocumentsDirectory = "Private Documents/"
1616
static let fileManagementLibraryDirectory = "Library/"

Tests/ParseSwiftTests/ParseInstallationTests.swift

+45
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,51 @@ class ParseInstallationTests: XCTestCase { // swiftlint:disable:this type_body_l
483483
XCTAssertEqual(merged, original)
484484
}
485485

486+
@MainActor
487+
func testMergeableRetainsAutomaticallyComputedProperties() async throws {
488+
var original = try await Installation.current()
489+
original.objectId = "yolo"
490+
original.createdAt = Date()
491+
original.updatedAt = Date()
492+
original.badge = 10
493+
original.deviceToken = "12345"
494+
original.channels = ["halo"]
495+
original.customKey = "newKey"
496+
var acl = ParseACL()
497+
acl.publicRead = true
498+
original.ACL = acl
499+
500+
// These properties should not be nil before merge
501+
XCTAssertNotNil(original.customKey)
502+
XCTAssertNotNil(original.deviceType)
503+
XCTAssertNotNil(original.deviceToken)
504+
XCTAssertNotNil(original.channels)
505+
XCTAssertNotNil(original.installationId)
506+
XCTAssertNotNil(original.ACL)
507+
XCTAssertNotNil(original.updatedAt)
508+
509+
let mergeable = original.mergeable
510+
511+
// These should always remain in the merge
512+
XCTAssertEqual(original.badge, mergeable.badge)
513+
XCTAssertEqual(original.timeZone, mergeable.timeZone)
514+
XCTAssertEqual(original.appName, mergeable.appName)
515+
XCTAssertEqual(original.appVersion, mergeable.appVersion)
516+
XCTAssertEqual(original.appIdentifier, mergeable.appIdentifier)
517+
XCTAssertEqual(original.parseVersion, mergeable.parseVersion)
518+
XCTAssertEqual(original.localeIdentifier, mergeable.localeIdentifier)
519+
XCTAssertEqual(original.createdAt, mergeable.createdAt)
520+
521+
// All other properties should be nil
522+
XCTAssertNil(mergeable.customKey)
523+
XCTAssertNil(mergeable.deviceType)
524+
XCTAssertNil(mergeable.deviceToken)
525+
XCTAssertNil(mergeable.channels)
526+
XCTAssertNil(mergeable.installationId)
527+
XCTAssertNil(mergeable.ACL)
528+
XCTAssertNil(mergeable.updatedAt)
529+
}
530+
486531
@MainActor
487532
func testMergeDifferentObjectId() async throws {
488533
var installation = Installation()

Tests/ParseSwiftTests/ParseObjectCustomObjectIdTests.swift

+92
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,36 @@ class ParseObjectCustomObjectIdTests: XCTestCase { // swiftlint:disable:this typ
661661
}
662662
}
663663

664+
func testCreateServerReturnsPointer() async throws {
665+
var score = GameScore(points: 10)
666+
score.objectId = "yarr"
667+
668+
var scoreOnServer = try score.toPointer()
669+
670+
let encoded: Data!
671+
do {
672+
encoded = try ParseCoding.jsonEncoder().encode(scoreOnServer)
673+
// Get dates in correct format from ParseDecoding strategy
674+
scoreOnServer = try score.getDecoder().decode(
675+
Pointer<GameScore>.self,
676+
from: encoded
677+
)
678+
} catch {
679+
XCTFail("Should encode/decode. Error \(error)")
680+
return
681+
}
682+
683+
MockURLProtocol.mockRequests { _ in
684+
return MockURLResponse(data: encoded, statusCode: 200)
685+
}
686+
do {
687+
let saved = try await score.create()
688+
XCTAssertTrue(saved.objectId == scoreOnServer.objectId)
689+
} catch {
690+
XCTFail(error.localizedDescription)
691+
}
692+
}
693+
664694
func testSaveNoObjectId() async throws {
665695
let score = GameScore(points: 10)
666696
do {
@@ -1276,6 +1306,37 @@ class ParseObjectCustomObjectIdTests: XCTestCase { // swiftlint:disable:this typ
12761306
}
12771307
}
12781308

1309+
func testUserCreateServerReturnsPointer() async throws {
1310+
var user = User()
1311+
user.objectId = "yarr"
1312+
user.ACL = nil
1313+
1314+
var userOnServer = try user.toPointer()
1315+
1316+
let encoded: Data!
1317+
do {
1318+
encoded = try ParseCoding.jsonEncoder().encode(userOnServer)
1319+
// Get dates in correct format from ParseDecoding strategy
1320+
userOnServer = try user.getDecoder().decode(
1321+
Pointer<User>.self,
1322+
from: encoded
1323+
)
1324+
} catch {
1325+
XCTFail("Should encode/decode. Error \(error)")
1326+
return
1327+
}
1328+
1329+
MockURLProtocol.mockRequests { _ in
1330+
return MockURLResponse(data: encoded, statusCode: 200)
1331+
}
1332+
do {
1333+
let saved = try await user.create()
1334+
XCTAssertTrue(saved.objectId == userOnServer.objectId)
1335+
} catch {
1336+
XCTFail(error.localizedDescription)
1337+
}
1338+
}
1339+
12791340
func testUserSaveNoObjectId() async throws {
12801341
let score = GameScore(points: 10)
12811342
do {
@@ -1829,6 +1890,37 @@ class ParseObjectCustomObjectIdTests: XCTestCase { // swiftlint:disable:this typ
18291890
}
18301891
}
18311892

1893+
func testInstallationCreateServerReturnsPointer() async throws {
1894+
var installation = Installation()
1895+
installation.objectId = "yarr"
1896+
installation.ACL = nil
1897+
1898+
var installationOnServer = try installation.toPointer()
1899+
1900+
let encoded: Data!
1901+
do {
1902+
encoded = try ParseCoding.jsonEncoder().encode(installationOnServer)
1903+
// Get dates in correct format from ParseDecoding strategy
1904+
installationOnServer = try installation.getDecoder().decode(
1905+
Pointer<Installation>.self,
1906+
from: encoded
1907+
)
1908+
} catch {
1909+
XCTFail("Should encode/decode. Error \(error)")
1910+
return
1911+
}
1912+
1913+
MockURLProtocol.mockRequests { _ in
1914+
return MockURLResponse(data: encoded, statusCode: 200)
1915+
}
1916+
do {
1917+
let saved = try await installation.create()
1918+
XCTAssertTrue(saved.objectId == installationOnServer.objectId)
1919+
} catch {
1920+
XCTFail(error.localizedDescription)
1921+
}
1922+
}
1923+
18321924
func testInstallationSaveNoObjectId() async throws {
18331925
let score = GameScore(points: 10)
18341926
do {

0 commit comments

Comments
 (0)