Detect voice call start/end state on iOS 10 - objective-c

I'm trying to detect call states on iOS 10.
I have tested this on iOS 9.3 and it works fine.
But on iOS 10, CTCallCenter is deprecated, so I used Callkit.
I can't detect the call state.
I can't find any correct answers.
My code is:
#property (nonatomic, strong) CXCallObserver *callObserver;
...
self.callObserver = [[CXCallObserver alloc] init];
[callObserver setDelegate:self queue:nil];
...
- (void)callObserver:(CXCallObserver *)callObserver callChanged:(CXCall *)call {
if (call.hasConnected) {
NSLog(#"connected/n");
// perform necessary actions
} else if(call.hasEnded) {
NSLog(#"disconnected/n");
}
}

This code is working for me. The only difference is I provide a dispatch queue for the observer, instead of using the main queue.
#import <CallKit/CXCallObserver.h>
#import <CallKit/CXCall.h>
#interface CallStatusIos10 : NSObject <CXCallObserverDelegate>
{
dispatch_queue_t dq;
}
#property (nonatomic, strong) CXCallObserver* observer;
#end
#implementation CallStatusIos10
#synthesize observer;
-(id) init
{
if(self = [super init])
{
observer = [[CXCallObserver alloc] init];
// Hang on to a reference to the queue, as the
// CXCallObserver only maintains a weak reference
dq = dispatch_queue_create("my.ios10.call.status.queue", DISPATCH_QUEUE_SERIAL);
[observer setDelegate: self queue: dq];
}
return self;
}
#pragma mark - CXCallObserverDelegate
- (void)callObserver:(CXCallObserver *)callObserver callChanged:(CXCall *)call
{
NSString* callId = [call.UUID UUIDString];
if(!call.hasConnected && !call.hasEnded && !call.onHold)
{
if(call.outgoing)
{
// outgoing call is being dialling;
}
else
{
// incoming call is ringing;
}
}
else if(call.onHold)
{
// call is on hold;
}
else if(call.hasEnded)
{
// call has disconnected;
}
else if(call.hasConnected)
{
// call has been answered;
}
}
#end

Try this
Appdelegate.h
#import <CoreTelephony/CTCallCenter.h>
#import <CoreTelephony/CTCall.h>
...
#property (nonatomic, strong) CTCallCenter* callCenter;
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
....
self.callCenter = [[CTCallCenter alloc] init];
[self handleCall]; //Call this method when you want to use it
....
}
-(void)handleCall
{
self.callCenter.callEventHandler = ^(CTCall *call){
if ([call.callState isEqualToString: CTCallStateConnected])
{
//NSLog(#"call stopped");
}
else if ([call.callState isEqualToString: CTCallStateDialing])
{
}
else if ([call.callState isEqualToString: CTCallStateDisconnected])
{
//NSLog(#"call played");
}
else if ([call.callState isEqualToString: CTCallStateIncoming])
{
//NSLog(#"call stopped");
}
};
}
Taken from: How to detect call incoming programmatically
To know more: CoreTelephony Framework iOS 7

Related

Unable to open Plaid Link UI, Spinning indefinetely

The error is as below.
The Handler is being deinitialized before Link has completed or been exited. Did you forget to strongly reference the Handler object returned by Plaid.create?
The functionality is working in the native app using the LinkKit framework. In order to use all the functions in objective C we have included a wrapper on top of it which is as below and when building a native app using the wrapper I am facing the above issue.
Wrapper Code.
// LinkKitSwiftwrapper.h
#import <Foundation/Foundation.h>
#import <LinkKit/LinkKit-Swift.h>
#import <UIKit/UIActivity.h>
NS_ASSUME_NONNULL_BEGIN
#interface LinkKitSwiftWrapper : NSObject
- (void)presentPlaidLinkUsingLinkToken: (NSString *)linkToken completionHandler:(void (^)(NSArray *array))callbackServer;
#property (strong, retain, nonatomic, readwrite) id<PLKHandler> linkHandler;
#end
NS_ASSUME_NONNULL_END
//LinkKitswiftWrapper.m
#import "LinkKitSwiftWrapper.h"
#import <LinkKit/LinkKit-Swift.h>
#import <UIKit/UIKit.h>
#import <LinkKit/LinkKit.h>
#implementation LinkKitSwiftWrapper
static void (^callBackObjectServer)(NSArray *);
- (void) presentPlaidLinkUsingLinkToken: (NSString *)linkToken completionHandler:(void (^)(NSArray *array))callbackServer
{
callBackObjectServer = callbackServer;
PLKLinkTokenConfiguration* linkConfiguration = [PLKLinkTokenConfiguration createWithToken:linkToken
onSuccess:^(PLKLinkSuccess *success) {
dispatch_async(dispatch_get_main_queue(), ^{
NSArray *result = #[#"SUCCESS", success.publicToken];
callBackObjectServer(result);
});
}];
linkConfiguration.onExit = ^(PLKLinkExit * exit) {
if (exit.error) {
NSLog(#"exit with %#\n%#", exit.error, exit.metadata);
} else {
NSLog(#"exit with %#", exit.metadata);
}
};
NSError *createError = nil;
id<PLKHandler> handler = [PLKPlaid createWithLinkTokenConfiguration:linkConfiguration
error:&createError];
self.linkHandler = nil;
if (handler) {
[handler retain]
self.linkHandler = handler;
UIViewController *vc = [LinkKitSwiftWrapper topViewController] ;
[self.linkHandler openWithContextViewController:vc];
} else if (createError) {
NSLog(#"Unable to create PLKHandler due to: %#", createError);
dispatch_async(dispatch_get_main_queue(), ^{
NSArray *result = #[#"ERROR", createError.description];
callBackObjectServer(result);
});
}else{
NSString *errorMessage = #"Unknown error occured while trying to create a Plaid link.";
NSLog(#"%#", errorMessage);
dispatch_async(dispatch_get_main_queue(), ^{
NSArray *result = #[#"ERROR", errorMessage];
callBackObjectServer(result);
});
}
}
+ (UIViewController *)topViewController{
return [self topViewController:[[LinkKitSwiftWrapper keyWindow] rootViewController ]];
}
+ (UIViewController *)topViewController:(UIViewController *)rootViewController
{
if (rootViewController.presentedViewController == nil) {
return rootViewController ;
}
if ([rootViewController.presentedViewController isKindOfClass:[UINavigationController class]]) {
UINavigationController *navigationController = (UINavigationController *)rootViewController.presentedViewController;
UIViewController *lastViewController = [[navigationController viewControllers] lastObject];
return [self topViewController:lastViewController];
}
UIViewController *presentedViewController = (UIViewController *)rootViewController.presentedViewController ;
return [self topViewController:presentedViewController];
}
+(UIWindow*)keyWindow
{
UIWindow *foundWindow = nil;
NSArray *windows = [[UIApplication sharedApplication]windows];
for (UIWindow *window in windows) {
if (window.isKeyWindow) {
foundWindow = window;
break;
}
}
return foundWindow;
}
#end
//Native App code
#import "ViewController.h"
#import "PlaidWrapper/LinkKitSwiftWrapper.h"
#interface ViewController ()
#end
static void (^callBackObjectServer)(NSArray *);
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (IBAction)ButtonTouchUpInside:(id)sender {
[ [LinkKitSwiftWrapper new] presentPlaidLinkUsingLinkToken:#"link-sandbox-0f671f81-c144-4160-8a64-0ce51e63dbcd" completionHandler:(void (^)(NSArray* array))callBackObjectServer];
}
#end
Please help.

EXC_BAD_ACCESS when invoke an NSInvocation of an argument as NSNumber

I have a class called "MyUploadClient" for handling video upload task. "MyUploadClient" has a protocol called "MyUploadClientDelegate". Someone use "MyUploadClient" to upload video needs to comfort to this protocol, and the delegate may be more than one, so I make a property called 'delegateSet' to store the delegates. The method '-invokeDelegateItemsWithAction:withObjects:...' is used to wrap the delegate method and invoke when it needs to.
But the delegate method "-uploadTask:progressChange:" has a parameter which type is float, the app crash when it execute "va_arg(args, id)".
To solve this problem, I change the parameter from float to NSNumber, but the app still crashed when execute "[invocation invoke]" with error "EXC_BAD_ACCESS".
Why did this happen and how can I fix this problem?
Relative code In file: MyUploadClient.h
#protocol MyUploadClientDelegate <NSObject>
- (void)startUploadTask:(MyVideo *)video;
- (void)didUploadTask:(MyVideo *)video error:(NSError * __nullable)error;
- (void)uploadTask:(NSString *)videoId progressChange:(float)progress;
……
#end
#interface MyUploadClient : NSObject
+ (instancetype)sharedClient;
- (void)addDelegate:(id<MyUploadClientDelegate>)delegate;
- (void)removeDelegate:(id<MyUploadClientDelegate>)delegate;
……
#end
Relative code In file: MyUploadClient.m
#interface MyUploadClientDelegateItem : NSObject
#property (nonatomic, weak) id<MyUploadClientDelegate> delegate;
- (instancetype)initWithDelegate:(id<MyUploadClientDelegate>)delegate;
#end
#implementation MyUploadClientDelegateItem
- (instancetype)initWithDelegate:(id<MyUploadClientDelegate>)delegate {
self = [super init];
if (self) {
[self setDelegate:delegate];
}
return self;
}
#end
#interface MyUploadClient ()
#property (nonatomic, strong) NSMutableSet *delegateSet;
#end
#implementation MyUploadClient
- (NSMutableSet *)delegateSet {
if (!_delegateSet) {
_delegateSet = [NSMutableSet set];
}
return _delegateSet;
}
- (void)addDelegate:(id<MyUploadClientDelegate>)delegate {
for (MyUploadClientDelegateItem *item in self.delegateSet) {
if (delegate == item.delegate) {
return ;
}
}
MyUploadClientDelegateItem *delegateItem = [[MyUploadClientDelegateItem alloc] initWithDelegate:delegate];
[self.delegateSet addObject:delegateItem];
}
- (void)removeDelegate:(id<MyUploadClientDelegate>)delegate {
MyUploadClientDelegateItem *deleteItem = nil;
for (MyUploadClientDelegateItem *item in self.delegateSet) {
if (delegate == item.delegate) {
deleteItem = item;
}
}
if (deleteItem) {
[self.delegateSet removeObject:deleteItem];
}
}
- (void)startUploadWithVideo:(MyVideo *)video {
……
[self invokeDelegateItemsWithAction:#selector(startUploadTask:) withObjects:video, nil];
……
}
- (void)uploadProgressChanged:(float)progress videoId:(NSString *)videoId {
……
[self invokeDelegateItemsWithAction:#selector(uploadTask:progressChange:) withObjects:videoId, progress, nil];
……
}
- (void)invokeDelegateItemsWithAction:(SEL)action withObjects:(id)firstObj, ... NS_REQUIRES_NIL_TERMINATION {
NSMutableArray *deleteItems = [NSMutableArray arrayWithCapacity:0];
NSSet *copySet = [NSSet setWithSet:self.delegateSet];
for (MyDelegateItem *item in copySet) {
if (item.delegate && [item.delegate respondsToSelector:action]) {
NSMethodSignature *sigOfAction = [(id)item.delegate methodSignatureForSelector:action];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:sigOfAction];
[invocation setTarget:item.delegate];
[invocation setSelector:action];
if (firstObj) {
NSInteger argsIndex = 2;
va_list args;
va_start(args, firstObj);
id argObject = firstObj;
while (argObject) {
if ([argObject isKindOfClass:[NSValue class]]) {
void *value;
[argObject getValue:&value];
[invocation setArgument:&value atIndex:argsIndex];
} else {
[invocation setArgument:&argObject atIndex:argsIndex];
}
argObject = va_arg(args, id);
argsIndex++;
}
va_end(args);
}
[invocation invoke];
}
if (!item.delegate) {
[deleteItems addObject:item];
}
}
for (id obj in deleteItems) {
[self.delegateSet removeObject:obj];
}
}
#end

Capture Key Presses In Many GKState Classes

I'm using SpriteKit in Objective C for Mac OS. I'm a noob when it comes to working with NSNotification, but I used the following method in another app I was working on, but it's now not working. Perhaps there's a better method, but I want to be able to capture key presses based on what state the app is in.
The Main Issue
The state begins in GameBeginState. Once the spacebar is pressed, it moves into GamePlayState. If I press any other keys, it shows that it's using the keyPressed: method from GameBeginState as opposed to the keyPressed method in GamePlayState. Any ideas why? I can't figure this out for the life of me.
Details
I created a custom view called CustomNotificationHandler and set my main SKView in the project.
Here's the code for CustomNotificationHandler:
CustomNotificationHandler
#import "CustomNotificationHandler.h"
#implementation CustomNotificationHandler:SKView {
// Add instance variables here
}
- (id) initWithCoder:(NSCoder *)coder {
self = [super initWithCoder:coder];
if (self) {
// Allocate and initialize your instance variables here
}
return self;
}
- (void) keyDown:(NSEvent *)theEvent {
[[NSNotificationCenter defaultCenter] postNotificationName:#"KeyPressedNotificationKey"
object:nil
userInfo:#{#"keyCode" : #(theEvent.keyCode)}];
}
#end
In my main class GameScene I have a GKStateMachine set up that initializes 3 states. The classes for the states are below:
GamePlayState.m
#import "GameReadyState.h"
#import "GamePlayState.h"
#interface GameReadyState()
#property (weak) GameScene *scene;
#end
#implementation GameReadyState
-(instancetype) initWithScene:(GameScene*)scene {
if (self = [super init]) {
self.scene = scene;
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyPressed:) name:#"KeyPressedNotificationKey" object:nil];
}
return self;
}
-(void) didEnterWithPreviousState:(GKState *)previousState {
}
-(void) willExitWithNextState:(GKState *)nextState {
}
-(BOOL) isValidNextState:(Class)stateClass {
return stateClass == [GamePlayState class];
}
-(void)keyPressed:(NSNotification*)notification {
NSNumber *keyCodeObject = notification.userInfo[#"keyCode"];
NSInteger keyCode = keyCodeObject.integerValue;
NSLog(#"GAME READY key = %ld", (long)keyCode);
switch (keyCode) {
case 49:
[self.stateMachine enterState:[GamePlayState class]];
break;
default:
break;
}
}
#end
Here's the 2nd state...pretty much the same as the 1st:
GamePlayState
#import "GamePlayState.h"
#import "GamePauseState.h"
#interface GamePlayState()
#property (weak) GameScene *scene;
#end
#implementation GamePlayState
-(instancetype) initWithScene:(GameScene*)scene {
if (self = [super init]) {
self.scene = scene;
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyPressed:) name:#"KeyPressedNotificationKey" object:nil];
}
return self;
}
-(void) didEnterWithPreviousState:(GKState *)previousState {
[_scene setPaused:NO];
}
-(BOOL) isValidNextState:(Class)stateClass {
return stateClass == [GamePauseState class];
}
-(void) updateWithDeltaTime:(NSTimeInterval)seconds {
[_scene.sequence moveLeft];
}
-(void)keyPressed:(NSNotification*)notification {
NSNumber *keyCodeObject = notification.userInfo[#"keyCode"];
NSInteger keyCode = keyCodeObject.integerValue;
NSLog(#"GAME PLAY key = %ld", (long)keyCode);
switch (keyCode) {
case 53:
[self.stateMachine enterState:[GamePauseState class]];
[_scene setPaused:YES];
break;
default:
break;
}
}
#end

My UIImage doesn't drop to the bottom of the screen when it's placed in the Delegate function

My code drops an UIImage to the bottom of the screen. But it's not working when I place the same code in isMyUIViewControllerDelegateTriggered. This is the same thread isn't it??
MyUIViewController.m
#interface MyUIViewController () <UIDynamicAnimatorDelegate>
#property (strong, nonatomic) UIDynamicAnimator *animator;
#property (strong, nonatomic) DropitBehavior *dropitBehavior;
#property (strong, nonatomic) IBOutlet UIView *jobLoadView;
#end
#synthesize someUIImage;
id refToSelf; /* Tried this to have a ref to the UIView, but doesn't work. */
static DropitBehavior * dropit;
- (void)viewDidLoad
{
refToSelf = self;
dropit = [[DropitBehavior alloc] init];
/* Placed inhere, this code works!
DropitBehavior * dropit = [refToSelf dropitBehavior];
[dropit addItem:someUIImage]; */
startupManager = [[StartupManager alloc] init];
[startupManager setDelegate:refToSelf];
[startupManager start];
}
- (DropitBehavior *)dropitBehavior
{
if (!_dropitBehavior) {
_dropitBehavior = [[DropitBehavior alloc] init];
[self.animator addBehavior:_dropitBehavior];
}
return _dropitBehavior;
}
- (UIDynamicAnimator *)animator
{
if (!_animator) {
_animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.jobLoadView];
_animator.delegate = self;
}
return _animator;
}
-(void)isStartupManagerDelegateTriggered:(NSString *) errorcode {
/* The startup manager code triggers 5 time, so I can make a loading effect. */
/* Doesn't work when I place the code inhere. */
dropit = [refToSelf dropitBehavior];
if ([dropit isKindOfClass:[DropitBehavior class]]) {
NSLog(#"is kind of class drop it ");
[dropit addItem:someUIImage];
}
}
DropitBehavior.m
#interface DropitBehavior()
#property (strong, nonatomic) UIGravityBehavior *gravity;
#property (strong, nonatomic) UICollisionBehavior *collider;
#end
#implementation DropitBehavior
/* Singleton */
static DropitBehavior *sharedSingleton;
+ (void)initialize {
static BOOL initialized = NO;
if(!initialized)
{
initialized = YES;
sharedSingleton = [[DropitBehavior alloc] init];
}
}
- (UIGravityBehavior *)gravity
{
if (!_gravity) {
_gravity = [[UIGravityBehavior alloc] init];
_gravity.magnitude = 0.9;
}
return _gravity;
}
- (UICollisionBehavior *)collider
{
if (!_collider) {
_collider = [[UICollisionBehavior alloc] init];
_collider.translatesReferenceBoundsIntoBoundary = YES;
}
return _collider;
}
- (void)addItem:(id <UIDynamicItem>)item
{
[self.gravity addItem:item];
[self.collider addItem:item];
}
- (instancetype)init
{
self = [super init];
[self addChildBehavior:self.gravity];
[self addChildBehavior:self.collider];
return self;
}
#end
StartupManager.m
#implementation StartupManager
JSONParser * jsonParser;
HTTPConnection * httpConnection;
static StartupManager *sharedSingleton;
#synthesize delegate;
+ (void)initialize {
static BOOL initialized = NO;
if(!initialized) {
initialized = YES;
sharedSingleton = [[StartupManager alloc] init];
jsonParser = [[JSONParser alloc] init];
httpConnection = [[HTTPConnection alloc] init];
}
}
- (void) doingSomeStuff {
[httpConnection setDelegate:self];
[httpConnection postRequestToSomeServer:#"some.instance.inthe.cloud";
}
-(void)isHTTPConnectionDelegateTriggered:(NSData *)jsonPost {
[jsonParser setDelegate:self];
[jsonParser parseJSON:jsonPost];
}
-(void)isJSONParserDelegateTriggered:(NSString *) message {
*/ this gets called periodically and that has to do a loading screen animation. /*
[delegate isStartupManagerDelegateTriggered:message];
}
StartupManager.h
#class StartupManager;
#protocol StartupManagerDelegate
#required
-(void)isStartupManagerDelegateTriggered:(NSString *) errorcode;
#end
#interface StartupManager : NSObject <JSONParserDelegate, HTTPConnectionDelegate>
#property (nonatomic, assign) id<StartupManagerDelegate> delegate;
- (void) doingSomeStuff;
Is there anyone who can point me in the right direction? Otherwise I'll have to ask the Grandfather.
I think you may be misunderstanding the UIDynamicAnimatorDelegate protocol. It doesn't start an animation for you, it just lets you know that the animation state has changed.
So you normally wouldn't start your animation from a delegate method. In any case, the code you posted doesn't implement any of the delegate methods. The only way to start the animation with the code as posted is to explicitly invoke isMyUIViewControllerDelegateTriggered
When do you want it to start? viewDidLoad is too early. viewDidAppear would normally be the earliest reasonable time for something you expect the user to see.

Why is Xcode saying my class implementation is incomplete?

I have created a singleton for my MusicBackground. And I receive a line code of imcomplete implementation of this line #implementation MyBgMusic. Can anyone tell me why ? Below is the code:
#import "MyBgMusic.h"
static MyBgMusic *sharedMyManager = nil;
#implementation MyBgMusic
#synthesize player,playBgMusic;
#pragma mark -
#pragma mark Singleton Methods
+ (MyBgMusic*)sharedInstance {
static MyBgMusic *_sharedInstance;
if(!_sharedInstance) {
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_sharedInstance = [[super allocWithZone:nil] init];
});
}
return _sharedInstance;
}
+ (id)allocWithZone:(NSZone *)zone {
return [self sharedInstance];
}
- (id)copyWithZone:(NSZone *)zone {
return self;
}
#if (!__has_feature(objc_arc))
- (id)retain {
return self;
}
- (unsigned)retainCount {
return UINT_MAX; //denotes an object that cannot be released
}
- (id)autorelease {
return self;
}
- (void)dealloc
{
[MyBgMusic release];
[playBgMusic release];
[player release];
[super dealloc];
}
#endif
#pragma mark -
#pragma mark Custom Methods
- (void)viewDidLoad
{
NSString *path = [[NSBundle mainBundle] pathForResource:#"music" ofType:#"mp3"];
self.player=[[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
player.delegate = self;
[player play];
player.numberOfLoops = -1;
[super viewDidLoad];
}
#end
For the M file, below is the code:
#import <Foundation/Foundation.h>
#import <AVFoundation/AVAudioPlayer.h>
#interface MyBgMusic : UIViewController <AVAudioPlayerDelegate> {
AVAudioPlayer *player;
UIButton *playBgMusic;
}
#property (nonatomic, retain) IBOutlet AVAudioPlayer *player;
#property (nonatomic, retain) IBOutlet UIButton *playBgMusic;
+ (id)sharedManager;
-(IBAction) toggleMusic;
#end
And how do I reference to my toggle button: Below is the code :
- (IBAction)toggleMusic {
if ([self.player isPlaying] == YES) {
[self.player stop];
} else {
[self.player play];
}
self.playBgMusic.enabled = YES;
}
It means that your MyBgMusic class isn't doing everything it promised to do in its header file, which includes being a UIViewController and implementing the AVAudioPlayerDelegate protocol. I'm not familiar with exactly what the AVAudioPlayerDelegate is, but it's quite possible that your class doesn't implement all of the required methods.
Also, you're declaring methods +(id)sharedManager and -(IBAction)toggleMusic, but I don't see them anywhere in the implementation file. That would be a case of promising something in the header and not implementing it in the class.
It would help if you posted the actual error message.
That error means your #implementation section does not contain everything described in the #interface section.
I can see two problems.
First you need to place this code:
- (IBAction)toggleMusic {
...
}
Somewhere in between #implementation and #end.
And you also need to rename the line + (MyBgMusic*)sharedInstance to + (id)sharedManager.
EDIT:
To access the toggle music method elsewhere in your code, you would do:
[[MyBgMusic sharedManager] toggleMusic];
Your +(id)sharedManagerimplementation is called +(id)sharedInstance. Just guessing, but it seems they are supposed to do the same.