Addressing issues with managing socket connections in background.

This commit is contained in:
Frederic Jacobs 2015-05-18 22:08:36 +02:00
parent 0f04132b81
commit bb1a4c1800
3 changed files with 96 additions and 74 deletions

View File

@ -37,7 +37,7 @@
static PushManager *sharedManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedManager = [self new];
sharedManager = [self new];
});
return sharedManager;
}
@ -48,11 +48,11 @@
if (self) {
self.notificationTracker = [NotificationTracker notificationTracker];
self.missingPermissionsAlertView =
[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"ACTION_REQUIRED_TITLE", @"")
message:NSLocalizedString(@"PUSH_SETTINGS_MESSAGE", @"")
delegate:nil
cancelButtonTitle:NSLocalizedString(@"OK", @"")
otherButtonTitles:nil, nil];
[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"ACTION_REQUIRED_TITLE", @"")
message:NSLocalizedString(@"PUSH_SETTINGS_MESSAGE", @"")
delegate:nil
cancelButtonTitle:NSLocalizedString(@"OK", @"")
otherButtonTitles:nil, nil];
}
return self;
}
@ -60,7 +60,7 @@
#pragma mark Manage Incoming Push
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
if ([self isRedPhonePush:userInfo]) {
ResponderSessionDescriptor* call;
if (![self.notificationTracker shouldProcessNotification:userInfo]){
@ -150,7 +150,6 @@
} else{
NSString *threadId = [notification.userInfo objectForKey:Signal_Thread_UserInfo_Key];
[Environment messageThreadId:threadId];
completionHandler();
}
}
@ -174,7 +173,7 @@
}
-(void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(NSString *)type
{
{
[self application:[UIApplication sharedApplication] didReceiveRemoteNotification:payload.dictionaryPayload];
}
@ -207,7 +206,7 @@
{
self.pushNotificationFutureSource = [TOCFutureSource new];
[UIApplication.sharedApplication registerForRemoteNotifications];
return self.pushNotificationFutureSource.future;
}
@ -217,10 +216,10 @@
[requestPushTokenFuture catchDo:^(id failureObj) {
[self.missingPermissionsAlertView show];
failure(failureObj);
DDLogError(@"This should not happen on iOS8. No push token was provided");
failure(failureObj);
DDLogError(@"This should not happen on iOS8. No push token was provided");
}];
[requestPushTokenFuture thenDo:^(NSData *pushToken) {
TOCFuture *voipPushTokenFuture = [self registerPushKitNotificationFuture];
@ -253,15 +252,15 @@
voipToken:fakeToken
withSuccess:success
failure:failure];
return;
}
[self requestPushTokenWithSuccess:^(NSData *pushToken, NSData *voipToken) {
[self registerTokenWithRedPhoneServer:pushToken voipToken:voipToken withSuccess:success failure:failure];
} failure:^(NSError *error) {
[self.missingPermissionsAlertView show];
failure([NSError errorWithDomain:pushManagerDomain code:400 userInfo:@{}]);
[self.missingPermissionsAlertView show];
failure([NSError errorWithDomain:pushManagerDomain code:400 userInfo:@{}]);
}];
}
@ -271,22 +270,22 @@
failure:(failedPushRegistrationBlock)failure
{
[RPServerRequestsManager.sharedInstance performRequest:[RPAPICall requestTextSecureVerificationCode]
success:^(NSURLSessionDataTask *task, id responseObject) {
NSError *error;
NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:responseObject options:0 error:&error];
NSString *tsToken = [dictionary objectForKey:@"token"];
if (!tsToken || !pushToken || error) {
failure(error);
return;
}
success(pushToken, voipToken, tsToken);
}
failure:^(NSURLSessionDataTask *task, NSError *error) {
failure(error);
}];
success:^(NSURLSessionDataTask *task, id responseObject) {
NSError *error;
NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:responseObject options:0 error:&error];
NSString *tsToken = [dictionary objectForKey:@"token"];
if (!tsToken || !pushToken || error) {
failure(error);
return;
}
success(pushToken, voipToken, tsToken);
}
failure:^(NSURLSessionDataTask *task, NSError *error) {
failure(error);
}];
}
- (TOCFuture *)registerForUserNotificationsFuture
@ -294,8 +293,8 @@
self.userNotificationFutureSource = [TOCFutureSource new];
UIUserNotificationSettings *settings =
[UIUserNotificationSettings settingsForTypes:(UIUserNotificationType)[self allNotificationTypes]
categories:[NSSet setWithObjects:[self userNotificationsCallCategory], [self userNotificationsMessageCategory], nil]];
[UIUserNotificationSettings settingsForTypes:(UIUserNotificationType)[self allNotificationTypes]
categories:[NSSet setWithObjects:[self userNotificationsCallCategory], [self userNotificationsMessageCategory], nil]];
[UIApplication.sharedApplication registerUserNotificationSettings:settings];
return self.userNotificationFutureSource.future;
@ -348,12 +347,12 @@
- (BOOL)wantRemoteNotifications
{
BOOL isSimulator = [UIDevice.currentDevice.model.lowercaseString rangeOfString:@"simulator"].location != NSNotFound;
if (isSimulator) {
// Simulator is used for debugging but can't receive push notifications, so don't bother trying to get them
return NO;
}
return YES;
}
@ -376,25 +375,25 @@
self.registerWithServerFutureSource = [TOCFutureSource new];
[RPServerRequestsManager.sharedInstance performRequest:[RPAPICall registerPushNotificationWithPushToken:pushToken voipToken:voipToken]
success:^(NSURLSessionDataTask *task, id responseObject) {
if ([task.response isKindOfClass:NSHTTPURLResponse.class]) {
NSInteger statusCode = [(NSHTTPURLResponse *)task.response statusCode];
if (statusCode == 200) {
[self.registerWithServerFutureSource trySetResult:@YES];
} else {
DDLogError(@"The server returned %@ instead of a 200 status code", task.response);
[self.registerWithServerFutureSource
trySetFailure:[NSError errorWithDomain:pushManagerDomain code:500 userInfo:nil]];
}
} else {
[self.registerWithServerFutureSource trySetFailure:task.response];
}
}
failure:^(NSURLSessionDataTask *task, NSError *error) {
[self.registerWithServerFutureSource trySetFailure:error];
}];
success:^(NSURLSessionDataTask *task, id responseObject) {
if ([task.response isKindOfClass:NSHTTPURLResponse.class]) {
NSInteger statusCode = [(NSHTTPURLResponse *)task.response statusCode];
if (statusCode == 200) {
[self.registerWithServerFutureSource trySetResult:@YES];
} else {
DDLogError(@"The server returned %@ instead of a 200 status code", task.response);
[self.registerWithServerFutureSource
trySetFailure:[NSError errorWithDomain:pushManagerDomain code:500 userInfo:nil]];
}
} else {
[self.registerWithServerFutureSource trySetFailure:task.response];
}
}
failure:^(NSURLSessionDataTask *task, NSError *error) {
[self.registerWithServerFutureSource trySetFailure:error];
}];
return self.registerWithServerFutureSource.future;
}

View File

@ -45,8 +45,6 @@
#import <CocoaLumberjack/DDLog.h>
#import <YapDatabase/YapDatabaseSecondaryIndex.h>
#define ddLogLevel LOG_LEVEL_VERBOSE
@interface TSMessagesManager ()
@property SystemSoundID newMessageSound;

View File

@ -21,8 +21,8 @@
#define kWebSocketHeartBeat 30
#define kWebSocketReconnectTry 5
#define kBackgroundConnectTimer 120
#define kBackgroundConnectKeepAlive 20
#define kBackgroundConnectTimer 10
#define kBackgroundConnectKeepAlive 15
NSString * const SocketOpenedNotification = @"SocketOpenedNotification";
NSString * const SocketClosedNotification = @"SocketClosedNotification";
@ -31,7 +31,7 @@ NSString * const SocketConnectingNotification = @"SocketConnectingNotification";
@interface TSSocketManager ()
@property (nonatomic, retain) NSTimer *pingTimer;
@property (nonatomic, retain) NSTimer *reconnectTimer;
@property (nonatomic, retain) NSTimer *backgroundKeepAliveTimer;
@property (nonatomic, retain) SRWebSocket *websocket;
@property (nonatomic) SocketStatus status;
@ -39,6 +39,8 @@ NSString * const SocketConnectingNotification = @"SocketConnectingNotification";
@property (nonatomic) UIBackgroundTaskIdentifier fetchingTaskIdentifier;
@property BOOL didFetchInBackground;
@property (nonatomic, retain) NSTimer *backgroundKeepAliveTimer;
@property (nonatomic, retain) NSTimer *backgroundConnectTimer;
@end
@ -61,7 +63,7 @@ NSString * const SocketConnectingNotification = @"SocketConnectingNotification";
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] init];
sharedMyManager.fetchingTaskIdentifier = UIBackgroundTaskInvalid;
sharedMyManager.didFetchInBackground = FALSE;
sharedMyManager.didFetchInBackground = NO;
});
return sharedMyManager;
}
@ -104,8 +106,8 @@ NSString * const SocketConnectingNotification = @"SocketConnectingNotification";
socket = [[SRWebSocket alloc] initWithURLRequest:request];
socket.delegate = [self sharedManager];
[socket open];
[[self sharedManager] setWebsocket:socket];
[socket open];
}
+ (void)resignActivity{
@ -126,14 +128,14 @@ NSString * const SocketConnectingNotification = @"SocketConnectingNotification";
[[NSRunLoop mainRunLoop] addTimer:self.pingTimer
forMode:NSDefaultRunLoopMode];
self.status = kSocketStatusOpen;
self.status = kSocketStatusOpen;
[self.reconnectTimer invalidate];
self.reconnectTimer = nil;
}
- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error {
DDLogError(@"Error connecting to socket %@", error);
[self.pingTimer invalidate];
self.status = kSocketStatusClosed;
[self scheduleRetry];
@ -177,6 +179,8 @@ NSString * const SocketConnectingNotification = @"SocketConnectingNotification";
}
- (void)keepAliveBackground {
[self.backgroundConnectTimer invalidate];
if (self.fetchingTaskIdentifier) {
[self.backgroundKeepAliveTimer invalidate];
@ -188,7 +192,7 @@ NSString * const SocketConnectingNotification = @"SocketConnectingNotification";
// Additionally, we want the reconnect timer to work in the background too.
[[NSRunLoop mainRunLoop] addTimer:self.backgroundKeepAliveTimer
forMode:NSDefaultRunLoopMode];
}
}
@ -215,10 +219,12 @@ NSString * const SocketConnectingNotification = @"SocketConnectingNotification";
}
- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean {
DDLogVerbose(@"WebSocket did close");
[self.pingTimer invalidate];
self.status = kSocketStatusClosed;
[self scheduleRetry];
if (!wasClean && [UIApplication sharedApplication].applicationState == UIApplicationStateActive) {
[self scheduleRetry];
}
}
- (void)webSocketHeartBeat {
@ -231,7 +237,9 @@ NSString * const SocketConnectingNotification = @"SocketConnectingNotification";
}
- (NSString*)webSocketAuthenticationString {
return [NSString stringWithFormat:@"?login=%@&password=%@", [[TSAccountManager registeredNumber] stringByReplacingOccurrencesOfString:@"+" withString:@"%2B"],[TSStorageManager serverAuthToken]];
return [NSString stringWithFormat:@"?login=%@&password=%@",
[[TSAccountManager registeredNumber] stringByReplacingOccurrencesOfString:@"+"
withString:@"%2B"],[TSStorageManager serverAuthToken]];
}
- (void)scheduleRetry {
@ -265,11 +273,24 @@ NSString * const SocketConnectingNotification = @"SocketConnectingNotification";
TSSocketManager *sharedInstance = [TSSocketManager sharedManager];
if (sharedInstance.fetchingTaskIdentifier == UIBackgroundTaskInvalid) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sharedInstance.backgroundConnectTimer = [NSTimer scheduledTimerWithTimeInterval:kBackgroundConnectTimer
target:sharedInstance
selector:@selector(backgroundConnectTimerExpired)
userInfo:nil
repeats:NO];
NSRunLoop *loop = [NSRunLoop currentRunLoop];
[loop addTimer:[TSSocketManager sharedManager].backgroundConnectTimer
forMode:NSDefaultRunLoopMode];
[loop run];
});
[sharedInstance.backgroundKeepAliveTimer invalidate];
sharedInstance.didFetchInBackground = NO;
sharedInstance.didFetchInBackground = NO;
sharedInstance.fetchingTaskIdentifier = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[TSSocketManager resignActivity];
[sharedInstance closeBackgroundTask];
[[TSSocketManager sharedManager] closeBackgroundTask];
}];
[self becomeActive];
@ -278,21 +299,25 @@ NSString * const SocketConnectingNotification = @"SocketConnectingNotification";
}
}
- (void)backgroundConnectTimerExpired {
[self backgroundTimeExpired];
}
- (void)backgroundTimeExpired {
[[self class] resignActivity];
[self closeBackgroundTask];
}
- (void)closeBackgroundTask {
UIBackgroundTaskIdentifier identifier = self.fetchingTaskIdentifier;
self.fetchingTaskIdentifier = UIBackgroundTaskInvalid;
[self.backgroundKeepAliveTimer invalidate];
[self.backgroundConnectTimer invalidate];
if (!self.didFetchInBackground) {
[self backgroundConnectTimedOut];
}
[[UIApplication sharedApplication] endBackgroundTask:identifier];
[[UIApplication sharedApplication] endBackgroundTask:self.fetchingTaskIdentifier];
self.fetchingTaskIdentifier = UIBackgroundTaskInvalid;
}
- (void)backgroundConnectTimedOut {