Home > Software design >  SwiftUI Image does not display image from existing file
SwiftUI Image does not display image from existing file

Time:10-12

In my SwiftUI app I have a view for people to edit a book's metadata, such as its title and author. In this view I have an image view that shows the book's cover image.

struct ImageView: View {

    @Binding var book: Book

    var body: some View {
        // coverFile is the URL to the image file.
        let image = NSImage(byReferencing: 
            book.metadata.coverFile)
        Image(nsImage: image)
            .aspectRatio(contentMode: .fit)
    }
}

When I open a book that I've set a cover image and open the metadata editor, the image view displays nothing. The metadata editor has a button to choose a cover image. When I click the button and choose an image file from the file importer, the image appears in the image view. If I close the book, reopen it, and open the metadata editor, the image appears in the image view. But if I restart the app, open the book, and open the metadata editor, the image view displays nothing.

I have tried the various NSImage initializers. I tried building the image from a Data object and from NSBitmapImageRep.

extension Book {
    func coverImage() -> NSImage {
        do {
            let data = try Data(contentsOf: metadata.coverFile)
            return NSImage(data: data) ?? NSImage()
        } catch {
            Swift.print("Error reading the image data. Error: \(error)")
        }
        
        return NSImage()
    }
}

struct ImageView: View {
    @Binding var book: Book

    var body: some View {
        let image = book.coverImage()
        Image(nsImage: image)
            .aspectRatio(contentMode: .fit)
    }
}

But when I set a breakpoint in the image view and check the image variable, it's either nil or invalid. When I check the coverFile variable, it displays the proper URL, including the .png extension.

In an AppKit version of this app, I have the same code to create the NSImage, and the image displays in the image view.

According to Apple's documentation, the NSImage byReferencing function does not attempt to retrieve the data from the specified URL or create any image representations from that data until an app attempts to draw the image or request information about it. How do I get the SwiftUI view to trigger retrieving the image data so it displays in the image view?

UPDATE

I created a computed property for the image in the ImageView struct.

var image: NSImage {
    do {
        let data = try Data(contentsOf: book.metadata.coverFile)
        return NSImage(data: data) ?? NSImage()
    } catch {
        Swift.print("Error creating the image. Error: \(error)")
    }
        return NSImage()
}

When I run this code, the try Data call throws an exception, the catch block runs, and the following error message is printed in Xcode's Console:

Error creating the image. Error: Error Domain=NSCocoaErrorDomain Code=257 "The file “ImageFile” couldn’t be opened because you don’t have permission to view it."

Choosing a file from the file importer causes the data to be created properly. I have read/write permission for the folder where the image file is.

CodePudding user response:

From what I understand the problem boils down to lazily initializing the NSImage.

Have you tried using init(contentsOfFile:) or init(contentsOf:)? It does not initialize it lazily therefore should not cause this problem.

CodePudding user response:

The image does not display because of a file permissions problem. If I turn on the App Sandbox and give the app Read access to the Pictures folder, the cover image appears when I open the metadata editor.

  • Related