ably-cocoa/Source/ARTPush.m

241 lines
8.6 KiB
Objective-C

#import "ARTPush+Private.h"
#import "ARTDeviceDetails.h"
#import "ARTDevicePushDetails.h"
#import "ARTRest+Private.h"
#import "ARTLog.h"
#import "ARTJsonEncoder.h"
#import "ARTJsonLikeEncoder.h"
#import "ARTEventEmitter.h"
#if TARGET_OS_IOS
#import <UIKit/UIKit.h>
#import "ARTPushActivationStateMachine+Private.h"
#endif
#import "ARTPushAdmin.h"
#import "ARTPushActivationEvent.h"
#import "ARTClientOptions+Private.h"
#import "ARTPushAdmin+Private.h"
#import "ARTLocalDevice+Private.h"
#import "ARTDeviceStorage.h"
#import "ARTRealtime+Private.h"
@implementation ARTPush {
ARTQueuedDealloc *_dealloc;
}
- (instancetype)initWithInternal:(ARTPushInternal *)internal queuedDealloc:(ARTQueuedDealloc *)dealloc {
self = [super init];
if (self) {
_internal = internal;
_dealloc = dealloc;
}
return self;
}
- (ARTPushAdmin *)admin {
return [[ARTPushAdmin alloc] initWithInternal:_internal.admin queuedDealloc:_dealloc];
}
#if TARGET_OS_IOS
+ (void)didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken rest:(ARTRest *)rest; {
return [ARTPushInternal didRegisterForRemoteNotificationsWithDeviceToken:deviceToken rest:rest];
}
+ (void)didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken realtime:(ARTRealtime *)realtime; {
return [ARTPushInternal didRegisterForRemoteNotificationsWithDeviceToken:deviceToken realtime:realtime];
}
+ (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error rest:(ARTRest *)rest; {
return [ARTPushInternal didFailToRegisterForRemoteNotificationsWithError:error rest:rest];
}
+ (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error realtime:(ARTRealtime *)realtime; {
return [ARTPushInternal didFailToRegisterForRemoteNotificationsWithError:error realtime:realtime];
}
- (void)activate {
[_internal activate];
}
- (void)deactivate {
[_internal deactivate];
}
#endif
@end
NSString *const ARTDeviceIdKey = @"ARTDeviceId";
NSString *const ARTDeviceSecretKey = @"ARTDeviceSecret";
NSString *const ARTDeviceIdentityTokenKey = @"ARTDeviceIdentityToken";
NSString *const ARTAPNSDeviceTokenKey = @"ARTAPNSDeviceToken";
@implementation ARTPushInternal {
__weak ARTRestInternal *_rest; // weak because rest owns self
ARTLog *_logger;
ARTPushActivationStateMachine *_activationMachine;
NSLock *_activationMachineLock;
}
- (instancetype)init:(ARTRestInternal *)rest {
if (self = [super init]) {
_rest = rest;
_logger = [rest logger];
_admin = [[ARTPushAdminInternal alloc] initWithRest:rest];
_activationMachine = nil;
_activationMachineLock = [[NSLock alloc] init];
_activationMachineLock.name = @"ActivationMachineLock";
}
return self;
}
- (dispatch_queue_t)queue {
return _rest.queue;
}
#if TARGET_OS_IOS
- (void)getActivationMachine:(void (^)(ARTPushActivationStateMachine *const))block {
if (!block) {
[NSException raise:NSInvalidArgumentException
format:@"block is nil."];
}
if (![_activationMachineLock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:60]]) {
block(nil);
return;
}
void (^callbackWithUnlock)(ARTPushActivationStateMachine *const machine) = ^(ARTPushActivationStateMachine *machine) {
[self->_activationMachineLock unlock];
block(machine);
};
if (_activationMachine == nil) {
const id<ARTPushRegistererDelegate, NSObject> delegate = _rest.options.pushRegistererDelegate;
if (delegate) {
callbackWithUnlock([self createActivationStateMachineWithDelegate:delegate]);
}
else {
dispatch_async(dispatch_get_main_queue(), ^{
// -[UIApplication delegate] is an UI API call, so needs to be called from main thread.
const id legacyDelegate = UIApplication.sharedApplication.delegate;
[self createActivationStateMachineWithDelegate:legacyDelegate
completionHandler:^(ARTPushActivationStateMachine *const machine) {
callbackWithUnlock(machine);
}];
});
}
}
else {
callbackWithUnlock(_activationMachine);
}
}
- (void)createActivationStateMachineWithDelegate:(const id<ARTPushRegistererDelegate, NSObject>)delegate
completionHandler:(void (^const)(ARTPushActivationStateMachine *_Nonnull))block {
dispatch_async(self.queue, ^{
block([self createActivationStateMachineWithDelegate:delegate]);
});
}
- (ARTPushActivationStateMachine *)createActivationStateMachineWithDelegate:(const id<ARTPushRegistererDelegate, NSObject>)delegate {
if (_activationMachine) {
[NSException raise:NSInternalInconsistencyException
format:@"_activationMachine already set."];
}
_activationMachine = [[ARTPushActivationStateMachine alloc] initWithRest:self->_rest delegate:delegate];
return _activationMachine;
}
- (ARTPushActivationStateMachine *)activationMachine {
if (![_activationMachineLock tryLock]) {
[NSException raise:NSInternalInconsistencyException
format:@"Failed to immediately acquire lock for internal testing purposes."];
}
ARTPushActivationStateMachine *const machine = _activationMachine;
if (!machine) {
[NSException raise:NSInternalInconsistencyException
format:@"There is no activation machine for internal testing purposes."];
}
[_activationMachineLock unlock];
return machine;
}
+ (void)didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceTokenData restInternal:(ARTRestInternal *)rest {
[rest.logger debug:@"ARTPush: device token data received: %@", [deviceTokenData base64EncodedStringWithOptions:0]];
NSUInteger dataLength = deviceTokenData.length;
const unsigned char *dataBuffer = deviceTokenData.bytes;
NSMutableString *hexString = [NSMutableString stringWithCapacity:(dataLength * 2)];
for (int i = 0; i < dataLength; ++i) {
[hexString appendFormat:@"%02x", dataBuffer[i]];
}
NSString *deviceToken = [hexString copy];
[rest.logger info:@"ARTPush: device token: %@", deviceToken];
NSString *currentDeviceToken = [rest.storage objectForKey:ARTAPNSDeviceTokenKey];
if ([currentDeviceToken isEqualToString:deviceToken]) {
// Already stored.
return;
}
[rest.device_nosync setAndPersistAPNSDeviceToken:deviceToken];
[rest.logger debug:@"ARTPush: device token stored"];
[rest.push getActivationMachine:^(ARTPushActivationStateMachine *stateMachine) {
[stateMachine sendEvent:[ARTPushActivationEventGotPushDeviceDetails new]];
}];
}
+ (void)didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken realtime:(ARTRealtime *)realtime {
[realtime internalAsync:^(ARTRealtimeInternal *realtime) {
[ARTPushInternal didRegisterForRemoteNotificationsWithDeviceToken:deviceToken restInternal:realtime.rest];
}];
}
+ (void)didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken rest:(ARTRest *)rest {
[rest internalAsync:^(ARTRestInternal *rest) {
[ARTPushInternal didRegisterForRemoteNotificationsWithDeviceToken:deviceToken restInternal:rest];
}];
}
+ (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error restInternal:(ARTRestInternal *)rest {
[rest.logger error:@"ARTPush: device token not received (%@)", [error localizedDescription]];
[rest.push getActivationMachine:^(ARTPushActivationStateMachine *stateMachine) {
[stateMachine sendEvent:[ARTPushActivationEventGettingPushDeviceDetailsFailed newWithError:[ARTErrorInfo createFromNSError:error]]];
}];
}
+ (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error realtime:(ARTRealtime *)realtime {
[realtime internalAsync:^(ARTRealtimeInternal *realtime) {
[ARTPushInternal didFailToRegisterForRemoteNotificationsWithError:error restInternal:realtime.rest];
}];
}
+ (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error rest:(ARTRest *)rest {
[rest internalAsync:^(ARTRestInternal *rest) {
[ARTPushInternal didFailToRegisterForRemoteNotificationsWithError:error restInternal:rest];
}];
}
- (void)activate {
[self getActivationMachine:^(ARTPushActivationStateMachine *stateMachine) {
[stateMachine sendEvent:[ARTPushActivationEventCalledActivate new]];
}];
}
- (void)deactivate {
[self getActivationMachine:^(ARTPushActivationStateMachine *stateMachine) {
[stateMachine sendEvent:[ARTPushActivationEventCalledDeactivate new]];
}];
}
#endif
@end