I need to update an application from us to support iOS 9 and iOS 10, so my problem is to use UNUserNotificationCenter for PushNotifications.
So with iOS 9 we have a method which returns result of UIUserNotificationSettings like
- (BOOL)alertEnabled {
UIUserNotificationSettings *theSettings = [[UIApplication sharedApplication] currentUserNotificationSettings];
return [theSettings types] & UIUserNotificationTypeAlert;
}
With iOS 10 I did something like
- (void)userNotificationsAuthorization :(void (^)(BOOL alertIsActive))completion {
[[UNUserNotificationCenter currentNotificationCenter] getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
completion(settings.alertSetting == UNNotificationSettingEnabled);
}];
}
Call it and get it via completion handler.
My Question: Is there some possibility to use getNotificationSettingsWithCompletionHandler and return the value instead of the completion handler that I can use it into my alertEnabled method?
Thanks a lot.
Update:
With this approach it's working
- (BOOL)alertIsEnabled {
__block BOOL alertIsActive = NO;
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"10.0")) {
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
[[UNUserNotificationCenter currentNotificationCenter] getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
alertIsActive = settings.alertSetting == UNNotificationSettingEnabled;
dispatch_semaphore_signal(sem);
}];
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
}
else {
UIUserNotificationSettings *theSettings = [[UIApplication sharedApplication] currentUserNotificationSettings];
alertIsActive = [theSettings types] & UIUserNotificationTypeAlert;
}
return alertIsActive;
}
but maybe there is a better solution
So, after a week of tests, it's working fine for me.
For my specific problem, I created a custom class.
SpecicPush.h
#import <UIKit/UIKit.h>
typedef NS_ENUM(NSInteger , PushNotificationType) {
PushNotificationTypeNone = 0, // the application may not present any UI upon a notification being received
PushNotificationTypeBadge = 1 << 0, // the application may badge its icon upon a notification being received
PushNotificationTypeSound = 1 << 1, // the application may play a sound upon a notification being received
PushNotificationTypeAlert = 1 << 2, // the application may display an alert upon a notification being received
};
#interface SpecificPush : NSObject
#property (nonatomic, readonly) PushNotificationType currentNotificationSettings;
+ (SpecificPush *)sharedInstance;
- (PushNotificationType)types;
#end
SpecificPush.m
#import "SpecificPush.h"
#import <UserNotifications/UserNotifications.h>
#interface SpecificPush()
#property (nonatomic) PushNotificationType currentNotificationSettings;
#end
#implementation SpecificPush
#pragma mark - Init
static SpecificPush *instance = nil;
+ (SpecificPush *)sharedInstance
{
#synchronized (self)
{
if (instance == nil)
{
[SpecificPush new];
}
}
return instance;
}
- (instancetype)init
{
NSAssert(!instance, #"WARNING - Instance of SpecifishPush already exists");
self = [super init];
if (self)
{
self.currentNotificationSettings = PushNotificationTypeNone;
}
instance = self;
return self;
}
- (PushNotificationType)types
{
if (IS_IOS10)
{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[[UNUserNotificationCenter currentNotificationCenter] getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings *settings) {
if ((settings.soundSetting == UNNotificationSettingDisabled) && (settings.alertSetting == UNNotificationSettingDisabled) && (settings.soundSetting == UNNotificationSettingDisabled))
{
self.currentNotificationSettings = PushNotificationTypeNone;
}
if (settings.badgeSetting == UNNotificationSettingEnabled)
{
self.currentNotificationSettings = PushNotificationTypeBadge;
}
if (settings.soundSetting == UNNotificationSettingEnabled)
{
self.currentNotificationSettings = PushNotificationTypeSound;
}
if (settings.alertStyle == UNNotificationSettingEnabled)
{
self.currentNotificationSettings = PushNotificationTypeAlert;
}
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_release(semaphore);
}
else
{
UIUserNotificationSettings *settings = [[UIApplication sharedApplication] currentUserNotificationSettings];
if (settings.types == UIUserNotificationTypeNone)
{
self.currentNotificationSettings = PushNotificationTypeNone;
}
if (settings.types & UIUserNotificationTypeBadge)
{
self.currentNotificationSettings = PushNotificationTypeBadge;
}
if (settings.types & UIUserNotificationTypeSound)
{
self.currentNotificationSettings = PushNotificationTypeSound;
}
if (settings.types & UIUserNotificationTypeAlert)
{
self.currentNotificationSettings = PushNotificationTypeAlert;
}
}
return self.currentNotificationSettings;
}
#end
I used the same NS_ENUM as UIUserNotificationType. Now I can easy use old implementation. Insted of
- (BOOL)alertEnabled {
UIUserNotificationSettings *theSettings = [[UIApplication sharedApplication] currentUserNotificationSettings];
return [theSettings types] & UIUserNotificationTypeAlert;
}
I use
- (BOOL)alertEnabled {
return [[SpecificPush sharedInstance] types] & PushNotificationTypeAlert;
}
Related
I am working on Twilio programmable voice SDK. I integrated it with the help of documentation and GitHub SDK. I can make a call from iOS to any number. It works fine.
The issue is on receiving a call on Twilio number. I did everything that was mentioned in Twilio installation documentation and GitHub SDK, but its not working.
here is the code:
#import PushKit;
#import CallKit;
#import TwilioVoice;
#import UserNotifications;
static NSString * kAccessToken = #"";
NSString * phoneNumber = #"";
NSString * newToken = #"";
static NSString *const kTwimlParamTo = #"to";
static NSInteger const kRegistrationTTLInDays = 365;
NSString * const kCachedDeviceToken = #"CachedDeviceToken";
NSString * const kCachedBindingTime = #"CachedBindingTime";
#interface RCTCallPackageModule () <TVONotificationDelegate, TVOCallDelegate, CXProviderDelegate, UITextFieldDelegate, AVAudioPlayerDelegate , PushKitEventDelegate, PKPushRegistryDelegate>
#property (nonatomic, weak) id<PushKitEventDelegate> pushKitEventDelegate;
#property (nonatomic, strong) void(^incomingPushCompletionCallback)(void);
#property (nonatomic, strong) void(^callKitCompletionCallback)(BOOL);
#property (nonatomic, strong) TVODefaultAudioDevice *audioDevice;
#property (nonatomic, strong) NSMutableDictionary *activeCallInvites;
#property (nonatomic, strong) NSMutableDictionary *activeCalls;
// activeCall represents the last connected call
#property (nonatomic, strong) TVOCall *activeCall;
#property (nonatomic, strong) CXProvider *callKitProvider;
#property (nonatomic, strong) CXCallController *callKitCallController;
#property (nonatomic, assign) BOOL userInitiatedDisconnect;
#property (nonatomic, assign) BOOL playCustomRingback;
#property (nonatomic, strong) AVAudioPlayer *ringtonePlayer;
#end
//#import <React/RCTLog.h>
#implementation RCTCallPackageModule
-(NSString *)fetchAccessToken {
NSString *accessToken = [NSString stringWithContentsOfURL:[NSURL URLWithString:#"http://54.172.240.200:4000/accessToken"]
encoding:NSUTF8StringEncoding
error:nil];
return accessToken;
}
-(void) mainIntializerFunction {
self.pushKitEventDelegate = self;
self.callKitCallController = [[CXCallController alloc] init];
// fetchData();
/* Please note that the designated initializer `[CXProviderConfiguration initWithLocalizedName:]` has been deprecated on iOS 14. */
CXProviderConfiguration *configuration = [[CXProviderConfiguration alloc] initWithLocalizedName:#"Voice Quickstart"];
configuration.maximumCallGroups = 1;
configuration.maximumCallsPerCallGroup = 1;
self.callKitProvider = [[CXProvider alloc] initWithConfiguration:configuration];
[self.callKitProvider setDelegate:self queue:nil];
self.audioDevice = [TVODefaultAudioDevice audioDevice];
TwilioVoiceSDK.audioDevice = self.audioDevice;
self.activeCallInvites = [NSMutableDictionary dictionary];
self.activeCalls = [NSMutableDictionary dictionary];
self.playCustomRingback = NO;
[self MainFunctionToCall]; }
- (void)dealloc {
if (self.callKitProvider) {
[self.callKitProvider invalidate];
} }
-(void) MainFunctionToCall {
if (self.activeCall != nil) {
self.userInitiatedDisconnect = YES;
[self performEndCallActionWithUUID:self.activeCall.uuid];
} else {
NSUUID *uuid = [NSUUID UUID];
NSString *handle = #"Voice Bot";
[self checkRecordPermission:^(BOOL permissionGranted) {
[center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert + UNAuthorizationOptionSound)
completionHandler:^(BOOL granted, NSError * _Nullable error) {
// Enable or disable features based on authorization.
}];
// [[UIApplication sharedApplication] registerForRemoteNotifications];
[self performStartCallActionWithUUID:uuid handle:handle];
}];
}
}
// performEndCallActiveUUID
- (void)performEndCallActionWithUUID:(NSUUID *)uuid {
CXEndCallAction *endCallAction = [[CXEndCallAction alloc] initWithCallUUID:uuid];
CXTransaction *transaction = [[CXTransaction alloc] initWithAction:endCallAction];
[self.callKitCallController requestTransaction:transaction completion:^(NSError *error) {
if (error) {
NSLog(#"EndCallAction transaction request failed: %#", [error localizedDescription]);
}
else {
}
}];
}
// checkRecordPermission
- (void)checkRecordPermission:(void(^)(BOOL permissionGranted))completion {
AVAudioSessionRecordPermission permissionStatus = [[AVAudioSession sharedInstance] recordPermission];
switch (permissionStatus) {
case AVAudioSessionRecordPermissionGranted:
// Record permission already granted.
completion(YES);
break;
case AVAudioSessionRecordPermissionDenied:
// Record permission denied.
completion(NO);
break;
case AVAudioSessionRecordPermissionUndetermined:
{
[[AVAudioSession sharedInstance] requestRecordPermission:^(BOOL granted) {
completion(granted);
}];
break;
}
default:
completion(NO);
break;
}
}
#pragma mark - CallKit Actions
- (void)performStartCallActionWithUUID:(NSUUID *)uuid handle:(NSString *)handle {
if (uuid == nil || handle == nil) {
return;
}
CXHandle *callHandle = [[CXHandle alloc] initWithType:CXHandleTypeGeneric value:handle];
CXStartCallAction *startCallAction = [[CXStartCallAction alloc] initWithCallUUID:uuid handle:callHandle];
CXTransaction *transaction = [[CXTransaction alloc] initWithAction:startCallAction];
[self.callKitCallController requestTransaction:transaction completion:^(NSError *error) {
if (error) {
NSLog(#"StartCallAction transaction request failed: %#", [error localizedDescription]);
} else {
CXCallUpdate *callUpdate = [[CXCallUpdate alloc] init];
callUpdate.remoteHandle = callHandle;
callUpdate.supportsDTMF = YES;
callUpdate.supportsHolding = YES;
callUpdate.supportsGrouping = NO;
callUpdate.supportsUngrouping = NO;
callUpdate.hasVideo = NO;
[self.callKitProvider reportCallWithUUID:uuid updated:callUpdate];
}
}];
}
#pragma mark - PushKitEventDelegate
- (void)credentialsUpdated:(PKPushCredentials *)credentials {
NSData *cachedDeviceToken = [[NSUserDefaults standardUserDefaults] objectForKey:kCachedDeviceToken];
if ([self registrationRequired] || ![cachedDeviceToken isEqualToData:credentials.token]) {
cachedDeviceToken = credentials.token;
[TwilioVoiceSDK registerWithAccessToken:kAccessToken
deviceToken:cachedDeviceToken
completion:^(NSError *error) {
if (error) {
} else {
// Save the device token after successfully registered.
[[NSUserDefaults standardUserDefaults] setObject:cachedDeviceToken forKey:kCachedDeviceToken];
[[NSUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:kCachedBindingTime];
}
}];
}
}
- (BOOL)registrationRequired {
BOOL registrationRequired = YES;
NSDate *lastBindingCreated = [[NSUserDefaults standardUserDefaults] objectForKey:kCachedBindingTime];
if (lastBindingCreated) {
NSDateComponents *dayComponent = [[NSDateComponents alloc] init];
// Register upon half of the TTL
dayComponent.day = kRegistrationTTLInDays / 2;
NSDate *bindingExpirationDate = [[NSCalendar currentCalendar] dateByAddingComponents:dayComponent toDate:lastBindingCreated options:0];
NSDate *currentDate = [NSDate date];
if ([bindingExpirationDate compare:currentDate] == NSOrderedDescending) {
registrationRequired = NO;
}
}
return registrationRequired;
}
- (void)credentialsInvalidated {
NSData *cachedDeviceToken = [[NSUserDefaults standardUserDefaults] objectForKey:kCachedDeviceToken];
if ([cachedDeviceToken length] > 0) {
[TwilioVoiceSDK unregisterWithAccessToken:kAccessToken
deviceToken:cachedDeviceToken
completion:^(NSError *error) {
if (error) {
} else {
}
}];
}
[[NSUserDefaults standardUserDefaults] removeObjectForKey:kCachedDeviceToken];
// Remove the cached binding as credentials are invalidated
[[NSUserDefaults standardUserDefaults] removeObjectForKey:kCachedBindingTime];
}
-(void)incomingPushReceived:(PKPushPayload *)payload withCompletionHandler:(void (^)(void))completion {
// The Voice SDK will use main queue to invoke `cancelledCallInviteReceived:error` when delegate queue is not passed
if (![TwilioVoiceSDK handleNotification:payload.dictionaryPayload delegate:self delegateQueue:nil]) {
}
if (completion) {
if ([[NSProcessInfo processInfo] operatingSystemVersion].majorVersion < 13) {
// Save for later when the notification is properly handled.
self.incomingPushCompletionCallback = completion;
} else {
completion();
}
}
}
- (void)incomingPushHandled {
if (self.incomingPushCompletionCallback) {
self.incomingPushCompletionCallback();
self.incomingPushCompletionCallback = nil;
}
}
#pragma mark - TVONotificationDelegate
- (void)callInviteReceived:(TVOCallInvite *)callInvite {
[[NSUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:kCachedBindingTime];
if (callInvite.callerInfo.verified != nil && [callInvite.callerInfo.verified boolValue]) {
}
NSString *from = #"Voice Bot";
if (callInvite.from) {
from = [callInvite.from stringByReplacingOccurrencesOfString:#"client:" withString:#""];
}
// Always report to CallKit
[self reportIncomingCallFrom:from withUUID:callInvite.uuid];
self.activeCallInvites[[callInvite.uuid UUIDString]] = callInvite;
if ([[NSProcessInfo processInfo] operatingSystemVersion].majorVersion < 13) {
[self incomingPushHandled];
}
}
- (void)cancelledCallInviteReceived:(TVOCancelledCallInvite *)cancelledCallInvite error:(NSError *)error {
TVOCallInvite *callInvite;
for (NSString *uuid in self.activeCallInvites) {
TVOCallInvite *activeCallInvite = [self.activeCallInvites objectForKey:uuid];
if ([cancelledCallInvite.callSid isEqualToString:activeCallInvite.callSid]) {
callInvite = activeCallInvite;
break;
}
}
if (callInvite) {
[self performEndCallActionWithUUID:callInvite.uuid];
[self.activeCallInvites removeObjectForKey:callInvite.uuid.UUIDString];
}
}
- (void)callDidStartRinging:(TVOCall *)call {
NSLog(#"callDidStartRinging:");
if (self.playCustomRingback) {
[self playRingback];
}
// [self.placeCallButton setTitle:#"Ringing" forState:UIControlStateNormal];
}
- (void)callDidConnect:(TVOCall *)call {
NSLog(#"callDidConnect:");
if (self.playCustomRingback) {
[self stopRingback];
}
[self sendEventWithName:#"onSessionConnect" body:#"Connected"];
self.callKitCompletionCallback(YES);
}
- (void)call:(TVOCall *)call isReconnectingWithError:(NSError *)error {
NSLog(#"Call is reconnecting");
}
- (void)callDidReconnect:(TVOCall *)call {
NSLog(#"Call reconnected");
}
- (void)call:(TVOCall *)call didFailToConnectWithError:(NSError *)error {
NSLog(#"Call failed to connect: %#", error);
self.callKitCompletionCallback(NO);
[self.callKitProvider reportCallWithUUID:call.uuid endedAtDate:[NSDate date] reason:CXCallEndedReasonFailed];
[self sendEventWithName:#"onSessionConnect" body:#"Failure"];
[self callDisconnected:call];
}
- (void)call:(TVOCall *)call didDisconnectWithError:(NSError *)error {
if (error) {
NSLog(#"Call failed: %#", error);
} else {
NSLog(#"Call disconnected");
}
if (!self.userInitiatedDisconnect) {
CXCallEndedReason reason = CXCallEndedReasonRemoteEnded;
if (error) {
reason = CXCallEndedReasonFailed;
}
[self.callKitProvider reportCallWithUUID:call.uuid endedAtDate:[NSDate date] reason:reason];
}
[self sendEventWithName:#"onSessionConnect" body:#"Disconnected"];
[self callDisconnected:call];
}
- (void)callDisconnected:(TVOCall *)call {
if ([call isEqual:self.activeCall]) {
self.activeCall = nil;
}
[self.activeCalls removeObjectForKey:call.uuid.UUIDString];
self.userInitiatedDisconnect = NO;
if (self.playCustomRingback) {
[self stopRingback];
}
}
- (void)call:(TVOCall *)call
didReceiveQualityWarnings:(NSSet<NSNumber *> *)currentWarnings
previousWarnings:(NSSet<NSNumber *> *)previousWarnings {
/**
* currentWarnings: existing quality warnings that have not been cleared yet
* previousWarnings: last set of warnings prior to receiving this callback
*
* Example:
* - currentWarnings: { A, B }
* - previousWarnings: { B, C }
* - intersection: { B }
*
* Newly raised warnings = currentWarnings - intersection = { A }
* Newly cleared warnings = previousWarnings - intersection = { C }
*/
NSMutableSet *warningIntersetction = [currentWarnings mutableCopy];
[warningIntersetction intersectSet:previousWarnings];
NSMutableSet *newWarnings = [currentWarnings mutableCopy];
[newWarnings minusSet:warningIntersetction];
if ([newWarnings count] > 0) {
[self qualityWarningUpdatePopup:newWarnings isCleared:NO];
}
NSMutableSet *clearedWarnings = [previousWarnings mutableCopy];
[clearedWarnings minusSet:warningIntersetction];
if ([clearedWarnings count] > 0) {
[self qualityWarningUpdatePopup:clearedWarnings isCleared:YES];
}
}
- (void)qualityWarningUpdatePopup:(NSSet *)warnings isCleared:(BOOL)cleared {
NSString *popupMessage = (cleared)? #"Warnings cleared:" : #"Warnings detected:";
for (NSNumber *warning in warnings) {
NSString *warningName = [self warningString:[warning unsignedIntValue]];
popupMessage = [popupMessage stringByAppendingString:[NSString stringWithFormat:#" %#", warningName]];
}
[UIView animateWithDuration:1.0f
animations:^{
} completion:^(BOOL finished) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[UIView animateWithDuration:1.0 animations:^{
} completion:^(BOOL finished) {
}];
});
}];
}
- (NSString *)warningString:(TVOCallQualityWarning)qualityWarning {
switch (qualityWarning) {
case TVOCallQualityWarningHighRtt:
return #"high-rtt";
break;
case TVOCallQualityWarningHighJitter:
return #"high-jitter";
break;
case TVOCallQualityWarningHighPacketsLostFraction:
return #"high-packets-lost-fraction";
break;
case TVOCallQualityWarningLowMos:
return #"low-mos";
break;
case TVOCallQualityWarningConstantAudioInputLevel:
return #"constant-audio-input-level";
break;
default:
return #"Unknown warning";
break;
}
}
-(void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(NSString *)type {
NSLog(#"pushRegistry:didReceiveIncomingPushWithPayload:forType:");
if ([type isEqualToString:PKPushTypeVoIP]) {
// The Voice SDK will use main queue to invoke `cancelledCallInviteReceived:error` when delegate queue is not passed
if (![TwilioVoiceSDK handleNotification:payload.dictionaryPayload delegate:self delegateQueue:nil]) {
NSLog(#"This is not a valid Twilio Voice notification.");
}
else
{
}
}
}
/**
* This delegate method is available on iOS 11 and above. Call the completion handler once the
* notification payload is passed to the `TwilioVoice.handleNotification()` method.
*/
- (void)pushRegistry:(PKPushRegistry *)registry
didReceiveIncomingPushWithPayload:(PKPushPayload *)payload
forType:(PKPushType)type
withCompletionHandler:(void (^)(void))completion {
NSLog(#"pushRegistry:didReceiveIncomingPushWithPayload:forType:withCompletionHandler:");
// Save for later when the notification is properly handled.
self.incomingPushCompletionCallback = completion;
if ([type isEqualToString:PKPushTypeVoIP]) {
// The Voice SDK will use main queue to invoke `cancelledCallInviteReceived:error` when delegate queue is not passed
if (![TwilioVoiceSDK handleNotification:payload.dictionaryPayload delegate:self delegateQueue:nil]) {
NSLog(#"This is not a valid Twilio Voice notification.");
}
}
if ([[NSProcessInfo processInfo] operatingSystemVersion].majorVersion < 13) {
// Save for later when the notification is properly handled.
self.incomingPushCompletionCallback = completion;
} else {
/**
* The Voice SDK processes the call notification and returns the call invite synchronously. Report the incoming call to
* CallKit and fulfill the completion before exiting this callback method.
*/
completion();
}
}
#pragma mark - AVAudioSession
- (void)toggleAudioRoute:(BOOL)toSpeaker {
// The mode set by the Voice SDK is "VoiceChat" so the default audio route is the built-in receiver. Use port override to switch the route.
self.audioDevice.block = ^ {
// We will execute `kDefaultAVAudioSessionConfigurationBlock` first.
kTVODefaultAVAudioSessionConfigurationBlock();
// Overwrite the audio route
AVAudioSession *session = [AVAudioSession sharedInstance];
NSError *error = nil;
if (toSpeaker) {
if (![session overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:&error]) {
NSLog(#"Unable to reroute audio: %#", [error localizedDescription]);
}
} else {
if (![session overrideOutputAudioPort:AVAudioSessionPortOverrideNone error:&error]) {
NSLog(#"Unable to reroute audio: %#", [error localizedDescription]);
}
}
};
self.audioDevice.block();
}
#pragma mark - CXProviderDelegate
- (void)providerDidReset:(CXProvider *)provider {
NSLog(#"providerDidReset:");
self.audioDevice.enabled = NO;
}
- (void)providerDidBegin:(CXProvider *)provider {
NSLog(#"providerDidBegin:");
}
- (void)provider:(CXProvider *)provider didActivateAudioSession:(AVAudioSession *)audioSession {
NSLog(#"provider:didActivateAudioSession:");
self.audioDevice.enabled = YES;
}
- (void)provider:(CXProvider *)provider didDeactivateAudioSession:(AVAudioSession *)audioSession {
NSLog(#"provider:didDeactivateAudioSession:");
self.audioDevice.enabled = NO;
}
- (void)provider:(CXProvider *)provider timedOutPerformingAction:(CXAction *)action {
NSLog(#"provider:timedOutPerformingAction:");
}
- (void)provider:(CXProvider *)provider performStartCallAction:(CXStartCallAction *)action {
NSLog(#"provider:performStartCallAction:");
[self.callKitProvider reportOutgoingCallWithUUID:action.callUUID startedConnectingAtDate:[NSDate date]];
__weak typeof(self) weakSelf = self;
[self performVoiceCallWithUUID:action.callUUID client:nil completion:^(BOOL success) {
__strong typeof(self) strongSelf = weakSelf;
if (success) {
NSLog(#"performVoiceCallWithUUID successful");
[strongSelf.callKitProvider reportOutgoingCallWithUUID:action.callUUID connectedAtDate:[NSDate date]];
} else {
NSLog(#"performVoiceCallWithUUID failed");
}
[action fulfill];
}];
}
- (void)provider:(CXProvider *)provider performAnswerCallAction:(CXAnswerCallAction *)action {
NSLog(#"provider:performAnswerCallAction:");
[self performAnswerVoiceCallWithUUID:action.callUUID completion:^(BOOL success) {
if (success) {
NSLog(#"performAnswerVoiceCallWithUUID successful");
} else {
NSLog(#"performAnswerVoiceCallWithUUID failed");
}
}];
[action fulfill];
}
- (void)provider:(CXProvider *)provider performEndCallAction:(CXEndCallAction *)action {
NSLog(#"provider:performEndCallAction:");
TVOCallInvite *callInvite = self.activeCallInvites[action.callUUID.UUIDString];
TVOCall *call = self.activeCalls[action.callUUID.UUIDString];
if (callInvite) {
[callInvite reject];
[self.activeCallInvites removeObjectForKey:callInvite.uuid.UUIDString];
} else if (call) {
[call disconnect];
} else {
NSLog(#"Unknown UUID to perform end-call action with");
}
[action fulfill];
}
- (void)provider:(CXProvider *)provider performSetHeldCallAction:(CXSetHeldCallAction *)action {
TVOCall *call = self.activeCalls[action.callUUID.UUIDString];
if (call) {
[call setOnHold:action.isOnHold];
[action fulfill];
} else {
[action fail];
}
}
- (void)provider:(CXProvider *)provider performSetMutedCallAction:(CXSetMutedCallAction *)action {
TVOCall *call = self.activeCalls[action.callUUID.UUIDString];
if (call) {
[call setMuted:action.isMuted];
[action fulfill];
} else {
[action fail];
}
}
#pragma mark - CallKit Actions
- (void)reportIncomingCallFrom:(NSString *) from withUUID:(NSUUID *)uuid {
CXHandle *callHandle = [[CXHandle alloc] initWithType:CXHandleTypeGeneric value:from];
CXCallUpdate *callUpdate = [[CXCallUpdate alloc] init];
callUpdate.remoteHandle = callHandle;
callUpdate.supportsDTMF = YES;
callUpdate.supportsHolding = YES;
callUpdate.supportsGrouping = NO;
callUpdate.supportsUngrouping = NO;
callUpdate.hasVideo = NO;
[self.callKitProvider reportNewIncomingCallWithUUID:uuid update:callUpdate completion:^(NSError *error) {
if (!error) {
}
else {
}
}];
}
- (void)performVoiceCallWithUUID:(NSUUID *)uuid
client:(NSString *)client
completion:(void(^)(BOOL success))completionHandler {
__weak typeof(self) weakSelf = self;
TVOConnectOptions *connectOptions = [TVOConnectOptions optionsWithAccessToken:kAccessToken block:^(TVOConnectOptionsBuilder *builder) {
__strong typeof(self) strongSelf = weakSelf;
builder.params = #{kTwimlParamTo:phoneNumber};
builder.uuid = uuid;
}];
TVOCall *call = [TwilioVoiceSDK connectWithOptions:connectOptions delegate:self];
if (call) {
self.activeCall = call;
self.activeCalls[call.uuid.UUIDString] = call;
}
self.callKitCompletionCallback = completionHandler;
}
- (void)performAnswerVoiceCallWithUUID:(NSUUID *)uuid
completion:(void(^)(BOOL success))completionHandler {
TVOCallInvite *callInvite = self.activeCallInvites[uuid.UUIDString];
NSAssert(callInvite, #"No CallInvite matches the UUID");
TVOAcceptOptions *acceptOptions = [TVOAcceptOptions optionsWithCallInvite:callInvite block:^(TVOAcceptOptionsBuilder *builder) {
builder.uuid = callInvite.uuid;
}];
TVOCall *call = [callInvite acceptWithOptions:acceptOptions delegate:self];
if (!call) {
completionHandler(NO);
} else {
self.callKitCompletionCallback = completionHandler;
self.activeCall = call;
self.activeCalls[call.uuid.UUIDString] = call;
}
[self.activeCallInvites removeObjectForKey:callInvite.uuid.UUIDString];
if ([[NSProcessInfo processInfo] operatingSystemVersion].majorVersion < 13) {
[self incomingPushHandled];
}
}
#pragma mark - Ringtone
- (void)playRingback {
NSString *ringtonePath = [[NSBundle mainBundle] pathForResource:#"ringtone" ofType:#"wav"];
if ([ringtonePath length] <= 0) {
return;
}
NSError *error;
self.ringtonePlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL URLWithString:ringtonePath] error:&error];
if (error != nil) {
} else {
self.ringtonePlayer.delegate = self;
self.ringtonePlayer.numberOfLoops = -1;
self.ringtonePlayer.volume = 1.0f;
[self.ringtonePlayer play];
}
}
- (void)stopRingback {
if (!self.ringtonePlayer.isPlaying) {
return;
}
[self.ringtonePlayer stop];
}
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {
if (flag) {
NSLog(#"Audio player finished playing successfully");
} else {
NSLog(#"Audio player finished playing with some error");
}
}
- (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError *)error {
NSLog(#"Decode error occurred: %#", error);
}
#end
I have integrated APNS and want to display image in remote notification like below;
I have used below code with reference link;
AppDelegate.h
#import <UserNotifications/UserNotifications.h>
#interface AppDelegate : UIResponder <UIApplicationDelegate,UNUserNotificationCenterDelegate>
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[self registerForRemoteNotification];
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
UIViewController *vc1 = [storyboard instantiateViewControllerWithIdentifier:#"mainscreen"];
self.window.rootViewController = vc1;
return YES;
}
- (void)registerForRemoteNotification
{
if(SYSTEM_VERSION_GRATERTHAN_OR_EQUALTO(#"10.0")) {
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
center.delegate = self;
[center requestAuthorizationWithOptions:(UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionBadge) completionHandler:^(BOOL granted, NSError * _Nullable error){
[[UIApplication sharedApplication] registerForRemoteNotifications];
}];
}
else {
[[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]];
[[UIApplication sharedApplication] registerForRemoteNotifications];
}
}
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken{
DeviceToken = [[NSString alloc]initWithFormat:#"%#",[[[deviceToken description] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:#"<>"]] stringByReplacingOccurrencesOfString:#" " withString:#""]];
NSLog(#"Device Token = %#",DeviceToken);
}
Then, I have created new target with UNNotificationServiceExtension and created new bundle id "com.RichPush.app.Service-Extension" , I have also created new certificate and provision profile with above bundle for UNNotificationServiceExtension.
NotificationService.h
#import <UserNotifications/UserNotifications.h>
#interface NotificationService : UNNotificationServiceExtension
#end
NotificationService.m
#import "NotificationService.h"
#interface NotificationService ()
#property (nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver);
#property (nonatomic, strong) UNMutableNotificationContent *bestAttemptContent;
#end
#implementation NotificationService
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
self.contentHandler = contentHandler;
self.bestAttemptContent = [request.content mutableCopy];
// Modify the notification content here...
//self.bestAttemptContent.body = [NSString stringWithFormat:#"%# [modified]", self.bestAttemptContent.body];
// check for media attachment, example here uses custom payload keys mediaUrl and mediaType
NSDictionary *userInfo = request.content.userInfo;
if (userInfo == nil) {
[self contentComplete];
return;
}
NSString *mediaUrl = userInfo[#"mediaUrl"];
NSString *mediaType = userInfo[#"mediaType"];
if (mediaUrl == nil || mediaType == nil) {
[self contentComplete];
return;
}
// load the attachment
[self loadAttachmentForUrlString:mediaUrl
withType:mediaType
completionHandler:^(UNNotificationAttachment *attachment) {
if (attachment) {
self.bestAttemptContent.attachments = [NSArray arrayWithObject:attachment];
}
[self contentComplete];
}];
}
- (void)serviceExtensionTimeWillExpire {
// Called just before the extension will be terminated by the system.
// Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
[self contentComplete];
}
- (void)contentComplete {
self.contentHandler(self.bestAttemptContent);
}
- (NSString *)fileExtensionForMediaType:(NSString *)type {
NSString *ext = type;
if ([type isEqualToString:#"image"]) {
ext = #"jpg";
}
return [#"." stringByAppendingString:ext];
}
- (void)loadAttachmentForUrlString:(NSString *)urlString withType:(NSString *)type completionHandler:(void(^)(UNNotificationAttachment *))completionHandler {
__block UNNotificationAttachment *attachment = nil;
NSURL *attachmentURL = [NSURL URLWithString:urlString];
NSString *fileExt = [self fileExtensionForMediaType:type];
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
[[session downloadTaskWithURL:attachmentURL
completionHandler:^(NSURL *temporaryFileLocation, NSURLResponse *response, NSError *error) {
if (error != nil) {
NSLog(#"%#", error.localizedDescription);
} else {
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *localURL = [NSURL fileURLWithPath:[temporaryFileLocation.path stringByAppendingString:fileExt]];
[fileManager moveItemAtURL:temporaryFileLocation toURL:localURL error:&error];
NSError *attachmentError = nil;
attachment = [UNNotificationAttachment attachmentWithIdentifier:#"" URL:localURL options:nil error:&attachmentError];
if (attachmentError) {
NSLog(#"%#", attachmentError.localizedDescription);
}
}
completionHandler(attachment);
}] resume];
}
#end
I used below plist for service extension:
My Playload is;
{
"aps": {
"alert": {
"title": "title",
"body": "Your message Here",
"mutable-content": "1",
"category": "myNotificationCategory"
}
},
"mediaUrl": "https://upload.wikimedia.org/wikipedia/commons/thumb/2/2a/FloorGoban.JPG/1024px-FloorGoban.jpg",
"mediaType": "image"
}
Please let me know where I am wrong.
Thanks in Advance.
You need to put the "mutable-content": "1", flag outside the "alert"-object in your payload.
You have a mistake here:
Then, I have created new target with UNNotificationServiceExtension and created new bundle id "com.RichPush.app.Service-Extension" , I have also created new certificate and provision profile with above bundle for UNNotificationServiceExtension.
use UNNotificationContentExtension instead of UNNotificationServiceExtension
after create new target, xcode will create swift (obj- c) file and storyboard interface
I have a simple example on swift code, which load image and show on large push notification view
class NotificationViewController: UIViewController, UNNotificationContentExtension {
//image provided from storyboard
#IBOutlet weak var image: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any required interface initialization here.
}
func didReceive(_ notification: UNNotification) {
let userInfo = notification.request.content.userInfo
// get image url
// and load image here
}
}
From Apple doc:
UNNotificationServiceExtension
Modifies the content of remote notifications before they are delivered to the user.
and
UNNotificationContentExtension
Presents a custom interface for a delivered local or remote notification.
Your notification payload should be like this:
aps = {
alert = {
body = Testing;
title = Dining;
};
badge = 1;
"content-available" = 1;
"mutable-content" = 1;
sound = "Bleep-Sound.wav";
};
}
Content-available and mutable-content should be outside the alert and also you should add the badge count too....
The result of the test is always 0, as the code after the getNotificationSettingsWithCompletionHandler: block is executed before notificationsAlertAuthorization value is set in the block.
How do I solve this problem?
in AppDelegate.h:
- (BOOL)testNotificationsAuthorization;
in AppDelegate.m:
- (BOOL)testNotificationsAuthorization {
__block BOOL notificationsAlertAuthorization = 0;
[[UNUserNotificationCenter currentNotificationCenter] getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
if (settings.alertSetting == UNNotificationSettingEnabled) {
notificationsAlertAuthorization = YES;
NSLog(#"settings.alertSetting: YES");
}
}];
// gets executed before notificationsAlertAuthorization is set,
// result is always NO.
if (notificationsAlertAuthorization == YES) {
NSLog(#"Alert Authorization Granted");
return YES;
} else {
NSLog(#"Alert Authorization NOT Granted");
return NO;
} }
in ViewController:
- (IBAction)testForNotificationSettings:(UIButton *)sender {
BOOL result = [[[UIApplication sharedApplication] delegate] testNotificationsAuthorization];
NSLog(#"testForNotificationSettings result: %d", result);}
log:
2016-11-20 17:18:51.221 UserNotificationsTest[11873:536763] Alert Authorization NOT Granted
2016-11-20 17:18:51.222 UserNotificationsTest[11873:536763] testForNotificationSettings result: 0
2016-11-20 17:18:51.223 UserNotificationsTest[11873:536812] settings.alertSetting: YES
You need to use CompletionHandlers in order to get the result, as you are getting the status for Notification in CompletionHandle and before getting the result you are comparing it, thats why you are getting wrong result.
Change your method like this
-(void) testNotificationsAuthorization :(void (^)(BOOL isActive))handler {
[[UNUserNotificationCenter currentNotificationCenter] getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
if (settings.alertSetting == UNNotificationSettingEnabled) {
handler(YES);
NSLog(#"settings.alertSetting: YES");
} else {
NSLog(#"settings.alertSetting: NO");
handler(NO);
}
}];
}
and use it like this
[[[UIApplication sharedApplication] delegate] isNotificationActive:^(BOOL isActive) {
if (isActive) {
NSLog(#"settings.alertSetting: YES");
} else {
NSLog(#"settings.alertSetting: NO");
}
}];
I want to create one application that show me paired devices in my app.(for example any device that paired me before detect and show me.)
also in the next time I want to send one NSString like "hello" to paired device.
I searching in google and I got so confused!!!
Please tell me first how to get paired device with my mobile and second how to send NSString value to them.
Here is an exemple for what you need :
You have to adapt it to match what u want. But you can see how it work ...
#implementation ViewController
{
CBPeripheralManager *_peripheralManager;
BOOL _isAdvertising;
}
- (void)_startAdvertising
{
NSUUID *estimoteUUID = [[NSUUID alloc] initWithUUIDString:#"B9407F30-F5F8-466E-AFF9-25556B57FE6D"];
CLBeaconRegion *region = [[CLBeaconRegion alloc] initWithProximityUUID:estimoteUUID
major:2
minor:1
identifier:#"SimEstimote"];
NSDictionary *beaconPeripheralData = [region peripheralDataWithMeasuredPower:nil];
[_peripheralManager startAdvertising:beaconPeripheralData];
}
- (void)_updateEmitterForDesiredState
{
if (_peripheralManager.state == CBPeripheralManagerStatePoweredOn)
{
// only issue commands when powered on
if (_isAdvertising)
{
if (!_peripheralManager.isAdvertising)
{
[self _startAdvertising];
}
}
else
{
if (_peripheralManager.isAdvertising)
{
[_peripheralManager stopAdvertising];
}
}
}
}
#pragma mark - CBPeripheralManagerDelegate
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral
{
[self _updateEmitterForDesiredState];
}
#pragma mark - Actions
- (IBAction)advertisingSwitch:(UISwitch *)sender
{
_isAdvertising = sender.isOn;
[self _updateEmitterForDesiredState];
}
#end
This for monitoring :
#implementation AppDelegate
{
CLLocationManager *_locationManager;
BOOL _isInsideRegion; // flag to prevent duplicate sending of notification
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)options
{
// create a location manager
_locationManager = [[CLLocationManager alloc] init];
// set delegate, not the angle brackets
_locationManager.delegate = self;
NSUUID *estimoteUUID = [[NSUUID alloc] initWithUUIDString:#"B9407F30-F5F8-466E-AFF9-25556B57FE6D"];
CLBeaconRegion *region = [[CLBeaconRegion alloc] initWithProximityUUID:estimoteUUID
identifier:#"Estimote Range"];
// launch app when display is turned on and inside region
region.notifyEntryStateOnDisplay = YES;
if ([CLLocationManager isMonitoringAvailableForClass:[CLBeaconRegion class]])
{
[_locationManager startMonitoringForRegion:region];
// get status update right away for UI
[_locationManager requestStateForRegion:region];
}
else
{
NSLog(#"This device does not support monitoring beacon regions");
}
// Override point for customization after application launch.
return YES;
}
- (void)_sendEnterLocalNotification
{
if (!_isInsideRegion)
{
UILocalNotification *notice = [[UILocalNotification alloc] init];
notice.alertBody = #"Inside Estimote beacon region!";
notice.alertAction = #"Open";
[[UIApplication sharedApplication] scheduleLocalNotification:notice];
}
_isInsideRegion = YES;
}
- (void)_sendExitLocalNotification
{
if (_isInsideRegion)
{
UILocalNotification *notice = [[UILocalNotification alloc] init];
notice.alertBody = #"Left Estimote beacon region!";
notice.alertAction = #"Open";
[[UIApplication sharedApplication] scheduleLocalNotification:notice];
}
_isInsideRegion = NO;
}
- (void)_updateUIForState:(CLRegionState)state
{
ViewController *vc = (ViewController *)self.window.rootViewController;
if (state == CLRegionStateInside)
{
vc.label.text = #"Inside";
}
else if (state == CLRegionStateOutside)
{
vc.label.text = #"Outside";
}
else
{
vc.label.text = #"Unknown";
}
}
#pragma mark - CLLocationManagerDelegate
- (void)locationManager:(CLLocationManager *)manager
didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region
{
// always update UI
[self _updateUIForState:state];
if ([UIApplication sharedApplication].applicationState == UIApplicationStateActive)
{
// don't send any notifications
return;
}
if (state == CLRegionStateInside)
{
[self _sendEnterLocalNotification];
}
else
{
[self _sendExitLocalNotification];
}
}
#end
You can have the complete description at this adress :
http://www.cocoanetics.com/2013/11/can-you-smell-the-ibeacon/
In GCHelper.h
#import <Foundation/Foundation.h>
#import <GameKit/GameKit.h>
#interface abGCHelper : NSObject {
BOOL gameCenterAvailable;
BOOL userAuthenticated;
}
#property (assign, readonly) BOOL gameCenterAvailable;
-(void)reportScore:(int64_t)score forLeaderboardID:(NSString*)identifier;
#end
In GCHelper.m
#import "abGCHelper.h"
#implementation abGCHelper
#synthesize gameCenterAvailable;
static abGCHelper *_sharedHelper = nil;
-(abGCHelper*)defaultHelper {
// dispatch_once will ensure that the method is only called once (thread-safe)
static dispatch_once_t pred = 0;
dispatch_once(&pred, ^{
_sharedHelper = [[abGCHelper alloc] init];
});
return _sharedHelper;
}
- (BOOL)isGameCenterAvailable {
// check for presence of GKLocalPlayer API
Class gcClass = (NSClassFromString(#"GKLocalPlayer"));
// check if the device is running iOS 4.1 or later
NSString *reqSysVer = #"4.1";
NSString *currSysVer = [[UIDevice currentDevice] systemVersion];
BOOL osVersionSupported = ([currSysVer compare:reqSysVer
options:NSNumericSearch] != NSOrderedAscending);
return (gcClass && osVersionSupported);
}
- (id)init {
if ((self = [super init])) {
gameCenterAvailable = [self isGameCenterAvailable];
if (gameCenterAvailable) {
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self selector:#selector(authenticationChanged) name:GKPlayerAuthenticationDidChangeNotificationName object:nil];
}
}
return self;
}
- (void)authenticateLocalUserOnViewController:(UIViewController*)viewController
setCallbackObject:(id)obj
withPauseSelector:(SEL)selector
{
if (!gameCenterAvailable) return;
GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];
NSLog(#"Authenticating local user...");
if (localPlayer.authenticated == NO) {
[localPlayer setAuthenticateHandler:^(UIViewController* authViewController, NSError *error) {
if (authViewController != nil) {
if (obj) {
[obj performSelector:selector withObject:nil afterDelay:0];
}
[viewController presentViewController:authViewController animated:YES completion:^ {
}];
} else if (error != nil) {
// process error
}
}];
}
else {
NSLog(#"Already authenticated!");
}
}
-(void)authenticationChanged {
if ([GKLocalPlayer localPlayer].isAuthenticated && !userAuthenticated) {
NSLog(#"Authentication changed: player authenticated.");
userAuthenticated = TRUE;
// Load the leaderboard info
// Load the achievements
} else if (![GKLocalPlayer localPlayer].isAuthenticated && userAuthenticated) {
NSLog(#"Authentication changed: player not authenticated.");
userAuthenticated = FALSE;
}
}
-(void)reportScore:(int64_t)score forLeaderboardID:(NSString*)identifier
{
GKScore *scoreReporter = [[GKScore alloc] initWithLeaderboardIdentifier: identifier];
scoreReporter.value = score;
scoreReporter.context = 0;
[GKScore reportScores:#[scoreReporter] withCompletionHandler:^(NSError *error) {
if (error == nil) {
NSLog(#"Score reported successfully!");
} else {
NSLog(#"Unable to report score!");
}
}];
}
I referred to online tutorials to do this. Now, the problem is:
How do I submit the highScore defined in MyScene.m ?
Where do I have to put my Leaderboard ID?
Please help
Thanks...