Skip to content

Instantly share code, notes, and snippets.

@tjbarber
Last active March 7, 2021 22:20
Show Gist options
  • Save tjbarber/7e2e2a61f272a8d94d50aa2b139289d5 to your computer and use it in GitHub Desktop.
Save tjbarber/7e2e2a61f272a8d94d50aa2b139289d5 to your computer and use it in GitHub Desktop.
Simple Image Downloading/Caching Service
//
// ImageCachingService.swift
// Exuberant
//
// Created by TJ Barber on 12/20/17.
// Copyright © 2017 Thomas J. Barber. All rights reserved.
//
import UIKit
enum ImageCachingError: Error {
case cannotEncodeString
case documentsDirectoryDoesNotExist
case newLocationDoesNotExist
case couldNotMoveFile
case couldNotOpenImageFile
}
class ImageCachingService: NSObject {
static let sharedInstance = ImageCachingService()
let sessionConfiguration = URLSessionConfiguration.default
let cache = NSCache<NSString, UIImage>()
let fileManager = FileManager.default
func downloadAndCacheImage(from url: URL, completion: @escaping (UIImage?, Error?) -> Void) {
guard let encodedAbsoluteString = encodeString(url.absoluteString) else {
completion(nil, ImageCachingError.cannotEncodeString)
return
}
let request = URLRequest(url: url, cachePolicy: .returnCacheDataElseLoad, timeoutInterval: 60.0)
let session = URLSession(configuration: sessionConfiguration)
let downloadTask = session.downloadTask(with: request) { location, response, error in
if let error = error {
completion(nil, error)
return
}
guard let location = location else { completion(nil, ImageCachingError.newLocationDoesNotExist); return }
guard let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { completion(nil, ImageCachingError.documentsDirectoryDoesNotExist); return }
let filePath = documentsDirectory.appendingPathComponent(encodedAbsoluteString)
do {
try self.fileManager.moveItem(at: location, to: filePath)
guard let imageData = self.fileManager.contents(atPath: filePath.absoluteString) else { completion(nil, ImageCachingError.couldNotOpenImageFile); return }
guard let image = UIImage(data: imageData) else { completion(nil, ImageCachingError.couldNotOpenImageFile); return }
completion(image, nil)
} catch (let e) {
print(e)
completion(nil, ImageCachingError.couldNotMoveFile)
}
}
downloadTask.resume()
}
func getImageWith(url: URL, completion: @escaping (UIImage?, Error?) -> Void) {
guard let encodedAbsoluteString = encodeString(url.absoluteString) else {
completion(nil, ImageCachingError.cannotEncodeString)
return
}
let urlString = NSString(string: encodedAbsoluteString)
if let image = self.cache.object(forKey: urlString) {
// We have the image in memory
print("loading file from cache...")
completion(image, nil)
return
}
guard let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { completion(nil, ImageCachingError.documentsDirectoryDoesNotExist); return }
let filePath = documentsDirectory.appendingPathComponent(encodedAbsoluteString)
if fileManager.fileExists(atPath: filePath.path) {
print("loading file from disk...")
guard let imageData = self.fileManager.contents(atPath: filePath.path) else { completion(nil, ImageCachingError.couldNotOpenImageFile); return }
guard let image = UIImage(data: imageData) else { completion(nil, ImageCachingError.couldNotOpenImageFile); return }
self.cache.setObject(image, forKey: urlString)
completion(image, nil)
return
}
print("downloading file...")
self.downloadAndCacheImage(from: url) { image, error in
if let error = error {
completion(nil, error)
return
}
guard let image = image else { fatalError("No error and no image") }
self.cache.setObject(image, forKey: urlString)
completion(image, nil)
}
}
func encodeString(_ string: String) -> String? {
return string.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlHostAllowed)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment