0

At the moment I try to create a package with SwiftUI. I created a ProfileImageView. I get an Url as an optional String, which I convert to a non-optional String with if...let... In the body I use this String for an AsyncImage. The AsyncImage just needs an optional URL, so I can create the Url together with the AnsyncImage.

AsyncImage(url: URL(string: NonOptionalString)){ image in
image
}

I can just modify the image inside the asyncImage-Block so that I need some details how modified the user wants his image. So I declared a constant called modifier, which is a ViewModifier. I wanted to use it like:

image
.modifier(modifier)

So at first I declared it like this:

let modifier: ViewModifier

I got the error message "Use of protocol 'ViewModifier' as a type must be written 'any ViewModifier'" So I changed all the usages to any ViewModifier and got the error message "Type 'any ViewModifier' cannot conform to 'ViewModifier'" at this code:

AsyncImage(url: URL(string: NonOptionalString)) { image in
image
.modifier(modifier)
}

How can I fix that? Thanks!

Code:

public struct ProfileImage<Content>: View where Content: View{
    let placeholder: () -> Content
    let urlString: String?
    let modifier: any ViewModifier
    
    public init(_ url: String?,
         @ViewBuilder placeholder: @escaping() -> Content,
         modifier: any ViewModifier) {
        self.placeholder = placeholder
        self.urlString = url
        self.modifier = modifier
    }
    public var body: some View{
        if let NonOptionalString = urlString{
            AsyncImage(url: URL(string: NonOptionalString)) { image in
                image
                    .modifier(modifier)
// Error message: "Type 'any ViewModifier' cannot conform to 'ViewModifier'"
            } placeholder: {
                placeholder()
            }

        } else {
            placeholder()
        }
    }
}

1 Answer 1

2

A possible solution is to make modifier also generic.

And rather than Optional Binding and an extra else clause use flatMap to evaluate the url string

public struct ProfileImage<Content, Modifier>: View where Content: View, Modifier: ViewModifier{
    let placeholder: () -> Content
    let urlString: String?
    let modifier: Modifier
    
    public init(_ url: String?,
         @ViewBuilder placeholder: @escaping() -> Content,
         modifier: Modifier) {
        self.placeholder = placeholder
        self.urlString = url
        self.modifier = modifier
    }
    public var body: some View{
        AsyncImage(url: urlString.flatMap(URL.init)) { image in
            image
                .modifier(modifier)
        } placeholder: {
            placeholder()
        }
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

Totally agree with adding the generic Modifier, but this should also get rid of the if let urlString and use urlString.flatMap(URL.init). This currently breaks view identity by having placeholder() in two legs of the conditional, which can break transitions and animations.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.