AsyncImage - Cancelled Loading before View is Visible

I have been playing around with the new AsyncImage Api in SwiftUI

I am using the initialiser that passes in a closure with the AsyncImagePhase, to view why an image may not load, when I looked at the error that is passed in if the phase is failure, the localised description of the error is "Cancelled" but this is happening before the view is being displayed.

I am loading these images in a list, I imagine I am probably doing something which is causing the system to decide to cancel the loading, but I cannot see what.

Are there any tips to investigate this further?

  • "...but I cannot see what.", we cannot see it either if you don't show us some code. "Are there any tips to investigate this further?, yes show us the code you are using.

  • This conversation in the Kingfisher async image library captures the ongoing issue(s) for many of us, including additional workarounds. There was a Feedback case filed (FB11564208), but those aren't public so I'm not sure how to report status.

Add a Comment

Replies

I have the same issue. List cells that show up when the app loads have images loaded fine, but as soon as I start scrolling any cells below the "fold" all have blank images. Checking the error code from the phase callback it shows the classic URL "cancelled" error (code 999 I believe?).

I suspect this may be a beta 1 SDK bug as it's hard to have a bug in the following, almost literally copied over from the Apple docs:

    AsyncImage(url: url) { phase in
        if let image = phase.image  {
            image
        } else if phase.error != nil {
            Image(systemName: "exclamationmark.triangle").padding()
            // the error here is "cancelled" on any view that wasn't visible at app launch
       } else {
            Spinner().padding()
        }
    }
  • As a followup, interestingly enough if I replace the List with a VStack inside a ScrollView then all works fine (because I assume no lazy loading occurs in that case) so this is definitely related to something between the lifecycle of the view as stated on the original post. I suspect that the "cancelled" state happens when a view is re-displayed or replaced by the system... Amusingly enough, a LazyVstack instead will kinda work, but only if you don't scroll fast enough. If you do (e.g. tap to scroll to the top) then the images in the top view will say "cancelled" :)

Add a Comment

For what it's worth, I did this hack in my code to get around this, hopefully temporarily. Not sure if I'd ship it :) but it keeps me moving forward while AsyncImage is either fixed, or I end up finding out there's some gotcha that I'm doing in my code that's causing it to cancel its loads in List cells. Time will tell :)

struct AsyncImageHack<Content> : View where Content : View {

    let url: URL?
    @ViewBuilder let content: (AsyncImagePhase) -> Content    

    @State private var currentUrl: URL?
    
    var body: some View {
        AsyncImage(url: currentUrl, content: content)
        .onAppear {
            if currentUrl == nil {
                DispatchQueue.main.async {
                    currentUrl = url
                }
            }
        }
    }
}
  • Thanks for the workaround. I'm still having the same issue with beta 2. Hopefully it's resolved in beta 3?!

  • Seems to be resolved in beta 4.

  • And yet I am still getting this issue in beta 5 :(

Confirming that this is still an issue in Xcode 13.0 beta 3.

  • I can confirm the problem persists in Xcode beta3. I can reproduce it in multiple projects, only when using List. Replacing List with a ForEach inside a ScrollView seems to work all the time.

  • Update: seems ok in beta4, just tried all of my projects where I experienced the issue. Thanks to whoever took the time to fix it!

Add a Comment

From the iOS & iPadOS 15 beta 4 release notes:

Resolved in iOS & iPadOS 15 beta 4

AsyncImage in List no longer cancels image downloads prematurely. (78187465, 78727070)

  • It's still not resolved for iOS 15.5

Add a Comment

I have the same issue with iOS 15.2. Actually the reason is clear and iOS behavior is acceptable. I am trying to fetch 20 images at the same time... On the other hand I have to overcome this issue... So my quick workaround is like the following;

I hope it works for your scenario. I also try to improve it(, if I have time).

AsyncImage(url: url) { phase in

	switch phase {
		case .success(let image):
			image
				.resizable()
				.scaledToFill()

		case .failure:

			//Call the AsynchImage 2nd time - when there is a failure. (I think you can also check NSURLErrorCancelled = -999)
			AsyncImage(url: url) { phase in
				if let image = phase.image {
					image
						.resizable()
						.scaledToFill()
				} else{
					Image(systemName: "xmark.octagon")
				}
			}

		//...

	}
}
  • This solution still working in Xcode 15.3 because we are facing the same issue

Add a Comment

Im still facing this issue with AsyncImage inside List in XCode Version 14.0 (14A309) for iOS 16.0

This is still a bug in iOS 16. The only solution that I found is to use ScrollView + LazyVStack instead of List.

I have the same problem too...even with the ScrollView and LazyVStack

There's another thread with a minimal working example here https://developer.apple.com/forums/thread/718480 Can someone from Apple take a look?

Can anyone confirm that the problem is still present on iOS17.2 / Xcode15.1?

Add a Comment

Still an issue! Testing with ScrollView + LazyVStack. Xcode 15.2.0 / iOS Simulator 17.2