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)
}
Related
I'm making an application with one Core Data entity, Park, which is decoded from the data returned from an API request and stores the image url and local file/download location (if it has been downloaded) for each image as attributes. I created a computed property that returns a dictionary of ImageInfoObjects (which is a struct that basically just bundles the information together) based on the stored attributes. The first time I run the app, everything works fine but when I close the app and run it again it gives me the error "the file couldn't be opened because there is no such file". So I know there must be an issue with the way I'm storing the file paths in Core Data, or the way I'm reading them back to display the images. Any help would be appreciated. Code snippets below.
The method which catches the error:
func displayPhoto(_ object: ImageInfoObject, imageView: UIImageView) {
guard let location = object.downloadLocation else { return }
do {
let imageData = try Data(contentsOf: location)
let image = UIImage(data: imageData)
DispatchQueue.main.async {
imageView.image = image
}
} catch (let error) {
print(error)
}
}
The URLSessionDownloadDelegate method which is called once each image is finished downloading:
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
let fileManager = FileManager.default
guard let documentsPath = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first,
let sourceURL = downloadTask.originalRequest?.url,
let download = self.photoDownloads[sourceURL] else {
fatalError()
}
let lastPathComponent = sourceURL.lastPathComponent
let destinationURL = documentsPath.appendingPathComponent(lastPathComponent)
do {
if fileManager.fileExists(atPath: destinationURL.path) {
try fileManager.removeItem(at: destinationURL)
}
try fileManager.copyItem(at: location, to: destinationURL)
let index = download.imageInfoObject.index
let newImageInfoObject = ImageInfoObject(url: sourceURL, index: index, downloadLocation: destinationURL)
self.park?.photoInfoObjects[index] = newImageInfoObject
switch newImageInfoObject.index {
case 1:
displayPhoto(newImageInfoObject, imageView: self.photo1View)
case 2:
displayPhoto(newImageInfoObject, imageView: self.photo2View)
default:
break
}
try context?.save()
} catch {
print(error)
}
}
The computed property that stores and retrieves the paths from the CoreData entity. The NSManaged attributes are self.photo1_url, self.photo1_location, self.photo2_url, and self.photo2_location.
public var photoInfoObjects: Dictionary<Int, ImageInfoObject> {
get {
var dictionary: Dictionary<Int, ImageInfoObject> = [:]
var object: ImageInfoObject
if let photo1_url = self.photo1_url,
let url = URL(string: photo1_url) {
if let photo1_location = self.photo1_location {
let location = URL(fileURLWithPath: photo1_location)
object = ImageInfoObject(url: url, index: 1, downloadLocation: location)
object.isDownloaded = true
} else {
object = ImageInfoObject(url: url, index: 1)
}
dictionary[1] = object
}
var object2: ImageInfoObject
if let photo2_url = self.photo2_url,
let url = URL(string: photo2_url) {
if let photo2_location = self.photo2_location {
let location = URL(fileURLWithPath: photo2_location)
object2 = ImageInfoObject(url: url, index: 2, downloadLocation: location)
object2.isDownloaded = true
} else {
object2 = ImageInfoObject(url: url, index: 2)
}
dictionary[2] = object2
}
return dictionary
}
set {
self.photo1_url = newValue[1]?.url.absoluteString
self.photo1_location = newValue[1]?.downloadLocation?.path
self.photo2_url = newValue[2]?.url.absoluteString
self.photo2_location = newValue[2]?.downloadLocation?.path
}
}
I am getting image from microsoft account by using this api :
https://graph.microsoft.com/beta/me/photo
And this api gets this data which is shown in output. But i don't know how to show this link into image in Swift.
Output :
"#odata.context" = "https://graph.microsoft.com/beta/$metadata#users('rahulchopra.93%40outlook.com')/photo/$entity";
"#odata.mediaContentType" = "image/jpeg";
height = 2;
id = 2X2;
width = 2;
Create extension of UIImageView
extension UIImageView {
func downloaded(from url: URL, contentMode mode: UIViewContentMode = .scaleAspectFit) {
contentMode = mode
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() {
self.image = image
}
}.resume()
}
func downloaded(from link: String, contentMode mode: UIViewContentMode = .scaleAspectFit) {
guard let url = URL(string: link) else { return }
downloaded(from: url, contentMode: mode)
}
}
and Use like this
imageView.downloaded(from: "http://www.apple.com/euro/ios/ios8/a/generic/images/og.png")
I have been getting images from my API and in the past I have loaded them into a UIImage with the extension you will see below. However, now I am trying to get the images from the API and load them into UIButton image views. I don't know what to do to the extension and the other code to make it work. I appreciate the help!
Extension
Extension UIImageView {
func getURL2(url: URL, contentMode mode: UIViewContentMode = .scaleAspectFit) {
contentMode = mode
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),
httpURLResponse.url == url
else { return }
DispatchQueue.main.async() {
self.image = image
}
}.resume()
}
func downloadedFrom2(link: String, contentMode mode: UIViewContentMode = .scaleAspectFit) {
guard let url = URL(string: link) else { return }
getURL2(url: url, contentMode: mode)
}
}
Other code
func loadProfilePhoto(image: UIButton, link: String) {
image.downloadedFrom2(link: link)
image.imageView!.clipsToBounds = true
image.imageView!.layer.cornerRadius = (image.imageView!.frame.height) / 2
image.imageView!.contentMode = .scaleAspectFill
}
func loadRandom8() {
if self.users.count == 8 {
let completelink1 = users[0].picture_url
//ex. https://api.adorable.io/avatars/200/AngelicAlling.png
let completelink2 = users[1].picture_url
let completelink3 = users[2].picture_url
let completelink4 = users[3].picture_url
let completelink5 = users[4].picture_url
let completelink6 = users[5].picture_url
let completelink7 = users[6].picture_url
let completelink8 = users[7].picture_url
loadProfilePhoto(image: p2Image, link: completelink1)
loadProfilePhoto(image: p2Image, link: completelink2)
loadProfilePhoto(image: p3Image, link: completelink3)
loadProfilePhoto(image: p4Image, link: completelink4)
loadProfilePhoto(image: p5Image, link: completelink5)
loadProfilePhoto(image: p6Image, link: completelink6)
loadProfilePhoto(image: p7Image, link: completelink7)
loadProfilePhoto(image: p8Image, link: completelink8)
Your current extension is for UIImageView but you want to load the image in a UIButton, so change the extension to UIButton and make sure the button's type is set to Custom and not System. You can do this from the storyboard.
extension UIButton {
func getURL2(url: URL, contentMode mode: UIViewContentMode = .scaleAspectFit) {
contentMode = mode
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),
httpURLResponse.url == url
else { return }
DispatchQueue.main.async() {
self.setImage(image, for: .normal)
self.imageView?.contentMode = mode
}
}.resume()
}
public func downloadedFrom2(link: String, contentMode mode: UIViewContentMode = .scaleAspectFit) {
guard let url = URL(string: link) else { return }
getURL2(url: url, contentMode: mode)
}
}
If it didn't work for you read this
I am new to iOS i want download image to display it is working code but here lot of code duplication
let url = URL(string: iteminfo.imageUrl!)
let urlRequest = URLRequest(url: url!)
let task = URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
if error != nil {
print(error)
}
if let data = data {
print(data)
self.imageViewItemPic.image = UIImage(data: data)
}
}
task.resume()
let url2 = URL(string: iteminfo.cookerProfilePicUrl!)
let urlRequest2 = URLRequest(url: url2!)
let task2 = URLSession.shared.dataTask(with: urlRequest2) { (data, response, error) in
if error != nil {
print(error)
}
if let data = data {
print(data)
self.imageViewCookerProfilePic.image = UIImage(data: data)
}
}
task2.resume()
So I want to reuse my code but i unfortunately i can not reach my goal. there have no error and url is correct . every time goes else statement . i am missing something but what is that ?
if let image = downlaodImage(urlImage: iteminfo.imageUrl){
print("first \(image)")
imageViewItemPic.image = image
}else{
print("first wrong......")
}
if let image = downlaodImage(urlImage: iteminfo.cookerProfilePicUrl){
print("second \(image)")
imageViewCookerProfilePic.image = image
}
else{
print("second wrong......")
}
Here is my method :
func downlaodImage(urlImage : String?) -> UIImage?{
var image : UIImage?
let url = URL(string: urlImage!)
let urlRequest = URLRequest(url: url!)
let task = URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
if let data = data {
// print(data)
image = UIImage(data: data)
}
}
task.resume()
return image
}
note: I am not sure is it best way or not . if it is not best practice feel free to guide me .
There's no need of so much hassle. You have the URL of the image so you can simply download the image from the URL. For example:
func downloadImage(imageURL: String) {
DispatchQueue.global().async {
let data = NSData.init(contentsOf: NSURL.init(string: imageURL) as! URL)
DispatchQueue.main.async {
let image = UIImage.init(data: data as! Data)
imageView.image = image
}
}
}
Edit: To reuse this code I would suggest to use extension of UIImageView. Here's an example:
extension UIImageView {
func setImageFromURL(url: String) {
DispatchQueue.global().async {
let data = NSData.init(contentsOf: NSURL.init(string: url) as! URL)
DispatchQueue.main.async {
let image = UIImage.init(data: data as! Data)
self.image = image
}
}
}
}
Use this method whenever you want to set the image of an imageView from a url like this:
self.imageViewCookerProfilePic.setImageFromURL(url: "https://encrypted-tbn1.gstatic.com/images?q=tbn:ANd9GcQNpKmjx1w3DRDZ9IXN81-uhSUA6qL6obkOthoUkb9RZkXf5pJ8")
Dude. You should learn some staff about async and sync code.
Here is the thing. Code in you downloadImage works synchronically, so it pass you URLTask and go straight to return, there you return you image variable, that is nil.
One of the solutions in to use callback block like this:
func downloadImage(urlImage : String?, complete: ((UIImage?)->Void)? = nil){
let url = URL(string: urlImage!)
let urlRequest = URLRequest(url: url!)
let task = URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
if let data = data {
complete?(UIImage(data: data))
}
}
task.resume()
}
And then use it like this:
{ ...
downloadImage(urlImage: "", complete: { image in
if let image = image{
self.imageViewItemPic.image = image
}else{
print("no image")
}
})
...
}
You should read some tutorials about async code and web in swift. You could start with this site
downlaodImage() downloads an image asynchronously so
if let image = downlaodImage(...) { ... }
is always going to fail because program execution has continued before your response data has come back.
It would be easier just to set your images in the callback function closure of downlaodImage() as below by adding a UIImageView parameter to downlaodImage(). This way you can reduce the repetition of if else blocks by moving them to the downlaodImage function.
func downlaodImage(urlImage : String?, imageView: UIImageView) -> UIImage?{
var image : UIImage?
let url = URL(string: urlImage!)
let urlRequest = URLRequest(url: url!)
let task = URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
if let data = data {
// print(data)
if let image = UIImage(data: data) {
imageView.image = image
} else {
print("failed to load image")
}
}
}
task.resume()
return image
}
Simplified code without if/else blocks
downlaodImage(urlImage: "https://encrypted-tbn1.gstatic.com/images?q=tbn:ANd9GcQNpKmjx1w3DRDZ9IXN81-uhSUA6qL6obkOthoUkb9RZkXf5pJ8", imageView: imageViewItemPic)
downlaodImage(urlImage: "https://www.dominos.co.nz/ManagedAssets/OLO/eStore/all/Product/NZ/P015/P015_ProductImage_Small_en_Default_20140203_105245.png", imageView: imageViewCookerProfilePic)
I want to Load image using JSON parsing in collectionView. I'm getting array of image URLs, but images are not shown on UIImageView. I have used this code after getting JSON array and displaying it in UICollectionView's UIImageView.
if let url = NSURL(string: self.Products[indexPath.row].image) {
if let data = NSData(contentsOfURL: url){
cell.product_image.contentMode = UIViewContentMode.ScaleAspectFit
cell.product_image.image = UIImage(data: data)
}
}
But I am not able to load image, but text is displayed. I have used this code in cellForItemAtIndexPath method.. Can anyone suggest me what am I doing wrong?
func jsonParsingFromURL(){
// 1
let reposURL = NSURL(string: "http://api.room2shop.com/api/product/GetProducts?categoryId=24&filter=2&pageNumber=1")
// 2
if let JSONData = NSData(contentsOfURL: reposURL!) {
// 3
do
{
if let json = try NSJSONSerialization.JSONObjectWithData(JSONData, options: .AllowFragments) as? NSDictionary {
// 4
if let reposArray = json["ProductList"] as? [NSDictionary] {
// 5
for item in reposArray {
Products.append(ProductList(json: item))
}
}
}
}
catch {
print("Error with Json: \(error)")
}
}
}
This is my JSON parsing code
to reflect changes, you need to use
self.collectionView?.reloadData()
EDIT
1- please replace this block with this, and tell me if you get the urls normally, for me I get the urls normally
for item in reposArray
{
//ProductList(json: item)
//Products.append(ProductList(json: item))
//print(item)
print("__________")
print(item["Image"]!)
}
2- i was getting
Transport Security has Blocked a cleartext HTTP
solution was here.
Transport security has blocked a cleartext HTTP
use this
// taking the URL , then request image data, then assigne UIImage(data: responseData)
let imgURL: NSURL = NSURL(string: "www.example.com/image.jpg")!
let request: NSURLRequest = NSURLRequest(URL: imgURL)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request) {
(data,response,error) -> Void in
if ( error == nil && data != nil ) {
func display_image() {
// imageView.post.image = your UIImage
self.imageViewPost.image = UIImage(data: data!)
}
dispatch_async(dispatch_get_main_queue(), display_image)
}
}
task.resume()
// end of loading img
The image is a URL which is not saved locally inside the image.xassets, so you need to parse the URL using the extension given below.
// add the extension
extension UIImageView {
func loadImage(urlString: String) {
let url = URL(string: urlString)!
URLSession.shared.dataTask(with: url, completionHandler: { (data, respones, error) in
if error != nil {
print(error ?? "")
return
}
DispatchQueue.main.async {
self.image = UIImage(data: data!)
}
}).resume()
}
}
// call this statement in your vc (where you polpulate)
yourImageView.loadImage(urlString:yourUrl)