Skip to content

Commit 233c8cb

Browse files
author
Adam Jones
committed
Merge pull request #4 from sideshow/feature-payload
Feature payload builder
2 parents 9f1c369 + 747a85b commit 233c8cb

File tree

2 files changed

+360
-0
lines changed

2 files changed

+360
-0
lines changed

payload/builder.go

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
// Package payload is a helper package which contains a payload
2+
// builder to make constructing notification payloads easier.
3+
package payload
4+
5+
import "encoding/json"
6+
7+
type payload struct {
8+
content map[string]interface{}
9+
}
10+
11+
type aps struct {
12+
Alert interface{} `json:"alert,omitempty"`
13+
Badge interface{} `json:"badge,omitempty"`
14+
Category string `json:"category,omitempty"`
15+
ContentAvailable int `json:"content-available,omitempty"`
16+
URLArgs []string `json:"url-args,omitempty"`
17+
Sound string `json:"sound,omitempty"`
18+
}
19+
20+
type alert struct {
21+
Action string `json:"action,omitempty"`
22+
ActionLocKey string `json:"action-loc-key,omitempty"`
23+
Body string `json:"body,omitempty"`
24+
LaunchImage string `json:"launch-image,omitempty"`
25+
LocArgs []string `json:"loc-args,omitempty"`
26+
LocKey string `json:"loc-key,omitempty"`
27+
Title string `json:"title,omitempty"`
28+
TitleLocArgs []string `json:"title-loc-args,omitempty"`
29+
TitleLocKey string `json:"title-loc-key,omitempty"`
30+
}
31+
32+
// NewPayload represents a notification payload.
33+
func NewPayload() *payload {
34+
return &payload{
35+
map[string]interface{}{
36+
"aps": &aps{},
37+
},
38+
}
39+
}
40+
41+
// Sets the aps alert on the payload.
42+
// This will display a notification alert message to the user.
43+
// {"aps":{"alert":alert}}
44+
func (p *payload) Alert(alert interface{}) *payload {
45+
p.aps().Alert = alert
46+
return p
47+
}
48+
49+
// Sets the aps badge on the payload.
50+
// This will display a numeric badge on the app icon.
51+
// {"aps":{"badge":b}}
52+
func (p *payload) Badge(b int) *payload {
53+
p.aps().Badge = b
54+
return p
55+
}
56+
57+
// Sets the aps badge on the payload to 0.
58+
// This will clear the badge on the app icon.
59+
// {"aps":{"badge":0}}
60+
func (p *payload) ZeroBadge() *payload {
61+
p.aps().Badge = 0
62+
return p
63+
}
64+
65+
// Removes the badge attribute from the payload.
66+
// This will leave the badge on the app icon unchanged.
67+
// If you wish to clear the app icon badge, use ZeroBadge() instead.
68+
// {"aps":{}}
69+
func (p *payload) UnsetBadge() *payload {
70+
p.aps().Badge = nil
71+
return p
72+
}
73+
74+
// Sets the aps sound on the payload.
75+
// This will play a sound from the app bundle, or the default sound otherwise.
76+
// {"aps":{"sound":sound}}
77+
func (p *payload) Sound(sound string) *payload {
78+
p.aps().Sound = sound
79+
return p
80+
}
81+
82+
// Sets the aps content-available on the payload to 1.
83+
// This will indicate to the app that there is new content available to download
84+
// and launch the app in the background.
85+
// {"aps":{"content-available":1}}
86+
func (p *payload) ContentAvailable() *payload {
87+
p.aps().ContentAvailable = 1
88+
return p
89+
}
90+
91+
// Custom payload
92+
93+
// Sets a custom key and value on the payload.
94+
// This will add custom key/value data to the notification payload at root level.
95+
// {"aps":{}, key:value}
96+
func (p *payload) Custom(key string, val interface{}) *payload {
97+
p.content[key] = val
98+
return p
99+
}
100+
101+
// Alert dictionary
102+
103+
// Sets the aps alert title on the payload.
104+
// This will display a short string describing the purpose of the notification.
105+
// Apple Watch & Safari display this string as part of the notification interface.
106+
// {"aps":{"alert":"title"}}
107+
func (p *payload) AlertTitle(title string) *payload {
108+
p.aps().alert().Title = title
109+
return p
110+
}
111+
112+
// Sets the aps alert title localization key on the payload.
113+
// This is the key to a title string in the Localizable.strings file for the
114+
// current localization. See Localized Formatted Strings in Apple documentation
115+
// for more information.
116+
// {"aps":{"alert":{"title-loc-key":key}}}
117+
func (p *payload) AlertTitleLocKey(key string) *payload {
118+
p.aps().alert().TitleLocKey = key
119+
return p
120+
}
121+
122+
// Sets the aps alert title localization args on the payload.
123+
// These are the variable string values to appear in place of the format
124+
// specifiers in title-loc-key. See Localized Formatted Strings in Apple
125+
// documentation for more information.
126+
// {"aps":{"alert":{"title-loc-args":args}}}
127+
func (p *payload) AlertTitleLocArgs(args []string) *payload {
128+
p.aps().alert().TitleLocArgs = args
129+
return p
130+
}
131+
132+
// Sets the aps alert body on the payload.
133+
// This is the text of the alert message.
134+
// {"aps":{"alert":{"body":body}}}
135+
func (p *payload) AlertBody(body string) *payload {
136+
p.aps().alert().Body = body
137+
return p
138+
}
139+
140+
// Sets the aps launch image on the payload.
141+
// This is the filename of an image file in the app bundle. The image is used
142+
// as the launch image when users tap the action button or move the action
143+
// slider.
144+
// {"aps":{"alert":{"launch-image":image}}}
145+
func (p *payload) AlertLaunchImage(image string) *payload {
146+
p.aps().alert().LaunchImage = image
147+
return p
148+
}
149+
150+
// Sets the aps alert localization key on the payload.
151+
// This is the key to an alert-message string in the Localizable.strings file
152+
// for the current localization. See Localized Formatted Strings in Apple
153+
// documentation for more information.
154+
// {"aps":{"alert":{"loc-key":key}}}
155+
func (p *payload) AlertLocKey(key string) *payload {
156+
p.aps().alert().LocKey = key
157+
return p
158+
}
159+
160+
// Sets the aps alert action on the payload.
161+
// This is the label of the action button, if the user sets the notifications
162+
// to appear as alerts. This label should be succinct, such as “Details” or
163+
// “Read more”. If omitted, the default value is “Show”.
164+
// {"aps":{"alert":{"action":action}}}
165+
func (p *payload) AlertAction(action string) *payload {
166+
p.aps().alert().Action = action
167+
return p
168+
}
169+
170+
// Sets the aps alert action localization key on the payload.
171+
// This is the the string used as a key to get a localized string in the current
172+
// localization to use for the notfication right button’s title instead of
173+
// “View”. See Localized Formatted Strings in Apple documentation for more
174+
// information.
175+
// {"aps":{"alert":{"action-loc-key":key}}}
176+
func (p *payload) AlertActionLocKey(key string) *payload {
177+
p.aps().alert().ActionLocKey = key
178+
return p
179+
}
180+
181+
// General
182+
183+
// Sets the aps category on the payload.
184+
// This is a string value that represents the identifier property of the
185+
// UIMutableUserNotificationCategory object you created to define custom actions.
186+
// {"aps":{"alert":{"category":category}}}
187+
func (p *payload) Category(category string) *payload {
188+
p.aps().Category = category
189+
return p
190+
}
191+
192+
// Sets the mdm on the payload.
193+
// This is for Apple Mobile Device Management (mdm) payloads.
194+
// {"aps":{}:"mdm":mdm}
195+
func (p *payload) Mdm(mdm string) *payload {
196+
p.content["mdm"] = mdm
197+
return p
198+
}
199+
200+
// Sets the aps category on the payload.
201+
// This specifies an array of values that are paired with the placeholders
202+
// inside the urlFormatString value of your website.json file.
203+
// See Apple Notification Programming Guide for Websites.
204+
// {"aps":{"url-args":urlArgs}}
205+
func (p *payload) URLArgs(urlArgs []string) *payload {
206+
p.aps().URLArgs = urlArgs
207+
return p
208+
}
209+
210+
func (p *payload) MarshalJSON() ([]byte, error) {
211+
return json.Marshal(p.content)
212+
}
213+
214+
func (p *payload) aps() *aps {
215+
return p.content["aps"].(*aps)
216+
}
217+
218+
func (a *aps) alert() *alert {
219+
if _, ok := a.Alert.(*alert); !ok {
220+
a.Alert = &alert{}
221+
}
222+
return a.Alert.(*alert)
223+
}

payload/builder_test.go

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
package payload_test
2+
3+
import (
4+
"encoding/json"
5+
"testing"
6+
7+
. "github.com/sideshow/apns2/payload"
8+
"github.com/stretchr/testify/assert"
9+
)
10+
11+
func TestEmptyPayload(t *testing.T) {
12+
payload := NewPayload()
13+
b, _ := json.Marshal(payload)
14+
assert.Equal(t, `{"aps":{}}`, string(b))
15+
}
16+
17+
func TestAlert(t *testing.T) {
18+
payload := NewPayload().Alert("hello")
19+
b, _ := json.Marshal(payload)
20+
assert.Equal(t, `{"aps":{"alert":"hello"}}`, string(b))
21+
}
22+
23+
func TestBadge(t *testing.T) {
24+
payload := NewPayload().Badge(1)
25+
b, _ := json.Marshal(payload)
26+
assert.Equal(t, `{"aps":{"badge":1}}`, string(b))
27+
}
28+
29+
func TestZeroBadge(t *testing.T) {
30+
payload := NewPayload().ZeroBadge()
31+
b, _ := json.Marshal(payload)
32+
assert.Equal(t, `{"aps":{"badge":0}}`, string(b))
33+
}
34+
35+
func TestUnsetBadge(t *testing.T) {
36+
payload := NewPayload().Badge(1).UnsetBadge()
37+
b, _ := json.Marshal(payload)
38+
assert.Equal(t, `{"aps":{}}`, string(b))
39+
}
40+
41+
func TestSound(t *testing.T) {
42+
payload := NewPayload().Sound("Default.caf")
43+
b, _ := json.Marshal(payload)
44+
assert.Equal(t, `{"aps":{"sound":"Default.caf"}}`, string(b))
45+
}
46+
47+
func TestContentAvailable(t *testing.T) {
48+
payload := NewPayload().ContentAvailable()
49+
b, _ := json.Marshal(payload)
50+
assert.Equal(t, `{"aps":{"content-available":1}}`, string(b))
51+
}
52+
53+
func TestCustom(t *testing.T) {
54+
payload := NewPayload().Custom("key", "val")
55+
b, _ := json.Marshal(payload)
56+
assert.Equal(t, `{"aps":{},"key":"val"}`, string(b))
57+
}
58+
59+
func TestCustomMap(t *testing.T) {
60+
payload := NewPayload().Custom("key", map[string]interface{}{
61+
"map": 1,
62+
})
63+
b, _ := json.Marshal(payload)
64+
assert.Equal(t, `{"aps":{},"key":{"map":1}}`, string(b))
65+
}
66+
67+
func TestAlertTitle(t *testing.T) {
68+
payload := NewPayload().AlertTitle("hello")
69+
b, _ := json.Marshal(payload)
70+
assert.Equal(t, `{"aps":{"alert":{"title":"hello"}}}`, string(b))
71+
}
72+
73+
func TestAlertTitleLocKey(t *testing.T) {
74+
payload := NewPayload().AlertTitleLocKey("GAME_PLAY_REQUEST_FORMAT")
75+
b, _ := json.Marshal(payload)
76+
assert.Equal(t, `{"aps":{"alert":{"title-loc-key":"GAME_PLAY_REQUEST_FORMAT"}}}`, string(b))
77+
}
78+
79+
func TestAlertTitleLocArgs(t *testing.T) {
80+
payload := NewPayload().AlertTitleLocArgs([]string{"Jenna", "Frank"})
81+
b, _ := json.Marshal(payload)
82+
assert.Equal(t, `{"aps":{"alert":{"title-loc-args":["Jenna","Frank"]}}}`, string(b))
83+
}
84+
85+
func TestAlertBody(t *testing.T) {
86+
payload := NewPayload().AlertBody("body")
87+
b, _ := json.Marshal(payload)
88+
assert.Equal(t, `{"aps":{"alert":{"body":"body"}}}`, string(b))
89+
}
90+
91+
func TestAlertLaunchImage(t *testing.T) {
92+
payload := NewPayload().AlertLaunchImage("Default.png")
93+
b, _ := json.Marshal(payload)
94+
assert.Equal(t, `{"aps":{"alert":{"launch-image":"Default.png"}}}`, string(b))
95+
}
96+
97+
func TestAlertLocKey(t *testing.T) {
98+
payload := NewPayload().AlertLocKey("LOC")
99+
b, _ := json.Marshal(payload)
100+
assert.Equal(t, `{"aps":{"alert":{"loc-key":"LOC"}}}`, string(b))
101+
}
102+
103+
func TestAlertAction(t *testing.T) {
104+
payload := NewPayload().AlertAction("action")
105+
b, _ := json.Marshal(payload)
106+
assert.Equal(t, `{"aps":{"alert":{"action":"action"}}}`, string(b))
107+
}
108+
109+
func TestAlertActionLocKey(t *testing.T) {
110+
payload := NewPayload().AlertActionLocKey("PLAY")
111+
b, _ := json.Marshal(payload)
112+
assert.Equal(t, `{"aps":{"alert":{"action-loc-key":"PLAY"}}}`, string(b))
113+
}
114+
115+
func TestCategory(t *testing.T) {
116+
payload := NewPayload().Category("NEW_MESSAGE_CATEGORY")
117+
b, _ := json.Marshal(payload)
118+
assert.Equal(t, `{"aps":{"category":"NEW_MESSAGE_CATEGORY"}}`, string(b))
119+
}
120+
121+
func TestMdm(t *testing.T) {
122+
payload := NewPayload().Mdm("996ac527-9993-4a0a-8528-60b2b3c2f52b")
123+
b, _ := json.Marshal(payload)
124+
assert.Equal(t, `{"aps":{},"mdm":"996ac527-9993-4a0a-8528-60b2b3c2f52b"}`, string(b))
125+
}
126+
127+
func TestURLArgs(t *testing.T) {
128+
payload := NewPayload().URLArgs([]string{"a", "b"})
129+
b, _ := json.Marshal(payload)
130+
assert.Equal(t, `{"aps":{"url-args":["a","b"]}}`, string(b))
131+
}
132+
133+
func TestCombined(t *testing.T) {
134+
payload := NewPayload().Alert("hello").Badge(1).Sound("Default.caf").Custom("key", "val")
135+
b, _ := json.Marshal(payload)
136+
assert.Equal(t, `{"aps":{"alert":"hello","badge":1,"sound":"Default.caf"},"key":"val"}`, string(b))
137+
}

0 commit comments

Comments
 (0)