3

I'm doing a VOIP app when the incoming call is coming and app is in the foreground. I play the ringer with the code below

self.ringTimer = [[TimerService sharedInstance] createIntervalTimerWithInterval:2500 queue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) andBlock:^{
    AudioServicesPlayAlertSound(kSystemSoundID_Vibrate);
}];

NSString *ringingPath = [[NSBundle mainBundle] pathForResource:@"incoming_sound" ofType:@"mp3"];

AVAudioSession *audioSession = [AVAudioSession sharedInstance];
[audioSession setCategory:AVAudioSessionCategoryAmbient withOptions:AVAudioSessionCategoryOptionAllowBluetooth | AVAudioSessionCategoryOptionMixWithOthers error:nil];

// for normal phone receiver route it to speaker
if ([[audioSession audioRoute] isEqualToString:kAudioSessionDevice_Phone]) {
    [audioSession overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:nil];
}

NSURL *soundUrl = [NSURL fileURLWithPath:ringingPath];
NSError *error;
self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:soundUrl error:&error];

self.audioPlayer.numberOfLoops = -1;
[self.audioPlayer play];

The above code works well and it also respect the silent switch button when playing sound.

However, if I minimize the app the device will stop vibrating and playing incoming sound. This can be overcome if I set AVAudioSession category to AVAudioSessionCategoryPlayback but this category will not silent the sound when the silent switch is on.

I see the other apps like

Line

  • (Receive incoming in the app) they can keep vibrate the device after minimize until the call timeout is reached (no one answer) about one minute without notification showing before stopping (Receive call end notification).
  • (Receive incoming when app closed) they also do the same because when I dismiss the notification the device can still vibrate for the same duration.

Facebook Messenger

  • (Receive incoming in the app) they show a long notification to keep device vibrate. If I dismiss that notification it will stop right away.

  • (Receive incoming when app closed) if I dismiss the notification, the device vibrate for 2-3 more times and show a long notification (same as when receive incoming in the app)

Viber, Skype

  • (Receive incoming in the app) I'm not sure for skype, but for viber when minimize the app it stop vibrate instantly.

  • (Receive incoming when app closed) I think they have done the same way as facebook (not sure)

How can I make the device vibrate without notification like Line when minimize the app?

If I manage to find a way and make it works like Line do I need to use Voip push when the app is not in foreground to implement that method? (Will normal push notification work?)

Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
SaintTail
  • 6,160
  • 4
  • 31
  • 51
  • Do you use push notification or Pushkit ( Silent push notification ) ? – Hasya Nov 23 '16 at 06:42
  • Does it worked? – Hasya Nov 24 '16 at 08:58
  • @Hasya Sorry haven't have time to look into that yet. I manage to make it work without `PushKit` for `Receive incoming in the app` (Keep vibrate device when minimize). But for when outside of the app, I think `PushKit` is required. Will follow your suggestion and give the feedback during weekend. – SaintTail Nov 24 '16 at 14:21
  • No worries, have happy coding. Let me know for any help for VOIP based app. – Hasya Nov 24 '16 at 16:48

2 Answers2

2

You have to use pushkit ( silent push notification ).

For application in background or kill state.

Skype, whatsapp, viber etc VOIP based app works like below architecture.

When you gets payload, you have to schedule local notification. you can set sound file in local notification. that sound file plays for 30 seconds max. If you want repeat sound file until end users dont receive or decline call that also you can do with flag management and reschduling local notifications.

Use below structure to achieve your task.

Use this simplepush.php file

<?php

// Put your device token here (without spaces):

    
      $deviceToken = '1234567890123456789';
//

    
// Put your private key's passphrase here:
$passphrase = 'ProjectName';

// Put your alert message here:
$message = 'My first push notification!';



$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', 'PemFileName.pem');
stream_context_set_option($ctx, 'ssl', 'passphrase', $passphrase);

// Open a connection to the APNS server
$fp = stream_socket_client(
//  'ssl://gateway.push.apple.com:2195', $err,
    'ssl://gateway.sandbox.push.apple.com:2195', $err,
    $errstr, 60, STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $ctx);

if (!$fp)
    exit("Failed to connect: $err $errstr" . PHP_EOL);

echo 'Connected to APNS' . PHP_EOL;

// Create the payload body

$body['aps'] = array(
                     'content-available'=> 1,
                     'alert' => $message,
                     'sound' => 'default',
                     'badge' => 0,
                     );

    

// Encode the payload as JSON
    
$payload = json_encode($body);

// Build the binary notification
$msg = chr(0) . pack('n', 32) . pack('H*', $deviceToken) . pack('n', strlen($payload)) . $payload;

// Send it to the server
$result = fwrite($fp, $msg, strlen($msg));

if (!$result)
    echo 'Message not delivered' . PHP_EOL;
else
    echo 'Message successfully delivered' . PHP_EOL;

// Close the connection to the server
fclose($fp);

Use below commands to create pem file and use it in above code

$ openssl x509 -in aps_development.cer -inform der -out PushCert.pem

# Convert .p12 to .pem. Enter your pass pharse which is the same pwd that you have given while creating the .p12 certificate. PEM pass phrase also same as .p12 cert.  
$ openssl pkcs12 -nocerts -out PushKey1.pem -in pushkey.p12

Enter Import Password:

MAC verified OK

Enter PEM pass phrase:

Verifying - Enter PEM pass phrase:

# To remove passpharse for the key to access globally. This only solved my stream_socket_client() & certificate capath warnings.
$ openssl rsa -in PushKey1.pem -out PushKey1_Rmv.pem

Enter pass phrase for PushChatKey1.pem:

writing RSA key

# To join the two .pem file into one file:
$ cat PushCert.pem PushKey1_Rmv.pem > ApnsDev.pem

After that go to simplepush.php location and fire command -> php simplepush.php

This way you can test your push kit notification setup architecture.

https://zeropush.com/guide/guide-to-pushkit-and-voip

https://www.raywenderlich.com/123862/push-notifications-tutorial

Download

import UIKit
import PushKit


class AppDelegate: UIResponder, UIApplicationDelegate,PKPushRegistryDelegate{



func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {


    let types: UIRemoteNotificationType = [.Alert, .Badge, .Sound]
    application.registerForRemoteNotificationTypes(types)

    self. PushKitRegistration()

    return true
}



//MARK: - PushKitRegistration

func PushKitRegistration()
{

    let mainQueue = dispatch_get_main_queue()
    // Create a push registry object
    if #available(iOS 8.0, *) {

        let voipRegistry: PKPushRegistry = PKPushRegistry(queue: mainQueue)

        // Set the registry's delegate to self

        voipRegistry.delegate = self

        // Set the push type to VoIP

        voipRegistry.desiredPushTypes = [PKPushTypeVoIP]

    } else {
        // Fallback on earlier versions
    }


}


@available(iOS 8.0, *)
func pushRegistry(registry: PKPushRegistry!, didUpdatePushCredentials credentials: PKPushCredentials!, forType type: String!) {
    // Register VoIP push token (a property of PKPushCredentials) with server

    let hexString : String = UnsafeBufferPointer<UInt8>(start: UnsafePointer(credentials.token.bytes),
        count: credentials.token.length).map { String(format: "%02x", $0) }.joinWithSeparator("")

    print(hexString)


}


@available(iOS 8.0, *)
func pushRegistry(registry: PKPushRegistry!, didReceiveIncomingPushWithPayload payload: PKPushPayload!, forType type: String!) {
    // Process the received push
    // From here you have to schedule your local notification

}

}

In parallel if you setup Socket architecture. Then Socket used to broadcast data for example which users are online / offline and you need to know without calling an API then you can use socket, socket architecture on server will send data on online / offline user to devices without making request from device.

Nimantha
  • 6,405
  • 6
  • 28
  • 69
Hasya
  • 9,792
  • 4
  • 31
  • 46
0

After some test I found a way how Line did that.

Receive incoming call in the app

They use

[[UIApplication sharedApplication] beginBackgroundTaskWithName:expirationHandler:]

to keep the app vibrate even though you are already minimize it. This method will keep the app running and have a time duration of 180 seconds before they expire.

Receive incoming call when the app is closed

This need to use PushKit as @Hasya mention to make app awake and also call the same method as above scenario (keep device vibrate)

[[UIApplication sharedApplication] beginBackgroundTaskWithName:expirationHandler:]

This time it will expire in 40 instead of 180 seconds. Also you cannot play incoming sound via AVAudioPlayer with Ambient category here.

So you need to attach a sound (Need an extended version since there is no repeat function for you) to UILocalNotification which I have read somewhere that you can play a sound up to 30 seconds.

Some concern when use PushKit

If you have old system which use APNS you need to maintain both token (APNS and VOIP) on server side. Because you cannot send APNS token with VOIP certificate. The token need to be sent with the correct certificate's type.

Community
  • 1
  • 1
SaintTail
  • 6,160
  • 4
  • 31
  • 51