Skip to content

Commit 9c1d7c6

Browse files
Add iOS 15 payload additions (#185)
* Add iOS 15 payload additions - Add interruption-level to payload interruption-level options: - passive - active (default if none is passed to apns) - time-sensitive - critical (requires Apple entitlement) - Add relevance-score to payload relevance-score is a number between 0 and 1 The highest score gets featured in the notification summary. * Update readme re iOS 15 features * Fix documentation typo Note that at the time of writing [Apple docs](https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/generating_a_remote_notification#2943360) have a typo, showing `time-senstive` as opposed to `time-sensitive`. Testing has shown that the correct spelling `time-sensitive` does indeed work. * Update builder.go Alphabetically order keys in struct as per @Singwai suggestion. * Allow relevance-score to be set to zero. * Updated to single InterruptionLevel function Co-authored-by: Chris Haines <chris.haines@braze.com>
1 parent 24a351f commit 9c1d7c6

File tree

3 files changed

+107
-10
lines changed

3 files changed

+107
-10
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ APNS/2 is a go package designed for simple, flexible and fast Apple Push Notific
1111
- Works with go 1.7 and later
1212
- Supports new Apple Token Based Authentication (JWT)
1313
- Supports new iOS 10 features such as Collapse IDs, Subtitles and Mutable Notifications
14+
- Supports new iOS 15 fetaures interruptionLevel and relevanceScore
1415
- Supports persistent connections to APNs
1516
- Supports VoIP/PushKit notifications (iOS 8 and later)
1617
- Modular & easy to use

payload/builder.go

Lines changed: 62 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,42 @@ package payload
44

55
import "encoding/json"
66

7+
// InterruptionLevel defines the value for the payload aps interruption-level
8+
type EInterruptionLevel string
9+
10+
const (
11+
// InterruptionLevelPassive is used to indicate that notification be delivered in a passive manner.
12+
InterruptionLevelPassive EInterruptionLevel = "passive"
13+
14+
// InterruptionLevelActive is used to indicate the importance and delivery timing of a notification.
15+
InterruptionLevelActive EInterruptionLevel = "active"
16+
17+
// InterruptionLevelTimeSensitive is used to indicate the importance and delivery timing of a notification.
18+
InterruptionLevelTimeSensitive EInterruptionLevel = "time-sensitive"
19+
20+
// InterruptionLevelCritical is used to indicate the importance and delivery timing of a notification.
21+
// This interruption level requires an approved entitlement from Apple.
22+
// See: https://developer.apple.com/documentation/usernotifications/unnotificationinterruptionlevel/
23+
InterruptionLevelCritical EInterruptionLevel = "critical"
24+
)
25+
726
// Payload represents a notification which holds the content that will be
827
// marshalled as JSON.
928
type Payload struct {
1029
content map[string]interface{}
1130
}
1231

1332
type aps struct {
14-
Alert interface{} `json:"alert,omitempty"`
15-
Badge interface{} `json:"badge,omitempty"`
16-
Category string `json:"category,omitempty"`
17-
ContentAvailable int `json:"content-available,omitempty"`
18-
MutableContent int `json:"mutable-content,omitempty"`
19-
Sound interface{} `json:"sound,omitempty"`
20-
ThreadID string `json:"thread-id,omitempty"`
21-
URLArgs []string `json:"url-args,omitempty"`
33+
Alert interface{} `json:"alert,omitempty"`
34+
Badge interface{} `json:"badge,omitempty"`
35+
Category string `json:"category,omitempty"`
36+
ContentAvailable int `json:"content-available,omitempty"`
37+
InterruptionLevel EInterruptionLevel `json:"interruption-level,omitempty"`
38+
MutableContent int `json:"mutable-content,omitempty"`
39+
RelevanceScore interface{} `json:"relevance-score,omitempty"`
40+
Sound interface{} `json:"sound,omitempty"`
41+
ThreadID string `json:"thread-id,omitempty"`
42+
URLArgs []string `json:"url-args,omitempty"`
2243
}
2344

2445
type alert struct {
@@ -324,6 +345,39 @@ func (p *Payload) SoundVolume(volume float32) *Payload {
324345
return p
325346
}
326347

348+
// InterruptionLevel defines the value for the payload aps interruption-level
349+
// This is to indicate the importance and delivery timing of a notification.
350+
// (Using InterruptionLevelCritical requires an approved entitlement from Apple.)
351+
// See: https://developer.apple.com/documentation/usernotifications/unnotificationinterruptionlevel/
352+
//
353+
// {"aps":{"interruption-level":passive}}
354+
func (p *Payload) InterruptionLevel(interruptionLevel EInterruptionLevel) *Payload {
355+
p.aps().InterruptionLevel = interruptionLevel
356+
return p
357+
}
358+
359+
// The relevance score, a number between 0 and 1,
360+
// that the system uses to sort the notifications from your app.
361+
// The highest score gets featured in the notification summary.
362+
// See https://developer.apple.com/documentation/usernotifications/unnotificationcontent/3821031-relevancescore.
363+
//
364+
// {"aps":{"relevance-score":0.1}}
365+
func (p *Payload) RelevanceScore(b float32) *Payload {
366+
p.aps().RelevanceScore = b
367+
return p
368+
}
369+
370+
// Unsets the relevance score
371+
// that the system uses to sort the notifications from your app.
372+
// The highest score gets featured in the notification summary.
373+
// See https://developer.apple.com/documentation/usernotifications/unnotificationcontent/3821031-relevancescore.
374+
//
375+
// {"aps":{"relevance-score":0.1}}
376+
func (p *Payload) UnsetRelevanceScore() *Payload {
377+
p.aps().RelevanceScore = nil
378+
return p
379+
}
380+
327381
// MarshalJSON returns the JSON encoded version of the Payload
328382
func (p *Payload) MarshalJSON() ([]byte, error) {
329383
return json.Marshal(p.content)

payload/builder_test.go

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,8 +188,50 @@ func TestAlertSummaryArgCount(t *testing.T) {
188188
assert.Equal(t, `{"aps":{"alert":{"summary-arg-count":3}}}`, string(b))
189189
}
190190

191+
func TestInterruptionLevelPassive(t *testing.T) {
192+
payload := NewPayload().InterruptionLevel(InterruptionLevelPassive)
193+
b, _ := json.Marshal(payload)
194+
assert.Equal(t, `{"aps":{"interruption-level":"passive"}}`, string(b))
195+
}
196+
197+
func TestInterruptionLevelActive(t *testing.T) {
198+
payload := NewPayload().InterruptionLevel(InterruptionLevelActive)
199+
b, _ := json.Marshal(payload)
200+
assert.Equal(t, `{"aps":{"interruption-level":"active"}}`, string(b))
201+
}
202+
203+
func TestInterruptionLevelTimeSensitive(t *testing.T) {
204+
payload := NewPayload().InterruptionLevel(InterruptionLevelTimeSensitive)
205+
b, _ := json.Marshal(payload)
206+
assert.Equal(t, `{"aps":{"interruption-level":"time-sensitive"}}`, string(b))
207+
}
208+
209+
func TestInterruptionLevelCritical(t *testing.T) {
210+
payload := NewPayload().InterruptionLevel(InterruptionLevelCritical)
211+
b, _ := json.Marshal(payload)
212+
assert.Equal(t, `{"aps":{"interruption-level":"critical"}}`, string(b))
213+
}
214+
215+
func TestRelevanceScore(t *testing.T) {
216+
payload := NewPayload().RelevanceScore(0.1)
217+
b, _ := json.Marshal(payload)
218+
assert.Equal(t, `{"aps":{"relevance-score":0.1}}`, string(b))
219+
}
220+
221+
func TestRelevanceScoreZero(t *testing.T) {
222+
payload := NewPayload().RelevanceScore(0)
223+
b, _ := json.Marshal(payload)
224+
assert.Equal(t, `{"aps":{"relevance-score":0}}`, string(b))
225+
}
226+
227+
func TestUnsetRelevanceScore(t *testing.T) {
228+
payload := NewPayload().RelevanceScore(0.1).UnsetRelevanceScore()
229+
b, _ := json.Marshal(payload)
230+
assert.Equal(t, `{"aps":{}}`, string(b))
231+
}
232+
191233
func TestCombined(t *testing.T) {
192-
payload := NewPayload().Alert("hello").Badge(1).Sound("Default.caf").Custom("key", "val")
234+
payload := NewPayload().Alert("hello").Badge(1).Sound("Default.caf").InterruptionLevel(InterruptionLevelActive).RelevanceScore(0.1).Custom("key", "val")
193235
b, _ := json.Marshal(payload)
194-
assert.Equal(t, `{"aps":{"alert":"hello","badge":1,"sound":"Default.caf"},"key":"val"}`, string(b))
236+
assert.Equal(t, `{"aps":{"alert":"hello","badge":1,"interruption-level":"active","relevance-score":0.1,"sound":"Default.caf"},"key":"val"}`, string(b))
195237
}

0 commit comments

Comments
 (0)