2

I've been trying to figure out this one for a bit now, so while trying to load an image remotely and I am running into the domain error. So here is what I get from the console:

Task <95AAB48F-8670-4E1D-AA29-21D47E0EE069>.<5> finished with error [-999] Error Domain=NSURLErrorDomain Code=-999 "cancelled" UserInfo={NSErrorFailingURLStringKey=http://gba.ergoware.io/cache/content/topstory/ergo_news_01.png, NSLocalizedDescription=cancelled, NSErrorFailingURLKey=http://gba.ergoware.io/cache/content/topstory/ergo_news_01.png}

Here is the class calling the image from the site:

// MARK: Remote Image Loading
class ImageLoader: ObservableObject {

    @Published var image: UIImage?
    private let url: URL
    private var cancellable: AnyCancellable?
    private var cache: ImageCache?

    init(url: URL, cache: ImageCache? = nil) {
        self.url = url
        self.cache = cache
    }

    func load() {
        if let image = cache?[url] {
            self.image = image
            return
        }

        cancellable = URLSession.shared.dataTaskPublisher(for: url)
        .map { UIImage(data: $0.data) }
        .replaceError(with: nil)
        .handleEvents(receiveOutput: { [weak self] in self?.cache($0) })
        .receive(on: DispatchQueue.main)
        .assign(to: \.image, on: self)
    }

    private func cache(_ image: UIImage?) {
        image.map { cache?[url] = $0 }
    }

    func cancel() {
        cancellable?.cancel()
    }
}

struct AsyncImage<Placeholder: View>: View {
    @ObservedObject private var loader: ImageLoader
    private let placeholder: Placeholder?

    init(url: URL, placeholder: Placeholder? = nil, cache: ImageCache? = nil) {
        loader = ImageLoader(url: url, cache: cache)
        self.placeholder = placeholder
    }

    var body: some View {
        image
            .onAppear(perform: loader.load)
            .onDisappear(perform: loader.cancel)
    }

    private var image: some View {
        Group {
            if loader.image != nil {
                Image(uiImage: loader.image!)
                    .resizable()
            } else {
                placeholder
            }
        }
    }
}

protocol ImageCache {
    subscript(_ url: URL) -> UIImage? { get set }
}

struct TemporaryImageCache: ImageCache {
    private let cache = NSCache<NSURL, UIImage>()

    subscript(_ key: URL) -> UIImage? {
        get { cache.object(forKey: key as NSURL) }
        set { newValue == nil ? cache.removeObject(forKey: key as NSURL) : cache.setObject(newValue!, forKey: key as NSURL) }
    }
}

// MARK: Image Cache

struct ImageCacheKey: EnvironmentKey {
    static let defaultValue: ImageCache = TemporaryImageCache()
}

extension EnvironmentValues {
    var imageCache: ImageCache {
        get { self[ImageCacheKey.self] }
        set { self[ImageCacheKey.self] = newValue }
    }
}

So how this is set up is, my interface calls the images, and for certain images like the user's profile image or achievement badges, the image will be cached during the session to prevent multiple request for the same image and cutting down on load time and server traffic in the long run.

Any help would be appreciated! Thanks!

Asperi
  • 228,894
  • 20
  • 464
  • 690
thealexvond
  • 69
  • 1
  • 7

2 Answers2

2

This error happens due to attached .onDisappear to placeholder, so when real image has loaded and assigned to published property the view is refreshed, so placeholder is removed, so cancel is called on completed but not yet finished data task, so the log.

Here is simplest fix. Tested with Xcode 11.4 / iOS 13.4.

var body: some View {
    image
        .onAppear(perform: loader.load)
        .onDisappear {
            if self.loader.image == nil { // cancel only if not loaded yet
                self.loader.cancel()
            }
        }
}
Asperi
  • 228,894
  • 20
  • 464
  • 690
  • Sweet, I updated that, but I am still seeing this in the console. ``` Task .<8> finished with error [-1002] Error Domain=NSURLErrorDomain Code=-1002 "unsupported URL" UserInfo={NSUnderlyingError=0x600001b61e00 {Error Domain=kCFErrorDomainCFNetwork Code=-1002 "(null)"}, NSErrorFailingURLStringKey=Optional(http://gba.ergoware.io/cache/avatars/tyler_bell.png), NSErrorFailingURLKey=Optional(http://gba.ergoware.io/cache/avatars/tyler_bell.png), NSLocalizedDescription=unsupported URL} ``` – thealexvond May 20 '20 at 19:43
0

Hello, Try to Allow Arbitrary loads in info.plist file Learn More by checking out this answer: https://stackoverflow.com/a/40299837/7443052

Regards, David

David
  • 71
  • 3
  • 1
    This solution is only for Development purposes because your app can be rejected by Apple: https://developer.apple.com/documentation/bundleresources/information_property_list/nsapptransportsecurity/nsallowsarbitraryloads It would be just better to use HTTPS if its possible. – Łukasz Łabuński May 19 '20 at 19:20
  • Thank you for the idea, I already had Arbitrary Loads enabled though. Well I should clarify, it is disabled, with exceptions to my domain and subdomains. – thealexvond May 19 '20 at 19:23