Kingfisher 低数据模式(Low Data Mode)适配指南:用 `.lowDataMode` 选项自动降级加载低分辨率图片
2026/9/12 6:27:20 网站建设 项目流程

Kingfisher 低数据模式(Low Data Mode)适配指南:用.lowDataMode选项自动降级加载低分辨率图片

【免费下载链接】KingfisherA lightweight, pure-Swift library for downloading and caching images from the web.项目地址: https://gitcode.com/GitHub_Trending/ki/Kingfisher

导读

自 iOS 13 起,Apple 为系统加入了「低数据模式」(Low Data Mode),允许用户在蜂窝网络与 Wi-Fi 环境下主动限制数据流量消耗。Kingfisher 针对该场景提供了开箱即用的支持:通过为图片加载请求指定一个「低数据模式替代源」(通常是低分辨率版本或本地占位图),Kingfisher 会在系统限制数据访问时自动切换加载目标,从而在保证图片功能可用的前提下显著节省流量。读完本文,你将掌握.lowDataMode选项的完整用法、底层切换原理,以及如何在 SwiftUI 与 UIKit 中落地这一能力。

什么是低数据模式(Low Data Mode)

低数据模式是 iOS 13 及后续系统版本(macOS 10.15、watchOS 6.0、tvOS 13.0 起同样适用)提供的一种系统级流量控制开关。用户在系统设置中开启后,系统会倾向于减少后台与前台的数据传输,部分请求甚至会被系统直接以「受限」原因拦截。

对图片类 App 而言,低数据模式最常见的表现是:高清大图请求被系统判定为「受限网络访问」而失败(对应的URLErrornetworkUnavailableReason.constrained)。如果 App 不做任何处理,用户会直接看到图片加载失败。Kingfisher 的解决方案是:为每个图片加载任务额外准备一个「低数据模式源」,在受限失败发生时自动降级,而不是让用户面对一个加载失败的图片占位符。

核心 API:.lowDataMode选项

低数据模式支持在 Kingfisher 中通过KingfisherOptionsInfoItem的一个 case 开启,其定义位于 Sources/General/KingfisherOptionsInfo.swift:

/// Specifies the `Source` to load when the user enables Low Data Mode and the original source fails due to the data /// constraint. case lowDataMode(Source?)

它的语义非常明确:

  • 关联值是一个可空的Source。传入Source时,Kingfisher 会为原始请求设置allowsConstrainedNetworkAccess = false(即不允许受限网络访问),当原始请求因受限失败后,改用该Source重新发起加载;
  • 传入nil或完全不设置该选项时,Kingfisher 会忽略设备的低数据模式设置,完全按照系统默认行为加载原始源。

在链式 API 中,KFOptionsSetter提供了对应的便捷方法lowDataModeSource(_:)(见 Sources/General/KFOptionsSetter.swift):

public func lowDataModeSource(_ source: Source?) -> Self { options.lowDataModeSource = source return self }

命名说明:部分文档示例中使用.lowDataSource(...)写法,而当前仓库源码中该选项的实际 case 名为.lowDataMode(Source?)(见上述KingfisherOptionsInfoItem定义与 解析逻辑)。本文示例一律采用与源码一致的可编译写法,请以.lowDataMode为准。

实战一:网络低分辨率 URL 作为降级源

最典型的使用场景是:正常状态下加载高清大图,低数据模式下自动切换为服务器上的低分辨率版本。核心代码如下:

imageView.kf.setImage( with: highResolutionURL, options: [ .lowDataMode(.network(lowResolutionURL)) ] )

其行为过程是:

  1. 设备未开启低数据模式时,直接使用highResolutionURL加载图片;
  2. 设备处于低数据模式,且highResolutionURL未命中缓存(此时请求被系统以.constrained原因拦截)时,Kingfisher 自动改用lowResolutionURL加载低分辨率版本以节省流量;
  3. highResolutionURL命中了缓存,则仍然直接使用缓存结果,不会触发降级加载。

需要注意的是:降级源同样会参与 Kingfisher 的缓存体系,因此首次在低数据模式下加载成功后,后续再次进入该模式时可能直接命中缓存,连低分辨率请求都无需再次发出。

实战二:本地图片 Provider 作为降级源

.lowDataMode的关联值是Source而非单纯的 URL,这意味着你可以传入任意满足Source协议的来源,包括各类ImageDataProvider。这样在低数据模式下可以完全避免发起任何网络下载,直接使用本地存储的图片(如内置占位图、离线包中的缩略图),这对于弱网或完全离线场景尤为实用:

imageView.kf.setImage( with: highResolutionURL, options: [ .lowDataMode( .provider(LocalFileImageDataProvider(fileURL: localFileURL)) ) ] )

LocalFileImageDataProvider定义于 Sources/General/ImageSource/ImageDataProvider.swift,其初始化参数为:

  • fileURL:本地图片文件 URL(必填);
  • cacheKey:缓存键,默认取fileURLabsoluteString
  • loadingQueue:文件读取执行的队列,默认使用DispatchQueue.global(qos: .userInitiated)

与直接使用UIImage(contentsOfFile:)不同,经由LocalFileImageDataProvider加载的本地图片同样会走完 Kingfisher 的完整管线——包括应用ImageProcessor处理器、存入ImageCache缓存等,因此可以获得与网络图一致的后续处理能力。

顺带一提,仓库还提供了Base64ImageDataProvider等其它内置 Provider(见 ImageDataProvider.swift),同样可以作为降级源传入。

底层原理:受限请求如何被识别并切换

低数据模式的整个降级链路由三处源码共同实现,理解它们有助于排查问题:

1. 请求阶段:关闭受限网络访问

在 Sources/Networking/ImageDownloader.swift 中,当用户设置了lowDataModeSource且系统版本支持时,Kingfisher 会显式声明原始请求不允许受限网络访问:

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 }

allowsConstrainedNetworkAccess = false意味着当系统处于低数据模式时,该请求会被系统判定为「受约束」而失败,从而让 Kingfisher 有机会感知到低数据模式状态,而不是静默使用系统默认行为。

2. 错误识别阶段:判断是否为受限失败

在 Sources/General/KingfisherError.swift 中,Kingfisher 封装了对「受限」错误的判定:

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 }

只有当底层URLErrornetworkUnavailableReason恰好等于.constrained时,才会被判定为低数据模式受限失败,进而触发降级逻辑——其它类型的网络错误不会误入降级分支。

3. 切换阶段:用降级源重新发起加载

在 Sources/General/KingfisherManager.swift 中,failCurrentSource对受限失败做了特殊处理——它优先于alternativeSources(备用源列表)逻辑执行:

// 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, retryContext: retryContext, downloadTaskUpdated: downloadTaskUpdated) } else { // This should not happen. completionHandler?(.failure(error)) } return }

这段代码有两个值得注意的细节:

  • 受限失败时,Kingfisher 会取走并清空lowDataModeSource后重新发起加载,确保降级源自身加载失败时不会再陷入递归切换;
  • 降级源加载失败后,Kingfisher 仍会继续走alternativeSources等后续逻辑,因此该特性可以与alternativeSourcesretryStrategy等选项共存组合使用。

测试验证:降级行为有据可依

Kingfisher 仓库在 Tests/KingfisherTests/ImageViewExtensionTests.swift 中提供了testLowDataModeSource测试用例,完整覆盖了降级链路:

@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) @MainActor func testLowDataModeSource() { let exp = expectation(description: #function) let url = testURLs[0] stub(url, data: testImageData) // Stub a failure of `.constrained`. It is what happens when an image downloading fails when low data mode on. let brokenURL = testURLs[1] let error = URLError( .notConnectedToInternet, userInfo: [NSURLErrorNetworkUnavailableReasonKey: URLError.NetworkUnavailableReason.constrained.rawValue] ) stub(brokenURL, error: error) imageView.kf.setImage(with: .network(brokenURL), options: [.lowDataMode(.network(url))]) { result in XCTAssertNotNil(result.value) XCTAssertEqual(result.value?.source.url, url) XCTAssertEqual(result.value?.originalSource.url, brokenURL) exp.fulfill() } waitForExpectations(timeout: 1, handler: nil) }

该测试通过伪造一个networkUnavailableReason == .constrainedURLError来模拟低数据模式受限失败,随后断言:加载最终成功、实际图片来自降级源url、而originalSource仍是原始地址brokenURL。这一断言同时印证了「降级后原始来源信息仍可通过originalSource追溯」的行为。

注意事项与默认行为

  • 未设置选项时的默认行为:如果不指定.lowDataMode选项,无论设备是否开启低数据模式,Kingfisher 都会按照系统默认行为加载原始源,不会主动降级,也不会因受限失败自动重试;
  • 版本可用性:该特性依赖系统对allowsConstrainedNetworkAccessURLError.NetworkUnavailableReason的支持,仅适用于 macOS 10.15 / iOS 13.0 / watchOS 6.0 / tvOS 13.0 及以上的系统版本,代码内部已通过#available做兼容保护;
  • 与备用源的区别alternativeSources是「原始源失败后按顺序尝试其它源」的通用机制,而.lowDataMode专门针对受限网络失败,且具有更高优先级(见 KingfisherManager.swift 的注释);
  • 降级源会进入缓存:低分辨率降级图加载成功后同样写入 Kingfisher 缓存,可配合transitionprocessor等既有选项一起使用,实现平滑过渡显示。

小结

Kingfisher 的低数据模式支持,用一句话概括就是:给图片加载任务多准备一条「省流量退路」。通过imageView.kf.setImage(with:options:)中的.lowDataMode(.network(...)).lowDataMode(.provider(...)),即可在系统级流量限制下自动切换低分辨率网络图或本地图,既保护了用户流量,也避免了图片加载失败的糟糕体验。其背后由ImageDownloader的请求属性设置、KingfisherError的错误分类与KingfisherManager的降级切换三部分协同完成,并有官方测试用例背书,可以放心在生产环境中使用。

【免费下载链接】KingfisherA lightweight, pure-Swift library for downloading and caching images from the web.项目地址: https://gitcode.com/GitHub_Trending/ki/Kingfisher

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询