iOS messages extension how to switch host app directly - ios

I have an app and I wanna add messages extension feature.
I thought the feature is if the user selects a message, it switches my host app directly like google map.
I made a MSMessage and set URL and the message has template layout which had caption and sub-caption.
let message = MSMessage()
message.url = "http://blahblah?customScheme=myHostAppLaunchScheme"
let template = MSMessageTemplateLayout()
template.image = sampleImage
template.caption = "this is a caption"
template.subCaption = "this is a sub caption"
message.layout = template
guard let conversation = activeConversation else {
print("blahblah")
return
}
conversation.insert(message) { (error) in
print("finish. error = \(error == nil ? "nil" : error!.localizedDescription)")
}
and i wrote a code extensionContext.open(url, completionHandler) in
willBecomeActive(with conversation: MSConversation)
didReceive(_ message: MSMessage, conversation: MSConversation)
of course, i parsed selectedMessage's URL.
but it didn't work I expected.
the messages extension switches expand mode automatically.
it works if I used
conversation.insertText("myHostAppLaunchScheme", nil)
but I don't want it because it can't add template :(
is there any idea to switch iMessage to host app directly?
thanks for any ideas.

i think i found the answer.
there is no way with using
conversation.insert(message, completionHandler)
i think apple music and google maps are using
conversation.insertText("some url", completionHandler)
because i copied an URL after long press a message which is shared by apple music or google map
then i use the URL in my code
conversation.insertText("the URL", completionHandler)
it's working they did!!

Related

How can I send message to specific contact through WhatsApp from my ios app using swift?

// 1
let urlWhats = "https://wa.me/\(mobile)/?text=\(text)"
// 2
if let urlString = urlWhats.addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed) {
// 3
if let whatsappURL = NSURL(string: urlString) {
// 4
if UIApplication.shared.canOpenURL(whatsappURL as URL) {
// 5
UIApplication.shared.open(whatsappURL as URL, options: [:], completionHandler: nil)
// UIApplication.shared.\
} else {
// 6
print("Cannot Open Whatsapp")
}
}
}
I'm able to launch whatsapp from my app from the above mentioned code, it is composing prefix text to the contact I wish to send and I need to click the send button in whatsapp manually . But I'm looking for a code which automatically sends whatsapp text to number from my app. Can anyone share your thoughts on this?
You can only compose the message for a particular contact using the Deep Linking method that you have used for it. For sending the message user has to click on the send button manually. You could provide the user with an alert that says so. But, it's not possible to do it for the user from your side. If you were able to send a message on Whatsapp by writing code without the user's confirmation it would be a break of user's privacy. Don't you think?

WhatsApp VS WhatsApp Business in SWIFT

We would really appreciate any help on the following. Through our app, the user can initiate a WhatsApp message (what happens is that the WhatsApp client starts with the phone + text preloaded, so the user just needs to tap the "send" button from the WhatsApp application).
We have an Android and an iOS app. In Android we are using the following code to select between WhatsApp and WhatsApp Business.
String url = "https://api.whatsapp.com/send?phone=" + phoneNumberToUse + "&text=" +
URLEncoder.encode(messageText, "UTF-8");
if(useWhatsAppBusiness){
intent.setPackage("com.whatsapp.w4b");
} else {
intent.setPackage("com.whatsapp");
}
URLEncoder.encode(messageText, "UTF-8");
intent.setPackage("com.whatsapp");
intent.setData(Uri.parse(url));
if (intent.resolveActivity(packageManager) != null) {
startActivity(intent);
} else {
Toast.makeText(this, "WhatsApp application not found", Toast.LENGTH_SHORT).show();
}
We are trying to achieve the same functionality on Swift for iOS, however, we did not find any way to programmatically define whether the OS should start WhatsApp or WhatsApp Business. The code listed below, always starts the one or other depending on which is installed. If both are installed, it starts the WhatsApp application.
let whatsApp = "https://wa.me/\(phoneNumber)/?text=\(shareableMessageText)"
guard let url = URL(string: whatsApp) else {
return
}
if #available(iOS 10.0, *) {
UIApplication.shared.open(url, options: convertToUIApplicationOpenExternalURLOptionsKeyDictionary([:]), completionHandler: nil)
} else {
UIApplication.shared.openURL(url)
}
So in simple words, is there any way, from our app, to select which WhatsApp application (WhatsApp or WhatsApp Business) is going to be launched?
Thanks
I have made some apps with WhatsApp but I had to use the web platform, not the business app.
You can check what app is installed in the device like this:
let app = UIApplication.shared
let appScheme = "App1://app"
if app.canOpenURL(URL(string: appScheme)!) {
print("App is install and can be opened")
} else {
print("App in not installed. Go to AppStore")
}
The 'App1' value must be changed for the app you want to check. WhatsApp App should use 'WhatsApp', and WhatsApp Business should use 'WhatsApp-Business'.
After that you can call the URL for each app, I mean, for WhatsApp you can use the URL with this format:
let whatsApp = "https://wa.me/\(phoneNumber)/?text=\(shareableMessageText)"
And for WhatsApp Business you have to use this format:
let whatsApp = "https://api.whatsapp.com/send?phone=\(phoneNumber)&text=\(shareableMessageText)"
It is possible too, that the first step was not necessary to do. Because of the call is made with the api, the device should open the Business app, and if it is made with the wa.me scheme, the device should open the WhatsApp as normal.
I am going to check my app to see if it is working or not.
UPDATE:
I have installed WhatsApp Business and I have made some test, with two different url calls.
The code I use is this:
let phoneNumber = "my_phone_number"
let shareableMessageText = "This_is_a_test_message"
let whatsApp = "https://wa.me/\(phoneNumber)/?text=\(shareableMessageText)"
if let urlString = whatsApp.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) {
if let whatsappURL = NSURL(string: urlString) {
if UIApplication.shared.canOpenURL(whatsappURL as URL) {
UIApplication.shared.openURL(whatsappURL as URL)
} else {
print("error")
}
}
}
Using this code you will see a prompt with a message giving you the option of send a message in WhatsApp or WhatsApp Business.
But if you use this other code:
let phoneNumber = "my_phone_number"
let shareableMessageText = "This_is_a_test_message"
let whatsApp = "whatsapp://send?phone=\(phoneNumber)&text=\(shareableMessageText)"
if let urlString = whatsApp.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) {
if let whatsappURL = NSURL(string: urlString) {
if UIApplication.shared.canOpenURL(whatsappURL as URL) {
UIApplication.shared.openURL(whatsappURL as URL)
} else {
print("error")
}
}
}
You will see a prompt asking you to open WhatsApp business. So the way to choose between WhatsApp and WhatsApp Business is the URL format. If you choose this format you will be ask to choose between one or another WA version:
let whatsApp = "https://wa.me/\(phoneNumber)/?text=\(shareableMessageText)"
But if you use this URL format, you will use WA Business directly:
let whatsApp = "whatsapp://send?phone=\(phoneNumber)&text=\(shareableMessageText)"

UNNotificationServiceExtension's didRecieve not called

I moved step by step for getting rich push notifications. Here they are :
Created Notification service extension with plist :
NotificationService didRecieve :
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: #escaping (UNNotificationContent) -> Void) {
func failEarly() {
contentHandler(request.content)
}
self.contentHandler = contentHandler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
// Get the custom data from the notification payload
if let data = request.content.userInfo as? [String: AnyObject] {
// Grab the attachment
// let notificationData = data["data"] as? [String: String]
if let urlString = data["attachment-url"], let fileUrl = URL(string: urlString as! String) {
// Download the attachment
URLSession.shared.downloadTask(with: fileUrl) { (location, response, error) in
if let location = location {
// Move temporary file to remove .tmp extension
let tmpDirectory = NSTemporaryDirectory()
let tmpFile = "file://".appending(tmpDirectory).appending(fileUrl.lastPathComponent)
let tmpUrl = URL(string: tmpFile)!
try! FileManager.default.moveItem(at: location, to: tmpUrl)
// Add the attachment to the notification content
if let attachment = try? UNNotificationAttachment(identifier: "video", url: tmpUrl, options:nil) {
self.bestAttemptContent?.attachments = [attachment]
}else if let attachment = try? UNNotificationAttachment(identifier: "image", url: tmpUrl, options:nil) {
self.bestAttemptContent?.attachments = [attachment]
}else if let attachment = try? UNNotificationAttachment(identifier: "audio", url: tmpUrl, options:nil) {
self.bestAttemptContent?.attachments = [attachment]
}else if let attachment = try? UNNotificationAttachment(identifier: "image.gif", url: tmpUrl, options: nil) {
self.bestAttemptContent?.attachments = [attachment]
}
}
// Serve the notification content
self.contentHandler!(self.bestAttemptContent!)
}.resume()
}
}
}
Configured AppId and provision profile for extension.
Rich notification is coming correctly :
But here are the issues I am facing :
didRecieve is not getting called. For that I attached the serviceExtension process to the app target and ran the app.
Note : Extension is getting called as soon as notification arrives but didRecieve is not called :
On opening the push notification (which has video attachment), nothing happens. Ideally it should get played.
If I have to open the video and play it, do I have to explicitly do something or extension will take care of that ?
Payload :
aps = {
alert = "This is what your message will look like! Type in your message in the text area and get a preview right here";
badge = 1;
"mutable-content" = 1;
sound = default;
};
"attachment-url" = "https://www.sample-videos.com/video/mp4/720/big_buck_bunny_720p_1mb.mp4";
deeplinkurl = "";
"message_id" = 1609;
}
I did try going through following posts but that didn't help :
iOS10 UNNotificationServiceExtension not called
NotificationServiceExtension not called
UNNotificationServiceExtension not working on iPhone 5 (iOS 10)
Good news! Your service extension is indeed being called - the image on your notification is evidence of that. What is probably happening here is that you are unable to debug the extension using the workflow you are used to with applications.
Debugging notification extensions is not like debugging an app. Extensions are plug-ins to an iOS process outside your application. Just setting a breakpoint is not a reliable way to debug them. Instead:
Debugging A Notification Service Extension
Launch the app from Xcode or the device
In Xcode, select Attach To Process or PID By Name... from the Debug menu
Enter the name of your notification extension
Trigger a notification (by sending a push, etc.).
When the notification is delivered the service extension should launch in to the debugger. Service extensions are only relevant to remote (push) notifications, so you will need a device to troubleshoot them.
Debugging A Notification Content Extension
There are at least two ways. The steps shown above for a service extension also work for a content extension. The second method is more familiar but less reliable.
Select the extension scheme in Xcode using the toolbar
In the Product menu, select Edit Scheme...
Set the Executable to the parent application.
Set a breakpoint inside the content extension.
Now build and run your extension. It will launch the parent application.
Trigger a notification that will cause the content extension to load.
It's worth noting that adding logging using the logging framework can be very useful for debugging and troubleshooting as well.
Why The Video May Not Be Playing
iOS limits the size of content that can be presented in notifications. This is described in the documentation for UNNotificationAttachment. For video it is generally 50Mb. Make sure your video is as small as you can make it in terms of bytes, and of course provide a video that is sized appropriately for the device it will be played on. Do not try to play a 1080p video in a notification that is 400 points wide!
In practice it is almost always better to use HLS instead of downloading video, and present it in a content extension.
Another thing in your code that may be problematic is the identifiers you are assigning to your attachments. Identifiers should be unique. Typically this would be a reverse-domain notation string like your bundle ID followed by a UUID string. You could also use the original URL of the content followed by a UUID string. If you provide an empty string iOS will create a unique identifier for you.
With the user notifications framework having non-unique identifiers (for notifications, attachments, etc.) tends to cause difficult to track down issues inside the framework. For example, this can cause an attached watchOS device to crash.
If you want to implement "auto play" for your video - it is not clear from your question wether that is what you are describing - you will need to implement your own player functionality in a content extension.
If you are going to do that, again, HLS is the preferred way to display video in a notification. It usually uses less RAM, offers a better user experience and tends to be more stable.

How to enable camera in MFMessageComposeViewController?

I am developing an iOS application with a button to report an issue using SMS/iMessage. I am using MFMessageComposeViewController to present the message composition interface using the following code (Swift 3):
if(MFMessageComposeViewController.canSendText()){
let controller = MFMessageComposeViewController()
controller.messageComposeDelegate = self
controller.body = "Example Message"
controller.recipients = ["2345678901"]
self.present(controller, animated: true, completion: nil)
}
I have also implemented the MFMessageComposeViewControllerDelegate function to dismiss properly. A standard text message / iMessage sends successfully, but the user does not have the option to attach an image. The buttons for camera, iMessage Apps, etc. are there, but they are disabled and cannot be pressed. How can I enable these buttons (camera, specifically) to allow my users to attach images to messages composed with the app?
The Buttons in Question:
EDIT:
Thanks Abdelahad for the suggestion. I've modified his response to allow multiple recipients and to include a message body. I also updated it to remove the deprecated addingPercentEscapes(using: ) method.
Here is a solution using a url to open the Messages app. NOTE: This takes users out of the app.
let recipients = "2345678901,3456789012" //Phone Numbers
let messageBody = "This is a test"
let sms: String = "sms://open?addresses=\(recipients)&body=\(messageBody)"
let smsEncoded = sms.addingPercentEncoding(withAllowedCharacters: .urlFragmentAllowed)
let url = URL(string: smsEncoded!)
UIApplication.shared.openURL(url!)
But still I would like a solution that does not take the user out of the app. Is this possible? Why would the MFMessageComposeViewController show the buttons without enabling them?
Don't use MFMessageComposeViewController use UIApplication.shared.openURL(url!) but this will takes the user out of the app
var phoneToCall: String = "sms: +201016588557"
var phoneToCallEncoded = phoneToCall.addingPercentEscapes(using: String.Encoding.ascii)
var url = URL(string: phoneToCallEncoded)
UIApplication.shared.openURL(url!)

Problems with structs in Swift and making a UIImage from url?

Alright, I am not familiar with structs or the ordeal I am dealing with in Swift, but what I need to do is create an iMessage in my iMessage app extension with a sticker in it, meaning the image part of the iMessage is set to the sticker.
I have pored over Apple's docs and https://www.captechconsulting.com/blogs/ios-10-imessages-sdk-creating-an-imessages-extension but I do not understand how to do this or really how structs work. I read up on structs but that has not helped me accomplishing what Apple does in their sample code (downloadable at Apple)
What Apple does is they first compose a message, which I understood, taking their struct as a property, but I take sticker instead
guard let conversation = activeConversation else { fatalError("Expected a conversation") }
//Create a new message with the same session as any currently selected message.
let message = composeMessage(with: MSSticker, caption: "sup", session: conversation.selectedMessage?.session)
// Add the message to the conversation.
conversation.insert(message) { error in
if let error = error {
print(error)
}
}
They then do this (this is directly from sample code) to compose the message:
fileprivate func composeMessage(with iceCream: IceCream, caption: String, session: MSSession? = nil) -> MSMessage {
var components = URLComponents()
components.queryItems = iceCream.queryItems
let layout = MSMessageTemplateLayout()
layout.image = iceCream.renderSticker(opaque: true)
layout.caption = caption
let message = MSMessage(session: session ?? MSSession())
message.url = components.url!
message.layout = layout
return message
}
}
Basically this line is what Im having the problem with as I need to set my sticker as the image:
layout.image = iceCream.renderSticker(opaque: true)
Apple does a whole complicated function thing that I don't understand in renderSticker to pull the image part out of their stickers, and I have tried their way but I think this is better:
let img = UIImage(contentsOfURL: square.imageFileURL)
layout.image = ing
layout.image needs a UIImage, and I can get the imageFileURL from the sticker, I just cant get this into a UIImage. I get an error it does not match available overloads.
What can I do here? How can I insert the image from my sticker into a message? How can I get an image from its imageFileURL?
I'm not sure what exactly the question is, but I'll try to address as much as I can --
As rmaddy mentioned, if you want to create an image given a file location, simply use the UIImage constructor he specified.
As far as sending just a sticker (which you asked about in the comments on rmaddy's answer), you can insert just a sticker into an iMessage conversation. This functionality is available as part of an MSConversation. Here is a link to the documentation:
https://developer.apple.com/reference/messages/msconversation/1648187-insert
The active conversation can be accessed from your MSMessagesAppViewController.
There is no init(contentsOfURL:) initializer for UIImage. The closest one is init(contentsOfFile:).
To use that one with your file URL you can do:
let img = UIImage(contentsOfFile: square.imageFileURL.path)

Resources