swift Kingfisher and MPMediaItemArtwork - ios

I am currently printing out the image for now playing using the following code.
if var strUrl = nowplaying.data.first?.track.imageurl {
strUrl = strUrl.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
self.imageurl.kf.setImage(with: URL(string: strUrl), placeholder: nil)
}
I want to send that image to
MPMediaItemPropertyArtwork
which is in the following function
func nowplaying(artist: String, song: String, artwork: String){
MPNowPlayingInfoCenter.default().nowPlayingInfo = [
MPMediaItemPropertyTitle:song,
MPMediaItemPropertyArtist:artist,
MPMediaItemPropertyArtwork:artwork
]
}
Currently I have MusicPlayer.shared.nowplaying(artist: $0.track.artist, song: $0.track.title, artwork: $0.track.imageurl) which sends the data correctly but then crashes - and I know it is because you can't have a remote image in the artwork section for MPMediaItemProperty...
So I am wondering how do I cache the image and send it?
code tried
let artwork = MPMediaItemArtwork(boundsSize: CGSize(width: 100, height: 100)) { size -> UIImage in
KingfisherManager.shared.retrieveImage(with: cover, options: nil, progressBlock: nil, completionHandler: { image, error, cacheType, imageURL in
print(image)
})
return UIImage()
}
it prints in the console log
Optional(<UIImage:0x280a4e0a0 anonymous {500, 500}>)
but seems not to print it to the
MPMediaItemPropertyArtwork

You can save an image in the cache and can retrieve it whenever you need.
let resource = ImageResource(downloadURL: url, cacheKey: "my_cache_key")
imageView.kf.setImage(with: resource)
You can retrieve an image from the cache by using
cache.retrieveImage(forKey: "cacheKey") { result in
//Your code
)}
For more information, you can check this link https://github.com/onevcat/Kingfisher/wiki/Cheat-Sheet
Hope this works...

The value of MPMediaItemPropertyArtwork should be MPMediaItemArtwork class, not URL or String. In those class you can load UIImage async and set as an artwork in Command Center
Here is a working example with Kingfisher (please update it according your model structure):
ImageDownloader.default.downloadImage(
with: artworkUrl,
options: nil,
progressBlock: nil) { (result) in
guard let image = try? result.get().image else { return }
DispatchQueue.main.async {
MPNowPlayingInfoCenter.default().nowPlayingInfo = [
MPMediaItemPropertyTitle: song,
MPMediaItemPropertyArtist: artist,
MPMediaItemPropertyArtwork: image
]
}
}

Related

CFNetwork internal error (0xc01a:/BuildRoot/Library/Caches/com.apple.xbs/Sources/CFNetwork_Sim/CFNetwork-808.2.16/Loading/URLConnectionLoader.cpp:304)

appName[8121:97068] 8121: CFNetwork internal error (0xc01a:/BuildRoot/Library/Caches/com.apple.xbs/Sources/CFNetwork_Sim/CFNetwork-808.2.16/Loading/URLConnectionLoader.cpp:304)
I have a chat app, and I want to send an image to others.
I: IPhone ; others: simulator
I take a photo by camera, and press "use this photo",then I'll send an url to others and upload image to server.
Others receive url message immediately, and I use sdWebimage to show this image.
But,when I receive the image, the image request print log error.
log say this image don't exist.
I dont know make this image download error,whether the image is uploading and the server don't have this image or not.
How can prevent this situation or have any function to set download again after 10 seconds of downloading error ?
this is my sd_image func:
cell.photoImageView.sd_setImage(with: url, placeholderImage: nil, options: .progressiveDownload, progress: nil
, completed: { (image, error, cacheType, url) in
guard image != nil else{
print("Image not exist!")
cell.photoImageView.image = resizeImage(image:#imageLiteral(resourceName: "img_refresh"), newWidth: 125)
return
}
print("image here!!!")
DispatchQueue.global().async {
let data = try? Data(contentsOf: url!) //make sure your image in this url does exist, otherwise unwrap in a if let check / try-catch
if data != nil
{
if let image = UIImage(data: data!)
{
if !FileManager.default.fileExists(atPath: fileURL.path) {
if let jpegData = UIImageJPEGRepresentation(image, 0.001)
{
do {
try jpegData.write(to: fileURL, options: .atomic)
print("image save local done!!!")
} catch {
debug(object: error)
}
}
} else {
print("image already esist")
}
DispatchQueue.main.async {
cell.photoImageView.image = resizeImage(image: image, newWidth: 175)
self.tableView.reloadRows(at: [indexPath], with: .automatic)
}
}
}
}
})
uploading
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
let uuid = NSUUID().uuidString
let imageName:String = chatroomId + "_" + uuid + ".jpg"
let documentsPath = NSHomeDirectory().appending("/Documents/\(chatroomId)/")
let imagePath = documentsPath.appending(imageName)
let imageUrl = URL(fileURLWithPath: imagePath)
print("imageUrl is here:\(imageUrl)")
photoImage = info[UIImagePickerControllerOriginalImage] as? UIImage
if picker.sourceType == .camera {
photoImage = info[UIImagePickerControllerOriginalImage] as? UIImage
UIImageWriteToSavedPhotosAlbum(photoImage!, nil, nil, nil)
}
let imageData:Data = UIImageJPEGRepresentation(photoImage!, 0.001)!
do {
try imageData.write(to: imageUrl,options: .atomic)
} catch let error {
print(error)
}
//uploading
let objectKey:String = "chatroom/" + imageName
server.uploadObjectAsync(imageUrl, objectKey: objectKey)
let message = server.url + imageName
self.room.send(message: message)
self.tableView.scrollToBottom()
self.tableView.reloadData()
self.dismiss(animated: true, completion: nil)
}
try to add .allowInvalidSSLCertificates to your sd_setImage function :
cell.photoImageView.sd_setImage(with: url, placeholderImage: nil, options: .allowInvalidSSLCertificates, progress: nil
, completed: { (image, error, cacheType, url) in

Fetch Thumbnail from Video URL using SDWebImage

I need to fetch thumbnail from a video URL to display in ImageView that is in a UITableViewCell.
I can get thumbnail using this method but it takes lots of resource and hence the tableview scrolling lags and stops for the time cellForRowAtIndexPath is called.
This is the method.
func createThumbnailOfVideoFromFileURL(videoURL: String) -> UIImage? {
let asset = AVAsset(url: URL(string: videoURL)!)
let assetImgGenerate = AVAssetImageGenerator(asset: asset)
assetImgGenerate.appliesPreferredTrackTransform = true
let time = CMTimeMakeWithSeconds(Float64(1), 100)
do {
let img = try assetImgGenerate.copyCGImage(at: time, actualTime: nil)
let thumbnail = UIImage(cgImage: img)
return thumbnail
} catch {
return UIImage(named: "ico_placeholder")
}
}
Can anyone please suggest me an alternative to use SDWebImage so that the images go to the cache and are not fetched everytime UITableView delegates are called.
Kindly suggest any other approach if i m wrong.
I used the above method as it is and cached the images in an array of UIImage and made a check in cellForRowAtIndexPath that if image exists at that particular index then use that else fetch the image in background.
Not using SDWebImage but this code works like a charm
func getThumbnailFromUrl(_ url: String?, _ completion: #escaping ((_ image: UIImage?)->Void)) {
guard let url = URL(string: (url ?? "")) else { return }
DispatchQueue.main.async {
let asset = AVAsset(url: url)
let assetImgGenerate = AVAssetImageGenerator(asset: asset)
assetImgGenerate.appliesPreferredTrackTransform = true
let time = CMTimeMake(value: 2, timescale: 1)
do {
let img = try assetImgGenerate.copyCGImage(at: time, actualTime: nil)
let thumbnail = UIImage(cgImage: img)
completion(thumbnail)
} catch let error{
print("Error :: ", error)
completion(nil)
}
}
}
Usage
self.getThumbnailFromUrl(video.url.description) { (image) in
//Use image where you want to use
}

How to get image name from UIImagePickerController taken with Camera

This below code is working perfectly fine for images picked from gallery. But will not work if taken with Camera. I tried to save image into storage and read again, but I was unable to do that. So could any one help me in this? Thank you.
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
if let referenceUrl = info[UIImagePickerControllerReferenceURL] as? NSURL, image = info[UIImagePickerControllerOriginalImage] as? UIImage {
let phAsset = PHAsset.fetchAssetsWithALAssetURLs([referenceUrl], options: nil).lastObject as! PHAsset
PHImageManager.defaultManager().requestImageDataForAsset(phAsset, options: PHImageRequestOptions(), resultHandler: { (imagedata, dataUTI, orientation, info) in
if info!.keys.contains(NSString(string: "PHImageFileURLKey")) {
let path = info![NSString(string: "PHImageFileURLKey")] as! NSURL
print("path q\(path)")
self.mImageUrl = path
self.mlocalPath = path.path
self.mImageExtension = path.pathExtension
self.mImageName = path.lastPathComponent!
print("mImageName q\(self.mImageName)")
}
})
}
dismissViewControllerAnimated(true, completion: nil)
}
Swift 5+
As the previous answers sugested, the image is not stored in gallery yet and hence no imageName. You need to store it in gallery. Use the below Helper class to save and get images from FileManager.
Thanks to this Answer
class CameraImageManager {
static let shared = CameraImageManager()
public func saveImage(imageName: String, image: UIImage) {
guard let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
let fileName = imageName
let fileURL = documentsDirectory.appendingPathComponent(fileName)
guard let data = image.jpegData(compressionQuality: 1) else { return }
//Checks if file exists, removes it if so.
if FileManager.default.fileExists(atPath: fileURL.path) {
do {
try FileManager.default.removeItem(atPath: fileURL.path)
print("Removed old image")
} catch let removeError {
print("couldn't remove file at path", removeError)
}
}
do {
try data.write(to: fileURL)
} catch let error {
print("error saving file with error", error)
}
}
public func getImagePathFromDiskWith(fileName: String) -> URL? {
let documentDirectory = FileManager.SearchPathDirectory.documentDirectory
let userDomainMask = FileManager.SearchPathDomainMask.userDomainMask
let paths = NSSearchPathForDirectoriesInDomains(documentDirectory, userDomainMask, true)
if let dirPath = paths.first {
let imageUrl = URL(fileURLWithPath: dirPath).appendingPathComponent(fileName)
return imageUrl
}
return nil
}
public func loadImageFromDiskWith(fileName: String) -> UIImage? {
let documentDirectory = FileManager.SearchPathDirectory.documentDirectory
let userDomainMask = FileManager.SearchPathDomainMask.userDomainMask
let paths = NSSearchPathForDirectoriesInDomains(documentDirectory, userDomainMask, true)
if let dirPath = paths.first {
let imageUrl = URL(fileURLWithPath: dirPath).appendingPathComponent(fileName)
let image = UIImage(contentsOfFile: imageUrl.path)
return image
}
return nil
}
}
Now, in your imagePickerController didFinishPickingMediaWithInfo callback function, this is how you can assign a name to an image and save it.
public func imagePickerController(_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
guard let image = info[.editedImage] as? UIImage else { return }
let imageName = "RDV_" + UUID().uuidString
CameraImageManager.shared.saveImage(imageName: imageName, image: image)
print("IMAGE NAME IS: ", imageName)
}
Hope It Helps.
You can use a notification with addObserver like this
ViewController A : where you want image to be changed, add this in viewDidLoad
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.methodOfImageChange(_:)), name:"ImageChanged", object: nil)
Add this method in ViewController A
func methodOfImageChange(notification: NSNotification){
let appStatus = notification.userInfo as? Dictionary<String,AnyObject>
// appStatus contains your image in "image" key
}
Now in didFinishPickingMediaWithInfo add this
let dictionary: [String:AnyObject] = [
"image" : (info[UIImagePickerControllerOriginalImage] as? UIImage)!,
]
NSNotificationCenter.defaultCenter().postNotificationName("ImageChanged", object: self, userInfo: dictionary)
picker .dismissViewControllerAnimated(true, completion: nil)
Hope this helps
The image isn't in the gallery yet, so I don't believe you have a name.
In my app the flow (via navigation controller) is:
Selection VC (choice of Camera or Photo Library) ->
UIImagePickerController ->
Edit VC (with back navigation and action button for - among others - saving to Photo Library)
If the user chooses Camera, they take a picture and the options are "Retake" or "Use Photo". Is they choose "Use Photo", they are in the Edit VC.
If they then choose to go back to the Select VC, the image is nowhere to be found.

Importing an image using Action Extension - URL to a local Image works but not with actual image data

My iOS app (Swift 3) needs to important images from other apps using an Action Extension. I'm using the standard Action Extension template code which works just fine for apps like iOS Mail and Photos where the image shared is a URL to a local file. But for certain apps where the image being shared is the actual image data itself, my action extension code isn't getting the image.
for item: Any in self.extensionContext!.inputItems {
let inputItem = item as! NSExtensionItem
for provider: Any in inputItem.attachments! {
let itemProvider = provider as! NSItemProvider
if itemProvider.hasItemConformingToTypeIdentifier(kUTTypeImage as String) { //we'll take any image type: gif, png, jpg, etc
// This is an image. We'll load it, then place it in our image view.
weak var weakImageView = self.imageView
itemProvider.loadItem(forTypeIdentifier: kUTTypeImage as String, options: nil, completionHandler: { (imageURL,
error) in
OperationQueue.main.addOperation {
if let strongImageView = weakImageView {
if let imageURL = imageURL as? NSURL {
strongImageView.image = UIImage(data: NSData(contentsOf: imageURL as URL)! as Data)
let imageData = NSData(contentsOf: imageURL as URL)! as Data
self.gifImageView.image = UIImage.gif(data: imageData)
let width = strongImageView.image?.size.width
let height = strongImageView.image?.size.height
.... my custom logic
}
}
For reference, I reached out to the developer for one of the apps where things aren't working and he shared this code on how he is sharing the image to the Action Extension.
//Here is the relevant code. At this point the scaledImage variable holds a UIImage.
var activityItems = Array<Any?>()
if let pngData = UIImagePNGRepresentation(scaledImage) {
activityItems.append(pngData)
} else {
activityItems.append(scaledImage)
}
//Then a little later it presents the share sheet:
let activityVC = UIActivityViewController(activityItems: activityItems,applicationActivities: [])
self.present(activityVC, animated: true, completion: nil)
Figured it out thanks to this post which explains the challenge quite well https://pspdfkit.com/blog/2017/action-extension/ . In summary, we don't know if the sharing app is giving us a URL to an existing image or just raw image data so we need to modify the out of the box action extension template code to handle both cases.
for item: Any in self.extensionContext!.inputItems {
let inputItem = item as! NSExtensionItem
for provider: Any in inputItem.attachments! {
let itemProvider = provider as! NSItemProvider
if itemProvider.hasItemConformingToTypeIdentifier(kUTTypeImage as String) { //we'll take any image type: gif, png, jpg, etc
// This is an image. We'll load it, then place it in our image view.
weak var weakImageView = self.imageView
itemProvider.loadItem(forTypeIdentifier: kUTTypeImage as String, options: nil, completionHandler: { (imageURL,
error) in
OperationQueue.main.addOperation {
if let strongImageView = weakImageView {
if let imageURL = imageURL as? NSURL {
strongImageView.image = UIImage(data: NSData(contentsOf: imageURL as URL)! as Data)
let imageData = NSData(contentsOf: imageURL as URL)! as Data
self.gifImageView.image = UIImage.gif(data: imageData)
let width = strongImageView.image?.size.width
let height = strongImageView.image?.size.height
.... my custom logic
}
else
guard let imageData = imageURL as? Data else { return } //can we cast to image data?
strongImageView_.image = UIImage(data: imageData_)
//custom logic
}

Converting NSSecureCoding to NSData - Xcode - Swift

I have some code that creates an NSSecureCoding variable called "content" and I want to convert that variable into NSData that can then be made into a UIImage or be sent to a local server. How do I convert this properly? I want this for a Share Extension I am making in my iOS app, so when you press share on a photo, it gets the photo contents and converts it into NSData. Here is my code:
inputItem = extensionContext!.inputItems.first as NSExtensionItem
attachment = inputItem.attachments![0] as NSItemProvider
if (attachment.hasItemConformingToTypeIdentifier(kUTTypeImage as String)){
attachment.loadItemForTypeIdentifier(kUTTypeImage as String,
options: nil,
completionHandler: {(content, error: NSError!) in
//insert code to convert "content"(NSSecureCoding) to NSData variable
})
}
DispatchQueue.global().async {
attachment.loadItem(forTypeIdentifier: kUTTypeImage as String, options: nil, completionHandler: { (item, error) in
if let error = error {
print(error.localizedDescription)
return
}
var image: UIImage?
if item is UIImage {
image = item as? UIImage
}
if item is URL {
let data = try? Data(contentsOf: item as! URL)
image = UIImage(data: data!)!
}
if item is Data {
image = UIImage(data: item as! Data)!
}
if let image = image {
DispatchQueue.main.async {
// image here
}
}
})
}
kinda late but this happened to me today and I solved it like this,
inside the completionHandler:
if let data = content {
self.imageData = UIImage(data: NSData(contentsOfURL: data as! NSURL)!)
}
imageData is UIImage type.

Resources