Browse Source

Add low data mode support

onevcat 5 years ago
parent
commit
21d2c77d3d

+ 5 - 0
Sources/General/KFOptionsSetter.swift

@@ -320,6 +320,11 @@ extension KFOptionSetter {
         options.retryStrategy = strategy
         return self
     }
+
+    public func lowDataModeSource(_ source: Source?) -> Self {
+        options.lowDataModeSource = source
+        return self
+    }
 }
 
 // MARK: - Request Modifier

+ 12 - 0
Sources/General/KingfisherError.swift

@@ -256,6 +256,18 @@ public enum KingfisherError: Error {
         }
         return false
     }
+
+    var isLowDataModeConstrained: Bool {
+        if #available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *),
+           case .responseError(reason: .URLSessionError(let sessionError)) = self,
+           let urlError = sessionError as? URLError,
+           urlError.networkUnavailableReason == .constrained
+        {
+            return true
+        }
+        return false
+    }
+
 }
 
 // MARK: - LocalizedError Conforming

+ 13 - 21
Sources/General/KingfisherManager.swift

@@ -241,7 +241,19 @@ public class KingfisherManager {
                 completionHandler?(.failure(error))
                 return
             }
+            // When low data mode constrained error, retry with the low data mode source instead of use alternative on fly.
+            guard !error.isLowDataModeConstrained else {
+                if let source = retrievingContext.options.lowDataModeSource {
+                    retrievingContext.options.lowDataModeSource = nil
+                    startNewRetrieveTask(with: source, downloadTaskUpdated: downloadTaskUpdated)
+                } else {
+                    // This should not happen.
+                    completionHandler?(.failure(error))
+                }
+                return
+            }
             if let nextSource = retrievingContext.popAlternativeSource() {
+                retrievingContext.appendError(error, to: source)
                 startNewRetrieveTask(with: nextSource, downloadTaskUpdated: downloadTaskUpdated)
             } else {
                 // No other alternative source. Finish with error.
@@ -276,27 +288,7 @@ public class KingfisherManager {
                         }
                     }
                 } else {
-
-                    // Skip alternative sources if the user cancelled it.
-                    guard !error.isTaskCancelled else {
-                        completionHandler?(.failure(error))
-                        return
-                    }
-                    if let nextSource = retrievingContext.popAlternativeSource() {
-                        retrievingContext.appendError(error, to: currentSource)
-                        startNewRetrieveTask(with: nextSource, downloadTaskUpdated: downloadTaskUpdated)
-                    } else {
-                        // No other alternative source. Finish with error.
-                        if retrievingContext.propagationErrors.isEmpty {
-                            completionHandler?(.failure(error))
-                        } else {
-                            retrievingContext.appendError(error, to: currentSource)
-                            let finalError = KingfisherError.imageSettingError(
-                                reason: .alternativeSourcesExhausted(retrievingContext.propagationErrors)
-                            )
-                            completionHandler?(.failure(finalError))
-                        }
-                    }
+                    failCurrentSource(currentSource, with: error)
                 }
             }
         }

+ 5 - 0
Sources/General/KingfisherOptionsInfo.swift

@@ -248,6 +248,9 @@ public enum KingfisherOptionsInfoItem {
     /// when pass to an `ImageDownloader` or `ImageCache`.
     ///
     case retryStrategy(RetryStrategy)
+
+
+    case lowDataSource(Source)
 }
 
 // Improve performance by parsing the input `KingfisherOptionsInfo` (self) first.
@@ -291,6 +294,7 @@ public struct KingfisherParsedOptionsInfo {
     public var progressiveJPEG: ImageProgressive? = nil
     public var alternativeSources: [Source]? = nil
     public var retryStrategy: RetryStrategy? = nil
+    public var lowDataModeSource: Source? = nil
 
     var onDataReceived: [DataReceivingSideEffect]? = nil
     
@@ -332,6 +336,7 @@ public struct KingfisherParsedOptionsInfo {
             case .progressiveJPEG(let value): progressiveJPEG = value
             case .alternativeSources(let sources): alternativeSources = sources
             case .retryStrategy(let strategy): retryStrategy = strategy
+            case .lowDataSource(let source): lowDataModeSource = source
             }
         }
 

+ 3 - 0
Sources/Networking/ImageDownloader.swift

@@ -239,6 +239,9 @@ open class ImageDownloader {
         // Creates default request.
         var request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: downloadTimeout)
         request.httpShouldUsePipelining = requestsUsePipelining
+        if #available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) , options.lowDataModeSource != nil {
+            request.allowsConstrainedNetworkAccess = false
+        }
 
         if let requestModifier = options.requestModifier {
             // Modifies request before sending.