Giter Site home page Giter Site logo

hanekeswift's Introduction

Haneke

Carthage compatible SwiftPM compatible Accio supported Platform Build Status Join the chat at https://gitter.im/Haneke/HanekeSwift

Haneke is a lightweight generic cache for iOS and tvOS written in Swift 4. It's designed to be super-simple to use. Here's how you would initalize a JSON cache and fetch objects from a url:

let cache = Cache<JSON>(name: "github")
let URL = NSURL(string: "https://api.github.com/users/haneke")!

cache.fetch(URL: URL).onSuccess { JSON in
    print(JSON.dictionary?["bio"])
}

Haneke provides a memory and LRU disk cache for UIImage, NSData, JSON, String or any other type that can be read or written as data.

Particularly, Haneke excels at working with images. It includes a zero-config image cache with automatic resizing. Everything is done in background, allowing for fast, responsive scrolling. Asking Haneke to load, resize, cache and display an appropriately sized image is as simple as:

imageView.hnk_setImageFromURL(url)

Really.

Features

  • Generic cache with out-of-the-box support for UIImage, NSData, JSON and String
  • First-level memory cache using NSCache
  • Second-level LRU disk cache using the file system
  • Asynchronous fetching of original values from network or disk
  • All disk access is performed in background
  • Thread-safe
  • Automatic cache eviction on memory warnings or disk capacity reached
  • Comprehensive unit tests
  • Extensible by defining custom formats, supporting additional types or implementing custom fetchers

For images:

  • Zero-config UIImageView and UIButton extensions to use the cache, optimized for UITableView and UICollectionView cell reuse
  • Background image resizing and decompression

Installation

Using CocoaPods:

use_frameworks!
pod 'HanekeSwift'

Using Carthage:

github "Haneke/HanekeSwift"

Using SwiftPM or Accio:

.package(url: "https://github.com/Haneke/HanekeSwift.git", .upToNextMajor(from: "0.11.2")),

Then link Haneke in your App target like so:

.target(
    name: "App",
    dependencies: [
        "Haneke",
    ]
),

Manually:

  1. Drag Haneke.xcodeproj to your project in the Project Navigator.
  2. Select your project and then your app target. Open the Build Phases panel.
  3. Expand the Target Dependencies group, and add Haneke.framework.
  4. Click on the + button at the top left of the panel and select New Copy Files Phase. Set Destination to Frameworks, and add Haneke.framework.
  5. import Haneke whenever you want to use Haneke.

Requirements

  • iOS 8.0+ or tvOS 9.1+
  • Swift 4

Using the cache

Haneke provides shared caches for UIImage, NSData, JSON and String. You can also create your own caches.

The cache is a key-value store. For example, here's how you would cache and then fetch some data.

let cache = Shared.dataCache

cache.set(value: data, key: "funny-games.mp4")

// Eventually...

cache.fetch(key: "funny-games.mp4").onSuccess { data in
    // Do something with data
}

In most cases the value will not be readily available and will have to be fetched from network or disk. Haneke offers convenience fetch functions for these cases. Let's go back to the first example, now using a shared cache:

let cache = Shared.JSONCache
let URL = NSURL(string: "https://api.github.com/users/haneke")!

cache.fetch(URL: URL).onSuccess { JSON in
   print(JSON.dictionary?["bio"])
}

The above call will first attempt to fetch the required JSON from (in order) memory, disk or NSURLCache. If not available, Haneke will fetch the JSON from the source, return it and then cache it. In this case, the URL itself is used as the key.

Further customization can be achieved by using formats, supporting additional types or implementing custom fetchers.

Extra ♡ for images

Need to cache and display images? Haneke provides convenience methods for UIImageView and UIButton with optimizations for UITableView and UICollectionView cell reuse. Images will be resized appropriately and cached in a shared cache.

// Setting a remote image
imageView.hnk_setImageFromURL(url)

// Setting an image manually. Requires you to provide a key.
imageView.hnk_setImage(image, key: key)

The above lines take care of:

  1. If cached, retrieving an appropriately sized image (based on the bounds and contentMode of the UIImageView) from the memory or disk cache. Disk access is performed in background.
  2. If not cached, loading the original image from web/memory and producing an appropriately sized image, both in background. Remote images will be retrieved from the shared NSURLCache if available.
  3. Setting the image and animating the change if appropriate.
  4. Or doing nothing if the UIImageView was reused during any of the above steps.
  5. Caching the resulting image.
  6. If needed, evicting the least recently used images in the cache.

Formats

Formats allow to specify the disk cache size and any transformations to the values before being cached. For example, the UIImageView extension uses a format that resizes images to fit or fill the image view as needed.

You can also use custom formats. Say you want to limit the disk capacity for icons to 10MB and apply rounded corners to the images. This is how it could look like:

let cache = Shared.imageCache

let iconFormat = Format<UIImage>(name: "icons", diskCapacity: 10 * 1024 * 1024) { image in
    return imageByRoundingCornersOfImage(image)
}
cache.addFormat(iconFormat)

let URL = NSURL(string: "http://haneke.io/icon.png")!
cache.fetch(URL: URL, formatName: "icons").onSuccess { image in
    // image will be a nice rounded icon
}

Because we told the cache to use the "icons" format Haneke will execute the format transformation in background and return the resulting value.

Formats can also be used from the UIKit extensions:

imageView.hnk_setImageFromURL(url, format: iconFormat)

Fetchers

The fetch functions for urls and paths are actually convenience methods. Under the hood Haneke uses fetcher objects. To illustrate, here's another way of fetching from a url by explictly using a network fetcher:

let URL = NSURL(string: "http://haneke.io/icon.png")!
let fetcher = NetworkFetcher<UIImage>(URL: URL)
cache.fetch(fetcher: fetcher).onSuccess { image in
    // Do something with image
}

Fetching an original value from network or disk is an expensive operation. Fetchers act as a proxy for the value, and allow Haneke to perform the fetch operation only if absolutely necessary.

In the above example the fetcher will be executed only if there is no value associated with "http://haneke.io/icon.png" in the memory or disk cache. If that happens, the fetcher will be responsible from fetching the original value, which will then be cached to avoid further network activity.

Haneke provides two specialized fetchers: NetworkFetcher<T> and DiskFetcher<T>. You can also implement your own fetchers by subclassing Fetcher<T>.

Custom fetchers

Through custom fetchers you can fetch original values from other sources than network or disk (e.g., Core Data), or even change how Haneke acceses network or disk (e.g., use Alamofire for networking instead of NSURLSession). A custom fetcher must subclass Fetcher<T> and is responsible for:

  • Providing the key (e.g., NSURL.absoluteString in the case of NetworkFetcher) associated with the value to be fetched
  • Fetching the value in background and calling the success or failure closure accordingly, both in the main queue
  • Cancelling the fetch on demand, if possible

Fetchers are generic, and the only restriction on their type is that it must implement DataConvertible.

Supporting additional types

Haneke can cache any type that can be read and saved as data. This is indicated to Haneke by implementing the protocols DataConvertible and DataRepresentable.

public protocol DataConvertible {
    typealias Result

    class func convertFromData(data:NSData) -> Result?

}

public protocol DataRepresentable {

    func asData() -> NSData!

}

This is how one could add support for NSDictionary:

extension NSDictionary : DataConvertible, DataRepresentable {

    public typealias Result = NSDictionary

    public class func convertFromData(data:NSData) -> Result? {
        return NSKeyedUnarchiver.unarchiveObjectWithData(data) as? NSDictionary
    }

    public func asData() -> NSData! {
        return NSKeyedArchiver.archivedDataWithRootObject(self)
    }

}

Then creating a NSDictionary cache would be as simple as:

let cache = Cache<NSDictionary>(name: "dictionaries")

Roadmap

Haneke Swift is in initial development and its public API should not be considered stable.

License

Copyright 2014 Hermes Pique (@hpique)
                 2014 Joan Romano (@joanromano)
                 2014 Luis Ascorbe (@lascorbe)
                 2014 Oriol Blanc (@oriolblanc)

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

hanekeswift's People

Contributors

aporat avatar banjun avatar cxa avatar dcharbonnier avatar hirohisa avatar hpique avatar jasoncabot-soundout avatar jasonnoahchoi avatar jeehut avatar jeffaburt avatar joanromano avatar keith avatar kevnm67 avatar lanserxt avatar lascorbe avatar marcelofabri avatar oriolblanc avatar paulosotu avatar readmecritic avatar s0meone avatar simonbs avatar tobiasoleary avatar treverj avatar z4r avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

hanekeswift's Issues

Introduce entities

Entities should be objects that know how to fetch an image asynchronously. These can be given to the Cache instead of a key when fetching.

Should we prefix extension functions?

Should we prefix extension functions? E.g., hnk_hasAlpha in the UIImage extension.

On one hand Swift is not dynamic so collisions would generate compiler errors.

On the other hand, I have no idea what would happen if compiled code runs in a future iOS version in which one of our extension methods is added.

Format<T>

Format currently has logic that is specific to images. This will have to go elsewhere. Maybe into something like ImageTransformer : Transformer, of which Format has an instance.

Image scaling modes and upscaling

Support scaling modes by specifying them in formats. In particular: Fill, Aspect fill, Aspect fit, and No scaling.

Additionally, allow formats to define if images can be upscaled or not.

UIImageView format

Allow to associate a format with a UIImageView. Return a format that describes the UIImageView if none is given.

Not happy with the name Entity. Should we rename?

The name Entity does not convey the responsibilities declared by the protocol. It's too vague. Should we rename it?

Ideas:

  • Fetcher: an entity basically fetches and object to be cached
  • Cacheable
  • CacheObject (should be CacheImage, but I'm thinking of Cache being generic)
  • LazyObject

Parent format

Allow formats to have a "parent" format. If an image is not available in the cache, Haneke should attempt to create it from the parent format (if any). This should work recursively.

For example, this is useful when you have a "fullsize" format and a "thumbnail" format. "fullsize" would be the "thumbnail" parent.

This feature is not currently available in the original Haneke.

Shared cache

Singleton cache to be used in UIKit extensions.

Image resizing

Support image resizing by specifying the desired image size in the format.

Images must be resized in background prior to being cached.

Add image formats to Cache

Formats acts as cache buckets, a secondary key for setting/fetching/removing objects from the cache.

For the scope of this issue, formats must have a name which should be also the name of the directory where its images are stored.

In the original Haneke, only Cache knows about formats. Each format has its DiskCache, so DiskCache doesn't know about them.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.