I used CoreTelephony framework introduced in iOS SDK 4.0 to know about Incoming call & its dropped state.
CTTelephonyNetworkInfo *tni = [[CTTelephonyNetworkInfo alloc] init];
callCenter = [[CTCallCenter alloc] init];
crtCarrierName = tni.subscriberCellularProvider.carrierName;
[callCenter setCallEventHandler:^(CTCall *call) {
if ([[call callState] isEqual:CTCallStateConnected]) {
//this call has just connected
} else if ([[call callState] isEqual:CTCallStateDisconnected]) {
//this call has just ended (dropped/hung up/etc)
}
}];
Can i use this event handler to track call state when my app is in background?
Can i also fetch incoming call phone number from CTCall object? or there is any other way around.
I dont want to use Private API.Is there way available from Apple iOS SDK?
No there is no way to do this in the official SDK, you can not use it in the background since it does not fall on of the background running categories unless you app does something else in the background then just monitoring the call.
You will never be able to get the phone number of the current call since this is private data Apple will not allow you to acces the data.
Related
I have camera view using AVFoundation and if phone call or Skype call is active then we can't use camera.
How can i check if AVFoundation will not open then i need to open other view without using camera.
if i will check this-
BOOL isPlayingWithOthers = [[AVAudioSession sharedInstance] isOtherAudioPlaying];
then it will not open when any other app playing audio.
Any suggestions ?
The CTCallCenter object has a currentCalls property which is an NSSet of the current calls. If there is a call then the currentCalls property should be != nil.
If you want to know if any of the calls is actually connected, then you'll have to iterate through the current calls and check the callState to determine if it is CTCallStateConnected or not.
#import <CoreTelephony/CTCallCenter.h>
#import <CoreTelephony/CTCall.h>
-(bool)isOnPhoneCall {
/*
Returns YES if the user is currently on a phone call
*/
CTCallCenter *callCenter = [[[CTCallCenter alloc] init] autorelease];
for (CTCall *call in callCenter.currentCalls) {
if (call.callState == CTCallStateConnected) {
return YES;
}
}
return NO;
}
I am using this method in Swift, Tarun answer helped me.
import CallKit
func isOnPhoneCall() -> Bool {
/*
Returns true if the user is currently on a phone call
*/
for call in CXCallObserver().calls {
if call.hasEnded == false {
return true
}
}
return false
}
Your app delegate will receive the -applicationDidResignActive message and your app can listen for the UIApplicationDidResignActiveNotification. These will be received when your app is interrupted by a call as well as in other cases where the app is interrupted, such as when the screen locks or the user presses the lock button.
For more details how to handle interruptions in Responding to Interruptions.
Also refer stack overflow post on How can we detect call interruption in our iphone application?
I am making a multiplayer game for iOS and I read the material in Apple Developer Center, specifically this one. Here is my code for custom matchmaking, which is pretty straightforward:
- (void)findProgrammaticMatch {
GKMatchRequest *request = [[GKMatchRequest alloc] init];
request.minPlayers = 2;
request.maxPlayers = 2;
request.defaultNumberOfPlayers = 2;
request.playersToInvite = nil;
request.playerAttributes = 0;
request.playerGroup = 0;
UILabel *loading = (UILabel *)[aiw viewWithTag:792];
[[GKMatchmaker sharedMatchmaker] findMatchForRequest:request withCompletionHandler:^(GKMatch *match, NSError *error) {
if (error){
//error handling
[loaderLayer stopAnimating];
UIButton *cancelButton = (UIButton *)[loaderLayer viewWithTag:442];
[cancelButton setTitle:#"Go Back" forState:UIControlStateNormal];
loading.text = #"Cannot find any players. Please try again later.";
} else if (match != nil) {
//save match
self.match = match;
self.match.delegate = self;
loading.text = #"Found a player. Preparing session...";
if (!self.matchStarted && match.expectedPlayerCount == 0) {
self.matchStarted = YES;
//begin game logic
[self.scene setState:1];
self.myTicket = 1000+arc4random_uniform(999);
[self.scene send:self.myTicket];
[self stopLoading];
}
}
}];
}
However, matchmaking fails when one or more devices are connected to the internet via cellular networks. When I investigated the underlying error I found out that even if it is a wifi to wifi case, the completion handler does not work as intended. That is, match.expectedPlayerCount is never 0. Instead, the game starts when - (void)match:(GKMatch *)match player:(NSString *)playerID didChangeState:(GKPlayerConnectionState)state handler is invoked after the completion handler as following:
...
- (void)match:(GKMatch *)match player:(NSString *)playerID didChangeState:(GKPlayerConnectionState)state {
switch (state) {
case GKPlayerStateConnected:
self.matchStarted = YES;
//begin game logic
[self.scene setState:1];
self.myTicket = 1000+arc4random_uniform(999);
[self.scene send:self.myTicket];
[self stopLoading];
break;
...
The problem now is if a device with 3g is connected (and matched-sort of) didChangeState is never invoked. I checked for several other related questions on the internet and this site, although they are far from being satisfactory. I also read that sandbox servers of Game Center are not reliable and for some people production version worked perfectly(it just works!) despite the errors in sandbox mode, but I don't want to take that risk. Has anybody have experienced similar problem with their multiplayer game?
Hgeg,
There is nothing wrong with your code.
You have to allow cellular data usage to your app which needs users permission.
The following paragraph is selected from Apple's support website :
At the Foundation layer, you can use the setAllowsCellularAccess:
method on NSMutableURLRequest to specify whether a request can be sent
over a cellular connection. You can also use the allowsCellularAccess
to check the current value.
At the Core Foundation layer, you can achieve the same thing by
setting the kCFStreamPropertyNoCellular property before opening a
stream obtained from the CFSocketStream or CFHTTPStream APIs.
In older versions of iOS, you can continue to use the
kSCNetworkReachabilityFlagsIsWWAN as a best-effort way of determining
whether traffic will be sent over a cellular connection, but you
should be aware of its limitations.
Good luck
Iman
According to the latest apple news, from iOS 9, the sand box mode will no longer exist, instead of the sandbox you'll have one unified environment.
So you'll have just one unified environments where you can share the same accounts, this should solve all the usual problems from the SandBox mode.
The new Unified System it's also compatible with TestFlight so you'll be able to test you code across multiple device and accounts.
All of these changes will be made directly by apple, so the only think that you can do it's to wait until they update to the new system, so far it's the only way to be sure that it's not a Sand Box problem.
For more info please have a loot at the WWDC video
Based on the code that you have shown us, there should'nt be any issue regardless of the connection type, 3G or otherwise; however, if you previously interspersed code for exception handling that was tied back to connection status, or for graphics which represent a loading state, something could be tied up elsewhere logically and produce this error at this point in the game logic. Even a corrupt spinner graphic can become an issue.
Did you have any other exception handlers in the code that called the following:
request.playersToInvite
or
request.playerGroup
or
that changed a loader layer characteristic?
I am developing geofencing application using worklight ios native apis.
I am using worklight 6.1 and testing application on my iPhone4 with ios 7.1.2.
Below is the native ios code i have written to create geofence.
WLGeoAcquisitionPolicy* geoPolicy = [WLGeoAcquisitionPolicy getLiveTrackingProfile];
id<WLDevice> wlDevice = [[WLClient sharedInstance] getWLDevice];
// now, set-up configuration for ongoing acquisition
WLLocationServicesConfiguration* config = [[WLLocationServicesConfiguration alloc] init];
// 1. Acquisition Policy (same one that is used for the one-time acquisition)
WLAcquisitionPolicy* policy = [[WLAcquisitionPolicy alloc] init];
[policy setGeoPolicy: geoPolicy];
[config setPolicy:policy];
WLTriggersConfiguration* triggers = [[WLTriggersConfiguration alloc] init];
WLGeoEnterTrigger *wlTypeAEnterRegionTrigger = [[WLGeoEnterTrigger alloc] init];
[wlTypeAEnterRegionTrigger setArea:[[WLCircle alloc] initWithCenter:[[WLCoordinate alloc] initWithLatitude:19.5687f longitude:72.8748f] radius:500.0f]];
[wlTypeAEnterRegionTrigger setConfidenceLevel:HIGH];
[wlTypeAEnterRegionTrigger setCallback:[WLCallbackFactory createTriggerCallback:^(id<WLDeviceContext> deviceContext) {
#try
{
[[triggers getGeoTriggers] removeObjectForKey:"Offer1"];
[self showLocationNotificationWithOfferID:"Offer1" andDescription:#"offer description"];
}
#catch (NSException *exception)
{
NSLog(#"Error Occured in LBSManager::enterTriggerCallBack : %#",[exception description]);
}
}]];
[[triggers getGeoTriggers] setObject:wlTypeAEnterRegionTrigger forKey:"Offer1"];
[config setTriggers:triggers];
[wlDevice startAcquisition:config];
After creating WLGeoEnterTrigger location service icon appears in status bar and after that i am putting application in background after few minutes location service icon disappears.
When i put application in foreground and i get this wlLocationServicesConfiguration as nil.
WLLocationServicesConfiguration *wlLocationServicesConfiguration = [[[WLClient sharedInstance] getWLDevice] getLocationServicesConfig];
I have also added neccessary BackgroundModes still it does not work.
even if i kill the app i get this wlLocationServicesConfiguration as nil.
WLLocationServicesConfiguration *wlLocationServicesConfiguration = [[[WLClient sharedInstance] getWLDevice] getLocationServicesConfig];
I believe the answer to the following question may explain it: MobileFirst 6.3 - Enabling location trigger in Background in Hybrid Applications for iOS environment
The answer by Carmel is as follows:
First see this for how to set permissions to run location services in
the background:
Background Location Services not working in iOS 7
There is limitations in Ios. After couple of minutes of running in the
background without any location update the app get suspend and all the
location update will be received once the user bring the app back to
life.
Is there any way to access the last time an ABAddressBook contact was accessed, interacted with, called, miss-called, etc.
I'm aware of the ABPerson properties, specifically kABPersonModificationDateProperty. But I was wondering if there any way of knowing more about the users interaction with that contact.
No apple does not allow access to the Call list. Since a call information is stored in the call and not in the addressbook there is no way to get the information you want from the addressbook.
I don't think you can access called history in iOS, especially after iOS 4. You can however know that a phone call was dialled using CoreTelephony framework.
I do it in applicationDidBecomeActive of my AppDelegate.m
...
typeof(self) __weak weakSelf = self;
self.center = [[CTCallCenter alloc]init];
self.center.callEventHandler = ^(CTCall *call) {
if(call.callState == CTCallStateDialing) {
weakSelf.callWasMade = YES;
}
};
...
We have a product where the user registers by providing their phone number.
However after they register they could potentially change their sim.
Is it possible to programatically determine if the sim has been removed or inserted?
(Thanks if you provide it, but any digression comments on the use of using the phone number in the first place would be irrelevant to this discussion, I don't want to discuss that aspect of things, only the sim aspect).
Yes, of course it is possible. Link CoreTelephony.framework to make following code compile:
CTTelephonyNetworkInfo* info = [[CTTelephonyNetworkInfo alloc] init];
CTCarrier* carrier = info.subscriberCellularProvider;
NSString *mobileCountryCode = carrier.mobileCountryCode;
NSString *carrierName = carrier.carrierName;
NSString *isoCountryCode = carrier.isoCountryCode;
NSString *mobileNetworkCode = carrier.mobileNetworkCode;
// Try this to track CTCarrier changes
info.subscriberCellularProviderDidUpdateNotifier = ^(CTCarrier* inCTCarrier) {
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"User did change SIM");
});
};
By values of mobileCountryCode, mobileNetworkCode, carrierName, isoCountryCode you can judge about presence of SIM. (Without SIM they become incorrect).
There is also some undocumented functions/notifications in CoreTelephony, but your app may be banned by Apple if you'll use them. Anyway:
// Evaluates to #"kCTSIMSupportSIMStatusReady" when SIM is present amd ready;
// there are some other values like #"kCTSIMSupportSIMStatusNotInserted"
NSString* CTSIMSupportGetSIMStatus();
// Use #"kCTSIMSupportSIMStatusChangeNotification" to track changes of SIM status:
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(SIMNotification:)
name:#"kCTSIMSupportSIMStatusChangeNotification"
object:nil
];
// This one copies current phone number
NSString* CTSettingCopyMyPhoneNumber()
Addendum Another possible (and legal) solution: if your company has a database of phone numbers, you can send an sms or call(and cut) any specific number to verify that user still uses the same phone number.
UPDATE Function NSString* CTSettingCopyMyPhoneNumber() doesn't work anymore (returns empty string).