Skip to content

Commit

Permalink
Merge pull request onevcat#1373 from onevcat/feature/customizable-def…
Browse files Browse the repository at this point in the history
…ault-cache-serializer

Feature customizable default cache serializer
  • Loading branch information
onevcat authored Dec 23, 2019
2 parents 29019b2 + 03ab86d commit 08136c7
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 5 deletions.
32 changes: 29 additions & 3 deletions Sources/Cache/CacheSerializer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
// THE SOFTWARE.

import Foundation
import CoreGraphics

/// An `CacheSerializer` is used to convert some data to an image object after
/// retrieving it from disk storage, and vice versa, to convert an image to data object
Expand Down Expand Up @@ -80,8 +81,22 @@ public struct DefaultCacheSerializer: CacheSerializer {

/// The default general cache serializer used across Kingfisher's cache.
public static let `default` = DefaultCacheSerializer()
private init() {}


/// The compression quality when converting image to a lossy format data. Default is 1.0.
public var compressionQuality: CGFloat = 1.0

/// Whether the original data should be preferred when serializing the image.
/// If `true`, the input original data will be checked first and used unless the data is `nil`.
/// In that case, the serialization will fall back to creating data from image.
public var preferCacheOriginalData: Bool = false

/// Creates a cache serializer that serialize and deserialize images in PNG, JPEG and GIF format.
///
/// - Note:
/// Use `DefaultCacheSerializer.default` unless you need to specify your own properties.
///
public init() { }

/// - Parameters:
/// - image: The image needed to be serialized.
/// - original: The original data which is just downloaded.
Expand All @@ -95,7 +110,18 @@ public struct DefaultCacheSerializer: CacheSerializer {
/// converted to the corresponding data type. Otherwise, if the `original` is provided but it is not
/// If `original` is `nil`, the input `image` will be encoded as PNG data.
public func data(with image: KFCrossPlatformImage, original: Data?) -> Data? {
return image.kf.data(format: original?.kf.imageFormat ?? .unknown)
if preferCacheOriginalData {
return original ??
image.kf.data(
format: original?.kf.imageFormat ?? .unknown,
compressionQuality: compressionQuality
)
} else {
return image.kf.data(
format: original?.kf.imageFormat ?? .unknown,
compressionQuality: compressionQuality
)
}
}

/// Gets an image deserialized from provided data.
Expand Down
11 changes: 9 additions & 2 deletions Sources/Image/Image.swift
Original file line number Diff line number Diff line change
Expand Up @@ -234,13 +234,20 @@ extension KingfisherWrapper where Base: KFCrossPlatformImage {
///
/// - Parameter format: The format in which the output data should be. If `unknown`, the `base` image will be
/// converted in the PNG representation.
///
/// - Returns: The output data representing.
public func data(format: ImageFormat) -> Data? {

/// Returns a data representation for `base` image, with the `format` as the format indicator.
/// - Parameters:
/// - format: The format in which the output data should be. If `unknown`, the `base` image will be
/// converted in the PNG representation.
/// - compressionQuality: The compression quality when converting image to a lossy format data.
public func data(format: ImageFormat, compressionQuality: CGFloat = 1.0) -> Data? {
return autoreleasepool { () -> Data? in
let data: Data?
switch format {
case .PNG: data = pngRepresentation()
case .JPEG: data = jpegRepresentation(compressionQuality: 1.0)
case .JPEG: data = jpegRepresentation(compressionQuality: compressionQuality)
case .GIF: data = gifRepresentation()
case .unknown: data = normalized.kf.pngRepresentation()
}
Expand Down
51 changes: 51 additions & 0 deletions Tests/KingfisherTests/KingfisherManagerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -917,6 +917,57 @@ class KingfisherManagerTests: XCTestCase {
coordinator.apply(.cachingOriginalImage) { called = true }
XCTAssertEqual(coordinator.state, .done)
}

func testCanUseCustomizeDefaultCacheSerializer() {
let exp = expectation(description: #function)
let url = testURLs[0]

var cacheSerializer = DefaultCacheSerializer()
cacheSerializer.preferCacheOriginalData = true

manager.cache.store(
testImage,
original: testImageData,
forKey: url.cacheKey,
processorIdentifier: DefaultImageProcessor.default.identifier,
cacheSerializer: cacheSerializer, toDisk: true) {
result in

let computedKey = url.cacheKey.computedKey(with: DefaultImageProcessor.default.identifier)
let fileURL = self.manager.cache.diskStorage.cacheFileURL(forKey: computedKey)
let data = try! Data(contentsOf: fileURL)
XCTAssertEqual(data, testImageData)

exp.fulfill()
}
waitForExpectations(timeout: 1.0)
}

func testCanUseCustomizeDefaultCacheSerializerStoreEncoded() {
let exp = expectation(description: #function)
let url = testURLs[0]

var cacheSerializer = DefaultCacheSerializer()
cacheSerializer.compressionQuality = 0.8

manager.cache.store(
testImage,
original: testImageJEPGData,
forKey: url.cacheKey,
processorIdentifier: DefaultImageProcessor.default.identifier,
cacheSerializer: cacheSerializer, toDisk: true) {
result in

let computedKey = url.cacheKey.computedKey(with: DefaultImageProcessor.default.identifier)
let fileURL = self.manager.cache.diskStorage.cacheFileURL(forKey: computedKey)
let data = try! Data(contentsOf: fileURL)
XCTAssertNotEqual(data, testImageJEPGData)
XCTAssertEqual(data, testImage.kf.jpegRepresentation(compressionQuality: 0.8))

exp.fulfill()
}
waitForExpectations(timeout: 1.0)
}
}

class SimpleProcessor: ImageProcessor {
Expand Down

0 comments on commit 08136c7

Please sign in to comment.