Skip to content

Commit 4f3224a

Browse files
docs: Document testing push notifications on iOS Simulator
1 parent 948e51a commit 4f3224a

File tree

1 file changed

+238
-0
lines changed

1 file changed

+238
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
# Testing Push Notifications on iOS Simulator
2+
3+
For documentation on testing push notifications on Android or a real
4+
iOS Device, see https://github.com/zulip/zulip-mobile/blob/main/docs/howto/push-notifications.md
5+
6+
This doc describes how to test client side changes on iOS Simulator.
7+
It will demonstrate how to use APNs payloads the server sends to
8+
Apple's Push Notification service to show notifications on iOS
9+
Simulator.
10+
11+
## 1. Setup dev server
12+
13+
[Follow the steps in this section to setup a development server, and
14+
register the development server to the production bouncer](https://github.com/zulip/zulip-mobile/blob/main/docs/howto/push-notifications.md#server).
15+
16+
## 2. (Optional) Setup the dev user to receive mobile notifications.
17+
18+
_Skip to step N in which you'll use pre-forged notification payloads,
19+
these intermediate steps records how to get those payloads, which may
20+
be useful in future._
21+
22+
We'll use the devlogin user `[email protected]` to test notifications,
23+
log in to that user by going to `/devlogin` on that server on Web.
24+
25+
And then follow the steps [here](https://zulip.com/help/mobile-notifications)
26+
to enable Mobile Notifications for "Channels".
27+
28+
## 3. (Optional) Login to the dev user on zulip-flutter.
29+
30+
<!-- TODO(405) Guide to use the new devlogin page instead -->
31+
32+
To login to this user in the Flutter app, you'll need the password
33+
that was generated by the development server. You can print the
34+
password by running this command inside your `vagrant ssh` shell:
35+
```
36+
$ ./manage.py print_initial_password [email protected]
37+
```
38+
39+
Then run the app on the iOS Simulator, accept the permission to
40+
receive push notifications, and then login to the dev user
41+
42+
43+
## 4. (Optional) Edit the server code to log the notification payload.
44+
45+
We need to retrieve the APNs payload the server generates and sends
46+
to the bouncer. To do that we can add a log statement after the
47+
server completes generating the APNs in `zerver/lib/push_notifications.py`:
48+
49+
```diff
50+
apns_payload = get_message_payload_apns(
51+
user_profile,
52+
message,
53+
trigger,
54+
mentioned_user_group_id,
55+
mentioned_user_group_name,
56+
can_access_sender,
57+
)
58+
gcm_payload, gcm_options = get_message_payload_gcm(
59+
user_profile, message, mentioned_user_group_id, mentioned_user_group_name, can_access_sender
60+
)
61+
logger.info("Sending push notifications to mobile clients for user %s", user_profile_id)
62+
+ logger.info("APNS payload %s", orjson.dumps(apns_payload))
63+
64+
android_devices = list(
65+
PushDeviceToken.objects.filter(user=user_profile, kind=PushDeviceToken.FCM).order_by("id")
66+
```
67+
68+
## 5. (Optional) Send messages to the dev user
69+
70+
To generate notifications to the dev user `[email protected]` we need to
71+
send messages from another user. For a variety of different types of
72+
payloads try sending a message in a topic, a message in a group DM,
73+
and one in one-one DM. Then look for the payloads in the server logs
74+
by searching for "APNS payload".
75+
76+
The logged payload JSON will not have a root "aps" key, to fix that,
77+
run the payload through the following command:
78+
79+
```shell-session
80+
$ echo '{"alert":{"title": ...' | jq '{aps: .}'
81+
```
82+
83+
## 6. Push APNs payload to iOS Simulator
84+
85+
_If you skipped steps 2-5, you'll need pre-forged APNs payloads for
86+
existing messages in a default development server messages for the
87+
88+
89+
<details>
90+
<summary>Preforged payload: dm.json</summary>
91+
92+
```json
93+
{
94+
"aps": {
95+
"alert": {
96+
"title": "Zoe",
97+
"subtitle": "",
98+
"body": "But wouldn't that show you contextually who is in the audience before you have to open the compose box?"
99+
},
100+
"sound": "default",
101+
"badge": 0,
102+
"custom": {
103+
"zulip": {
104+
"server": "zulipdev.com:9991",
105+
"realm_id": 2,
106+
"realm_uri": "http://localhost:9991",
107+
"realm_url": "http://localhost:9991",
108+
"realm_name": "Zulip Dev",
109+
"user_id": 11,
110+
"sender_id": 7,
111+
"sender_email": "[email protected]",
112+
"time": 1740890583,
113+
"recipient_type": "private",
114+
"message_ids": [
115+
87
116+
]
117+
}
118+
}
119+
}
120+
}
121+
```
122+
123+
</details>
124+
125+
<details>
126+
<summary>Preforged payload: group_dm.json</summary>
127+
128+
```json
129+
{
130+
"aps": {
131+
"alert": {
132+
"title": "Othello, the Moor of Venice, Polonius (guest), Iago",
133+
"subtitle": "Othello, the Moor of Venice:",
134+
"body": "Sit down awhile; And let us once again assail your ears, That are so fortified against our story What we have two nights seen."
135+
},
136+
"sound": "default",
137+
"badge": 0,
138+
"custom": {
139+
"zulip": {
140+
"server": "zulipdev.com:9991",
141+
"realm_id": 2,
142+
"realm_uri": "http://localhost:9991",
143+
"realm_url": "http://localhost:9991",
144+
"realm_name": "Zulip Dev",
145+
"user_id": 11,
146+
"sender_id": 12,
147+
"sender_email": "[email protected]",
148+
"time": 1740533641,
149+
"recipient_type": "private",
150+
"pm_users": "11,12,13",
151+
"message_ids": [
152+
17
153+
]
154+
}
155+
}
156+
}
157+
}
158+
```
159+
160+
</details>
161+
162+
<details>
163+
<summary>Preforged payload: stream.json</summary>
164+
165+
```json
166+
{
167+
"aps": {
168+
"alert": {
169+
"title": "#devel > plotter",
170+
"subtitle": "Desdemona:",
171+
"body": "Despite the fact that such a claim at first glance seems counterintuitive, it is derived from known results. Electrical engineering follows a cycle of four phases: location, refinement, visualization, and evaluation."
172+
},
173+
"sound": "default",
174+
"badge": 0,
175+
"custom": {
176+
"zulip": {
177+
"server": "zulipdev.com:9991",
178+
"realm_id": 2,
179+
"realm_uri": "http://localhost:9991",
180+
"realm_url": "http://localhost:9991",
181+
"realm_name": "Zulip Dev",
182+
"user_id": 11,
183+
"sender_id": 9,
184+
"sender_email": "[email protected]",
185+
"time": 1740558997,
186+
"recipient_type": "stream",
187+
"stream": "devel",
188+
"stream_id": 11,
189+
"topic": "plotter",
190+
"message_ids": [
191+
40
192+
]
193+
}
194+
}
195+
}
196+
}
197+
```
198+
199+
</details>
200+
201+
To receive a notification on the iOS Simulator, we need to push
202+
the APNs payload to the specific running iOS Simulator by using it's
203+
device ID, you can get the device ID by running the following command:
204+
205+
```shell-session
206+
$ xcrun simctl list 'devices' 'booted'
207+
```
208+
209+
<details>
210+
<summary>Example output:</summary>
211+
212+
```shell-session
213+
$ xcrun simctl list 'devices' 'booted'
214+
== Devices ==
215+
-- iOS 18.3 --
216+
iPhone 16 Pro (90CC33B2-679B-4053-B380-7B986A29F28C) (Booted)
217+
```
218+
219+
</details>
220+
221+
And then push the payload using the following command:
222+
223+
```shell-session
224+
$ xcrun simctl push [device-id] com.zulip.flutter [payload json path]
225+
```
226+
227+
<details>
228+
<summary>Example output:</summary>
229+
230+
```shell-session
231+
$ xcrun simctl push 90CC33B2-679B-4053-B380-7B986A29F28C com.zulip.flutter ./dm.json
232+
Notification sent to 'com.zulip.flutter'
233+
```
234+
235+
</details>
236+
237+
Now, on the iOS Simulator you should have a notification and tapping
238+
on it should route to the specific conversation.

0 commit comments

Comments
 (0)