Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

defaultOptions only invokes _last_ requestModifier in chain #2210

Open
3 tasks done
nodediggity opened this issue Feb 18, 2024 · 2 comments
Open
3 tasks done

defaultOptions only invokes _last_ requestModifier in chain #2210

nodediggity opened this issue Feb 18, 2024 · 2 comments

Comments

@nodediggity
Copy link

nodediggity commented Feb 18, 2024

Check List

Thanks for considering to open an issue. Before you submit your issue, please confirm these boxes are checked.

Issue Description

What

I am trying to chain multiple requestModifier instances within a SwiftUI app. In my "real world" example, each modifier serves a different purpose and contains conditional logic that either modifiers the request or allows it to pass through unchanged.

To avoid complexity in our views I am using KingfisherManager.shared.defaultOptions to configure this away from my SwiftUI views.

It is my understanding setting a value here will provide this behaviour on a global level - all instances of KFImage will inherit these options.

final class FirstRequestModifier: ImageDownloadRequestModifier {
    func modified(for request: URLRequest) -> URLRequest? {
        print("^^ FirstRequestModifier")
        return request
    }
}

final class SecondRequestModifier: ImageDownloadRequestModifier {
    func modified(for request: URLRequest) -> URLRequest? {
        print("^^ SecondRequestModifier")
        return request
    }
}


KingfisherManager.shared.defaultOptions = [
    .requestModifier(FirstRequestModifier()),        
    .requestModifier(SecondRequestModifier())
]

I also understood that modifiers are invoked in the order in which they are appear in the options array.

When running my app however I am finding that only the last option in the array is invoked.

Any modifiers previous to this are either overwritten or ignored.

Reproduce

Attached is a basic example that replicates this.

  • Launch App
  • Tap 'Load Image'
  • Check Xcode console output and only the second print statement is actioned.

Each modifier should log to the console in the order they are created.

KingfisherExample.zip

Other Comment

I am using v7.11.0 and supporting iOS 17.

@onevcat
Copy link
Owner

onevcat commented Feb 18, 2024

Currently the requestModifier option only keeps the last value, it is not linkable and cannot be called in a chain.

Instead of setting multiple . requestModifier, you need to create a "multiple version" of ImageDownloadRequestModifier yourself, like this:

final class ConcatenatedRequestModifier: ImageDownloadRequestModifier {
    let modifiers: [any ImageDownloadRequestModifier]
    init(modifiers: [any ImageDownloadRequestModifier]) {
        self.modifiers = modifiers
    }
    
    func modified(for request: URLRequest) -> URLRequest? {
        var r = request
        for modifier in modifiers {
            if let modified = modifier.modified(for: r) {
                r = modified
            } else {
                return nil
            }
        }
        return r
    }   
}

Indeed it is a good idea to allow the option accepts multiple .requestModifier as a chained behavior. However, it breaks the current behavior and usage (although this should be a rare use case). I will consider it in the following major upgrade!

Thanks for the suggestion.

@nodediggity
Copy link
Author

Ah thank you so much, I misunderstood how it works, your suggestion worked perfectly.

Feel free to close this, I really appreciate the speedy response 🙏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants