How to intercept "Messages" sent from CallKit incoming call screen? - ios

I'm using iOS 10's CallKit to receive incoming calls. The calls in my app do not come from "phone numbers" or "email addresses", but from an internal identifier in my protocol. I thus report incoming calls with the CXHandleType of CXHandleTypeGeneric (and not CXHandleTypePhoneNumber or CXHandleTypeEmailAddress), using a custom string as the "value" of the handle.
When I report the incoming call, and the phone is not locked, the user sees an incoming call screen, with the buttons "Remind Me", "Message", "Decline", and "Accept". If the user presses the "Message" button, and selects one of the message strings on the following menu, it tries to send that string as a text message through the Messages app, with the destination being the custom string I used as the "value" of the handle of the call, as though it were a phone number or email address, even though it isn't. This usually causes the message to fail to send due to an invalid destination, but, depending on the string, it might actually send to a valid destination the user did not want to send to; both outcomes are bad.
I am looking to see if there is a way to have the message not sent through the Messages app (which is always incorrect in my case), but instead be passed into my app so that I can send the message to the caller correctly through my internal protocol.
Update: the "Remind Me" and "Message" buttons no longer appear on iOS 10.1

"Message" button has appeared if support SiriKit in iOS 12.
(add INSendMessageIntent to intent's info.plist)
How to intercept:
Run the Intent Extension
Deal with handler(for intent:)

iOS 10.1 Beta 1 has changed this behavior to no longer show the 'Remind Me' or 'Message' buttons for CallKit VoIP apps, so I encourage you to re-test your app using that Beta OS.
If you would like the ability for incoming calls from your app to continue showing the 'Message' button but for your app receive the message request instead of the system's native Messages app, please file a bug with Apple to request this capability.

Related

Detect Caller Name when I get Incoming call or Outgoing Call [duplicate]

I'm trying to get caller ID (phone number) at the time of incoming call.
TrueCaller has implemented this and they get the phone number of incoming caller.
I've found this information:
1. CoreTelephony Framework(It gives only calling states)
2. Apple's 9.0 update : "Maybe" contacts sync. with mail app and detects the incoming phone number.
###EDIT###
iOS 10 and above:
Use Callkit, take a look at call directory extension
According to documentation,
Identifying Incoming Callers
When a phone receives an incoming call, the system first consults the user’s contacts to find a matching phone number. If no match is found, the system then consults your app’s Call Directory extension to find a matching entry to identify the phone number. This is useful for applications that maintain a contact list for a user that’s separate from the system contacts, such as a social network, or for identifying incoming calls that may be initiated from within the app, such as for customer service support or a delivery notification.
For example, consider a user who is friends with Jane in a social networking app, but doesn’t have her phone number in her contacts. The social networking app has a Call Directory Extension, which downloads and add the phone numbers of all of the user’s friends. Because of this, when the user gets an incoming call from Jane, the system displays something like “(App Name) Caller ID: Jane Appleseed” rather than “Unknown Caller”.
To provide identifying information about incoming callers, you use the addIdentificationEntry(withNextSequentialPhoneNumber:label:) method in the implementation of beginRequest(with:).
class CustomCallDirectoryProvider: CXCallDirectoryProvider {
override func beginRequest(with context: CXCallDirectoryExtensionContext) {
let labelsKeyedByPhoneNumber: [CXCallDirectoryPhoneNumber: String] = [ … ]
for (phoneNumber, label) in labelsKeyedByPhoneNumber.sorted(by: <) {
context.addIdentificationEntry(withNextSequentialPhoneNumber: phoneNumber, label: label)
}
context.completeRequest()
}
}
Because this method is called only when the system launches the app extension and not for each individual call, you must specify call identification information all at once; you cannot, for example, make a request to a web service to find information about an incoming call.
iOS 9 and earlier:
As Kakshil mentioned, Caller ID is not possible on non jailbroken devices.
And I will give you some findings on how true caller works,
Its not reading the call history, they actually created Action
extension, wherever you try to share a contact, it will display
truecaller app extension, which coded for showing the contact
details fetched from their server
You might also get confused with push notification received for few
calls, saying
"Some X calls you". This is where truecaller used a trick. If you
noticed clearly, that push notification will be received only when
you get a call from an android user with truecaller installed. Let
me explain you in details,
X(android user with truecaller installed), calling Y(ios user with truecaller installed), the android version notify the server that X making call to Y. And server will send push notification to Y's iPhone.
Caller ID is not possible on non jailbroken devices.
However there is one way, in which you can know which user is calling, but not sure if it will be useful to you.
If you have a bluetooth device, connected to the iPhone, it will get all notifications, including incoming calls, which you can then get the caller ID for.
Apart from this, there is currently no other way at all to know the called ID.

Get the callers phone number from an incoming call on iPhone

I'm trying to get caller ID (phone number) at the time of incoming call.
TrueCaller has implemented this and they get the phone number of incoming caller.
I've found this information:
1. CoreTelephony Framework(It gives only calling states)
2. Apple's 9.0 update : "Maybe" contacts sync. with mail app and detects the incoming phone number.
###EDIT###
iOS 10 and above:
Use Callkit, take a look at call directory extension
According to documentation,
Identifying Incoming Callers
When a phone receives an incoming call, the system first consults the user’s contacts to find a matching phone number. If no match is found, the system then consults your app’s Call Directory extension to find a matching entry to identify the phone number. This is useful for applications that maintain a contact list for a user that’s separate from the system contacts, such as a social network, or for identifying incoming calls that may be initiated from within the app, such as for customer service support or a delivery notification.
For example, consider a user who is friends with Jane in a social networking app, but doesn’t have her phone number in her contacts. The social networking app has a Call Directory Extension, which downloads and add the phone numbers of all of the user’s friends. Because of this, when the user gets an incoming call from Jane, the system displays something like “(App Name) Caller ID: Jane Appleseed” rather than “Unknown Caller”.
To provide identifying information about incoming callers, you use the addIdentificationEntry(withNextSequentialPhoneNumber:label:) method in the implementation of beginRequest(with:).
class CustomCallDirectoryProvider: CXCallDirectoryProvider {
override func beginRequest(with context: CXCallDirectoryExtensionContext) {
let labelsKeyedByPhoneNumber: [CXCallDirectoryPhoneNumber: String] = [ … ]
for (phoneNumber, label) in labelsKeyedByPhoneNumber.sorted(by: <) {
context.addIdentificationEntry(withNextSequentialPhoneNumber: phoneNumber, label: label)
}
context.completeRequest()
}
}
Because this method is called only when the system launches the app extension and not for each individual call, you must specify call identification information all at once; you cannot, for example, make a request to a web service to find information about an incoming call.
iOS 9 and earlier:
As Kakshil mentioned, Caller ID is not possible on non jailbroken devices.
And I will give you some findings on how true caller works,
Its not reading the call history, they actually created Action
extension, wherever you try to share a contact, it will display
truecaller app extension, which coded for showing the contact
details fetched from their server
You might also get confused with push notification received for few
calls, saying
"Some X calls you". This is where truecaller used a trick. If you
noticed clearly, that push notification will be received only when
you get a call from an android user with truecaller installed. Let
me explain you in details,
X(android user with truecaller installed), calling Y(ios user with truecaller installed), the android version notify the server that X making call to Y. And server will send push notification to Y's iPhone.
Caller ID is not possible on non jailbroken devices.
However there is one way, in which you can know which user is calling, but not sure if it will be useful to you.
If you have a bluetooth device, connected to the iPhone, it will get all notifications, including incoming calls, which you can then get the caller ID for.
Apart from this, there is currently no other way at all to know the called ID.

Is anybody else seeing duplicate calls to handleActionWithIdentifier:forLocalNotification: in WatchKit?

I am getting some very strange behavior with my WatchKit handling of local notification actions that I'm pretty sure is a system bug. I'm wondering if anybody else is seeing the same thing.
(This is using iOS 8.4 and WatchKit 1.0, with an Objective-C app build with Xcode 6.4)
It's too much code to post, and the code is property of the client, so I'll have to describe it.
The background:
I am adding custom "long look" notification support to a client's app.
The app creates geofences around vendor locations. When the user enters one of the geofences, the location manager send a didEnterRegion message to my class that handles geofences.
I turn around and generate a local notification of a defined category. That category is defined as having 2 different UIMutableUserNotificationActions attached to it, one for showing more info about the vendor, and one for displaying driving directions to that vendor's location. (We won't talk about the fact that the user is in shouting distance of the vendor when the geofence fires, so they can SEE the vendor's shop. This is what the client wants, and he's paying me to do it this way.)
The local notification is set to fire immediately (or for testing, I create notifications set to fire in a few seconds.)
The system can do one of 3 things when the notification fires.
1. If the app is running in the foreground, it will send the app delegate a `application:didReceiveLocalNotification:` message.
2. If phone is awake but the app is in the background, it displays the local notification with a system banner/alert (depending on the user's settings.) That banner/alert has buttons for my 2 actions.
3. If the phone is locked and the user has a paired Apple Watch that is allowed to receive notifications, the local notification is sent to the watch.
The Apple Watch app has a custom subclass of WKUserNotificationInterfaceController that is set up to handle this category of user notifications. It adds an image, a title, and a message body to a custom interface controller, with data it gets from the the userInfo dictionary attached to the local notification.
If the user taps one of the action buttons on the WKUserNotificationInterfaceController ("more info" or "directions"), the watch's main interface controller gets a handleActionWithIdentifier:forLocalNotification: message. The code is set up to then send an `openParentApplication:reply:error:' message to the iPhone app. It passes along the user info dictionary it received in the local notification.
The iPhone app responds to the openParentApplication:reply:error message by either requesting driving directions from the location manager (which launches the maps app) or displaying the appropriate info page from the app for the specified vendor.
If the phone is locked when the watch sends the `openParentApplication:reply:error: message to the iPhone, the user doesn't get any feedback, since the phone is locked and Apple doesn't allow a phone to wake itself up.
In that case I therefore invoke the reply block with a dictionary entry of #{#inBackGround: #(YES)}. The watch's reply block checks for inBackground==YES, and if it is, it displays a message to the user that they nee to open the iPhone app in order to see the info/directions.
The problem:
If I launch the iPhone app and trigger a local notification when the phone is locked the first time, the message goes to the watch, the watch displays my custom long look with "more info" and "directions" buttons, and tapping one of the action buttons invokes the watch's handleActionWithIdentifier:forLocalNotification: method, as expected. The watch's handleActionWithIdentifier:forLocalNotification: method sends an openParentApplication:reply:error message to the phone, and the phone displays the appropriate response to the user when the user goes back to the app.
However, the problem comes in if I then trigger a new local notification (also with the phone locked) for a different vendor, with different GPS coordinates and userInfo that points to a different screen of information to display on the phone. When my watch buzzes and I raise it to my wrist, as the "long look" notification for the new local notification is displayed, the watch's handleActionWithIdentifier:forLocalNotification: method fires again, with the identifier and userInfo dictionary from the previous local notification. (I haven't tapped any action buttons on this new notification, or responded to a local notification message on the phone.)
Then, if the user clicks the "more info" action button on the watch's new long look notification controller, that action fires.
The result of this is that when the user goes to his phone, he sees the information for the new vendor he asked about, but when he clicks that away, there is a duplicate copy of the info for the first vendor on his screen.
I've debugged this very carefully, and confirmed that the watch app's interface controller's handleActionWithIdentifier:forLocalNotification: method is being called spuriously. I added a unique NSDate timestamp to the userInfo in the local notification that the iPhone posts, and I see that exact same timestamp repeated in the second (spurious) invocation of the first handleActionWithIdentifier:forLocalNotification: when the second long look notification is displayed.
Has anybody else run across this problem? I guess it's time to file a Radar bug, but I'm not sure what set of steps from my client's app triggers the problem, and it might take me a full day or more to work out a minimum demo app to demonstrate the problem. I know from experience that Apple won't pay any attention to my bug report unless I give them an app that lets them create a repeatable fail-case, along with painfully detailed instructions on how to use it.
The fix:
The fix I have come up with is a dreadful hack. On the phone, I embed a unique "actionFireDate" NSDate into the userInfo dictionary for the local notification. On the watch, I create an empty NSMutableSet "actionFireDates" at startup. When I get a handleActionWithIdentifier:forLocalNotification: call, I get the userInfo for the local notification, get the timestamp NSDate I put in the userInfo dictionary, and check to see if that unique NSDate is in my "actionFireDates" set. If it is, I simply ignore the action. If it's not, I add the new NSDate from the userInfo dictionary in to my set of action fire dates.
I'm pretty sure this is a system bug, and have opened a bug at Apple's bug reporter website (A.K.A. Filed a "radar bug".)
The fix I came up with for this was to add code to my custom WKInterfaceController class's awakeWithContext method that calls dispatch_after to call dismissController 10 seconds after the alert is displayed. If the user does nothing, the alert goes away on it's own.
I'm not going to accept my answer for another couple of days in case somebody else has insights to share on the subject.
EDIT:
Apple closed my Radar bug as a duplicate. I take that to mean that this really is a system bug.

Sending notification or SMS in response to an event Xcode

In Xcode I want to make an app that when a certain condition is met, it will send an SMS to a predefined contact.
I am aware this is not directly possible, but what are the alternatives to make this happen?
It should send without confirmation to a pre-defined contact you have selected and have a pre-defined message
very simple: when event happen , your handler (IBAction method) sent a message to your server (e.g. web service) to send this pre-defined message.

Receive Notification for SMS using iPhone

I have been searching for this on Google for a while and maybe I'm not wording it right because I'm not an expert iPhone developer, but what I'm wondering is if there is some kind of notification or system event that my iPhone application I'm developing can "hook into" that will get raised whenever a text message arrives on my phone. I don't need to be able to read the message. I just need to know when a text message has arrive on my phone. If possible, I'd like to be able to get the phone number of the person sending the message. Is this possible in Objective-C/Cocoa?
No, you can't do this with the public iOS APIs.

Resources