issue with image cache in getting image back firebase swift iOS - ios

[][!I want load image from download url from firebase using image cache how can I can create an inheritance where my image gets loaded I guess I am missing OOP concept somewhere. my image view is swipe label, how too create an instance where I can display the image on my view.
import UIKit
import Firebase
class swipeLabelViewController: UIViewController {
#IBOutlet weak var UserAgeText: UILabel!
var user:User? {
didSet {
let userNameSwipe = user?.userName
userNameLabel.text = userNameSwipe
let userAgeSwipe = user?.userAge
UserAgeText.text = userAgeSwipe
guard let profileImageUrl = user?.profileImageUrl else {
return }
profileImageView.loadImage(with: profileImageUrl)
print(profileImageUrl)
// let userFetchedImage = user?.profileImageUrl
// swipeLabel.image = userFetchedImage
// self.swipeLabel.image = UIImage(contentsOfFile: profileImageUrl)
}
}
var profileImageView: CustomImageView = {
let iv = CustomImageView()
return iv
}()
var imageI : UIImage!
// var swipepic = CustomImageView()
#IBOutlet weak var swipeLabel: UIImageView!
#IBOutlet weak var userNameLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
swipeLabel.image = imageI
let gesture = UIPanGestureRecognizer(target: self, action:
selector(wasDragged(gestureRecognizer:)))
swipeLabel.addGestureRecognizer(gesture)
fetchCurrentUserData()
}
// user model class``
class User {
// attributes getting users info so i can set up from firebase
var userName:String!
var userAge:String!
var uid:String!
var profileImageUrl: String!
init (uid:String,dictionary:Dictionary) {
self.uid = uid
if let userName = dictionary [ "userName" ] as? String{
self.userName = userName
}
if let userAge = dictionary [ "userAge" ] as? String{
self.userAge = userAge
}
if let profileImageUrl = dictionary["profileImageURL"] as? String {
self.profileImageUrl = profileImageUrl
}
}
}
// and lastly image cache class customImageView
import Foundation
import UIKit
var imageCache = String: UIImage
class CustomImageView: UIImageView {
var lastImgUrlUsedToLoadImage: String?
func loadImage(with urlString: String) {
// set image to nil
self.image = nil
// set lastImgUrlUsedToLoadImage
lastImgUrlUsedToLoadImage = urlString
// check if image exists in cache
if let cachedImage = imageCache[urlString] {
self.image = cachedImage
return
}
// url for image location
guard let url = URL(string: urlString) else { return }
// fetch contents of URL
URLSession.shared.dataTask(with: url) { (data, response, error) in
// handle error
if let error = error {
print("Failed to load image with error",
error.localizedDescription)
}
if self.lastImgUrlUsedToLoadImage != url.absoluteString {
return
}
// image data
guard let imageData = data else { return }
// create image using image data
let photoImage = UIImage(data: imageData)
// set key and value for image cache
imageCache[url.absoluteString] = photoImage
// set image
DispatchQueue.main.async {
self.image = photoImage
}
}.resume()
}
}
no errors I just can't see image on my swipelabel UIImage

You need to make a separate request to download the image from the url that firebase returns. Here is a playground example:
//: A SpriteKit based Playground
import PlaygroundSupport
import UIKit
enum ImageDownloadError: Error {
case failedToConvertDataToImage
}
func dowloadImage(from url: URL, completion: #escaping (Result<UIImage, Error>) -> Void) {
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard let image = data.flatMap(UIImage.init(data:)) else {
return completion (.failure(error ?? ImageDownloadError.failedToConvertDataToImage))
}
completion(.success(image))
}.resume()
}
let imageView = UIImageView()
dowloadImage(from: URL(string: "https://res.cloudinary.com/demo/image/upload/sample.jpg")!) { [weak imageView] result in
switch result {
case .failure(let error):
print(error.localizedDescription)
case .success(let image):
imageView?.image = image
}
}
imageView.frame = .init(origin: .zero, size: .init(width: 300, height: 300))
PlaygroundPage.current.liveView = imageView
PlaygroundPage.current.needsIndefiniteExecution = true

Related

Share Extension don't open/share .HEIC type image

i created share extension for my app which can share image/videos from photos(Gallery) and it work properly but here problem is if i select .HEIC type file then click on my share extension it show error as shown in below screenshot .
import UIKit
import Social
import MobileCoreServices
import AVKit
import Toast_Swift
//#objc(ShareExtensionViewController)
class ShareViewController: UIViewController {
#IBOutlet weak var image: UIImageView!
#IBOutlet weak var btn: UIButton!
#IBOutlet weak var lbl: UILabel!
#IBOutlet weak var view2: UIView!
#IBOutlet weak var cancel: UIButton!
#IBOutlet weak var blurview: UIVisualEffectView!
var allMedia = [Data()]
var singleImage = UIImage()
var imagesUrl = [URL]()
let groupPath = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "
my group identifire ")
var saveDone = Bool()
override func viewDidLoad() {
super.viewDidLoad()
let attachments = (self.extensionContext?.inputItems.first as? NSExtensionItem)?.attachments ?? []
self.btn.setTitle("Add", for: .normal)
self.btn.layer.cornerRadius = 10
self.cancel.layer.cornerRadius = 10
self.image.clipsToBounds = false
applyshadow(image: image)
blurview.isHidden = true
}
override func viewDidAppear(_ animated: Bool) {
self.handleSharedFile()
}
// MARK:- function
func applyshadow(image: UIImageView) {
image.clipsToBounds = false
image.layer.shadowColor = UIColor.systemGray.cgColor
image.layer.shadowOpacity = 1
image.layer.shadowOffset = CGSize.zero
image.layer.shadowRadius = 7.5
}
func videotext(string : String) ->Bool{
let videoextension = [".MP4",".mp4",".mkv",".MKV",".AVI",".avi",".mov", ".MOV"]
if videoextension.contains(string){
return true
}
return false
}
func videoThumb(filepath: URL) -> UIImage{
do{
// let filepath = data?.appendingPathComponent(lbl[indexPath.row])
let asset = AVURLAsset(url: filepath, options: nil)
let imgGenerator = AVAssetImageGenerator(asset: asset)
imgGenerator.appliesPreferredTrackTransform = true
let cgImage = try imgGenerator.copyCGImage(at: CMTimeMake(value: 0, timescale: 1), actualTime: nil)
let uiImage = UIImage(cgImage: cgImage)
return uiImage
}catch let error {
print("Error: \(error.localizedDescription)")
return UIImage()
}
}
private func handleSharedFile() {
// extracting the path to the URL that is being shared
let attachments = (self.extensionContext?.inputItems.first as? NSExtensionItem)?.attachments ?? []
let contentType = kUTTypeData as String
for provider in attachments {
// Check if the content type is the same as we expected
if provider.hasItemConformingToTypeIdentifier(contentType) {
provider.loadItem(forTypeIdentifier: contentType,
options: nil) { [unowned self] (data, error) in
guard error == nil else { return }
if let url = data as? URL {
imagesUrl.append(url)
print(imagesUrl)
if videotext(string: String(url.lastPathComponent.suffix(4))){
DispatchQueue.main.async {
self.image.image = videoThumb(filepath: url)
if attachments.count > 1 {
lbl.text = "\(attachments.count) Items"
}else{
lbl.text = url.lastPathComponent
}
}
}else {
let imageData = try? Data(contentsOf: url)
DispatchQueue.main.async {
if provider == attachments.last{
self.image.image = UIImage(data: imageData!)
}
self.singleImage = UIImage(data: imageData!)!
if attachments.count > 1 {
lbl.text = "\(attachments.count) Items"
}else{
lbl.text = url.lastPathComponent
}
}
}
}else {
print("Impossible to save image")
}
}
}
}
}
thank You in advance for read, giving attention, answer and upvote my question :)

TableView with labels, images, gifs and video hangs / gets stuck incorrect while fetch from firestore in iOS, Swift

I have tableview with label, imageView (for image, gif & video thumbnail). I am sure that doing something wrong and I can't handle its completion handler due to which the app is hanged and gets stuck for a long time.
My model is like,
struct PostiisCollection {
var id :String?
var userID: String?
var leadDetails : NSDictionary?
var company: NSDictionary?
var content: String?
init(Doc: DocumentSnapshot) {
self.id = Doc.documentID
self.userID = Doc.get("userID") as? String ?? ""
self.leadDetails = Doc.get("postiiDetails") as? NSDictionary
self.company = Doc.get("company") as? NSDictionary
self.content = Doc.get("content") as? String ?? ""
}
}
I wrote in my view controller for fetch this,
var postiisCollectionDetails = [PostiisCollection]()
override func viewDidLoad() {
super.viewDidLoad()
let docRef = Firestore.firestore().collection("PostiisCollection").whereField("accessType", isEqualTo: "all_access")
docRef.getDocuments { (querysnapshot, error) in
if let doc = querysnapshot?.documents, !doc.isEmpty {
print("Document is present.")
for document in querysnapshot!.documents {
_ = document.documentID
if let compCode = document.get("company") as? NSDictionary {
do {
let jsonData = try JSONSerialization.data(withJSONObject: compCode)
let companyPost: Company = try! JSONDecoder().decode(Company.self, from: jsonData)
if companyPost.companyCode == AuthService.instance.companyId ?? ""{
print(AuthService.instance.companyId ?? "")
if (document.get("postiiDetails") as? NSDictionary) != nil {
let commentItem = PostiisCollection(Doc: document)
self.postiisCollectionDetails.append(commentItem)
}
}
} catch {
print(error.localizedDescription)
}
DispatchQueue.main.async {
self.tableView.isHidden = false
self.tableView.reloadData()
}
}
}
}
}
}
I need to check for the index path with image view is either image or gif or video with different parameters, I tried with tableview delegate and datasource method by,
extension AllAccessPostiiVC: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return postiisCollectionDetails.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "AllAccessCell", for: indexPath)
let label1 = cell.viewWithTag(1) as? UILabel
let imagePointer = cell.viewWithTag(3) as? UIImageView
let getGif = arrPostiisCollectionFilter[indexPath.row].leadDetails?.value(forKey: "gif") as? NSArray
let getPhoto = arrPostiisCollectionFilter[indexPath.row].leadDetails?.value(forKey: "photo") as? NSArray
let getVideo = arrPostiisCollectionFilter[indexPath.row].leadDetails?.value(forKey: "video") as? NSArray
label1?.text = "\(arrPostiisCollectionFilter[indexPath.row].leadDetails?.value(forKey: "title"))"
if getGif != nil {
let arrGif = getGif?.value(forKey: "gifUrl") as! [String]
print(arrGif[0])
let gifURL : String = "\(arrGif[0])"
let imageURL = UIImage.gifImageWithURL(gifURL)
imagePointer?.image = imageURL
playButton?.isHidden = true
}
if getPhoto != nil {
let arrPhoto = getPhoto?.value(forKey: "photoUrl") as! [String]
print(arrPhoto[0])
let storageRef = Storage.storage().reference(forURL: arrPhoto[0])
storageRef.downloadURL(completion: { (url, error) in
do {
let data = try Data(contentsOf: url!)
let image = UIImage(data: data as Data)
DispatchQueue.main.async {
imagePointer?.image = image
playButton?.isHidden = true
}
} catch {
print(error)
}
})
}
if getVideo != nil {
let arrVideo = getVideo?.value(forKey: "videoUrl") as! [String]
let videoURL = URL(string: arrVideo[0])
let asset = AVAsset(url:videoURL!)
if let videoThumbnail = asset.videoThumbnail{
SVProgressHUD.dismiss()
imagePointer?.image = videoThumbnail
playButton?.isHidden = false
}
}
}
}
If I run, the app hangs in this page and data load time is getting more, some cases the preview image is wrongly displayed and not able to handle its completion
As others have mentioned in the comments, you are better of not performing the background loading in cellFroRowAtIndexPath.
Instead, it's better practice to add a new method fetchData(), where you perform all the server interaction.
So for example:
// Add instance variables for fast access to data
private var images = [UIImage]()
private var thumbnails = [UIImage]()
private func fetchData(completion: ()->()) {
// Load storage URLs
var storageURLs = ...
// Load data from firebase
let storageRef = Storage.storage().reference(forURL: arrPhoto[0])
storageRef.downloadURL(completion: { (url, error) in
// Parse data and store resulting image in image array
...
// Call completion handler to indicate that loading has finished
completion()
})
}
Now you can call fetchData() whenever you would like to refresh data and call tableview.reloadData() within the completion handler. That of course must be done on the main thread.
This approach simplifies your cellForRowAtIndexPath method.
There you can just say:
imagePointer?.image = ...Correct image from image array...
Without any background loading.
I suggest using below lightweight extension for image downloading from URL
using NSCache
extension UIImageView {
func downloadImage(urlString: String, success: ((_ image: UIImage?) -> Void)? = nil, failure: ((String) -> Void)? = nil) {
let imageCache = NSCache<NSString, UIImage>()
DispatchQueue.main.async {[weak self] in
self?.image = nil
}
if let image = imageCache.object(forKey: urlString as NSString) {
DispatchQueue.main.async {[weak self] in
self?.image = image
}
success?(image)
} else {
guard let url = URL(string: urlString) else {
print("failed to create url")
return
}
let request = URLRequest(url: url)
let task = URLSession.shared.dataTask(with: request) {(data, response, error) in
// response received, now switch back to main queue
DispatchQueue.main.async {[weak self] in
if let error = error {
failure?(error.localizedDescription)
}
else if let data = data, let image = UIImage(data: data) {
imageCache.setObject(image, forKey: url.absoluteString as NSString)
self?.image = image
success?(image)
} else {
failure?("Image not available")
}
}
}
task.resume()
}
}
}
Usage:
let path = "https://i.stack.imgur.com/o5YNI.jpg"
let imageView = UIImageView() // your imageView, which will download image
imageView.downloadImage(urlString: path)
No need to put imageView.downloadImage(urlString: path) in mainQueue, its handled in extension
In your case:
You can implement following logic in cellForRowAt method
if getGif != nil {
let arrGif = getGif?.value(forKey: "gifUrl") as! [String]
let urlString : String = "\(arrGif[0])"
let image = UIImage.gifImageWithURL(urlString)
imagePointer?.image = image
playButton?.isHidden = true
}
else if getPhoto != nil {
let arrPhoto = getPhoto?.value(forKey: "photoUrl") as! [String]
let urlString = Storage.storage().reference(forURL: arrPhoto[0])
imagePointer?.downloadImage(urlString: urlString)
playButton?.isHidden = true
}
elseif getVideo != nil {
let arrVideo = getVideo?.value(forKey: "videoUrl") as! [String]
let urlString = arrVideo[0]
imagePointer?.downloadImage(urlString: urlString)
playButton?.isHidden = false
}
If you have one imageView to reload in tableView for photo, video and gif. then use one image array to store it prior before reloading. So that your main issue of hang or stuck will be resolved. Here the main issue is each time in table view cell collection data is being called and checked while scrolling.
Now I suggest to get all photo, gifs and video (thumbnail) as one single array prior to table view reload try this,
var cacheImages = [UIImage]()
private func fetchData(completionBlock: () -> ()) {
for (index, _) in postiisCollectionDetails.enumerated() {
let getGif = postiisCollectionDetails[index].leadDetails?.value(forKey: "gif") as? NSArray
let getPhoto = postiisCollectionDetails[index].leadDetails?.value(forKey: "photo") as? NSArray
let getVideo = postiisCollectionDetails[index].leadDetails?.value(forKey: "video") as? NSArray
if getGif != nil {
let arrGif = getGif?.value(forKey: "gifUrl") as! [String]
let gifURL : String = "\(arrGif[0])"
self.randomList.append(gifURL)
/////---------------------------
let imageURL = UIImage.gifImageWithURL(gifURL)
self.cacheImages.append(imageURL!)
//////=================
}
else if getVideo != nil {
let arrVideo = getVideo?.value(forKey: "videoUrl") as! [String]
let videoURL: String = "\(arrVideo[0])"
let videoUrl = URL(string: arrVideo[0])
let asset = AVAsset(url:videoUrl!)
if let videoThumbnail = asset.videoThumbnail{
////--------------
self.cacheImages.append(videoThumbnail)
//-----------
}
self.randomList.append(videoURL)
}else if getPhoto != nil {
let arrPhoto = getPhoto?.value(forKey: "photoUrl") as! [String]
let photoURL : String = "\(arrPhoto[0])"
/////---------------------------
let url = URL(string: photoURL)
let data = try? Data(contentsOf: url!)
if let imageData = data {
let image = UIImage(data: imageData)
if image != nil {
self.cacheImages.append(image!)
}
else {
let defaultImage: UIImage = UIImage(named:"edit-user-80")!
self.cacheImages.append(defaultImage)
}
}
//////=================
}
else {
//-----------------
let defaultImage: UIImage = UIImage(named:"edit-user-80")!
self.cacheImages.append(defaultImage)
//--------------------
}
}
completionBlock()
}
After getting all UIImage as array where loop is being called. Now you call this function inside your viewDidLoad. So after all values in images fetched then try to call tableView like this,
override func viewDidLoad() {
self.fetchData {
DispatchQueue.main.async
self.tableView.reloadData()
}
}
}
Now atlast, you may use SDWebImage or any other background image class or download method to call those in tableView cellforRow method,
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// your cell idetifier & other stuffs
if getVideo != nil {
imagePointer?.image = cacheImages[indexPath.row]
playButton?.isHidden = false
}else {
imagePointer?.image = cacheImages[indexPath.row]
// or get photo with string via SdWebImage
// imagePointer?.sd_setImage(with: URL(string: photoURL), placeholderImage: UIImage(named: "edit-user-80"))
playButton?.isHidden = true
}
return cell
}
You're handling data in a totally wrong manner. Data(contentsOf: url!) - This is wrong. You should chache the images and should download it to directory. When you convert something into data it takes place into the memory(ram) and it is not good idea when playing with large files. You should use SDWebImage kind of library to set images to imageview.
Second thing if let videoThumbnail = asset.videoThumbnail - This is also wrong. Why you're creating assets and then getting thumbnail from it? You should have separate URL for the thumbnail image for your all videos in the response of the API and then again you can use SDWebImage to load that thumbnail.
You can use SDWebImage for gif as well.
Alternative of SDWebImage is Kingfisher. Just go through both libraries and use whatever suitable for you.

How to populate an UICollectionView cell with images from Firebase Storage?

I'm building an app that needs an image slideshow as the background of the welcome controller. My plan is to import images into a folder in Firebase Storage, set a Service function to download the folder's images and append to a model, then populate the controller's collection view cell with the images. Am I on the correct path to do an image slideshow? Thanks.
// BackgroundImage Model
struct BackgroundImage {
let backgroundImageUrl: String
init(dictionary: [String : Any]) {
self.backgroundImageUrl = dictionary["backgroundImageUrl"] as? String ?? ""
}
}
// Service
struct BgImgs {
let backgroundImage: UIImage
}
static func fetchBackgroundImages(bgImgs: BgImgs, completion: #escaping([BackgroundImage]) -> Void) {
var backgroundImages = [BackgroundImage]()
guard let imageData = bgImgs.backgroundImage.jpegData(compressionQuality: 0.3) else { return }
let filename = NSUUID().uuidString
let storageRef = STORAGE_REF.reference(withPath: "/background_images/\(filename)")
storageRef.putData(imageData, metadata: nil) { (meta, error) in
storageRef.downloadURL { (url, error) in
guard let backgroundImageUrl = url?.absoluteString else { return }
let values = ["backgroundImageUrl" : backgroundImageUrl]
let bgImages = BackgroundImage(dictionary: values)
backgroundImages.append(bgImages)
completion(backgroundImages)
}
}
}
// WelcomeController
private var backgroundImages = [BackgroundImage]()
func fetchBackgroundImages() {
Service.fetchBackgroundImages(bgImgs: backgroundImages) { backgroundImages in
self.backgroundImages = backgroundImages
}
}
You'll have to reload the UICollectionView after fetchBackgroundImages fetch is done.
func fetchBackgroundImages() {
Service.fetchBackgroundImages(bgImgs: backgroundImages) { backgroundImages in
self.backgroundImages = backgroundImages
self.collectionView.reloadData()
}
}

UICollectionViewCell image reload issue after API call

I am trying to load image for the particular cell for whose indexpath the URL is present. Have function to download the image and send it through call back method, but after callback the other cells are also getting loaded by the downloaded image. Thanks in Adv.
Here is the code sample. In method cellForItemAtIndexPath
let stationCollectionViewcell : StationCollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "stationCell", for: indexPath) as! StationCollectionViewCell
if imageURL.contains("http") {
self.loadImageWithURL(url: URL(string: station.imageURL)!) { (image) in
stationCollectionViewcell.radioImgView.image = image
}
} else if imageURL != "" {
stationCollectionViewcell.radioImgView.image = UIImage(named: "station-therockfm")
} else {
stationCollectionViewcell.radioImgView.image = UIImage(named: "stationImage")
}
And the function that will download the image
func loadImageWithURL(url: URL, callback: #escaping (UIImage) -> ()) {
print("This is getting excuted loadImageWithURL")
let session = URLSession.shared
let downloadTask = session.downloadTask(with: url, completionHandler: {
url, response, error in
if error == nil && url != nil {
if let data = NSData(contentsOf: url!) {
if let image = UIImage(data: data as Data) {
DispatchQueue.main.async(execute: {
callback(image)
})
}
}
}
})
downloadTask.resume()
}
You should reset image before reusing cell, because it is reused with previous image before new is downloaded. Also, you should compare saved url with url in callback, because callback may return when cell is reused.
// reset image
override func prepareForReuse() {
super.prepareForReuse()
radioImgView.image = nil // set nil or default image
}
Add url to callback
func loadImageWithURL(url: URL, callback: #escaping (UIImage, URL) -> ()) {
print("This is getting excuted loadImageWithURL")
let session = URLSession.shared
let downloadTask = session.downloadTask(with: url, completionHandler: {
url, response, error in
if error == nil && url != nil {
if let data = NSData(contentsOf: url!) {
if let image = UIImage(data: data as Data) {
DispatchQueue.main.async(execute: {
callback(image, url!)
})
}
}
}
})
downloadTask.resume()
}
Compare url
let stationCollectionViewcell : StationCollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "stationCell", for: indexPath) as! StationCollectionViewCell
if imageURL.contains("http") {
let url = URL(string: station.imageURL)!
self.loadImageWithURL(url: url) { (image, callbackUrl) in
guard url == callbackUrl else { return }
stationCollectionViewcell.radioImgView.image = image
}
} else if imageURL != "" {
stationCollectionViewcell.radioImgView.image = UIImage(named: "station-therockfm")
} else {
stationCollectionViewcell.radioImgView.image = UIImage(named: "stationImage")
}
I hope, it will run Fine.
import UIKit
let imageCache = NSCache<AnyObject, AnyObject>()
class CustomImageView : UIImageView {
var imgUrlString : String?
func loadImageWithURL(urlString : String) {
imgUrlString = urlString
guard let url = URL(string: urlString) else { return }
image = nil
if let imgFromCache = imageCache.object(forKey: urlString as AnyObject) as? UIImage {
self.image = imgFromCache
return
}
URLSession.shared.dataTask(with: url) { (data, resposne, error) in
if error != nil {
print(error)
return
}
DispatchQueue.main.sync {
let imageToCache = UIImage(data: data!)
imageCache.setObject(imageToCache!, forKey: urlString as AnyObject)
if self.imgUrlString == urlString {
self.image = imageToCache
}
}
}.resume()
}
}
Intialize your instance with this Class.
let thumbnailImageView : CustomImageView = {
let imgView = CustomImageView()
imgView.translatesAutoresizingMaskIntoConstraints = false
imgView.image = #imageLiteral(resourceName: "taylor_swift_blank_space")
imgView.contentMode = .scaleAspectFill
imgView.clipsToBounds = true
return imgView
}()
Cells are reusing each time, also you have async call in the controller. In the downloading completion, you need to reload cell by IndexPath.
Also, reset image in prepareForReuse
override func prepareForReuse() {
super.prepareForReuse()
stationCollectionViewcell.radioImgView.image = nil
}
The best solution with async images it's to have cache
let imageCache: NSCache<NSString, UIImage> = NSCache<NSString, UIImage>()
after loading has been completed save the image:
imageCache.setObject(image, forKey: url as NSString)
and next time, when cell appears, check is image already exists in imageCache
if yes, use image from the cache, if not, download an image
Code should looks like this:
if imageURL.contains("http") {
if let image = imageCache.object(forKey: station.imageURL as NSString) {
stationCollectionViewcell.radioImgView.image = image
} else {
self.loadImageWithURL(url: URL(string: station.imageURL)!) { [weak self] (image) in
self?.imageCache.setObject(image, forKey: url as NSString)
self?.collectionView.collectionView.reloadItems(at: [indexPath])
}
}
} else if imageURL != "" {
stationCollectionViewcell.radioImgView.image = UIImage(named: "station-therockfm")
} else {
stationCollectionViewcell.radioImgView.image = UIImage(named: "stationImage")
}

Swift iOS8 asynchronous image

I need to cache the image in Swift on iOS 8. I have a custom Table Cell View.
Here's my code:
import UIKit
class WorksTableViewCell: UITableViewCell {
#IBOutlet var workImage: UIImageView!
#IBOutlet var workTitle: UILabel!
#IBOutlet var workDescription: UILabel!
func configureCellWith(work: Work){
workTitle.text = work.title
workDescription.text = work.description
if let url = NSURL(string: work.image) {
if let data = NSData(contentsOfURL: url) {
var thumb = UIImage(data: data)
workImage.image = thumb
}
}
}
}
Create a dictionary mapping a String to a UIImage in the table's view controller and modify the data in the cellForRowAtIndexPath function. Jameson Quave has a nice tutorial on this very issue found here. It's updated to use new Swift 1.2 syntax as well.
Thanks I fixed like this:
import UIKit
class WorksTableViewCell: UITableViewCell {
#IBOutlet var workImage: UIImageView!
#IBOutlet var workTitle: UILabel!
#IBOutlet var workDescription: UILabel!
var imageCache = [String:UIImage]()
func configureCellWith(work: Work){
workTitle.text = work.title
workDescription.text = work.description
var imgURL = NSURL(string: work.image)
// If this image is already cached, don't re-download
if let img = imageCache[work.image] {
workImage.image = img
}else {
// The image isn't cached, download the img data
// We should perform this in a background thread
let request: NSURLRequest = NSURLRequest(URL: imgURL!)
let mainQueue = NSOperationQueue.mainQueue()
NSURLConnection.sendAsynchronousRequest(request, queue: mainQueue, completionHandler: { (response, data, error) -> Void in
if error == nil {
// Convert the downloaded data in to a UIImage object
let image = UIImage(data: data)
// Store the image in to our cache
self.imageCache[work.image] = image
// Update the cell
dispatch_async(dispatch_get_main_queue(), {
self.workImage.image = image
})
}else {
println("Error: \(error.localizedDescription)")
}
})
}
}
}
You can also create a UIImage extension and just call the async function in the configureCellWith function if you want a cleaner solution for Swift 3.
import Foundation
import UIKit
let imageCache = NSCache <AnyObject,AnyObject>()
extension UIImageView {
func loadUsingCache(_ theUrl: String) {
self.image = nil
//check cache for image
if let cachedImage = imageCache.object(forKey: theUrl as AnyObject) as? UIImage{
self.image = cachedImage
return
}
//otherwise download it
let url = URL(string: theUrl)
URLSession.shared.dataTask(with: url!, completionHandler: {(data, response, error) in
//print error
if (error != nil){
print(error!)
return
}
DispatchQueue.main.async(execute: {
if let downloadedImage = UIImage(data: data!){
imageCache.setObject(downloadedImage, forKey: theUrl as AnyObject)
self.image = downloadedImage
}
})
}).resume()
}
}

Resources