11

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)
    }
}
fabian
  • 5,433
  • 10
  • 61
  • 92

9 Answers9

16

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)
Fokke Zandbergen
  • 3,866
  • 1
  • 11
  • 28
  • 3
    This is the correct answer. The image will not show up on a device unless you use NSURLSession. +1 – MSU_Bulldog Feb 01 '16 at 20:38
  • This code works for load image from http or https but can you please share the code for load image from local file storage url ? e.g. file:///private/var/mobile/Containers/Shared/AppGroup/CB69B2CD-3D2A-4428-9633-56385B28FEA0/Library/MMS/wi2gb8bahijqonom3dvsq.png – Parth Barot Dec 22 '21 at 09:19
7

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)
fabian
  • 5,433
  • 10
  • 61
  • 92
5

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.

Hieu Duc Pham
  • 1,074
  • 1
  • 10
  • 24
4

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)
                }
            }
        }
    }
}
vikas prajapati
  • 1,868
  • 2
  • 18
  • 26
3

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)")
        }
    }
}

}

Yasin
  • 72
  • 7
1
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
    }
}
unixb0y
  • 979
  • 1
  • 10
  • 39
0
     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.

Arvind Kumar
  • 2,371
  • 1
  • 18
  • 25
  • 1
    Like the others this solution works fine in watchOS simulator for me but not on device. After adding logs I saw that the second if-statement returns false. The image I use if http://openweathermap.org/img/w/10d.png – Fokke Zandbergen Dec 01 '15 at 10:20
  • You can try this for asynchronous image loading: https://github.com/natelyman/SwiftImageLoader – Arvind Kumar Dec 01 '15 at 11:17
  • Thanks, I just added another answer using NSURLSession that works fine on both simulator and device – Fokke Zandbergen Dec 01 '15 at 11:47
0

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)
Edison
  • 11,881
  • 5
  • 42
  • 50
0

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.

Robert Dresler
  • 10,580
  • 2
  • 22
  • 40