admin管理员组

文章数量:1316030

I have been using AsyncImage for my project which gets the images using urls. I thought AsyncImage is the best way to go since SwiftUI provides a ready to use component for it.

However, if a image load fails, then I am getting a common error and not a detailed error message of why the url request failed (eg: no permissions to access URL or some other reason).

The reason I am constantly seeing is The operation couldn’t be completed. (SwiftUI.(unknown context at $1d2600574).LoadingError error 1.)

Executable code:

import SwiftUI

class Foo {
    var title: String
    var url: String
    var image: Image?
    
    init(title: String, url: String, image: Image? = nil) {
        self.title = title
        self.url = url
        self.image = image
    }
}

struct ContentViewA: View {
    @State var showSheetA: Bool = false
    
    var body: some View {
        VStack {
            Text("This is main view")
        }
        .onAppear{
            showSheetA = true
        }
        .sheet(isPresented: $showSheetA) {
            SheetViewA()
        }
    }
}

struct SheetViewA: View {
    @State var data = [
        Foo(title: "Image E", url: ".jpg123", image: nil)
    ]

    @State var panelDetent: PresentationDetent = .medium
    
    var body: some View {
        NavigationStack {
            VStack {
                ScrollView(.horizontal, showsIndicators: false) {
                     HStack(alignment: .center, spacing: 10) {
                         ForEach(data, id: \.title) { item in
                             if let urlObject = URL(string: item.url) {
                                 AsyncImage(url: urlObject,
                                            scale: 1.0,
                                            transaction: Transaction(animation: .spring(response: 0.5, dampingFraction: 0.65, blendDuration: 0.025)),
                                            content: { renderPhoto(phase: $0, item: item) })
                             } else {
                                 /// Note: Shows placeholder view
                                 EmptyView()
                             }
                         }
                     }
                     .background(Color.gray.opacity(0.2))
                     .padding(.leading, 0)
                     .padding(.trailing, 16)
                     .frame(maxWidth: .infinity, minHeight: 65, maxHeight: 65, alignment: .topLeading)
                }
            }
            .padding([.top, .bottom], 150.0)
            .presentationDetents([.medium, .large], selection: $panelDetent)
        }
    }
    
    @ViewBuilder
    private func renderPhoto(phase: AsyncImagePhase, item: Foo) -> some View {
        switch phase {
            case .success(let image):
                let _ = print("Success state")
            case .failure(let error):
                let _ = print("%%% detailed error is - \(error.localizedDescription) %%%%")
            case .empty:
                let _ = print("Empty state")
            @unknown default:
                EmptyView()
        }
    }
}

Is there a way to get detailed error message using AsycnImage? Or will I have to get images using the old URLSession request method? I need to display detailed error message on the UI.

I have been using AsyncImage for my project which gets the images using urls. I thought AsyncImage is the best way to go since SwiftUI provides a ready to use component for it.

However, if a image load fails, then I am getting a common error and not a detailed error message of why the url request failed (eg: no permissions to access URL or some other reason).

The reason I am constantly seeing is The operation couldn’t be completed. (SwiftUI.(unknown context at $1d2600574).LoadingError error 1.)

Executable code:

import SwiftUI

class Foo {
    var title: String
    var url: String
    var image: Image?
    
    init(title: String, url: String, image: Image? = nil) {
        self.title = title
        self.url = url
        self.image = image
    }
}

struct ContentViewA: View {
    @State var showSheetA: Bool = false
    
    var body: some View {
        VStack {
            Text("This is main view")
        }
        .onAppear{
            showSheetA = true
        }
        .sheet(isPresented: $showSheetA) {
            SheetViewA()
        }
    }
}

struct SheetViewA: View {
    @State var data = [
        Foo(title: "Image E", url: "https://t3.ftcdn/jpg/10/08/34/50/360_F_1008345045_VOe4ziz83vb6d3E3X6KI00qHyLd32D4l.jpg123", image: nil)
    ]

    @State var panelDetent: PresentationDetent = .medium
    
    var body: some View {
        NavigationStack {
            VStack {
                ScrollView(.horizontal, showsIndicators: false) {
                     HStack(alignment: .center, spacing: 10) {
                         ForEach(data, id: \.title) { item in
                             if let urlObject = URL(string: item.url) {
                                 AsyncImage(url: urlObject,
                                            scale: 1.0,
                                            transaction: Transaction(animation: .spring(response: 0.5, dampingFraction: 0.65, blendDuration: 0.025)),
                                            content: { renderPhoto(phase: $0, item: item) })
                             } else {
                                 /// Note: Shows placeholder view
                                 EmptyView()
                             }
                         }
                     }
                     .background(Color.gray.opacity(0.2))
                     .padding(.leading, 0)
                     .padding(.trailing, 16)
                     .frame(maxWidth: .infinity, minHeight: 65, maxHeight: 65, alignment: .topLeading)
                }
            }
            .padding([.top, .bottom], 150.0)
            .presentationDetents([.medium, .large], selection: $panelDetent)
        }
    }
    
    @ViewBuilder
    private func renderPhoto(phase: AsyncImagePhase, item: Foo) -> some View {
        switch phase {
            case .success(let image):
                let _ = print("Success state")
            case .failure(let error):
                let _ = print("%%% detailed error is - \(error.localizedDescription) %%%%")
            case .empty:
                let _ = print("Empty state")
            @unknown default:
                EmptyView()
        }
    }
}

Is there a way to get detailed error message using AsycnImage? Or will I have to get images using the old URLSession request method? I need to display detailed error message on the UI.

Share Improve this question asked Jan 29 at 22:56 tech_humantech_human 7,16617 gold badges75 silver badges134 bronze badges 3
  • In your case .failure you can try casting the error, for example: if let urlError = error as? URLError {...} or if let decodingError = error as? DecodingError {...} etc... to try to get more info from the returned error. If you use a URLSession instead of the AsyncImage you get what you would if you used your browser, that is, a html with File Not Found. Note, you should use struct Foo and have @State private var – workingdog support Ukraine Commented Jan 29 at 23:31
  • Don’t print localizedDescription. Just print error directly. – Sweeper Commented Jan 30 at 7:59
  • I tried everything. Just printing error gives me "SwiftUI.AsyncImage<SwiftUI.AnyView>.(unknown context at $1c552a084).LoadingError()" and printing as "error as? URLError" or "error as? DecodingError" or "error as? NSError" everything is giving me nil. – tech_human Commented Jan 30 at 23:51
Add a comment  | 

1 Answer 1

Reset to default 1

Here is my test code that prints the error and error.localizedDescription, giving very little info to a user, just LoadingError.

Included is a test fetching the image (which does not exist) using URLSession. The data/info your get seems to give you more information (File Not Found), but this is just because the server sends you this info.

Note, casting the error to URLError and DecodingError return nil, because this is not the type of error you get this time, but you may well get something next time.

struct Foo {
    var title: String
    var url: String
    var image: Image?
    
    init(title: String, url: String, image: Image? = nil) {
        self.title = title
        self.url = url
        self.image = image
    }
}

struct ContentView: View {
    @State private var showSheetA: Bool = false
    
    var body: some View {
        Text("This is main view")
        Button("show sheet") {
            showSheetA = true
        }.buttonStyle(.bordered)
        .sheet(isPresented: $showSheetA) {
            SheetViewA()
        }
    }
}

struct SheetViewA: View {
    @State private var data = [
        Foo(title: "Image E", url: "https://t3.ftcdn/jpg/10/08/34/50/360_F_1008345045_VOe4ziz83vb6d3E3X6KI00qHyLd32D4l.jpg123", image: nil)
    ]
    
    @State private var panelDetent: PresentationDetent = .medium
    
    var body: some View {
        NavigationStack {
            VStack {
                ScrollView(.horizontal, showsIndicators: false) {
                    HStack(alignment: .center, spacing: 10) {
                        ForEach(data, id: \.title) { item in
                            if let urlObject = URL(string: item.url) {
                                AsyncImage(url: urlObject,
                                           scale: 1.0,
                                           transaction: Transaction(animation: .spring(response: 0.5, dampingFraction: 0.65, blendDuration: 0.025)),
                                           content: { renderPhoto(phase: $0, item: item) })
                            } else {
                                /// Note: Shows placeholder view
                                EmptyView()
                            }
                        }
                    }
                    .background(Color.gray.opacity(0.2))
                    .padding(.leading, 0)
                    .padding(.trailing, 16)
                    .frame(maxWidth: .infinity, minHeight: 65, maxHeight: 65, alignment: .topLeading)
                }
            }
            .padding([.top, .bottom], 150.0)
            .presentationDetents([.medium, .large], selection: $panelDetent)
            // for testing
            .task {
                await getImage()
            }
        }
    }
    
    @ViewBuilder
    private func renderPhoto(phase: AsyncImagePhase, item: Foo) -> some View {
        switch phase {
        case .success(let image):
            let _ = print("Success state")
            
        case .failure(let error):
            let _ = print("%%% error is - \(error) %%%%")
            let _ = print("%%% localizedDescription error is - \(error.localizedDescription)")

            let _ = print("%%% ---> URLError error is - \(error as? URLError) %%%%")
            let _ = print("%%% ---> DecodingError error is - \(error as? DecodingError) %%%%")
                          
        case .empty:
            let _ = print("Empty state")
        @unknown default:
            EmptyView()
        }
    }
    
    // for testing
    func getImage() async {
        guard let url = URL(string: "https://t3.ftcdn/jpg/10/08/34/50/360_F_1008345045_VOe4ziz83vb6d3E3X6KI00qHyLd32D4l.jpg123") else {
            print("bad URL")
            return
        }
        do {
            let (data, _) = try await URLSession.shared.data(from: url)
            print("\n---> URLSession data: \(String(data: data, encoding: .utf8) as AnyObject)\n")
        } catch {
            print("\n---> URLSession error: \(error)\n")
        }
    }
    
}

Note, the server returns a html result:

 <html>
   <head>
   </head>
   <body>
     <h1>File Not Found</h1>
   </body>
 </html>

When AsyncImage tries to make sense of this, it concludes that this cannot be loaded (because this is not an image).

本文标签: iosDetailed error from AsyncImage in SwiftUIStack Overflow