Skip to content

Commit 51a44ef

Browse files
authored
feat: add ParsePush notifications (#371)
* doc nits * remove force unwrap * make query codable * make QueryConstraint Codable * make QueryWhere and QueryConstraint decodable * improve ParsePushStatus * Add ParsePush * fix test * Improve push payload * Add channels to push * use query directly. * nits * Update playground * fix sending push status * still a bug decoding ParsePushStatus * decode around server encoding bugs * add ParsePushApplePayloadFCM * refactor to ParsePushPayloadAny * Fix Query.Order decoder * update spi file * add new FCM * add complete FCM legacy support * doc nits * add changelog * initial tests * willSet not working * cover PushStatus * remove old tests * additions * improve signature of ParsePushStatus * more PushStatus coverage * add more tests * add missing files * coverage * more coverage * nits
1 parent c32e269 commit 51a44ef

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+3643
-159
lines changed

.spi.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ builder:
33
configs:
44
- platform: ios
55
scheme: "ParseSwift (iOS)"
6-
documentation_targets: ["ParseSwift (iOS)"]
76
- platform: macos-xcodebuild
87
scheme: "ParseSwift (macOS)"
8+
documentation_targets: ["ParseSwift (iOS)", "ParseSwift (macOS)", "ParseSwift (tvOS)", "ParseSwift (watchOS)"]
99
- platform: macos-xcodebuild-arm
1010
scheme: "ParseSwift (macOS)"
1111
- platform: tvos

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* _Contributing to this repo? Add info about your change here to be included in the next release_
77

88
__New features__
9+
- Add the ability to send APN and FCM push notifications. Also adds the ability to query _PushStatus ([#371](https://github.com/parse-community/Parse-Swift/pull/371)), thanks to [Corey Baker](https://github.com/cbaker6).
910
- Add ParseSchema, ParseCLP, and ParseFieldOptions. Should only be used when using the Swift SDK on a secured server ([#370](https://github.com/parse-community/Parse-Swift/pull/370)), thanks to [Corey Baker](https://github.com/cbaker6).
1011

1112
### 4.5.0

ParseSwift.playground/Pages/20 - Schema.xcplaygroundpage/Contents.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,4 +251,5 @@ gameScoreSchema.purge { result in
251251
}
252252
}
253253

254+
PlaygroundPage.current.finishExecution()
254255
//: [Next](@next)
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
//: [Previous](@previous)
2+
3+
import PlaygroundSupport
4+
import Foundation
5+
import ParseSwift
6+
7+
PlaygroundPage.current.needsIndefiniteExecution = true
8+
initializeParse()
9+
10+
struct Installation: ParseInstallation {
11+
//: These are required by `ParseObject`.
12+
var objectId: String?
13+
var createdAt: Date?
14+
var updatedAt: Date?
15+
var ACL: ParseACL?
16+
var originalData: Data?
17+
18+
//: These are required by `ParseInstallation`.
19+
var installationId: String?
20+
var deviceType: String?
21+
var deviceToken: String?
22+
var badge: Int?
23+
var timeZone: String?
24+
var channels: [String]?
25+
var appName: String?
26+
var appIdentifier: String?
27+
var appVersion: String?
28+
var parseVersion: String?
29+
var localeIdentifier: String?
30+
31+
//: Your custom keys
32+
var customKey: String?
33+
34+
//: Implement your own version of merge
35+
func merge(with object: Self) throws -> Self {
36+
var updated = try mergeParse(with: object)
37+
if updated.shouldRestoreKey(\.customKey,
38+
original: object) {
39+
updated.customKey = object.customKey
40+
}
41+
return updated
42+
}
43+
}
44+
45+
/**
46+
We will begin by creating the payload information we want to
47+
send in the push notification.
48+
*/
49+
let helloAlert = ParsePushAppleAlert(body: "Hello from ParseSwift!")
50+
let applePayload = ParsePushPayloadApple(alert: helloAlert)
51+
.setBadge(1)
52+
53+
/*:
54+
We now crate a query where the `objectId`
55+
is not null or undefined.
56+
*/
57+
let installationQuery = Installation.query(isNotNull(key: "objectId"))
58+
59+
//: Now create a new push notification using the payload and query.
60+
let push = ParsePush(payload: applePayload, query: installationQuery)
61+
62+
//: Creating this property to use later in the playground.
63+
var pushStatusId = ""
64+
65+
//: You can send the push notification whenever you are ready.
66+
push.send { result in
67+
switch result {
68+
case .success(let statusId):
69+
print("The push was created with id: \"\(statusId)\"")
70+
//: Update the stored property with the lastest status id.
71+
pushStatusId = statusId
72+
case .failure(let error):
73+
print("Couldn't create push: \(error)")
74+
}
75+
}
76+
77+
//: You can fetch the status of notificaiton if you know it's id.
78+
push.fetchStatus(pushStatusId) { result in
79+
switch result {
80+
case .success(let pushStatus):
81+
print("The push status is: \"\(pushStatus)\"")
82+
case .failure(let error):
83+
print("Couldn't fetch push status: \(error)")
84+
}
85+
}
86+
87+
/*:
88+
Lets create another Push, this time by incrementing the badge
89+
and using channels instead of a query.
90+
*/
91+
let helloAgainAlert = ParsePushAppleAlert(body: "Hello from ParseSwift again!")
92+
let applePayload2 = ParsePushPayloadApple(alert: helloAgainAlert)
93+
.incrementBadge()
94+
95+
var push2 = ParsePush(payload: applePayload2)
96+
//: Set all channels the notificatioin should be published to.
97+
push2.channels = Set(["newDevices"])
98+
99+
//: You can send the push notification whenever you are ready.
100+
push2.send { result in
101+
switch result {
102+
case .success(let statusId):
103+
print("The push was created with id: \"\(statusId)\"")
104+
//: Update the stored property with the lastest status id.
105+
pushStatusId = statusId
106+
case .failure(let error):
107+
print("Couldn't create push: \(error)")
108+
}
109+
}
110+
111+
/*:
112+
Similar to before, you can fetch the status of notificaiton
113+
if you know the id.
114+
*/
115+
push2.fetchStatus(pushStatusId) { result in
116+
switch result {
117+
case .success(let pushStatus):
118+
print("The push status is: \"\(pushStatus)\"")
119+
case .failure(let error):
120+
print("Couldn't fetch push status: \(error)")
121+
}
122+
}
123+
124+
/*:
125+
You can also send push notifications using Firebase Cloud Messanger.
126+
*/
127+
let helloNotification = ParsePushFirebaseNotification(body: "Hello from ParseSwift using FCM!")
128+
let firebasePayload = ParsePushPayloadFirebase(notification: helloNotification)
129+
130+
let push3 = ParsePush(payload: firebasePayload, query: installationQuery)
131+
132+
//: You can send the push notification whenever you are ready.
133+
push3.send { result in
134+
switch result {
135+
case .success(let statusId):
136+
print("The Firebase push was created with id: \"\(statusId)\"")
137+
//: Update the stored property with the lastest status id.
138+
pushStatusId = statusId
139+
case .failure(let error):
140+
print("Couldn't create push: \(error)")
141+
}
142+
}
143+
144+
/*:
145+
Similar to before, you can fetch the status of notificaiton
146+
if you know the id.
147+
*/
148+
push3.fetchStatus(pushStatusId) { result in
149+
switch result {
150+
case .success(let pushStatus):
151+
print("The Firebase push status is: \"\(pushStatus)\"")
152+
case .failure(let error):
153+
print("Couldn't fetch push status: \(error)")
154+
}
155+
}
156+
157+
/*:
158+
If you have a mixed push environment and are querying
159+
multiple ParsePushStatus's you will can use the any
160+
payload, `ParsePushPayloadAny`.
161+
*/
162+
let query = ParsePushStatus<ParsePushPayloadAny>
163+
.query(isNotNull(key: "objectId"))
164+
165+
/*:
166+
Be sure to add the `.userMasterKey option when doing
167+
anything with `ParsePushStatus` directly.
168+
*/
169+
query.findAll(options: [.useMasterKey]) { result in
170+
switch result {
171+
case .success(let pushStatus):
172+
print("All matching statuses: \"\(pushStatus)\"")
173+
case .failure(let error):
174+
print("Couldn't perform query: \(error)")
175+
}
176+
}
177+
178+
PlaygroundPage.current.finishExecution()
179+
//: [Next](@next)

ParseSwift.playground/Pages/6 - Installation.xcplaygroundpage/Contents.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,14 @@ currentInstallation?.save { results in
6565
}
6666
}
6767

68-
/*: Update your `ParseInstallation` `customKey` value.
68+
/*: Update your `ParseInstallation` `customKey` and `channels` values.
6969
Performs work on background queue and returns to designated on
7070
designated callbackQueue. If no callbackQueue is specified it
7171
returns to main queue.
7272
*/
7373
var installationToUpdate = Installation.current?.mergeable
7474
installationToUpdate?.customKey = "myCustomInstallationKey2"
75+
installationToUpdate?.channels = ["newDevices"]
7576
installationToUpdate?.save { results in
7677

7778
switch results {

ParseSwift.playground/contents.xcplayground

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,7 @@
2020
<page name='17 - SwiftUI - Finding Objects'/>
2121
<page name='18 - SwiftUI - Finding Objects With Custom ViewModel'/>
2222
<page name='19 - SwiftUI - LiveQuery'/>
23+
<page name='20 - Schema'/>
24+
<page name='21 - Push Notifications'/>
2325
</pages>
2426
</playground>

0 commit comments

Comments
 (0)