Odd compiler error with using .assign() to a keypath

I'm working with Combine in Xcode 11 beta 3 and IOS 13 beta 3, and I'm hitting an odd compiler error. I'm not sure if I'm not specifying something sufficiently or completely, or if this is a error with the swift compiler not being able to correctly infer what it needs to know.


I have a UIImageView reference, and I'm making my code is a Combine pipeline that retrieves a URL to populate that imageview and then (tries to use) assign to set it into place.


The short form of this code (full code at https://github.com/heckj/swiftui-notes/blob/master/UIKit-Combine/ViewController.swift#L167-L231):

URLSession.shared.dataTaskPublisher(for: URL(string: firstUser.avatar_url)!)
                    // ^^ this hands back (Data, response) objects
                    .handleEvents(receiveSubscription: { _ in
                        DispatchQueue.main.async {
                            self.activityIndicator.startAnimating()
                        }
                    }, receiveCompletion: { _ in
                        DispatchQueue.main.async {
                            self.activityIndicator.stopAnimating()
                        }
                    }, receiveCancel: {
                        DispatchQueue.main.async {
                            self.activityIndicator.stopAnimating()
                        }
                    })
                    .map { $0.data }
                    // ^^ pare down to just the Data object
                    .map { UIImage(data: $0)!}
                    // ^^ convert Data into a UIImage with its initializer
                    .subscribe(on: self.myBackgroundQueue)
                    // ^^ do this work on a background Queue so we don't *****
                    // with the UI responsiveness
                    .catch { err in
                        return Just(UIImage())
                    }
            .receive(on: RunLoop.main)
            // ^^ and then switch to receive and process the data on the main
            // queue since we're messin with the UI
            .assign(to: \.image, on: self.githubAvatarImageView)


This sequence of code results in the compiler error:

/Users/heckj/src/swiftui-notes/UIKit-Combine/ViewController.swift:166:26: error: type of expression is ambiguous without more context
             .assign(to: \.image, on: self.githubAvatarImageView)
                         ^~~~~~~


If I instead use sink subscriber, then it applies just fine:

            .sink(receiveValue: { image in
                self.githubAvatarImageView.image = image
            })


Is there something I should be doing to make the express less ambiguous, or a means to writing this a different form of constructing the keypath that would resolve this?

Accepted Reply

The problem here is that the

assign(to:on:)
wants a key path to a
UIImage
but the
image
property on
UIImageView
is a
UIImage?
(that is, an optional). You can ‘fix’ the problem by adding a non-optional property:
extension UIImageView {
    var imageNonOptional: UIImage {
        get {
            fatalError()
        }
        set {
            fatalError()
        }
    }
}

and then using that for your key path:

.assign(to: \.imageNonOptional, on: self.githubAvatarImageView)

Three things:

  • The above isn’t a real fix because you can’t make a valid non-optional property for this.

  • Given that, using

    sink(…)
    is probably your best bet.
  • Please make sure to file a bug about the non-helpful error message (and post your bug number here, just for the record).

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Replies

The problem here is that the

assign(to:on:)
wants a key path to a
UIImage
but the
image
property on
UIImageView
is a
UIImage?
(that is, an optional). You can ‘fix’ the problem by adding a non-optional property:
extension UIImageView {
    var imageNonOptional: UIImage {
        get {
            fatalError()
        }
        set {
            fatalError()
        }
    }
}

and then using that for your key path:

.assign(to: \.imageNonOptional, on: self.githubAvatarImageView)

Three things:

  • The above isn’t a real fix because you can’t make a valid non-optional property for this.

  • Given that, using

    sink(…)
    is probably your best bet.
  • Please make sure to file a bug about the non-helpful error message (and post your bug number here, just for the record).

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thank you Quinn!


I have opened a feedback/radar/bug (whatever we call it these days) - FB6602199, which includes all relevant sample code and the resulting error that appears. I will also reference this thread in the feedback to make it easier to associate the details you provided.


Thank you again!


- joe