make resolver configurable
This commit is contained in:
@@ -12,55 +12,59 @@ class AssetRequest: Request {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class AssetResolver {
|
class AssetResolver {
|
||||||
private static let requestQueue = DispatchQueue(label: "assets.requests", qos: .userInitiated)
|
private let requestQueue: DispatchQueue
|
||||||
private static let processingQueue = DispatchQueue(label: "assets.processing", qos: .userInitiated)
|
private let processingQueue: DispatchQueue
|
||||||
|
|
||||||
private static var batchTimer: DispatchWorkItem?
|
private var batchTimer: DispatchWorkItem?
|
||||||
private static let batchLock = NSLock()
|
private let batchLock = NSLock()
|
||||||
private static let batchTimeout: TimeInterval = 0.00025 // 250μs
|
private let batchTimeout: TimeInterval
|
||||||
|
|
||||||
private static let fetchOptions = {
|
private let fetchOptions: PHFetchOptions
|
||||||
let fetchOptions = PHFetchOptions()
|
private var assetRequests = [AssetRequest]()
|
||||||
fetchOptions.wantsIncrementalChangeDetails = false
|
private let assetCache: NSCache<NSString, PHAsset>
|
||||||
return fetchOptions
|
|
||||||
}()
|
|
||||||
private static var assetRequests = [AssetRequest]()
|
|
||||||
private static let assetCache = {
|
|
||||||
let assetCache = NSCache<NSString, PHAsset>()
|
|
||||||
assetCache.countLimit = 10000
|
|
||||||
return assetCache
|
|
||||||
}()
|
|
||||||
|
|
||||||
static func requestAsset(request: AssetRequest) {
|
init(fetchOptions: PHFetchOptions, batchTimeout: TimeInterval = 0.00025, cacheSize: Int = 10000, qos: DispatchQoS = .unspecified) {
|
||||||
|
self.fetchOptions = fetchOptions
|
||||||
|
self.batchTimeout = batchTimeout
|
||||||
|
self.assetCache = {
|
||||||
|
let assetCache = NSCache<NSString, PHAsset>()
|
||||||
|
assetCache.countLimit = cacheSize
|
||||||
|
return assetCache
|
||||||
|
}()
|
||||||
|
self.requestQueue = DispatchQueue(label: "assets.requests", qos: qos)
|
||||||
|
self.processingQueue = DispatchQueue(label: "assets.processing", qos: qos)
|
||||||
|
}
|
||||||
|
|
||||||
|
func requestAsset(request: AssetRequest) {
|
||||||
requestQueue.async {
|
requestQueue.async {
|
||||||
if (request.isCancelled) {
|
if (request.isCancelled) {
|
||||||
request.completion(nil)
|
request.completion(nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if let cachedAsset = assetCache.object(forKey: request.assetId as NSString) {
|
if let cachedAsset = self.assetCache.object(forKey: request.assetId as NSString) {
|
||||||
request.completion(cachedAsset)
|
request.completion(cachedAsset)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
batchLock.lock()
|
self.batchLock.lock()
|
||||||
if (request.isCancelled) {
|
if (request.isCancelled) {
|
||||||
batchLock.unlock()
|
self.batchLock.unlock()
|
||||||
request.completion(nil)
|
request.completion(nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
assetRequests.append(request)
|
self.assetRequests.append(request)
|
||||||
|
|
||||||
batchTimer?.cancel()
|
self.batchTimer?.cancel()
|
||||||
let timer = DispatchWorkItem(block: processBatch)
|
let timer = DispatchWorkItem(block: self.processBatch)
|
||||||
batchTimer = timer
|
self.batchTimer = timer
|
||||||
batchLock.unlock()
|
self.batchLock.unlock()
|
||||||
processingQueue.asyncAfter(deadline: .now() + batchTimeout, execute: timer)
|
self.processingQueue.asyncAfter(deadline: .now() + self.batchTimeout, execute: timer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func processBatch() {
|
private func processBatch() {
|
||||||
batchLock.lock()
|
batchLock.lock()
|
||||||
if assetRequests.isEmpty {
|
if assetRequests.isEmpty {
|
||||||
batchLock.unlock()
|
batchLock.unlock()
|
||||||
@@ -90,13 +94,13 @@ class AssetResolver {
|
|||||||
|
|
||||||
guard !activeAssetIds.isEmpty else { return }
|
guard !activeAssetIds.isEmpty else { return }
|
||||||
|
|
||||||
let assets = PHAsset.fetchAssets(withLocalIdentifiers: activeAssetIds, options: Self.fetchOptions)
|
let assets = PHAsset.fetchAssets(withLocalIdentifiers: activeAssetIds, options: self.fetchOptions)
|
||||||
assets.enumerateObjects { asset, _, _ in
|
assets.enumerateObjects { asset, _, _ in
|
||||||
let assetId = asset.localIdentifier
|
let assetId = asset.localIdentifier
|
||||||
for completion in completionMap.removeValue(forKey: assetId)! {
|
for completion in completionMap.removeValue(forKey: assetId)! {
|
||||||
completion(asset)
|
completion(asset)
|
||||||
}
|
}
|
||||||
requestQueue.async { assetCache.setObject(asset, forKey: assetId as NSString) }
|
self.requestQueue.async { self.assetCache.setObject(asset, forKey: assetId as NSString) }
|
||||||
}
|
}
|
||||||
|
|
||||||
for completions in completionMap.values {
|
for completions in completionMap.values {
|
||||||
|
|||||||
@@ -15,6 +15,11 @@ class ThumbnailRequest: Request {
|
|||||||
|
|
||||||
class ThumbnailResolver: ThumbnailApi {
|
class ThumbnailResolver: ThumbnailApi {
|
||||||
private static let imageManager = PHImageManager.default()
|
private static let imageManager = PHImageManager.default()
|
||||||
|
private static let assetResolver = AssetResolver(fetchOptions: {
|
||||||
|
let fetchOptions = PHFetchOptions()
|
||||||
|
fetchOptions.wantsIncrementalChangeDetails = false
|
||||||
|
return fetchOptions
|
||||||
|
}(), qos: .userInitiated)
|
||||||
private static let requestOptions = {
|
private static let requestOptions = {
|
||||||
let requestOptions = PHImageRequestOptions()
|
let requestOptions = PHImageRequestOptions()
|
||||||
requestOptions.isNetworkAccessAllowed = true
|
requestOptions.isNetworkAccessAllowed = true
|
||||||
@@ -67,7 +72,7 @@ class ThumbnailResolver: ThumbnailApi {
|
|||||||
func requestImage(assetId: String, requestId: Int64, width: Int64, height: Int64, isVideo: Bool, completion: @escaping (Result<[String: Int64], any Error>) -> Void) {
|
func requestImage(assetId: String, requestId: Int64, width: Int64, height: Int64, isVideo: Bool, completion: @escaping (Result<[String: Int64], any Error>) -> Void) {
|
||||||
let cancellationToken = CancellationToken()
|
let cancellationToken = CancellationToken()
|
||||||
let thumbnailRequest = ThumbnailRequest(cancellationToken: cancellationToken, completion: completion)
|
let thumbnailRequest = ThumbnailRequest(cancellationToken: cancellationToken, completion: completion)
|
||||||
AssetResolver.requestAsset(request: AssetRequest(cancellationToken: cancellationToken, assetId: assetId) { asset in
|
Self.assetResolver.requestAsset(request: AssetRequest(cancellationToken: cancellationToken, assetId: assetId) { asset in
|
||||||
if cancellationToken.isCancelled {
|
if cancellationToken.isCancelled {
|
||||||
return completion(Self.cancelledResult)
|
return completion(Self.cancelledResult)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user