Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add iOS 15 payload additions #185

Merged
merged 7 commits into from
Sep 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ APNS/2 is a go package designed for simple, flexible and fast Apple Push Notific
- Works with go 1.7 and later
- Supports new Apple Token Based Authentication (JWT)
- Supports new iOS 10 features such as Collapse IDs, Subtitles and Mutable Notifications
- Supports new iOS 15 fetaures interruptionLevel and relevanceScore
- Supports persistent connections to APNs
- Supports VoIP/PushKit notifications (iOS 8 and later)
- Modular & easy to use
Expand Down
70 changes: 62 additions & 8 deletions payload/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,42 @@ package payload

import "encoding/json"

// InterruptionLevel defines the value for the payload aps interruption-level
type EInterruptionLevel string

const (
// InterruptionLevelPassive is used to indicate that notification be delivered in a passive manner.
InterruptionLevelPassive EInterruptionLevel = "passive"

// InterruptionLevelActive is used to indicate the importance and delivery timing of a notification.
InterruptionLevelActive EInterruptionLevel = "active"

// InterruptionLevelTimeSensitive is used to indicate the importance and delivery timing of a notification.
InterruptionLevelTimeSensitive EInterruptionLevel = "time-sensitive"

// InterruptionLevelCritical is used to indicate the importance and delivery timing of a notification.
// This interruption level requires an approved entitlement from Apple.
// See: https://developer.apple.com/documentation/usernotifications/unnotificationinterruptionlevel/
InterruptionLevelCritical EInterruptionLevel = "critical"
)

// Payload represents a notification which holds the content that will be
// marshalled as JSON.
type Payload struct {
content map[string]interface{}
}

type aps struct {
Alert interface{} `json:"alert,omitempty"`
Badge interface{} `json:"badge,omitempty"`
Category string `json:"category,omitempty"`
ContentAvailable int `json:"content-available,omitempty"`
MutableContent int `json:"mutable-content,omitempty"`
Sound interface{} `json:"sound,omitempty"`
ThreadID string `json:"thread-id,omitempty"`
URLArgs []string `json:"url-args,omitempty"`
Alert interface{} `json:"alert,omitempty"`
Badge interface{} `json:"badge,omitempty"`
Category string `json:"category,omitempty"`
ContentAvailable int `json:"content-available,omitempty"`
InterruptionLevel EInterruptionLevel `json:"interruption-level,omitempty"`
MutableContent int `json:"mutable-content,omitempty"`
RelevanceScore interface{} `json:"relevance-score,omitempty"`
Sound interface{} `json:"sound,omitempty"`
ThreadID string `json:"thread-id,omitempty"`
URLArgs []string `json:"url-args,omitempty"`
}

type alert struct {
Expand Down Expand Up @@ -324,6 +345,39 @@ func (p *Payload) SoundVolume(volume float32) *Payload {
return p
}

// InterruptionLevel defines the value for the payload aps interruption-level
// This is to indicate the importance and delivery timing of a notification.
// (Using InterruptionLevelCritical requires an approved entitlement from Apple.)
// See: https://developer.apple.com/documentation/usernotifications/unnotificationinterruptionlevel/
//
// {"aps":{"interruption-level":passive}}
func (p *Payload) InterruptionLevel(interruptionLevel EInterruptionLevel) *Payload {
p.aps().InterruptionLevel = interruptionLevel
return p
}

// The relevance score, a number between 0 and 1,
// that the system uses to sort the notifications from your app.
// The highest score gets featured in the notification summary.
// See https://developer.apple.com/documentation/usernotifications/unnotificationcontent/3821031-relevancescore.
//
// {"aps":{"relevance-score":0.1}}
func (p *Payload) RelevanceScore(b float32) *Payload {
p.aps().RelevanceScore = b
return p
}

// Unsets the relevance score
// that the system uses to sort the notifications from your app.
// The highest score gets featured in the notification summary.
// See https://developer.apple.com/documentation/usernotifications/unnotificationcontent/3821031-relevancescore.
//
// {"aps":{"relevance-score":0.1}}
func (p *Payload) UnsetRelevanceScore() *Payload {
p.aps().RelevanceScore = nil
return p
}

// MarshalJSON returns the JSON encoded version of the Payload
func (p *Payload) MarshalJSON() ([]byte, error) {
return json.Marshal(p.content)
Expand Down
46 changes: 44 additions & 2 deletions payload/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,50 @@ func TestAlertSummaryArgCount(t *testing.T) {
assert.Equal(t, `{"aps":{"alert":{"summary-arg-count":3}}}`, string(b))
}

func TestInterruptionLevelPassive(t *testing.T) {
payload := NewPayload().InterruptionLevel(InterruptionLevelPassive)
b, _ := json.Marshal(payload)
assert.Equal(t, `{"aps":{"interruption-level":"passive"}}`, string(b))
}

func TestInterruptionLevelActive(t *testing.T) {
payload := NewPayload().InterruptionLevel(InterruptionLevelActive)
b, _ := json.Marshal(payload)
assert.Equal(t, `{"aps":{"interruption-level":"active"}}`, string(b))
}

func TestInterruptionLevelTimeSensitive(t *testing.T) {
payload := NewPayload().InterruptionLevel(InterruptionLevelTimeSensitive)
b, _ := json.Marshal(payload)
assert.Equal(t, `{"aps":{"interruption-level":"time-sensitive"}}`, string(b))
}

func TestInterruptionLevelCritical(t *testing.T) {
payload := NewPayload().InterruptionLevel(InterruptionLevelCritical)
b, _ := json.Marshal(payload)
assert.Equal(t, `{"aps":{"interruption-level":"critical"}}`, string(b))
}

func TestRelevanceScore(t *testing.T) {
payload := NewPayload().RelevanceScore(0.1)
b, _ := json.Marshal(payload)
assert.Equal(t, `{"aps":{"relevance-score":0.1}}`, string(b))
}

func TestRelevanceScoreZero(t *testing.T) {
payload := NewPayload().RelevanceScore(0)
b, _ := json.Marshal(payload)
assert.Equal(t, `{"aps":{"relevance-score":0}}`, string(b))
}

func TestUnsetRelevanceScore(t *testing.T) {
payload := NewPayload().RelevanceScore(0.1).UnsetRelevanceScore()
b, _ := json.Marshal(payload)
assert.Equal(t, `{"aps":{}}`, string(b))
}

func TestCombined(t *testing.T) {
payload := NewPayload().Alert("hello").Badge(1).Sound("Default.caf").Custom("key", "val")
payload := NewPayload().Alert("hello").Badge(1).Sound("Default.caf").InterruptionLevel(InterruptionLevelActive).RelevanceScore(0.1).Custom("key", "val")
b, _ := json.Marshal(payload)
assert.Equal(t, `{"aps":{"alert":"hello","badge":1,"sound":"Default.caf"},"key":"val"}`, string(b))
assert.Equal(t, `{"aps":{"alert":"hello","badge":1,"interruption-level":"active","relevance-score":0.1,"sound":"Default.caf"},"key":"val"}`, string(b))
}