iMessage extension action when tapped - ios

I have successfully created an iMessage extension where the sender can choose an image, type text on top of the image and then send the new image and text combined to another recipient.
Currently, when the recipient taps the received message, it takes them to the iMessage App Store to download the app.
What I would like is for the recipient to NOT be redirected to the app store, but simply be presented with a larger view of the image they received.
Any help on how to achieve this (if possible) would be appreciated!
Edited:
After more research, I'm wondering if it's possible to send the newly created image (containing the image and text combined) as a MSSticker so when the user taps it, it simply enlarges?

Sending the image as a Message follows the MSMessage format (clicking on the message will direct a user to the app or app store). You can either send the image as a sticker or insert the image into the conversation!

(In case anyone's looking for belated enlightenment).
If you sent the image you create as an Attachment then it appears in the receiver's stream as just an image, without being associated with an app. Only when you send an MSMessage does the signature matching kick in and require the receiver to have the app.
See my im-plausibilities sample for a demo, specifically
func sendAttachment() {
guard let conversation = activeConversation else { fatalError("Expected a conversation") }
guard
let imageData = imageView?.image?.jpegData(compressionQuality: 0.8),
let docUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
else {
dismiss()
return
}
// WARNING this is not great practice - not robust if muliple messages sent without completing upload
attachmentPath = URL(fileURLWithPath: "imPhoto.jpg", relativeTo: docUrl)
if (try? imageData.write(to: attachmentPath!)) != nil {
conversation.insertAttachment(attachmentPath!, withAlternateFilename: "imPhoto.jpg") { (error) in
if let error = error {
os_log("Error with insertAttachment(message)")
print(error)
}
}
}
dismiss()
}

Related

How do you get the attachment image from a local notification on the Apple Watch?

My Objective-C iOS app schedules a local notification that has a userInfo dictionary and one small JPEG image attachment.
The image is attached like this:
content.attachments = #[[UNNotificationAttachment attachmentWithIdentifier:myIdentifier
URL:[imageURL filePathURL]
options:#{UNNotificationAttachmentOptionsTypeHintKey : UTTypeJPEG}
error:&error]];
This works fine. The notification is correctly scheduled.
If I ignore the Watch and let the notification appear on my phone's Lock Screen, the image is there.
Going back to the Watch. The Watch app receives the notification, and the didReceive method is called in the NotificationController.
No matter what I try, I can't get the image from the notification.
I've tried converting the image to NSData and adding it to the userInfo dictionary, but the data is too large despite the image actually being only a few Kb.
NotificationController.swift: (image is an Image type, and is sent to the NotificationView to use as the background.)
guard let attachment = notification.request.content.attachments.first
else {
print("Couldn't get the first attachment, using default image")
image = Image.init(kDefaultImage)
return
}
// We get here, so we know there's an attachment
if attachment.url.startAccessingSecurityScopedResource() {
let imageData = try? Data.init(contentsOf: attachment.url)
if let imageData = imageData {
image = Image(uiImage: UIImage(data: imageData) ??
UIImage.init(imageLiteralResourceName: kDefaultImageMasked))
}
attachment.url.stopAccessingSecurityScopedResource()
} else {
print("Couldn't access the file, using default")
image = Image.init(kDefaultImageMasked)
}
I always get told I can't access the file in the security scoped bit.
If I take out that check I get Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value because the code doesn't put anything into image.
Obviously I would put the default image in there instead of crashing, but once the Watch app crashes the notification that's shown on the Watch shows the correct image, but it's obviously not using my NotificationView because that crashed. It's using the standard mirror feature to show the notification, so it's completely bypassing my code at that point.
I've also tried this:
if(attachment.url.isFileURL) {
print("attachment image is a file url")
}
It does print out that it's a file url, and it's: file:///var/mobile/Library/BulletinDistributor/Attachments/com.mycompany.myapp/<UUID>.jpeg.
But this fails, so the file doesn't exist at that path, or I'm not allowed to load files from there (?):
if(FileManager.default.fileExists(atPath: attachment.url.path)) {
...
How do I get the image from the attachment? I've tried getting the image as Data, as an Image, as a UIImage. Nothing works.
This was simple to do in the old Objective-C WatchKit extension stuff:
NSArray *attachments = notification.request.content.attachments;
if(attachments.count == 1) {
[_backgroundGroup setBackgroundImage:attachments[0]];
}
No problems there; it always worked.
Any ideas?

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?

How to set crop information for `thumbnailImageData` of CNMutableContact

I create/update contacts, using CNMutableContact.
I can set new image via imageData property, but I need to set custom crop information for creating thumbnail. Property thumbnailImageData is read-only.
Code:
let cnContact = CNMutableContact()
cnContact.imageData = imageData //created before
How to add custom thumbnailImage crop?
It seems that setting a thumbnail is not possible in iOS. However, by definition, a thumbnail of an image is the same image cropped to a smaller dimension. Hence iOS will auto generate the thumbnail from the image data set on the contact while saving the contact.
If you want to setup a different images for thumbnail and actual contact image, iOS will not allow you to do this.
Problem I have:
Before adding a new contact (CNMutableContact reference) in the user's contacts, I want to display the contact to the user. I can use the imageData to setup the new contact's image. However, when using CNContactViewController to display this new contact, the image is not cropped as per thumbnail. The thumbnail image showed looks super weird and scaled. How to resolve this?
Solution:
This occurs because the thumbnailImageData property on the CNMutableContact object is nil. This property cannot be set by the developers. This property can only be set by iOS internals and is auto generated by iOS while saving the contact.
So, before displaying the CNMutableContact object, you should save it to the users contacts, to kick in the auto-thumbnail-generation, and then immediately delete the contact.
The following extension on CNMutableContact depicts what you can do to achieve this.
extension CNMutableContact {
func generateThumbnailImage() {
if self.thumbnailImageData != nil {
return
}
// contact.thumbnailImageData is nil
// First save the contact for the thumbnail to be generated
let saveRequest = CNSaveRequest()
saveRequest.add(self, toContainerWithIdentifier: nil)
do {
try CNContactStore().execute(saveRequest)
} catch let error {
print("Error occurred while saving the request \(error)")
}
// self.thumbnailImageData is not nil. Contact Store will generate the thumbnail for this contact with the imageData provided.
// Now delete the contact
let deleteRequest = CNSaveRequest()
deleteRequest.delete(self)
do {
try CNContactStore().execute(deleteRequest)
} catch let error {
print("Error occurred while deleting the request \(error)")
}
// The contact is removed from the Contact Store
// However, the contact.thumbnailImageData is not nil anymore. Contacts Store has generated the thumbnail automatically with the imageData provided.
}
}

MSConversation.insertAttachment with UIImage downloaded via SDWebImage

I have an iMessage app that displays some remote content using SDWebImage. The images are downloaded and cached on disk. After choosing an image, I want to attach it to the message as a plain UIImage (not a MSMessage).
Here's the code I'm using
// image is already downloaded
let cache = SDImageCache.shared()
let key = remoteImageUrl
let fileUrlString = cache.defaultCachePath(forKey: key)!
let fileUrl = URL(string: fileUrlString)!
// image holds the correct UIImage
let image = UIImage(contentsOfFile: fileUrlString)
activeConversation?.insertAttachment(fileUrl, withAlternateFilename: "a funny gif", completionHandler: { (error) in
// error is nil here
print("error: \(error)")
})
Here's what the message looks like
It seems like the Messages framework can't find the image at that path.
Note: after tapping send, I the iMessage app crashes "MobileSMS quit unexpectedly."
I found out that I needed to use
let fileUrl = URL(fileURLWithPath: fileUrlString)
Hope this helps someone else

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