let url = URL(string: (pinsFIREBASE[marker.snippet!]?.imageURL)!)
let task = URLSession.shared.dataTask(with: url!) { data, response, error in
guard let data = data, error == nil else { return }
DispatchQueue.main.async() {
self.postImage.image = UIImage(data: data)
}
}
task.resume()
I have the following code that takes a url from firebase, in the form http://i.imgur.com/nkomPpP.jpg, and is supposed to turn that url into a UIImage that can be placed on a view. However, while extracting the text from the firebase object works, parsing the image URL doesn't seem to be working as I get an empty view. What am I doing wrong?
I know why, your code works. The problem is your image link. Your imageURL's HTTP type. iOS don't like HTTP type request because it's not safe.
Plan A: Try a HTTPS type image link, it works.
Plan B: Add "App Transport Security Settings" in project info ,and set "Allow
Arbitrary Loads" yes in "App Transport Security Settings" dictionary.
I suggested use Plan A, that's Apple want iOSDev to do.
You need to remove the () from after DispatchQueue.main.async(). Try this:
let url = URL(string: (pinsFIREBASE[marker.snippet!]?.imageURL)!)
let task = URLSession.shared.dataTask(with: url!) { data, response, error in
guard let data = data, error == nil else { return }
DispatchQueue.main.async {
self.postImage.image = UIImage(data: data)
}
}
task.resume()
Related
I am trying to parse a image from a url the url is
https://a.ppy.sh/9795284
it doesn't have a specific image extension as far as i can tell, it's just a link my current code (which works for getting the username and when I print the user_id i do get 9795284 so I know the code works (I also already get other information I wanted to get since as the username however I can not for the life of me get the users image to show here is my code for dealing with parsing
fetchCoursesJSON { (res) in
switch res {
case .success(let playerinfo):
playerinfo.forEach({ (player) in
print(player.username)
DispatchQueue.main.async {
self.myLabel.text = player.username
self.avatar.image = UIImage(named: "https://a.ppy.sh/\(player.user_id)")
}
})
case .failure(let err):
print("Failed to fetch courses:", err)
}
}
I expected the output to show the users profile pick in the avatar image but it does not it's just blank.
You need to download the image first, you can do this by using a lib like SDWebImage, AlamofireImage, Kingfisher or using the native URLSession
The UIImage(named:) is used when the resource is in your assets.
The UIImage(named:) will attempt to retrieve the image from your app.. not from the internet. Here is some code that will help you retrieve the image from your url:
guard let url = URL(string: yourUrlString ?? "") else { return }
URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) -> Void in
guard let data = data, error == nil else { return }
let image = UIImage(data: data)
DispatchQueue.main.async {
self.avatar.image = image
}
}).resume()
I am a fairly decent Objective C developer, and I am now learning Swift (of which I am finding quite difficult, not only because of new concepts, such as optionals, but also because Swift is continually evolving, and much of the available tutorials are severely outdated).
Currently I am trying parse a JSON from a url into an NSDictionary and then use one of its value to display an image (which is also a url). Something like this:
URL -> NSDictionary -> init UIImage from url -> display UIImage in UIImageView
This is quite easy in Objective C (and there may even be a shorter answer):
NSURL *url = [NSURL URLWithString:#"https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY"];
NSData *apodData = [NSData dataWithContentsOfURL:url];
NSDictionary *apodDict = [NSJSONSerialization JSONObjectWithData:apodData options:0 error:nil];
The above code snippet gives me back a standard NSDictionary, in which I can refer to the "url" key to get the address of the image I want to display:
"url" : "https://apod.nasa.gov/apod/image/1811/hillpan_apollo15_4000.jpg"
This I then convert into a UIImage and give it to a UIImageView:
NSURL *imageURL = [NSURL URLWithString: [apodDict objectForKey:#"url"]];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
UIImage *apodImage = [UIImage imageWithData:imageData];
UIImageView *apodView = [[UIImageView alloc] initWithImage: apodImage];
Now, I am basically trying to replicate the above Objective C code in Swift but continuously run into walls. I have tried several tutorials (one of which actually did the exact same thing: display a NASA image), as well as find a few stack overflow answers but none could help because they are either outdated or they do things differently than what I need.
So, I would like to ask the community to provide the Swift 4 code for the these problems:
1. Convert data from url into a Dictionary
2. Use key:value pair from dict to get url to display an image
If it is not too much already, I would also like to ask for detailed descriptions alongside the code because I would like the answer to be the one comprehensive "tutorial" for this task that I believe is currently not available anywhere.
Thank you!
First of all I'm pretty sure that in half a year you will find Objective-C very complicated and difficult. 😉
Second of all even your ObjC code is discouraged. Don't load data from a remote URL with synchronous Data(contentsOf method. Regardless of the language use an asynchronous way like (NS)URLSession.
And don't use Foundation collection types NSArray and NSDictionary in Swift. Basically don't use NS... classes at all if there is a native Swift counterpart.
In Swift 4 you can easily decode the JSON with the Decodable protocol directly into a (Swift) struct,
the URL string can be even decoded as URL.
Create a struct
struct Item: Decodable {
// let copyright, date, explanation: String
// let hdurl: String
// let mediaType, serviceVersion, title: String
let url: URL
}
Uncomment the lines if you need more than the URL.
And load the data with two data tasks.
let url = URL(string: "https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY")!
let task = URLSession.shared.dataTask(with: url) { (data, _, error) in
if let error = error { print(error); return }
do {
let decoder = JSONDecoder()
// this line is only needed if all JSON keys are decoded
decoder.keyDecodingStrategy = .convertFromSnakeCase
let result = try decoder.decode(Item.self, from: data!)
let imageTask = URLSession.shared.dataTask(with: result.url) { (imageData, _, imageError) in
if let imageError = imageError { print(imageError); return }
DispatchQueue.main.async {
let apodImage = UIImage(data: imageData!)
let apodView = UIImageView(image: apodImage)
// do something with the image view
}
}
imageTask.resume()
} catch { print(error) }
}
task.resume()
You can use this extension
extension UIImage {
public static func loadFrom(url: URL, completion: #escaping (_ image: UIImage?) -> ()) {
DispatchQueue.global().async {
if let data = try? Data(contentsOf: url) {
DispatchQueue.main.async {
completion(UIImage(data: data))
}
} else {
DispatchQueue.main.async {
completion(nil)
}
}
}
}
}
Using
guard let url = URL(string: "http://myImage.com/image.png") else { return }
UIImage.loadFrom(url: url) { image in
self.photo.image = image
}
Since image loading is a trivial and at the same time task which could be implemented in many different ways, I would recommend you to not "reinvent the wheel" and have a look to an image loading library such as Nuke, since it already covers most of the cases you might need during your development process.
It allows you to load and show image asynchronously into your view, using simple api:
Nuke.loadImage(with: url, into: imageView)
And also if you need - to specify how image should be loaded and presented:
let options = ImageLoadingOptions(
placeholder: UIImage(named: "placeholder"),
failureImage: UIImage(named: "failure_image"),
contentModes: .init(
success: .scaleAspectFill,
failure: .center,
placeholder: .center
)
)
Nuke.loadImage(with: url, options: options, into: imageView)
Create an UIIimageView Extension and the following code
extension UIImageView {
public func imageFromServerURL(urlString: String) {
self.image = nil
let urlStringNew = urlString.replacingOccurrences(of: " ", with: "%20")
URLSession.shared.dataTask(with: NSURL(string: urlStringNew)! as URL, completionHandler: { (data, response, error) -> Void in
if error != nil {
print(error as Any)
return
}
DispatchQueue.main.async(execute: { () -> Void in
let image = UIImage(data: data!)
self.image = image
})
}).resume()
}}
and
self.UploadedImageView.imageFromServerURL(urlString: imageURLStirng!)
I have just extended on vadian's answer, separated some concerns to clearly understand the basics. His answer should suffice.
First, you have to build your structure. This will represent the JSON structure you retrieved from the webservice.
struct Item: Codable {
let url, hdurl : URL,
let copyright, explanation, media_type, service_version, title : String
}
Then make you request methods. I usually create a separate file for it. Now, vadian mentioned about completion handlers. These are represented by escaping closures. Here, closure ()-> is passed on both functions and called having the decoded data as argument.
struct RequestCtrl {
func fetchItem(completion: #escaping (Item?)->Void) {
let url = URL(string: "https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY")!
//URLSessionDataTask handles the req and returns the data which you will decode based on the Item structure we defined above.
let task = URLSession.shared.dataTask(with: url) { (data, _, _) in
let jsonDecoder = JSONDecoder()
if let data = data,
let item = try? jsonDecoder.decode(Item.self, from: data){
//jsonDecoder requires a type of our structure represented by .self and the data from the request.
completion(item)
} else {
completion(nil)
}
}
task.resume()
}
func fetchItemPhoto(usingURL url: URL, completion: #escaping (Data?)-> Void) {
let task = URLSession.shared.dataTask(with: url) { (data, _, _) in
if let data = data { completion(data) } else { completion(nil) }
}
task.resume()
}
}
Now in you ViewController, call your request and handle the execution of your closure.
class ViewController: UIViewController {
let requestCtrl = RequestCtrl()
override func viewDidLoad() {
super.viewDidLoad()
requestCtrl.fetchItem { (fetchedItem) in
guard let fetchedItem = fetchedItem else { return }
self.getPhoto(with: fetchedItem)
}
}
func getPhoto(with item: Item) {
requestCtrl.fetchItemPhoto(usingURL: item.url) { (fetchedPhoto) in
guard let fetchedPhoto = fetchedPhoto else { return }
let photo = UIImage(data: fetchedPhoto)
//now you have a photo at your disposal
}
}
}
These are not the best of practices since I am also still learning, so by all means do some research on topics especially closures, ios concurrency and URLComponents on Apple's documentation :)
you need to convert url into string and data to add in imageview
let imageURL:URL=URL(string: YourImageURL)!
let data=NSData(contentsOf: imageURL)
Yourimage.image=UIImage(data: data! as Data)
First add the pod in Podfile
pod 'Alamofire',
pod 'AlamofireImage'
you can check this link for install pods => https://cocoapods.org/pods/AlamofireImage
// Use this function for load image from URL in imageview
imageView.af_setImage(
withURL: url,
placeholderImage: placeholderImage //its optional if you want to add placeholder
)
Check this link for method of alamofireImage
https://github.com/Alamofire/AlamofireImage/blob/master/Documentation/AlamofireImage%203.0%20Migration%20Guide.md
Update for Xcode 13.3 , Swift 5
To load the Image asynchronously from a URL string, use this extension:
extension UIImageView {
public func getImageFromURLString(imageURLString: String) {
guard let imageURL = URL(string: imageURLString) else { return}
Task {
await requestImageFromURL(imageURL)
}
}
private func requestImageFromURL(_ imageURL: URL) async{
let urlRequest = URLRequest(url: imageURL)
do {
let (data, response) = try await URLSession.shared.data(for: urlRequest)
if let httpResponse = response as? HTTPURLResponse{
if httpResponse.statusCode == 200{
print("Fetched image successfully")
}
}
// Loading the image here
self.image = UIImage(data: data)
} catch let error {
print(error)
}
}
}
Usage:
imageView.getImageFromURLString(imageURLString: "https://apod.nasa.gov/apod/image/1811/hillpan_apollo15_4000.jpg")
This question already has answers here:
Loading/Downloading image from URL on Swift
(39 answers)
Closed 5 years ago.
EDIT 3: Please also read my comment in the "answered" tagged answer. I think I won't use my synchronous method but change to the suggested asynchronous methods that were also given!
Ok I am struggling with some basic concepts of showing images from an URL from the internet on my app.
I use this code to show my image on an UIIamgeView in my ViewController:
func showImage() {
let myUrlImage = URL(string: linkToTheImage)
let image = try? Data(contentsOf: myUrlImage!)
imageView1.image = UIImage(data: image!)
}
Now basically I have the following question:
Is the whole image downloaded in this process?
Or works the UIImageView like a "browser" in this case and doesn't download the whole picture but only "positions" the image from the URL into my UIImageView?
EDIT:
The reason I asked is, I am basically doing a quiz app and all I need in the view is an image from a URL for each question - so it's no difference if I do it asynchronous or synchronous because the user has to wait for the image anyways. I am more interested in how do I get the fastest result:
So I wanted to know if my code really downloads the picture as a whole from the URL or just "Positions" it into the UIImageView?
If in my code the picture is downloaded in its full resolution anyways, then you are right, I could download 10 pictures asynchronously when the player starts the quiz, so he hopefully doesn't have to wait after each answer as long as he would wait when I start downloading synchronously after each answer.
Edit 2:
Because my Question was tagged as similar to another some more explanation:
I already read about synchronous and asynchronous downloads, and I am aware of the downsides of synchronous loading.
I am more interested in a really basic question, and I get the feeling I had one basic thing really wrong:
My initial thought was that if I open a link in my browser, for example this one,
https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/68dd54ca-60cf-4ef7-898b-26d7cbe48ec7/10-dithering-opt.jpg
the browser doesn't download the whole picture. But I guess this isn't the case? The whole picture is downloaded?
Never use Data(contentsOf:) to display data from a remote URL. That initializer of Data is synchronous and is only meant to load local URLs into your app, not remote ones. Use URLSession.dataTask to download image data, just as you would with any other network request.
You can use below code to download an image from a remote URL asynchronously.
extension UIImage {
static func downloadFromRemoteURL(_ url: URL, completion: #escaping (UIImage?,Error?)->()) {
URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data, error == nil, let image = UIImage(data: data) else {
DispatchQueue.main.async{
completion(nil,error)
}
return
}
DispatchQueue.main.async() {
completion(image,nil)
}
}.resume()
}
}
Display the image in a UIImageView:
UIImage.downloadFromRemoteURL(yourURL, completion: { image, error in
guard let image = image, error == nil else { print(error);return }
imageView1.image = image
})
You can do it this way. But in most cases it is better to download the image first by yourself and handle the displaying then (this is more or less what the OS is doing in the background). Also this method is more fail proof and allows you to respond to errors.
extension FileManager {
open func secureCopyItem(at srcURL: URL, to dstURL: URL) -> Bool {
do {
if FileManager.default.fileExists(atPath: dstURL.path) {
try FileManager.default.removeItem(at: dstURL)
}
try FileManager.default.copyItem(at: srcURL, to: dstURL)
} catch (let error) {
print("Cannot copy item at \(srcURL) to \(dstURL): \(error)")
return false
}
return true
}
}
func download() {
let storagePathUrl = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString).appendingPathComponent("image.jpg")
let imageUrl = "https://www.server.com/image.jpg"
let urlRequest = URLRequest(url: URL(string: imageUrl)!)
let task = URLSession.shared.downloadTask(with: urlRequest) { tempLocalUrl, response, error in
guard error == nil, let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
print("error")
return
}
guard FileManager.default.secureCopyItem(at: tempLocalUrl!, to: storagePathUrl) else {
print("error")
return
}
}
task.resume()
}
I'm trying to load some pictures from different URL in order to add them to my Table View Cells. Each cell contains an UIImageView and the pictures (UIImage) are not loading correctly.
Sometimes the URL connection fails, and sometimes not. I'm going crazy,
help me please!!
This is the part of my code that tries to download a picture from an specific URL:
let imageURL = NSURL(string: "https://upload.wikimedia.org/wikipedia/commons/thumb/9/98/Angels_Stadium.JPG/1920px-Angels_Stadium.JPG")!
let task = URLSession.shared.dataTask(with: imageURL as URL) { (data, response, error) in
guard error == nil, let data = data else { return }
let downloadedImage = UIImage(data: data)
self.foto = downloadedImage!
tableWiew.reloadData()
}
task.resume()
This code is inside the init function of my class (User). After instantiate the class, I try to add the picture to my tableview in other class like this:
let user:User = User(json: obj, tableWiew: self.tableView)
addCell(cell: cell, name: user.nombre, job: user.puesto, nIdeas: "0", mProp: "0", image: user.foto)
The "addCell" method just creates a custom cell and inserts it into my tableView. That's working fine except for the damn picture.
If I add a picture from my Assets it works fine, but I don't know what's wrong when I try to add a picture from an URL.
Please download an image inside cellForRowAt method of UITableViewDataSource and if possible use SDWebImage that will help you with many options eg placeholder. It handles the caching internally and it is async.
let imageURL = NSURL(string: "https://upload.wikimedia.org/wikipedia/commons/thumb/9/98/Angels_Stadium.JPG/1920px-Angels_Stadium.JPG")!
let request = URLRequest(url: imageURL as URL)
let session = URLSession.shared
let task = session.dataTask(with: request, completionHandler: {data, response, error -> Void in
// print("Task completed")
// print(response)
DispatchQueue.main.async(execute: {
self.imgVW.image = UIImage(data: data!)
})
})
task.resume()
I've seen that you can download an image in IOS through a URL. However, this requires that the URL be public. I'd much rather do it in such a way where my application makes a request to the server and if the necessary requirements are met, the server responds with an image. I do not want my images to be visible from the web.
The easiest option is to put the password in the URL:
let url = NSURL(string: "http://exapmle.com/2XwLZAgAO2VP9JqXg1s73zmB/foo.png")
let dataOptional: NSData? = NSData(contentsOfURL: url)
if let data = dataOptional {
let image = UIImage(data: data)
} else {
println("Error loading \(url)")
}