Last active
March 7, 2021 22:20
-
-
Save tjbarber/7e2e2a61f272a8d94d50aa2b139289d5 to your computer and use it in GitHub Desktop.
Simple Image Downloading/Caching Service
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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