I am clueless at the moment, I dont know why I am getting nil each time when I am trying to retrieve. I am passing the correct image data and identifier. Here's a little code
let photoCache = AutoPurgingImageCache(
memoryCapacity: 100 * 1024 * 1024,
preferredMemoryUsageAfterPurge: 60 * 1024 * 1024
)
func cacheImage(image: Image, urlString: String) {
print("Image size: \(image.size) and string as identifier: \(urlString)")
self.photoCache.addImage(image, withIdentifier: urlString)
}
func cachedImage(urlString: String) -> Image? {
print("What is url? : \(urlString)")
print("What we are actually returning? : \(self.photoCache.imageWithIdentifier(urlString))")
return self.photoCache.imageWithIdentifier(urlString)
}
Print for the cacheImage function: Image size: (2826.0, 3722.0) and string as identifier: http:www.somelink.com/DT199.jpg
Print for the cachedImage when I am trying retrieving it: what is the URL? : http:www.somelink.com/DT199.jpg What we are actually returning is : nil
I don't know why it is happening I tried it several times before and it worked perfectly. I also read about these functions, they don't return any boolean response through which we can get to know whether the image is in cache or not.
Later on, i tried using the download manager as well nothing happen still clueless. If any one can help me? here's the code
let imageDownloader = ImageDownloader(
configuration: ImageDownloader.defaultURLSessionConfiguration(),
downloadPrioritization: .FIFO,
maximumActiveDownloads: 15,
imageCache: AutoPurgingImageCache()
)
let downloader = imageDownloader
Alamofire.request(.GET, request, headers: headers)
.responseJSON { response in
if let JSON = response.result.value {
self.imagesArray = NSMutableArray()
self.imagesArray = JSON["results"] as! NSMutableArray
for results in self.imagesArray{
let URLRequest = NSURLRequest(URL: NSURL(string:results["url"] as! String)!)
downloader.downloadImage(URLRequest: URLRequest, filter: nil, completion: { response in
if response.response?.statusCode == 200{
print("In the house fetching url: \(results["url"] as! String)")
let image = response.result.value
print("Size of image: \(image?.size)")
let returnValue = downloader.imageCache?.addImage(image!, withIdentifier: results["url"] as! String)
print("return status: \(returnValue)")
if results .isEqual(self.imagesArray.lastObject){
self.activityIndicatorView.stopAnimating()
self.activityIndicatorView.hidden = true
let fic = self.imagesArray.firstObject
print("getting image for this url : \(fic!["url"] as! String)")
var imageC = UIImage()
imageC = (downloader.imageCache?.imageWithIdentifier(fic!["url"] as! String))!
self.ssImage.image = imageC
print("Image : \(imageC.size))")
}
}
})
}
}
}
Maybe you have to initialize your imageDownloader with the photoCache you used before to cache the images manually. The AutoPurgingImageCache isn't a shared instance. Only the UIImageView+AlamofireImage.swift extension has a af_sharedImageDownloader which keeps one AutoPurgingImageCache.
In dependence on your code example it should look like this:
let imageDownloader = ImageDownloader(
configuration: ImageDownloader.defaultURLSessionConfiguration(),
downloadPrioritization: .FIFO,
maximumActiveDownloads: 15,
imageCache: self.photoCache
)
Related
I am trying to show an image into my table cell view from an API. But it has given a partial link there, as a result, I am getting NSURL connection error code -1002.
Here is my API link: https://api.opendota.com/api/heroStats
I am trying to parse "icon" among them:
"img": "/apps/dota2/images/heroes/antimage_full.png?",
"icon": "/apps/dota2/images/heroes/antimage_icon.png",
My code:
// Generating imageview
if let imageURL = URL(string: heroes[indexPath.row].icon){
print (imageURL)
DispatchQueue.global().async {
let data = try? Data (contentsOf: imageURL)
if let data = data {
let image = UIImage(data: data)
DispatchQueue.main.async {
cell.charIcon.image = image
} //end of 2nd dispatch
}//end of if
}//end of 1st dispatch
}// end of imageURL
How can I solve this problem? Any easy way for swift 4?
You can get the url components of your api link and use your icon "partial link" to set the path property of the URL components. After that you just need to get the resulting url of the url components:
let apiLink = "https://api.opendota.com/api/heroStats"
let apiURL = URL(string: apiLink)!
if var urlComponents = URLComponents(url: apiURL, resolvingAgainstBaseURL: false) {
let iconString = "/apps/dota2/images/heroes/antimage_icon.png"
urlComponents.path = iconString
if let iconURL = urlComponents.url {
print(iconURL.absoluteString)
}
}
This will print
https://api.opendota.com/apps/dota2/images/heroes/antimage_icon.png
You can create a custom method to return a new URL based on the new path string as follow:
extension URL {
var urlComponents: URLComponents? {
return URLComponents(url: self, resolvingAgainstBaseURL: false)
}
func bySettingNew(path: String) -> URL? {
guard var urlComponents = urlComponents else { return nil }
urlComponents.path = path
return urlComponents.url
}
}
let apiLink = "https://api.opendota.com/api/heroStats"
let apiURL = URL(string: apiLink)!
let iconString = "/apps/dota2/images/heroes/antimage_icon.png"
if let iconURL = apiURL.bySettingNew(path: iconString) {
print(iconURL.absoluteString)
}
You can also add this helper to your project to make it easier for you to download an image asynchronously into your image view:
extension UIImageView {
func downloaded(from url: URL, contentMode mode: UIView.ContentMode = .scaleAspectFit) {
URLSession.shared.dataTask(with: url) { data, response, error in
guard
let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200,
let mimeType = response?.mimeType, mimeType.hasPrefix("image"),
let data = data, error == nil,
let image = UIImage(data: data)
else { return }
DispatchQueue.main.async() { [weak self] in
self?.contentMode = mode
self?.image = image
}
}.resume()
}
}
if let imageURL = apiURL.bySettingNew(path: heroes[indexPath.row].icon) {
cell.charIcon.downloaded(from: imageURL)
}
I am calling API in iOS (swift). Everything works perfectly, but it's taking too much time while getting response approximately 40 or 60 seconds. I don't know why this is happening. Let me show you my API calling method:
Code
func userDetailAPI(){
let preferences = UserDefaults.standard
let uid = "u_id"
let acctkn = "acc_tkn"
if preferences.object(forKey: uid) == nil {
// Doesn't exist
} else {
let u_id = preferences.object(forKey: uid) as! String
print(u_id)
let acc_tkn = preferences.object(forKey: acctkn) as! String
print(acc_tkn)
let userprofile: [String : Any] = ["user_id":u_id,"access_token":acc_tkn]
print(userprofile)
Alamofire.request(userDetails, method: .post, parameters: userprofile).responseJSON { response in
print("RESPONSE : \(response)")
let result = response.result.value
if result != nil{
let data = result as! [String : AnyObject]
let userdata = data["data"] as! NSDictionary
let email = userdata["email"]
let name = userdata["name"]
let photo = userdata["photo"]
//let u_type = userdata["user_type"]!
self.lblUserName.text = name as? String
self.lblEmailID.text = email as? String
let proimgurl = NSURL(string: photo as! String)
self.imgProPic.image = UIImage(data: NSData(contentsOf: proimgurl! as URL)! as Data)
// }
}
}
}
}
Please check and help me - is this the right method for API calling or is there any other, better way?
Because of this line
self.imgProPic.image = UIImage(data: NSData(contentsOf: proimgurl! as URL)! as Data)
so you have almofire request plus blocking main thread until image is downloaded , so consider using the asynchronous , automatic cashing SDWebImage
self.imgProPic.sd_setImage(with: proimgurl!, placeholderImage: UIImage(named: "placeholder.png"))
Also in swift avoid using NS stuff like here
let userdata = data["data"] as! NSDictionary // use [String:Any]
and
let proimgurl = NSURL(string: photo as! String) // use URL
You should download the ImageView's image from Url in another thread. If you do it in the main thread, it'll slow down your app and ultimately run out of memory.
The below-given line is which causes the problem is below
self.imgProPic.image = UIImage(data: NSData(contentsOf: proimgurl! as URL)! as Data)
I suggest you use the SDWebImage library.
You can do like something below
let imageUrl = URL(string: photo as! String)
self.imgProPic.image.sd_setImage(with: imageUrl, placeholderImage: UIImage(named: "profile"), options: .refreshCached, completed: nil)
If this doesn't solve your problem, try calling the same web service using API clients such as Postman. If it's taking the same amount of time, then you can't do much about it. Ask the web service developer to optimize the performance.
Hey by the way there is also alamofire image pod is available.
https://github.com/Alamofire/AlamofireImage
eg:
do import AlamofireImage into your file and call image url like below:
Alamofire.request(image_url, method: .get).responseImage(completionHandler: { (response) in
self.your_UIImage_variable.image = response.result.value
})
I try to download picture with Alamofire and this image must be append to custom class. But I didnt do that. Image is downloaded with async and i didnt fix this issue. Where is my mistake ?
Image always return nil
// MARK: CUSTOM FUNCTION
import UIKit
import Alamofire
import AlamofireImage
class DownloadPhotoWAlamofire {
func download(imageUid:String , completion : #escaping (UIImage) -> () ) {
let url = "\(Config.fileService)file?uid=\(String(describing: imageUid))"
print("URL \(url)")
Alamofire.request(url, method: .get ).responseImage { response in
print("Image Response \(response)")
let image = response.result.value
completion(image!)
}
}
}
// MARK : IN VIEWCONTROL
guard let objectElement = o as? [String:Any] else {return}
let managerName = objectElement["managerName"] as? String
let managerAvatarUid = objectElement["managerProfilePictureFileUid"] as? String
let assistantsOfDiariesUid = objectElement["assistantsOfDiariesUid"] as? String
var image:UIImage? = nil
if managerAvatarUid != nil {
DownloadPhotoWAlamofire().download(imageUid: managerAvatarUid!, completion: { (imageD) in
image = imageD
})
}
let e = AssistantInviteElement(managerName: managerName!, managerAvatarUid: managerAvatarUid, assistantsOfDiariesUid: assistantsOfDiariesUid!,avatarImage:image)
self.managerList.append(e)
Change it to :
if managerAvatarUid != nil {
DownloadPhotoWAlamofire().download(imageUid: managerAvatarUid!, completion: { (imageD) in
let image = imageD
let e = AssistantInviteElement(managerName: managerName!, managerAvatarUid: managerAvatarUid, assistantsOfDiariesUid: assistantsOfDiariesUid!,avatarImage:image)
self.managerList.append(e)
})
}
As downloading is asynchronous call and you this line is executing before image is downloaded :
let e = AssistantInviteElement(managerName: managerName!, managerAvatarUid: managerAvatarUid, assistantsOfDiariesUid: assistantsOfDiariesUid!,avatarImage:image)
at that time image is nil. So call it only when you have image downloaded and it should work.
Also don't unwrap optional with "!", do a if let/ guard let. So change Almofire code to :
Alamofire.request(url, method: .get ).responseImage { response in
print("Image Response \(response)")
if let image = response.result.value {
print("image downloaded: \(image)")
completion(image)
} else {
print("Image is nil")
}
}
You set the image directly after starting the asynchronous download, when it of course is still nil. The point of using a callback as you do in your code is that you only can use a requested resource once the callback gets called; so you should move the code that's using the image into the callback block.
I'm converting the image I'm picking from the gallery into its URL like so...
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let image = info[UIImagePickerControllerOriginalImage] as? UIImage {
UIImageWriteToSavedPhotosAlbum(image, self, #selector(image(_:didFinishSavingWithError:contextInfo:)), nil)
let imageURL = info[UIImagePickerControllerReferenceURL] as? NSURL
let imageName = imageURL?.lastPathComponent
let documentDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
let photoURL = NSURL(fileURLWithPath: documentDirectory)
self.localPath = photoURL.appendingPathComponent(imageName!)
do {
try UIImageJPEGRepresentation(image, 1.0)?.write(to: localPath!)
print("File Saved")
imageArray.append(image)
} catch { //Error }
self.collectionView.reloadData()
} else { //Error here }
self.dismiss(animated: true, completion: nil)
}
Now if I have 2 images, I want to pass them one by one to my API call as a parameter. This I'm doing like so...
for imgURL in imageArray {
let url = "http://myapp.com/vw/images_upload"
let headers = [ "Content-Type":"application/x-www-form-urlencoded"]
let Parameters =
[
"image": imgURL,
"seller_id": id
] as [String : Any]
Alamofire.request(url, method: .post, parameters: Parameters, encoding: URLEncoding.httpBody, headers: headers)
.responseJSON { (response) in
if let httpResponse = response.response {
print("error \(httpResponse.statusCode)")
if httpResponse.statusCode == 200 {
if let result = response.result.value as? [String:Any] {
if result["success"] as! Int == 0 {
print("Something went wrong!")
} else if result["success"] as! Int == 1 {
print("UPLOADED IMAGE SUCCESSFULLY!!")
}}}}}}
But in the parameter, in imgURL, I'm not getting the url of the image. Above I had got the url in the localPath. But I cannot loop through localPath as it gives an error. Also, in the imageArray, I'm passing the image which is not in the url format...it is in this format: <UIImage: 0x60800009f9f0> size {4288, 2848} orientation 0 scale 1.000000...How the url format can be passed into the imageArray, that I'm not able to understand.Could this be the issue..?
Also how can I get the url of the image so that I can pass it into the API call...? Please help...
hi what i understand here from your question is that you are setting an image to an imageview and you want to pass that image's path to an api.
so i have written a function to do the same with a resizing ability which you can avoid or modify according to your needs.
So pass your image picked by the image picker and a default name string (eg:"image1") and you will get the image path/url in return
func storeImage(image:UIImage, fileName:String) -> String {
var resizedImage = image
func storeImage(image:UIImage, fileName:String) -> String {
var resizedImage = image
if (image.size.width > 200) {
let width = 200.0 as CGFloat
let height = image.size.height * width / image.size.width
resizedImage = image.scaleImage(toSize: CGSize(width: width, height: height))!
}
else if (image.size.height > 200) {
let height = 200.0 as CGFloat
let width = image.size.width * height / image.size.height
resizedImage = image.scaleImage(toSize: CGSize(width: width, height: height))!
}
let imageData = NSData(data:UIImagePNGRepresentation(resizedImage)!)
let paths = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)
let docs: String = paths[0]
let fullPath = docs.stringByAppendingPathComponent(path: fileName)
_ = imageData.write(toFile: fullPath, atomically: true)
return fullPath
}
I try to parse json with SwiftyJSON. One of the fields have url to image and i try to save it as NSData but I face crash and console errors. Crash appears when compiler comes to object creation
code it the following
var JSONStorage : [Article?]?
var objects = [Article?]()
override func viewDidLoad() {
super.viewDidLoad()
let number = arc4random_uniform(1000)
let urlString = "http://wirehead.ru/article.json?\(number)"
if let url = NSURL(string: urlString) {
if let data = try? NSData(contentsOfURL: url, options: []) {
let json = JSON(data: data)
for element in json["article"].arrayValue {
let id = Int(element["id"].stringValue)
let title = element["title"].stringValue
let subtitle = element["subtitle"].stringValue
let body = element["body"].stringValue
let img = element["images"]["main"].rawValue
let obj:Article = Article(id: id!, title: title, subtitle: subtitle, body: body, mainImage: img as! NSData)
objects.append(obj)
print("We are inside if let")
}
}
}
print(objects)
}
Link to JSON is http://wirehead.ru/article.json and here is with highlight http://pastebin.com/AAEFjsQN
Error that I get is
Any advice ?
["images"]["main"] contains an URL represented by a String
To get the image data, use something like this
let imgURLString = element["images"]["main"].stringValue
if let url = NSURL(string:imgURLString) {
let img = NSData(contentsOfURL:url)
}