Skip to content

Commit 2178fe6

Browse files
docs: Document testing push notifications on iOS Simulator
1 parent 031c323 commit 2178fe6

File tree

1 file changed

+233
-0
lines changed

1 file changed

+233
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
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 6 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 have different structure than what an
77+
iOS device actually receives, to fix that, run the payload through
78+
the following command:
79+
80+
```shell-session
81+
$ echo '{"alert":{"title": ...' | jq '{aps: {alert: .alert, sound: .sound, badge: .badge}, zulip: .custom.zulip}'
82+
```
83+
84+
## 6. Push APNs payload to iOS Simulator
85+
86+
_If you skipped steps 2-5, you'll need pre-forged APNs payloads for
87+
existing messages in a default development server messages for the
88+
89+
90+
<details>
91+
<summary>Preforged payload: dm.json</summary>
92+
93+
```json
94+
{
95+
"aps": {
96+
"alert": {
97+
"title": "Zoe",
98+
"subtitle": "",
99+
"body": "But wouldn't that show you contextually who is in the audience before you have to open the compose box?"
100+
},
101+
"sound": "default",
102+
"badge": 0,
103+
},
104+
"zulip": {
105+
"server": "zulipdev.com:9991",
106+
"realm_id": 2,
107+
"realm_uri": "http://localhost:9991",
108+
"realm_url": "http://localhost:9991",
109+
"realm_name": "Zulip Dev",
110+
"user_id": 11,
111+
"sender_id": 7,
112+
"sender_email": "[email protected]",
113+
"time": 1740890583,
114+
"recipient_type": "private",
115+
"message_ids": [
116+
87
117+
]
118+
}
119+
}
120+
```
121+
122+
</details>
123+
124+
<details>
125+
<summary>Preforged payload: group_dm.json</summary>
126+
127+
```json
128+
{
129+
"aps": {
130+
"alert": {
131+
"title": "Othello, the Moor of Venice, Polonius (guest), Iago",
132+
"subtitle": "Othello, the Moor of Venice:",
133+
"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."
134+
},
135+
"sound": "default",
136+
"badge": 0,
137+
},
138+
"zulip": {
139+
"server": "zulipdev.com:9991",
140+
"realm_id": 2,
141+
"realm_uri": "http://localhost:9991",
142+
"realm_url": "http://localhost:9991",
143+
"realm_name": "Zulip Dev",
144+
"user_id": 11,
145+
"sender_id": 12,
146+
"sender_email": "[email protected]",
147+
"time": 1740533641,
148+
"recipient_type": "private",
149+
"pm_users": "11,12,13",
150+
"message_ids": [
151+
17
152+
]
153+
}
154+
}
155+
```
156+
157+
</details>
158+
159+
<details>
160+
<summary>Preforged payload: stream.json</summary>
161+
162+
```json
163+
{
164+
"aps": {
165+
"alert": {
166+
"title": "#devel > plotter",
167+
"subtitle": "Desdemona:",
168+
"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."
169+
},
170+
"sound": "default",
171+
"badge": 0,
172+
},
173+
"zulip": {
174+
"server": "zulipdev.com:9991",
175+
"realm_id": 2,
176+
"realm_uri": "http://localhost:9991",
177+
"realm_url": "http://localhost:9991",
178+
"realm_name": "Zulip Dev",
179+
"user_id": 11,
180+
"sender_id": 9,
181+
"sender_email": "[email protected]",
182+
"time": 1740558997,
183+
"recipient_type": "stream",
184+
"stream": "devel",
185+
"stream_id": 11,
186+
"topic": "plotter",
187+
"message_ids": [
188+
40
189+
]
190+
}
191+
}
192+
```
193+
194+
</details>
195+
196+
To receive a notification on the iOS Simulator, we need to push
197+
the APNs payload to the specific running iOS Simulator by using it's
198+
device ID, you can get the device ID by running the following command:
199+
200+
```shell-session
201+
$ xcrun simctl list 'devices' 'booted'
202+
```
203+
204+
<details>
205+
<summary>Example output:</summary>
206+
207+
```shell-session
208+
$ xcrun simctl list 'devices' 'booted'
209+
== Devices ==
210+
-- iOS 18.3 --
211+
iPhone 16 Pro (90CC33B2-679B-4053-B380-7B986A29F28C) (Booted)
212+
```
213+
214+
</details>
215+
216+
And then push the payload using the following command:
217+
218+
```shell-session
219+
$ xcrun simctl push [device-id] com.zulip.flutter [payload json path]
220+
```
221+
222+
<details>
223+
<summary>Example output:</summary>
224+
225+
```shell-session
226+
$ xcrun simctl push 90CC33B2-679B-4053-B380-7B986A29F28C com.zulip.flutter ./dm.json
227+
Notification sent to 'com.zulip.flutter'
228+
```
229+
230+
</details>
231+
232+
Now, on the iOS Simulator you should have a notification and tapping
233+
on it should route to the specific conversation.

0 commit comments

Comments
 (0)