Skip to content

Commit 692415d

Browse files
committed
Merge pull request #610 from ParsePlatform/nlutsenko.pushDate
Add ability to specify scheduled delivery time when sending push notifications.
2 parents 16685f7 + 4d348bf commit 692415d

File tree

10 files changed

+73
-12
lines changed

10 files changed

+73
-12
lines changed

Parse/Internal/Commands/PFRESTPushCommand.m

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ + (instancetype)sendPushCommandWithPushState:(PFPushState *)state
4343
parameters[@"expiration_interval"] = state.expirationTimeInterval;
4444
}
4545

46+
if (state.pushDate) {
47+
parameters[@"push_time"] = [[PFDateFormatter sharedFormatter] preciseStringFromDate:state.pushDate];
48+
}
49+
4650
// TODO (nlutsenko): Probably we need an assert here, as there is no reason to send push without message
4751
if (state.payload) {
4852
parameters[@"data"] = state.payload;

Parse/Internal/Push/State/PFMutablePushState.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ PF_TV_UNAVAILABLE PF_WATCH_UNAVAILABLE @interface PFMutablePushState : PFPushSta
2020
@property (nullable, nonatomic, copy, readwrite) PFQueryState *queryState;
2121

2222
@property (nullable, nonatomic, strong, readwrite) NSDate *expirationDate;
23-
@property (nullable, nonatomic, copy, readwrite) NSNumber *expirationTimeInterval;
23+
@property (nullable, nonatomic, strong, readwrite) NSNumber *expirationTimeInterval;
24+
@property (nullable, nonatomic, strong, readwrite) NSDate *pushDate;
2425

2526
@property (nullable, nonatomic, copy, readwrite) NSDictionary *payload;
2627

Parse/Internal/Push/State/PFMutablePushState.m

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ @implementation PFMutablePushState
1717
@dynamic queryState;
1818
@dynamic expirationDate;
1919
@dynamic expirationTimeInterval;
20+
@dynamic pushDate;
2021
@dynamic payload;
2122

2223
///--------------------------------------

Parse/Internal/Push/State/PFPushState.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ PF_TV_UNAVAILABLE PF_WATCH_UNAVAILABLE @interface PFPushState : PFBaseState <NSC
2626
@property (nullable, nonatomic, copy, readonly) PFQueryState *queryState;
2727

2828
@property (nullable, nonatomic, strong, readonly) NSDate *expirationDate;
29-
@property (nullable, nonatomic, copy, readonly) NSNumber *expirationTimeInterval;
29+
@property (nullable, nonatomic, strong, readonly) NSNumber *expirationTimeInterval;
30+
@property (nullable, nonatomic, strong, readonly) NSDate *pushDate;
3031

3132
@property (nullable, nonatomic, copy, readonly) NSDictionary *payload;
3233

Parse/Internal/Push/State/PFPushState.m

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#import "PFMutablePushState.h"
1414
#import "PFQueryState.h"
15+
#import "PFAssert.h"
1516

1617
@implementation PFPushState
1718

@@ -20,13 +21,12 @@ @implementation PFPushState
2021
///--------------------------------------
2122

2223
+ (NSDictionary *)propertyAttributes {
23-
return @{
24-
@"channels": [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeCopy],
25-
@"queryState": [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeCopy],
26-
@"expirationDate": [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeStrong],
27-
@"expirationTimeInterval": [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeStrong],
28-
@"payload": [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeCopy]
29-
};
24+
return @{ @"channels": [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeCopy],
25+
@"queryState": [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeCopy],
26+
@"expirationDate": [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeStrong],
27+
@"expirationTimeInterval": [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeStrong],
28+
@"pushDate": [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeStrong],
29+
@"payload": [PFPropertyAttributes attributesWithAssociationType:PFPropertyInfoAssociationTypeCopy] };
3030
}
3131

3232
///--------------------------------------
@@ -45,6 +45,19 @@ + (instancetype)stateWithState:(PFPushState *)state {
4545
#pragma mark - NSCopying
4646
///--------------------------------------
4747

48+
- (void)setPushDate:(NSDate *)pushDate {
49+
if (self.pushDate != pushDate) {
50+
NSTimeInterval interval = [pushDate timeIntervalSinceNow];
51+
PFParameterAssert(interval > 0, @"Can't set the scheduled push time in the past.");
52+
PFParameterAssert(interval <= 60 * 24 * 14, @"Can't set the schedule push time more than two weeks from now.");
53+
_pushDate = pushDate;
54+
}
55+
}
56+
57+
///--------------------------------------
58+
#pragma mark - NSCopying
59+
///--------------------------------------
60+
4861
- (id)copyWithZone:(NSZone *)zone {
4962
return [[PFPushState allocWithZone:zone] initWithState:self];
5063
}

Parse/Internal/Push/State/PFPushState_Private.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ NS_ASSUME_NONNULL_BEGIN
1717
@property (nullable, nonatomic, copy, readwrite) PFQueryState *queryState;
1818

1919
@property (nullable, nonatomic, strong, readwrite) NSDate *expirationDate;
20-
@property (nullable, nonatomic, copy, readwrite) NSNumber *expirationTimeInterval;
20+
@property (nullable, nonatomic, strong, readwrite) NSNumber *expirationTimeInterval;
21+
@property (nullable, nonatomic, strong, readwrite) NSDate *pushDate;
2122

2223
@property (nullable, nonatomic, copy, readwrite) NSDictionary *payload;
2324

Parse/PFPush.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,15 @@ PF_TV_UNAVAILABLE PF_WATCH_UNAVAILABLE @interface PFPush : NSObject <NSCopying>
134134
*/
135135
- (void)clearExpiration;
136136

137+
/*!
138+
@abstract Date at which to send this push notification.
139+
140+
@discussion Push notificaitons with this date will be delivered at the local time matching the <PFInstallation.timeZone>.
141+
142+
@warning The date cannot be in the past, and can be up to two weeks in the future.
143+
*/
144+
@property (nullable, nonatomic, strong) NSDate *pushDate;
145+
137146
///--------------------------------------
138147
/// @name Sending Push Notifications
139148
///--------------------------------------

Parse/PFPush.m

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,14 @@ - (void)clearExpiration {
114114
self.state.expirationTimeInterval = nil;
115115
}
116116

117+
- (void)setPushDate:(NSDate *)pushDate {
118+
self.state.pushDate = pushDate;
119+
}
120+
121+
- (NSDate *)pushDate {
122+
return self.state.pushDate;
123+
}
124+
117125
- (void)setData:(NSDictionary *)data {
118126
self.state.payload = data;
119127
}

Tests/Unit/PushCommandTests.m

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,20 @@ - (void)testPushCommandExpirationTimeInterval {
8585
XCTAssertEqualObjects(command.sessionToken, @"yarr");
8686
}
8787

88+
- (void)testPushCommandPushDate {
89+
PFMutablePushState *state = [[PFMutablePushState alloc] init];
90+
state.pushDate = [NSDate dateWithTimeIntervalSinceNow:1.0];
91+
92+
PFRESTPushCommand *command = [PFRESTPushCommand sendPushCommandWithPushState:state sessionToken:@"yarr"];
93+
XCTAssertNotNil(command);
94+
XCTAssertEqualObjects(command.httpPath, @"push");
95+
XCTAssertEqualObjects(command.httpMethod, PFHTTPRequestMethodPOST);
96+
XCTAssertNil(command.parameters[@"expiration_time"]);
97+
XCTAssertNil(command.parameters[@"expiration_interval"]);
98+
XCTAssertNotNil(command.parameters[@"push_time"]);
99+
XCTAssertEqualObjects(command.sessionToken, @"yarr");
100+
}
101+
88102
- (void)testPushCommandPayload {
89103
PFMutablePushState *state = [[PFMutablePushState alloc] init];
90104
state.payload = @{ @"alert" : @"yolo" };

Tests/Unit/PushStateTests.m

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#import "PFMutablePushState.h"
1111
#import "PFQueryState.h"
1212
#import "PFTestCase.h"
13+
#import "PFAssert.h"
1314

1415
@interface PushStateTests : PFTestCase
1516

@@ -27,6 +28,7 @@ - (PFPushState *)samplePushState {
2728
state.queryState = [[PFQueryState alloc] init];
2829
state.expirationDate = [NSDate date];
2930
state.expirationTimeInterval = @1.0;
31+
state.pushDate = [NSDate dateWithTimeIntervalSinceNow:120];
3032
state.payload = @{ @"alert" : @"yarr" };
3133
return [state copy];
3234
}
@@ -36,6 +38,7 @@ - (void)assertPushState:(PFPushState *)state equalToState:(PFPushState *)differe
3638
XCTAssertEqualObjects(state.queryState, differentState.queryState);
3739
XCTAssertEqualObjects(state.expirationDate, differentState.expirationDate);
3840
XCTAssertEqualObjects(state.expirationTimeInterval, differentState.expirationTimeInterval);
41+
XCTAssertEqualObjects(state.pushDate, differentState.pushDate);
3942
XCTAssertEqualObjects(state.payload, differentState.payload);
4043
}
4144

@@ -50,15 +53,15 @@ - (void)testInit {
5053
XCTAssertNil(state.queryState);
5154
XCTAssertNil(state.expirationDate);
5255
XCTAssertNil(state.expirationTimeInterval);
53-
XCTAssertNil(state.expirationTimeInterval);
56+
XCTAssertNil(state.pushDate);
5457

5558
state = [[PFMutablePushState alloc] init];
5659
XCTAssertNotNil(state);
5760
XCTAssertNil(state.channels);
5861
XCTAssertNil(state.queryState);
5962
XCTAssertNil(state.expirationDate);
6063
XCTAssertNil(state.expirationTimeInterval);
61-
XCTAssertNil(state.expirationTimeInterval);
64+
XCTAssertNil(state.pushDate);
6265
}
6366

6467
- (void)testInitWithState {
@@ -125,4 +128,10 @@ - (void)testSetPayloadWithMessage {
125128
XCTAssertNil(state.payload);
126129
}
127130

131+
- (void)testSetPushTimeValidation {
132+
PFMutablePushState *state = [[PFMutablePushState alloc] init];
133+
PFAssertThrowsInvalidArgumentException(state.pushDate = [NSDate distantPast]);
134+
PFAssertThrowsInvalidArgumentException(state.pushDate = [NSDate distantFuture]);
135+
}
136+
128137
@end

0 commit comments

Comments
 (0)