onevcat / Kingfisher

A lightweight, pure-Swift library for downloading and caching images from the web.
MIT License
23.11k stars 2.64k forks source link

Intercepting King-Fisher Requests with custom URLProtocol #845

Closed SebastianBoldt closed 6 years ago

SebastianBoldt commented 6 years ago

We are currently trying to write an UI-Tests that checks if the loading indicator is visible for the user if loading takes longer than expected. We tried to register a custom URLProtocol inside the AppDelegate like so: URLProtocol.registerClass(NetworkDelayProtocol.self)

The NetworkDelayProtocol does not do much but it should at least get asked if it canInit the Request. But for some reason the custom URLProtocol class does not receive KingFishers image requests. Does someone have any ideas whats going on here? I know that Kingfisher is using a ephemeral session configuration for requesting images, maybe it has something to do with it?

import Foundation
import UIKit

public class NetworkDelayProtocol: URLProtocol {

    public override init(request: URLRequest, cachedResponse: CachedURLResponse?, client: URLProtocolClient?) {
        super.init(request: request, cachedResponse: cachedResponse, client: client)
    }
    override public static func canInit(with request: URLRequest) -> Bool {
        return true
    }

    override public class func canonicalRequest(for request: URLRequest) -> URLRequest {
        print(request)
        return request
    }

    override public func startLoading() {
        // do nothing
    }

    override public func stopLoading() {
        // nothing to do
    }
}
dreampiggy commented 6 years ago

For URLSession, you should register your custom URLProtocol class. See protocolclasses

SebastianBoldt commented 6 years ago

I solved this issue by swizzling URLSessions ephemeral property with my custom implementation.

markst commented 5 years ago

@SebastianBoldt why was swizzling necessary? Shouldn't it be possible to define the protocol classes on the sessionConfiguration?

init() {
  assetDownloader.sessionConfiguration.protocolClasses = [NetworkDelayProtocol.self]
  assetDownloader.delegate = self
}

edit: you need to redefine the sessionConfiguration for the session to be invalidated & reinitialised.

let sessionConfiguration = URLSessionConfiguration.ephemeral
sessionConfiguration.protocolClasses = [NetworkDelayProtocol.self]
assetDownloader.sessionConfiguration = sessionConfiguration