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

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)

Related

Sharing a story to Instagram with a background image and a sticker - IOS Swift

I'm trying to share a story with a background image a a sticker image via URL Scheme on my ios app, i am using the attached code and it dose not work.
When i'm trying to share just a background image or just a sticker it does work. But when im trying share both a background image and a sticker in top of it, it dose not work.
Any Ideas?
func shareToInstagram(deepLinkString : String){
let url = URL(string: "instagram-stories://share")!
if UIApplication.shared.canOpenURL(url){
let backgroundData = UIImageJPEGRepresentation(UIImage(named: "shop_placeholder")!, 1.0)!
let creditCardImage = UIImage(named: "share_instagram")!
let stickerData = UIImagePNGRepresentation(creditCardImage)!
let pasteBoardItems = [
["com.instagram.sharedSticker.backgroundImage" : backgroundData],
["com.instagram.sharedSticker.stickerImage" : stickerData],
]
if #available(iOS 10.0, *) {
UIPasteboard.general.setItems(pasteBoardItems, options: [.expirationDate: Date().addingTimeInterval(60 * 5)])
} else {
UIPasteboard.general.items = pasteBoardItems
}
UIApplication.shared.openURL(url)
}
I copy pasted OP's code for use in my own app (only substituting different UIImages) and found only 1 issue, pasteboard items should be contained in a single array otherwise instagram will render only the first item (in this case the background layer). To fix this, replace the declaration of pasteboard items with the following code
let pasteBoardItems = [
["com.instagram.sharedSticker.backgroundImage" : backgroundData,
"com.instagram.sharedSticker.stickerImage" : stickerData]
]
(basically just remove the close and open bracket separating the two items)
Also as a previous answer stated, make sure "instagram-stories" is included in LSApplicationQueriesSchemes in the info.plist file
I use this exact code in my app and it now works perfect
Everything is correct, my code is similar and it works for iOS 11+. I suggest you the following:
check the image data you pass to pasteboard (jpg can't be converted with
UIImagePNGRepresentation and vice versa)
check the info.plist. You should enable "instagram-stories" scheme in it (LSApplicationQueriesSchemes key)
Like Alec said, you need to put all of Instagram data in one list, not multiple lists. look at the example from the meta documents:
NSArray *pasteboardItems = #[#{#"com.instagram.sharedSticker.stickerImage" : stickerImage,
#"com.instagram.sharedSticker.backgroundTopColor" : backgroundTopColor,
#"com.instagram.sharedSticker.backgroundBottomColor" : backgroundBottomColor}];
2. For more recent readers, as of swift 4.2 and iOS 12 UIImageJPEGRepresentation is replaced by jpegData. change
let backgroundData = UIImageJPEGRepresentation(yourImage, 1.0)
with
let backgroundData = yourImage.jpegData(compressionQuality: 1.0)

Updating image in Chat bubble using Chatto library ios

I have existing Model where I am adding Chatto for UI so I am able to create a message with placeholder but unable to figure out the best way to change the same after download.
Library Link
https://github.com/badoo/Chatto
i am using chatto test app with DemoPhotoMessageModel.
then i use the follwing to add
var imgMess = DemoPhotoMessageModel(messageModel: messageModel, imageSize: placeholder.size, image: placeholder)
self.slidingWindow.insertItem(imgMess, position: .bottom)
self.delegate?.chatDataSourceDidUpdate(self)
ImageDownloader.default.downloadImage(with: downloadURL, options: [], progressBlock: nil) {[unowned self]
(image, error, url, data) in
print("Downloaded Image: \(image)")
if let img = image, let imgData = UIImagePNGRepresentation(img){
imgMess.setImage(img: img) //I updated the default implementation changing the image from var to let constant
}
}
Wha I have also tried is updating the object in slidingWindow itself. That also doesn't work. What will be the possible solution to create a new object with new uid or anything else.
You can download image in willBeShown() method of view model. As you need to pass url to view model, you need to subclass couple of classes. Please see the full solution in the gist. Please feel free to ask any questions.

iMessage extension action when tapped

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()
}

Get PHAsset from iOS Share Extension

I am developing a share extension for photos for my iOS app. Inside the extension, I am able to successfully retrieve the UIImage object from the NSItemProvider.
However, I would like to be able to share the image with my container app, without having to store the entire image data inside my shared user defaults. Is there a way to get the PHAsset of the image that the user has chosen in the share extension (if they have picked from their device)?
The documentation on the photos framework (https://developer.apple.com/library/ios/documentation/Photos/Reference/Photos_Framework/) has a line that says "This architecture makes it easy, safe, and efficient to work with the same assets from multiple threads or multiple apps and app extensions."
That line makes me think there is a way to share the same PHAsset between extension and container app, but I have yet to figure out any way to do that? Is there a way to do that?
This only works if the NSItemProvider gives you a URL with the format:
file:///var/mobile/Media/DCIM/100APPLE/IMG_0007.PNG
which is not always true for all your assets, but if it returns a URL as:
file:///var/mobile/Media/PhotoData/OutgoingTemp/2AB79E02-C977-4B4A-AFEE-60BC1641A67F.JPG
then PHAsset will never find your asset. Further more, the latter is a copy of your file, so if you happen to have a very large image/video, iOS will duplicate it in that OutgoingTemp directory. Nowhere in the documentation says when it's going to be deleted, hopefully soon enough.
I think this is a big gap Apple has left between Sharing Extensions and PHPhotoLibrary framework. Apple should've be creating an API to close it, and soon.
You can get PHAsset if image is shared from Photos app. The item provider will give you a URL that contains the image's filename, you use this to match PHAsset.
/// Assets that handle through handleImageItem:completionHandler:
private var handledAssets = [PHAsset]()
/// Key is the matched asset's original file name without suffix. E.g. IMG_193
private lazy var imageAssetDictionary: [String : PHAsset] = {
let options = PHFetchOptions()
options.includeHiddenAssets = true
let fetchResult = PHAsset.fetchAssetsWithOptions(options)
var assetDictionary = [String : PHAsset]()
for i in 0 ..< fetchResult.count {
let asset = fetchResult[i] as! PHAsset
let fileName = asset.valueForKey("filename") as! String
let fileNameWithoutSuffix = fileName.componentsSeparatedByString(".").first!
assetDictionary[fileNameWithoutSuffix] = asset
}
return assetDictionary
}()
...
provider.loadItemForTypeIdentifier(imageIdentifier, options: nil) { imageItem, _ in
if let image = imageItem as? UIImage {
// handle UIImage
} else if let data = imageItem as? NSData {
// handle NSData
} else if let url = imageItem as? NSURL {
// Prefix check: image is shared from Photos app
if let imageFilePath = imageURL.path where imageFilePath.hasPrefix("/var/mobile/Media/") {
for component in imageFilePath.componentsSeparatedByString("/") where component.containsString("IMG_") {
// photo: /var/mobile/Media/DCIM/101APPLE/IMG_1320.PNG
// edited photo: /var/mobile/Media/PhotoData/Mutations/DCIM/101APPLE/IMG_1309/Adjustments/FullSizeRender.jpg
// cut file's suffix if have, get file name like IMG_1309.
let fileName = component.componentsSeparatedByString(".").first!
if let asset = imageAssetDictionary[fileName] {
handledAssets.append(asset)
imageCreationDate = asset.creationDate
}
break
}
}
}

'NSURLRequest?' does not have a member named 'URL' - Swift

Hi I am really new to coding in Swift, and am trying to follow the codes in this book: http://www.apress.com/9781484202098. Learn iOS 8 App Development 2nd Edition by James Bucanek
In particular, I am working through Chapter 3 - building a URL shortening app, but despite having copied the code exactly, I am getting an error on the code in Page 76:
if let toShorten = webView.request.URL.absoluteString {
which states 'NSURLRequest?' does not have a member named 'URL'.
I have tried googling an answer, but unfortunately have not come across anything. Any response I can find seems to suggest that my code ought to be working (e.g. How to get url which I hit on UIWebView?). This seems to have the closest answer SWIFT: Why I can't get the current URL loaded in UIWebView? but the solution does not appear to work for me. If I add a ? after the request, it will then at least build it, but I then have a nil variable returned.
I am using Xcode v6.1.1. Here is the piece of code that is coming up with the error in ViewController.swift:
let GoDaddyAccountKey = "0123456789abcdef0123456789abcdef" //this is replaced by my actual account key in my own code
var shortenURLConnection: NSURLConnection?
var shortURLData: NSMutableData?
#IBAction func shortenURL( AnyObject ) {
if let toShorten = webView.request?.URL.absoluteString { // ? now added
let encodedURL = toShorten.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)
let urlString = "http://api.x.co/Squeeze.svc/text/\(GoDaddyAccountKey)?url=\(encodedURL)"
shortURLData = NSMutableData()
if let firstrequest = NSURL(string: urlString) //added if here and removed !
let request = NSURLRequest(URL:firstrequest)
shortenURLConnection = NSURLConnection(request:request, delegate:self)
shortenButton.enabled = false
}
}
}
If you have any suggestions on how I can fix this, I would really appreciate it!
Update:
Following suggestions from Ashley below, I have amended my code so that it is no longer bringing up the error (see comments above). However, it is now no longer running. This appears to be because the urlString is being created as http://api.x.co/Squeeze.svc/text/d558979bb9b84eddb76d8c8dd9740ce3?url=Optional("http://www.apple.com/"). The problem is therefore the Optional() that is included and thus makes it an invalid URL. Does anyone have a suggestion on how to remove this please?
request is an optional property on UIWebView:
var request: NSURLRequest? { get }
also stringByAddingPercentEscapesUsingEncoding returns an optional:
func stringByAddingPercentEscapesUsingEncoding(_ encoding: UInt) -> String?
What you need is to make user of optional binding in a few places:
if let toShorten = webView.request?.URL.absoluteString {
if let encodedURL = toShorten.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding) {
let urlString = "http://api.x.co/Squeeze.svc/text/\(GoDaddyAccountKey)?url=\(encodedURL)"
shortURLData = NSMutableData()
if let firstrequest = NSURL(string: urlString) { // If a method can return a nil, don't force unwrap it
let request = NSURLRequest(URL:first request)
shortenURLConnection = NSURLConnection(request:request, delegate:self)
shortenButton.enabled = false
}
}
}
See Apple's docs on optional chaining for details
See Apple's docs for NSURL class

Resources