fabian
fabian

Reputation: 5463

Load image from URL on WatchKit

Is there a more elegant solution to load an external image on the watch than the following ?

let image_url:String = "http://placehold.it/350x150"

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
    let url:NSURL = NSURL(string:image_url)!
    var data:NSData = NSData(contentsOfURL: url)!
    var placeholder = UIImage(data: data)!

    // update ui
    dispatch_async(dispatch_get_main_queue()) {
        self.imageView.setImage(placeholder)
    }
}

Upvotes: 11

Views: 8364

Answers (9)

Robert Dresler
Robert Dresler

Reputation: 11210

This answer provides little bit more generic answer.

When you need to get Data you need to retrieve it from somewhere. This "somewhere" can be either on your local device or somewhere outside on the server and you need to retrieve it using network connection.

When you need to retrieve Data locally from your device, feel free to use Data(contentsOf:). But, when you need to download that data using network connection, don't use it - as is stated and explained here in docs. You can e.g. use async data(from:) instead.

So, eventhough it is not best to use Data(contentsOf:) to retrieve data using network, you can do it and in most cases, it will work. However, this init probably uses NSURLConnection internally, which is not supported in watchOS (source) on the real device.

Upvotes: 0

unixb0y
unixb0y

Reputation: 1048

public extension WKInterfaceImage {
    public func setImageWithUrl(url:String, scale: CGFloat = 1.0) -> WKInterfaceImage? {
        URLSession.shared.dataTask(with: NSURL(string: url)! as URL) { data, response, error in
            if (data != nil && error == nil) {
                let image = UIImage(data: data!, scale: scale)

                DispatchQueue.main.async() {
                   self.setImage(image)
                }
            }
        }.resume()

        return self
    }
}

Upvotes: 1

Edison
Edison

Reputation: 11987

Swift 4.2

Using URLSession, proper GCD and @discardableResult to silence the Result of call to '...' is unused warning.

plist
App Transport Security Settings
Allow Arbitrary Loads - YES

You can set a fixed image size of 100x100 in the storyboard if you like.

let url = "https://i.imgur.com/UZbLC0Q.jpg"

public extension WKInterfaceImage {

    @discardableResult public func setImageWithUrl(url:String, scale: CGFloat = 1.0) -> WKInterfaceImage? {

        URLSession.shared.dataTask(with: NSURL(string: url)! as URL) { data, response, error in
            if (data != nil && error == nil) {
                let image = UIImage(data: data!, scale: scale)

                DispatchQueue.main.async {
                    self.setImage(image)
                }
            }
            }.resume()

        return self
    }
}

call

row.image.setImageWithUrl(url: url, scale: 1.0)

Upvotes: 0

Yasin
Yasin

Reputation: 72

Just had the same task, the answers here helped me, but I needed to do some modifications. So I wanted to share the updated version (without any forced unwraps) of the common answers here (should work with Swift 4.2):

public extension WKInterfaceImage {

public func setBackgroundImage(url: String) {
    let asyncQueue = DispatchQueue(label: "backgroundImage")
    asyncQueue.async {
        do {
            if let url = URL(string: url) {
                let data = try Data(contentsOf: url)
                if let placeholder = UIImage(data: data) {
                    self.setImage(placeholder)
                }
            }
        } catch let error {
            print("Could not set backgroundImage for WKInterfaceImage: \(error.localizedDescription)")
        }
    }
}

}

Upvotes: 3

vikas prajapati
vikas prajapati

Reputation: 1956

I think by this solution you can store image in cache and display image from cache also.so you can call this function and use it.

func loadImage(url:String, forImageView: WKInterfaceImage) {

    forImageView.setImageNamed("placeholder")
    let image_url:String = url

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {

        let url:NSURL = NSURL(string:image_url)!
        print(url)

        //if image is already stored in cache
        if WKInterfaceDevice.currentDevice().cachedImages[image_url] != nil{
            dispatch_async(dispatch_get_main_queue()) {
                forImageView.setImageNamed(image_url)
            }
        }else{
            if let data = NSData(contentsOfURL: url){

                //load image
                let image = UIImage(data: data)!
                //Store image in cache
                WKInterfaceDevice.currentDevice().addCachedImage(image, name: image_url)
                dispatch_async(dispatch_get_main_queue()) {
                    forImageView.setImage(placeholder)
                }
            }
        }
    }
}

Upvotes: 4

Fokke Zandbergen
Fokke Zandbergen

Reputation: 3866

NSURL is meant to be used for local files. Instead use NSURLSession. It's also useful to set the scale for the remote image.

import WatchKit

public extension WKInterfaceImage {

    public func setImageWithUrl(url:String, scale: CGFloat = 1.0) -> WKInterfaceImage? {

        NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: url)!) { data, response, error in
            if (data != nil && error == nil) {
                let image = UIImage(data: data!, scale: scale)

                dispatch_async(dispatch_get_main_queue()) {
                    self.setImage(image)
                }
            }
        }.resume()

        return self
    }
}

Use it like this

self.imageView.setImageWithUrl(image_url, scale: 2.0)

Upvotes: 16

Arvind Kumar
Arvind Kumar

Reputation: 2421

     if let url = NSURL(string: "http://google.net/img/upload/photo2.png") {
            if let data = NSData(contentsOfURL: url){

             imageWK.setImage(UIImage(data: data))

        }
    }

Try this code. Dont forget to add NSTransportSecurity in your Plist.

Upvotes: 0

fabian
fabian

Reputation: 5463

Here is the category

import WatchKit

public extension WKInterfaceImage {

    public func setImageWithUrl(url:String) -> WKInterfaceImage? {

        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
            let url:NSURL = NSURL(string:url)!
            var data:NSData = NSData(contentsOfURL: url)!
            var placeholder = UIImage(data: data)!

            dispatch_async(dispatch_get_main_queue()) {
                self.setImage(placeholder)
            }
        }

        return self
    }

}

Use it like this

 self.imageView.setImageWithUrl(image_url)

Upvotes: 7

Hieu Duc Pham
Hieu Duc Pham

Reputation: 1074

I thinks that solution is good because it can help your application out of lagging when you're trying to load some Images from web. you can make a new function like this:

func loadImage(url:String, forImageView: WKInterfaceImage) {
// load image
    let image_url:String = url
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
        let url:NSURL = NSURL(string:image_url)!
        var data:NSData = NSData(contentsOfURL: url)!
        var placeholder = UIImage(data: data)!

// update ui
        dispatch_async(dispatch_get_main_queue()) {
            forImageView.setImage(placeholder)
        }
    }

}

after that any where you want to load image from urlString you can use like this:

loadImage("http://...", forImageView: self.myImageView)

Hope this help.

Upvotes: 5

Related Questions