Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI - Unwrap optional image data to create Image based on UIImage(data)

Tags:

swift

swiftui

I have found this link and some answer says that we can solve two tasks:

  • Display view element if needed
  • Or display default value instead if nil found

I have data model which contain optional Data properly which I use for my Image element on the View.

Currently as I have not found a way how to workaround this and use this sign ! which is unsafe:

Image(uiImage: UIImage(data: userService.user.imageData!)!)

For sure I can't guarantee it and I should not use this, but need a help how to unwrap this.

As a quick workaround I can make this image data property as non optional and then load some data from the local file, but even then I still have one more force unwrapping here UIImage(data.

like image 391
Matrosov Oleksandr Avatar asked Jan 01 '23 03:01

Matrosov Oleksandr


2 Answers

You have a few options available. I think the most straightforward is to make an extension on Image and define a new initialiser that will serve your needs:

extension Image {

    public init?(data: Data?) {
        guard let data = data,
            let uiImage = UIImage(data: data) else {
                return nil
        }
        self = Image(uiImage: uiImage)
    }
}

Then you can simply use it like this:

Image(data: userService.user.imageData)

since it is nullable it needs to be contained in another View with at least one other View.

If you want to provide a placeholder in place of a missing image you could use an extension like this:

extension Image {

    public init(data: Data?, placeholder: String) {
        guard let data = data,
          let uiImage = UIImage(data: data) else {
            self = Image(placeholder)
            return
        }
        self = Image(uiImage: uiImage)
    }
}

Then you can simply use it like this:

Image(data: userService.user.imageData, placeholder: "nameOfPlaceholder")

since it is not nullable it doe not need to be contained in another View with at least one other View.

like image 73
LuLuGaGa Avatar answered May 26 '23 04:05

LuLuGaGa


Approach:

  • Use control statements
  • Use AnyView to erase type

Reason for AnyView

  • You need to erase the type so that your view consistently returns the same view type irrespective which execution path it takes (whether if statement is satisfied or not).
  • That way it returns AnyView no matter what and will satisfy the body type (some View)

Example:

  • If the data is valid image data an image would be displayed
  • If not then an empty view is displayed

Note: Instead of the empty view you could display any other view.

Code:

import SwiftUI

class Model : ObservableObject {
    var data : Data?

    init(data: Data?) {
        self.data = data
    }
}

struct ContentView: View {

    @ObservedObject var model : Model

    var body: some View {

        let view : AnyView

        if let data = model.data,
            let uiimage = UIImage(data: data) {
            view = AnyView(Image(uiImage: uiimage))
        }
        else {
            view = AnyView(EmptyView()) //You could have view = AnyView(Text("no image found"))
        }

        return view
    }
}
like image 39
user1046037 Avatar answered May 26 '23 05:05

user1046037