@@ -50,6 +50,8 @@ final class APNSClientIntegrationTests: XCTestCase {
5050 override func tearDown( ) async throws {
5151 try await client? . shutdown ( )
5252 try await server? . shutdown ( )
53+ client = nil
54+ server = nil
5355 try await super. tearDown ( )
5456 }
5557
@@ -108,6 +110,10 @@ final class APNSClientIntegrationTests: XCTestCase {
108110 XCTAssertEqual ( sent. count, 1 )
109111 XCTAssertEqual ( sent [ 0 ] . deviceToken, " 1111111111111111111111111111111111111111111111111111111111111111 " )
110112 XCTAssertEqual ( sent [ 0 ] . pushType, " alert " )
113+
114+ let json = try XCTUnwrap ( JSONSerialization . jsonObject ( with: sent [ 0 ] . payload) as? NSDictionary )
115+ let aps = try XCTUnwrap ( json [ " aps " ] as? NSDictionary )
116+ XCTAssertEqual ( aps [ " badge " ] as? Int , 5 )
111117 }
112118
113119 func testSendAlertNotification_withSound( ) async throws {
@@ -128,6 +134,10 @@ final class APNSClientIntegrationTests: XCTestCase {
128134 let sent = server. getSentNotifications ( )
129135 XCTAssertEqual ( sent. count, 1 )
130136 XCTAssertEqual ( sent [ 0 ] . deviceToken, " 2222222222222222222222222222222222222222222222222222222222222222 " )
137+
138+ let json = try XCTUnwrap ( JSONSerialization . jsonObject ( with: sent [ 0 ] . payload) as? NSDictionary )
139+ let aps = try XCTUnwrap ( json [ " aps " ] as? NSDictionary )
140+ XCTAssertEqual ( aps [ " sound " ] as? String , " default " )
131141 }
132142
133143 // MARK: - Background Notifications
@@ -222,11 +232,177 @@ final class APNSClientIntegrationTests: XCTestCase {
222232 XCTAssertEqual ( sent [ 0 ] . pushType, " complication " )
223233 }
224234
235+ // MARK: - Live Activity Notifications
236+
237+ func testSendLiveActivityNotification( ) async throws {
238+ struct ContentState : Codable {
239+ let value : String
240+ }
241+
242+ let notification = APNSLiveActivityNotification (
243+ expiration: . immediately,
244+ priority: . immediately,
245+ appID: " com.example.app " ,
246+ contentState: ContentState ( value: " update-value " ) ,
247+ event: . update,
248+ timestamp: 1_234_567_890
249+ )
250+
251+ _ = try await client. sendLiveActivityNotification (
252+ notification,
253+ deviceToken: " 7777777777777777777777777777777777777777777777777777777777777777 "
254+ )
255+
256+ let sent = try XCTUnwrap ( server. getSentNotifications ( ) . first)
257+ XCTAssertEqual ( sent. pushType, " liveactivity " )
258+ XCTAssertEqual ( sent. topic, " com.example.app.push-type.liveactivity " )
259+
260+ let json = try XCTUnwrap ( JSONSerialization . jsonObject ( with: sent. payload) as? NSDictionary )
261+ let aps = try XCTUnwrap ( json [ " aps " ] as? NSDictionary )
262+ XCTAssertEqual ( aps [ " event " ] as? String , " update " )
263+ XCTAssertNotNil ( aps [ " timestamp " ] )
264+ XCTAssertNotNil ( aps [ " content-state " ] )
265+ }
266+
267+ func testSendStartLiveActivityNotification( ) async throws {
268+ struct Attributes : Codable { }
269+ struct ContentState : Codable {
270+ let value : String
271+ }
272+
273+ let notification = APNSStartLiveActivityNotification (
274+ expiration: . immediately,
275+ priority: . immediately,
276+ appID: " com.example.app " ,
277+ contentState: ContentState ( value: " start-value " ) ,
278+ timestamp: 1_234_567_890 ,
279+ attributes: Attributes ( ) ,
280+ attributesType: " MyActivityAttributes " ,
281+ alert: . init( title: . raw( " Live Activity Started " ) )
282+ )
283+
284+ _ = try await client. sendStartLiveActivityNotification (
285+ notification,
286+ pushToStartToken: " 8888888888888888888888888888888888888888888888888888888888888888 "
287+ )
288+
289+ let sent = try XCTUnwrap ( server. getSentNotifications ( ) . first)
290+ XCTAssertEqual ( sent. pushType, " liveactivity " )
291+ XCTAssertEqual ( sent. topic, " com.example.app.push-type.liveactivity " )
292+
293+ let json = try XCTUnwrap ( JSONSerialization . jsonObject ( with: sent. payload) as? NSDictionary )
294+ let aps = try XCTUnwrap ( json [ " aps " ] as? NSDictionary )
295+ XCTAssertEqual ( aps [ " event " ] as? String , " start " )
296+ XCTAssertEqual ( aps [ " attributes-type " ] as? String , " MyActivityAttributes " )
297+ }
298+
299+ // MARK: - Push To Talk Notifications
300+
301+ func testSendPushToTalkNotification( ) async throws {
302+ struct Payload : Encodable {
303+ let activeSpeaker = " Alice "
304+ }
305+
306+ let notification = APNSPushToTalkNotification (
307+ appID: " com.example.app " ,
308+ payload: Payload ( )
309+ )
310+
311+ _ = try await client. sendPushToTalkNotification (
312+ notification,
313+ deviceToken: " 9999999999999999999999999999999999999999999999999999999999999999 "
314+ )
315+
316+ let sent = try XCTUnwrap ( server. getSentNotifications ( ) . first)
317+ XCTAssertEqual ( sent. pushType, " pushtotalk " )
318+ XCTAssertEqual ( sent. topic, " com.example.app.voip-ptt " )
319+ XCTAssertEqual ( sent. deviceToken, " 9999999999999999999999999999999999999999999999999999999999999999 " )
320+ // payload shape asserted in encode tests (see PR1)
321+ }
322+
323+ // MARK: - Widgets Notifications
324+
325+ func testSendWidgetsNotification( ) async throws {
326+ let notification = APNSWidgetsNotification ( appID: " com.example.app " )
327+
328+ _ = try await client. sendWidgetsNotification (
329+ notification: notification,
330+ deviceToken: " aaaa1111aaaa1111aaaa1111aaaa1111aaaa1111aaaa1111aaaa1111aaaa1111 "
331+ )
332+
333+ let sent = try XCTUnwrap ( server. getSentNotifications ( ) . first)
334+ XCTAssertEqual ( sent. pushType, " widgets " )
335+ XCTAssertEqual ( sent. topic, " com.example.app.push-type.widgets " )
336+
337+ let expectedJSONString = """
338+ { " aps " :{ " content-changed " :true}}
339+ """
340+ let jsonObject1 = try JSONSerialization . jsonObject ( with: sent. payload) as! NSDictionary
341+ let jsonObject2 = try JSONSerialization . jsonObject ( with: expectedJSONString. data ( using: . utf8) !) as! NSDictionary
342+ XCTAssertEqual ( jsonObject1, jsonObject2)
343+ }
344+
345+ // MARK: - Location Notifications
346+
347+ func testSendLocationNotification( ) async throws {
348+ let notification = APNSLocationNotification (
349+ priority: . immediately,
350+ appID: " com.example.app "
351+ )
352+
353+ _ = try await client. sendLocationNotification (
354+ notification,
355+ deviceToken: " bbbb2222bbbb2222bbbb2222bbbb2222bbbb2222bbbb2222bbbb2222bbbb2222 "
356+ )
357+
358+ let sent = try XCTUnwrap ( server. getSentNotifications ( ) . first)
359+ XCTAssertEqual ( sent. pushType, " location " )
360+ XCTAssertEqual ( sent. topic, " com.example.app.location-query " )
361+ // sendLocationNotification forces expiration to `.none`, so no header should be sent.
362+ XCTAssertNil ( sent. expiration)
363+ }
364+
365+ // MARK: - Alert Notification Header Variants
366+
367+ func testSendAlert_consideringDevicePower( ) async throws {
368+ let notification = APNSAlertNotification (
369+ alert: . init( title: . raw( " Power Test " ) ) ,
370+ expiration: . immediately,
371+ priority: . consideringDevicePower,
372+ topic: " com.example.app " ,
373+ payload: EmptyPayload ( )
374+ )
375+
376+ _ = try await client. sendAlertNotification (
377+ notification,
378+ deviceToken: " cccc3333cccc3333cccc3333cccc3333cccc3333cccc3333cccc3333cccc3333 "
379+ )
380+
381+ let sent = try XCTUnwrap ( server. getSentNotifications ( ) . first)
382+ XCTAssertEqual ( sent. priority, " 5 " )
383+ }
384+
385+ func testSendAlert_noExpirationHeaderWhenNone( ) async throws {
386+ let notification = APNSAlertNotification (
387+ alert: . init( title: . raw( " Expiration None Test " ) ) ,
388+ expiration: . none,
389+ priority: . immediately,
390+ topic: " com.example.app " ,
391+ payload: EmptyPayload ( )
392+ )
393+
394+ _ = try await client. sendAlertNotification (
395+ notification,
396+ deviceToken: " dddd4444dddd4444dddd4444dddd4444dddd4444dddd4444dddd4444dddd4444 "
397+ )
398+
399+ let sent = try XCTUnwrap ( server. getSentNotifications ( ) . first)
400+ XCTAssertNil ( sent. expiration)
401+ }
402+
225403 // MARK: - Multiple Notifications
226404
227405 func testSendMultipleNotifications( ) async throws {
228- server. clearSentNotifications ( )
229-
230406 // Send 3 different notifications
231407 let alert = APNSAlertNotification (
232408 alert: . init( title: . raw( " Alert " ) ) ,
0 commit comments