QuickBlox / quickblox-ios-sdk

QuickBlox iOS SDK for messaging and video calling
https://quickblox.com/developers/IOS
MIT License
397 stars 358 forks source link

Callkit is not running when the app in background, screen lock or terminate #1161

Closed ericbui148 closed 5 years ago

ericbui148 commented 5 years ago

Callkit is not running when the app in background, screen lock or terminate. it run ok when the app is active. This is my code


#import "AppDelegate.h"
#import <ChatSDK/UI.h>
#import <ChatSDK/Core.h>
#import <Quickblox/Quickblox.h>
#import <QuickbloxWebRTC/QBRTCConfig.h>
#import <QuickbloxWebRTC/QBRTCClient.h>
#import <QuickbloxWebRTC/QBRTCSession.h>
#import <ChatSDK/QBCore.h>
#import <ChatSDK/CallKitManager.h>
#import <PushKit/PushKit.h>
#import <Intents/Intents.h>

const CGFloat kQBRingThickness = 1.f;
const NSTimeInterval kQBAnswerTimeInterval = 60.f;
const NSTimeInterval kQBDialingTimeInterval = 5.f;

const NSUInteger kApplicationID = 76637;
NSString *const kAuthKey        = @"************";
NSString *const kAuthSecret     = @"************";
NSString *const kAccountKey     = @"************";
static NSString * const kVoipEvent = @"VOIPCall";

@interface AppDelegate () <QBRTCClientDelegate, PKPushRegistryDelegate>
    @property (strong, nonatomic) PKPushRegistry *voipRegistry;
    @property (strong, nonatomic) NSUUID *callUUID;
    @property (assign, nonatomic) UIBackgroundTaskIdentifier backgroundTask;
    @property (weak, nonatomic) QBRTCSession *session;
    @property (weak, nonatomic) CallKitManager *callkitInstance;
@end

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Create a network adapter to communicate with Firebase
    // The network adapter handles network traffic

    BConfiguration * config = [BConfiguration configuration];
    config.rootPath = @"***************";
    config.allowUsersToCreatePublicChats = NO;
    config.googleMapsApiKey = @"***************";
    config.clearDataWhenRootPathChanges = YES;
    config.loginUsernamePlaceholder = @"Email";
    config.pushNotificationSound = @"end_of_call.wav";
    config.messageColorMe = @"007aff";
    config.messageColorReply = @"f1f0f0";
    config.messageTextColorMe = @"ffffff";
    config.messageTextColorReply = @"000000";
    config.showEmptyChats = YES;

    // Twitter Setup
    config.twitterApiKey = @"*****************";
    config.twitterSecret = @"******************";

    // Facebook Setup
    config.facebookAppId = @"***************";

    // Google Setup
    config.googleClientKey = @"*******************";
    if ([self isFirstLauched]) {
        config.clearDatabaseWhenDataVersionChanges = YES;
    }
    config.showUserAvatarsOn1to1Threads = NO;

    [BChatSDK initialize:config app:application options:launchOptions];
    // TODO: Fix Firebase UI!!!!!!!
    UIViewController * rootViewController = BChatSDK.ui.splashScreenNavigationController;
    [self.window setRootViewController:rootViewController];
    [self enableQuickBlox];
    self.callkitInstance = CallKitManager.instance;
    return YES;
}

-(BOOL) isFirstLauched {
    if ([[NSUserDefaults standardUserDefaults] boolForKey:@"isAppAlreadyLaunchedOnce"]) {
        return true;
    } else {
        [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"isAppAlreadyLaunchedOnce"];
        [[NSUserDefaults standardUserDefaults] synchronize];
        return false;
    }
}

-(void)enableQuickBlox {
    _backgroundTask = UIBackgroundTaskInvalid;
    //Calls
    [QBSettings setApplicationID:kApplicationID];
    [QBSettings setAuthKey:kAuthKey];
    [QBSettings setAuthSecret:kAuthSecret];
    [QBSettings setAccountKey:kAccountKey];

    [QBSettings setLogLevel:QBLogLevelDebug];
    [QBSettings enableXMPPLogging];

    [QBRTCConfig setAnswerTimeInterval:kQBAnswerTimeInterval];
    [QBRTCConfig setDialingTimeInterval:kQBDialingTimeInterval];
    [QBRTCConfig setStatsReportTimeInterval:1.f];

    [QBRTCClient initializeRTC];
    [QBRTCClient.instance addDelegate:self];
    self.voipRegistry = [[PKPushRegistry alloc] initWithQueue:dispatch_get_main_queue()];
    self.voipRegistry.delegate = self;
    self.voipRegistry.desiredPushTypes = [NSSet setWithObject:PKPushTypeVoIP];
}

-(BOOL) application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
    return [BChatSDK application: app openURL: url options: options];
}

// MARK: - Application states
- (void)applicationWillEnterForeground:(UIApplication *)application {
    [BChatSDK applicationWillEnterForeground];
}

- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
    [BChatSDK application:application didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
}

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {

    NSLog(@"Did receive remote notification %@", userInfo);
    [BChatSDK application:application didReceiveRemoteNotification:userInfo];
}

- (void)applicationWillResignActive:(UIApplication *)application {
    [self openPassCodeWindow];
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
}

- (void)applicationWillTerminate:(UIApplication *)application {}

- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {

    NSLog(@"Did fail to register for remote notification with error %@", error.localizedDescription);
}

- (void) openPassCodeWindow {
    id<PUser> currentUser = BChatSDK.currentUser;
    if (currentUser != NULL && [currentUser.passCode length] != 0) {
        NSString *isEnableVerifyPassCode = [currentUser getMetaValue:bUserEnableVerifyPassCode];
        if ([isEnableVerifyPassCode isEqualToString:@"true"]) {
            UIViewController *mainViewController = self.window.rootViewController;
            [mainViewController presentViewController:BChatSDK.ui.passCodeViewController animated:YES completion:nil];
        }
    }
}

// MARK: - PKPushRegistryDelegate protocol

- (void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)pushCredentials forType:(PKPushType)type {

    //  New way, only for updated backend
    NSString *deviceIdentifier = [[[UIDevice currentDevice] identifierForVendor] UUIDString];

    QBMSubscription *subscription = [QBMSubscription subscription];
    subscription.notificationChannel = QBMNotificationChannelAPNSVOIP;
    subscription.deviceUDID = deviceIdentifier;
    subscription.deviceToken = [self.voipRegistry pushTokenForType:PKPushTypeVoIP];

    [QBRequest createSubscription:subscription successBlock:^(QBResponse *response, NSArray *objects) {
        NSLog(@"Create Subscription request - Success");
    } errorBlock:^(QBResponse *response) {
        NSLog(@"Create Subscription request - Error");
    }];
}

- (void)pushRegistry:(PKPushRegistry *)registry didInvalidatePushTokenForType:(PKPushType)type {
    NSString *deviceIdentifier = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
    [QBRequest unregisterSubscriptionForUniqueDeviceIdentifier:deviceIdentifier successBlock:^(QBResponse * _Nonnull response) {
        NSLog(@"Unregister Subscription request - Success");
    } errorBlock:^(QBError * _Nonnull error) {
        NSLog(@"Unregister Subscription request - Error");
    }];
}

- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(PKPushType)type {
    if (CallKitManager.isCallKitAvailable) {
        if ([payload.dictionaryPayload objectForKey:kVoipEvent] != nil) {
            UIApplication *application = [UIApplication sharedApplication];
            if (application.applicationState == UIApplicationStateBackground
                && _backgroundTask == UIBackgroundTaskInvalid) {
                _backgroundTask = [application beginBackgroundTaskWithExpirationHandler:^{
                    [application endBackgroundTask:self->_backgroundTask];
                    self->_backgroundTask = UIBackgroundTaskInvalid;
                }];
            }
            if (![QBChat instance].isConnected) {
                [[QBCore instance] loginWithCurrentUser];
            }
        }
    }
}

#pragma mark -
#pragma mark QBRTCClientDelegate

- (void)didReceiveNewSession:(QBRTCSession *)session userInfo:(NSDictionary *)userInfo {
    if (self.session != nil) {
        [session rejectCall:@{@"reject" : @"busy"}];
        return;
    }

    self.session = session;
    if (CallKitManager.isCallKitAvailable) {
        self.callUUID = [NSUUID UUID];
        NSMutableArray *opponentIDs = [@[session.initiatorID] mutableCopy];
        for (NSNumber *userID in session.opponentsIDs) {
            if ([userID integerValue] != [QBCore instance].currentUser.ID) {
                [opponentIDs addObject:userID];
            }
        }
        [self.callkitInstance reportIncomingCallWithUserIDs:[opponentIDs copy] session:session uuid:self.callUUID onAcceptAction:^{
            NSInteger initiatorID = session.initiatorID.integerValue;
            [QBRequest userWithID:initiatorID successBlock:^(QBResponse * _Nonnull response, QBUUser * _Nonnull user) {
                NSString * userEntityID = user.customData;
                UIViewController * viewController = [BChatSDK.ui voiceCallViewController:session andCallUUID:self.callUUID andUserEntityID: userEntityID];
                UINavigationController *nc =[[UINavigationController alloc] initWithRootViewController:viewController];
                UIViewController *mainViewController = self.window.rootViewController;
                [mainViewController presentViewController:nc animated:YES completion:nil];
            }  errorBlock:^(QBResponse * _Nonnull response) {
                //Code in here
                NSLog(@"%@", response.description);
            }];

        } completion:nil];
    } else {
        UIViewController * viewController = [BChatSDK.ui incomingCallViewController:session];
        UINavigationController *nc =[[UINavigationController alloc] initWithRootViewController:viewController];
        UIViewController *mainViewController = self.window.rootViewController;
        [mainViewController presentViewController:nc animated:YES completion:nil];
    }

}

- (void)sessionDidClose:(QBRTCSession *)session {

    if (session == self.session) {
        if (_backgroundTask != UIBackgroundTaskInvalid) {
            [[UIApplication sharedApplication] endBackgroundTask:_backgroundTask];
            _backgroundTask = UIBackgroundTaskInvalid;
        }

        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground
                && self->_backgroundTask == UIBackgroundTaskInvalid) {
                // dispatching chat disconnect in 1 second so message about call end
                // from webrtc does not cut mid sending
                // checking for background task being invalid though, to avoid disconnecting
                // from chat when another call has already being received in background
                [QBChat.instance disconnectWithCompletionBlock:nil];
            }
        });

        if (CallKitManager.isCallKitAvailable) {
            [CallKitManager.instance endCallWithUUID:self.callUUID completion:nil];
            self.callUUID = nil;
            self.session = nil;
        }
    }
}

@end
ericbui148 commented 5 years ago

I found this from quickblox document, it look like Quickblox not support similar Whatapps, skype


Calling offline users
We made it easy to call offline users.

Quickblox iOS SDK provides methods to notify an application about new events even if application is closed.

How to configure Push-notifications in your application you can find here

Assuming you have working push notifications it is very easy to notify users about new call.

Objective-CSwift
- (void)sendPushToOpponentsAboutNewCall {
    NSString *currentUserLogin = [[[QBSession currentSession] currentUser] login];
    [QBRequest sendPushWithText:[NSString stringWithFormat:@"%@ is calling you", currentUserLogin]
               toUsers:[self.session.opponentsIDs componentsJoinedByString:@","]
               successBlock:^(QBResponse * _Nonnull response, NSArray<QBMEvent *> * _Nullable events) {
        NSLog(@"Push sent!");
    } errorBlock:^(QBError * _Nullable error) {
        NSLog(@"Can not send push: %@", error);
    }];
}
ericbui148 commented 5 years ago

I missed to upload APNS certificates. Issue is fixed

shoaibhassan1 commented 4 years ago

i have moved all code of PushKit framework from UsersViewController to AppDelegate, Voip Notification recieved, callkit showing incoming call, call accepted, call get connected but audio only, both users can see their own video but not of other peer. Any Help please.