Skip to content

ChrishonWyllie/Celestial

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Celestial

CI Status Version License Platform

Celestial is an in-app cache manager that allows you to easily cache both videos and images. You can use built-in UIViews URLImageView and URLVideoPlayerView to quickly display cached images and videos respectively. These two UIView classes provide flexible options such as determing where the cached image or video will be stored: in memory or in the local file system.



In this small demonstration, scrolling through requires each image to constantly be re-fetched, which results in redundant API calls and UI issues with flickering cells

Table of Contents


  • Xcode 8.0 or higher
  • iOS 10.0 or higher

Celestial is available through CocoaPods. To install it, simply add the following line to your Podfile:

pod 'Celestial'

For caching images, use the URLImageView which is a subclass of the default UIImageView . The URLImageView can be initialized with a URL pointing to the image or this image can be manually loaded.

The first initializer accepts a sourceURLString: String which is the absoluteString of the URL at which the image file is located. Both initializers share 2 arguments:

  • delegate: The URLCachableViewDelegate notifies the receiver of events such as download completion, progress, errors, etc.
  • cacheLocation: The ResourceCacheLocation determines where the downloaded image will be stored upon completion. By default, images are stored inMemory. Images or videos stored with this setting are not persisted across app sessions and are subject to automatic removal by the system if memory is strained. Storing with .fileSystem will persist the images across app sessions until manually deleted. However, for images, caching to memory is often sufficient. Set to .none for no caching
import Celestial

let sourceURLString = <your URL string>
let imageView = URLImageView(sourceURLString: sourceURLString, delegate: nil, cacheLocation: .inMemory)

// Will automatically load the image from the sourceURLString, and cache the downloaded image
// in a local NSCache, for reuse later



Caching images in UICollectionViewCells and UITableViewCells is slightly different. In such cases, the URLImageView needs to be initialized first and the urlString will likely be provided some time later as the cell is dequeued.

In such cases, use the func loadImageFrom(urlString:) function:

struct CellModel {
    let urlString: String
}

class ImageCell: UICollectionViewCell {

    // Initialize the URLImageView within the cell as a variable.
    // NOTE: The second initializer is used which does NOT have the urlString argument.
    private var imageView: URLImageView = {
        let img = URLImageView(delegate: nil, cacheLocation: .inMemory)
        img.translatesAutoresizingMaskIntoConstraints = false
        return img
    }()
    
    
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        addSubview(imageView)
        
        // Handle layout...
    }
    
    // This function is called during the cell dequeue process and will load the image
    // using the `CellModel` struct. However, this would be replaced with your method.
    public func configureCell(someCellModel: CellModel) {
        imageView.loadImageFrom(urlString: someCellModel.urlString)
    }

}

There are three possible events that occur when downloading images and videos:

  • The download completes successfully
  • The download is currently in progress
  • An error occurred whil downloading the image or video

Both URLImageView and URLVideoPlayerView offer different ways to observe these events:

With delegation:

Extend the URLCachableViewDelegate to be notified of such events

let sourceURLString = <your URL string>
let imageView = URLImageView(sourceURLString: sourceURLString, delegate: self, cacheLocation: .inMemory)

...


extension ViewController: URLCachableViewDelegate {
    
    func urlCachableView(_ view: URLCachableView, didFinishDownloading media: Any) {
        // Image has finished downloading and will be cached to specified cache location
    }
    
    func urlCachableView(_ view: URLCachableView, downloadFailedWith error: Error) {
        // Investigate download error
    }
    
    func urlCachableView(_ view: URLCachableView, downloadProgress progress: Float, humanReadableProgress: String) {
        // Update UI with download progress if necessary
    }
}

With completion blocks

The other option is receive these events using in-line completion blocks

let imageView = URLImageView(delegate: nil, cacheLocation: .inMemory, defaultImage: nil)

let sourceURLString = <your URL string>

imageView.loadImageFrom(urlString: sourceURLString,
progressHandler: { (progress) in
    print("current downlod progress: \(progress)")
}, completion: {
    print("Image has finished loading")
}) { (error) in
    print("Error loading image: \(error)")
}



Similar to caching and displaying images, the URLVideoPlayerView will display videos and cache them later for reuse. It encapsulates the usually tedious process or creating instances of AVAsset, AVPlayerItem, AVPlayerLayer and AVPlayer . Instead, all you need to do is provide a URL for it play.

If you have read the Caching Images section, the initializers and functions are virtually identical between URLImageView and URLVideoPlayerView

let sourceURLString = <your URL string>
let videoView = URLVideoPlyerView(sourceURLString: sourceURLString, delegate: nil, cacheLocation: .fileSystem)

In this example, the video will be played and cached to the local file system. NOTE Caching to the local system will persist the image or video across app sessions until manually deleted.

Caching videos in cells

As previously mentioned, the functions provided in URLVideoPlayerView are virtually identical to those of URLImageView

public func configureCell(someCellModel: CellModel) {
    playerView.loadVideoFrom(urlString: someCellModel.urlString)
}

struct CellModel {
    let urlString: String
}

class VideoCell: UICollectionViewCell {

    private var playerView: URLVideoPlayerView = {
        // Observe events with delegation...
        let v = URLVideoPlayerView(delegate: self, cacheLocation: .fileSystem)
        v.translatesAutoresizingMaskIntoConstraints = false
        v.playerLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
        return v
    }()
    
    
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        addSubview(playerView)
        
        // Handle layout...
    }
    
    // This function is called during the cell dequeue process and will load the image
    // using the `CellModel` struct. However, this would be replaced with your method.
    public func configureCell(someCellModel: CellModel) {
        
        // Or with completion handlers...
        
        playerView.loadVideoFrom(urlString: someCellModel.urlString, 
        progressHandler: { (progress) in
            print("current downlod progress: \(progress)")
        }, completion: {
            print("Video has finished loading")
        }) { (error) in
            print("Error loading video")=
        }
    }
}

Check out the full documentation here

To run the example project, clone the repo, and run pod install from the Example directory first.

Author

ChrishonWyllie, chrishon595@yahoo.com

License

Celestial is available under the MIT license. See the LICENSE file for more info.