Bibi's DevLog ๐ค๐
[Swift] URL์์ ๋น๋๊ธฐ๋ก ์ด๋ฏธ์ง ๋ค์ด๋ก๋ ๋ฐ ์บ์ฑํ๊ธฐ - NSCache ๋ณธ๋ฌธ
[Swift] URL์์ ๋น๋๊ธฐ๋ก ์ด๋ฏธ์ง ๋ค์ด๋ก๋ ๋ฐ ์บ์ฑํ๊ธฐ - NSCache
๋น๋น bibi 2022. 5. 18. 11:53
์ฐธ๊ณ ํ ๊ณต์ ๋ฌธ์ : https://developer.apple.com/documentation/uikit/views_and_controls/table_views/asynchronously_loading_images_into_table_and_collection_views
ํด์ : ๋น๋๊ธฐ์ ์ผ๋ก ํ ์ด๋ธ๋ทฐ, ์ปฌ๋ ์ ๋ทฐ์ ์ด๋ฏธ์ง ๋ก๋ํ๊ธฐ Asynchronously Loading Images into Table and Collection Views
UICollectionView๋ฅผ ๊ตฌํํ๋ฉฐ ์
๋ง๋ค ์ด๋ฏธ์ง๋ฅผ API์์ ๋ฐ์์ค๋ ์์
์ ํ๋ค.
์คํฌ๋กค์ด ๋ ๋๋ง๋ค ๊ฐ์ ์ด๋ฏธ์ง๋ฅผ ๋ฐ๋ณตํด์ ์์ฒญํด์ผ ํ๋๋ฐ, ์ด ๋ API์ ์ฌ๋ฌ ๋ฒ ๋ฐ๋ณตํด์ ์์ฒญ์ ๋ณด๋ด๊ฒ ๋๋ฏ๋ก ๊ฐ์ ๋์์ ์ฌ๋ฌ ๋ฒ ํ๋ ๋ญ๋น์ด๊ธฐ๋ ํ๊ณ , ์ต์
์ ๊ฒฝ์ฐ API์์ ๋ฐด์ ๋นํ ์๋ ์๋ค๊ณ ์๊ฐํ๋ค.
๊ทธ๋์ ์ด๋ฏธ์ง ์บ์ฑ์ ๋ฐฉ๋ฒ์ ์์๋ณด๊ฒ ๋์๋ค.
ImageCacheManager
์ด๋ฏธ์ง ๋ค์ด๋ก๋ URL์ ํตํด ์ด๋ฏธ์ง๋ฅผ ๋ค์ด๋ฐ๋ ์์ ์ ํจ
- ํด๋ก์ ๋ก ๋น๋๊ธฐ ์ฒ๋ฆฌ : ๋ค์ด๋ก๋๊ฐ ์๋ฃ๋ ๋ ๊น์ง ์ฑ์ด ๋ฉ์ถฐ์๋ ๊ฒ์ ๋ฐฉ์ง
- NSCache๋ฅผ ํ์ฉํด ์ด๋ฏธ์ง ์บ์ฑ : ์ด๋ฏธ์ง ์์ฒญ์ด ์์ ๋ ์บ์์ ์์ผ๋ฉด ๋ค์ด๋ก๋, ์บ์์ ์์ผ๋ฉด ์บ์ฑ๋ ์ด๋ฏธ์ง๋ฅผ ๋ฆฌํด
- ๊ฐ์ ์ด๋ฏธ์ง์ ๋ํ ๋ฐ๋ณต์ ์ธ ์์ฒญ : ๋์ ๋๋ฆฌ๋ฅผ ์ฌ์ฉํด URL์ ๋์๋๋ ์ด๋ฏธ์ง๋ฅผ ์ ์ฅํด ๋๊ณ , ๊ฐ์ ์ด๋ฏธ์ง์ ๋ํ ์ฌ๋ฌ ์์ฒญ์ ํ๊บผ๋ฒ์ ์ด๋ฏธ์ง ๋ฆฌํด
ImageCacheManager
//
// ImageCache.swift
// starbuckst
//
// Created by Bibi on 2022/05/16.
//
import UIKit
import Foundation
public class ImageCacheManager {
public static let publicCacheManager = ImageCacheManager() // ์ฑ๊ธํค?
// ์ฌ์ฉ ์ ImageCache.publicCacheManager.load()์ ๊ฐ์ด ์ฌ์ฉ..
// URL์ ๋ํ ์ด๋ฏธ์ง๋ฅผ ์ ์ฅํ๋ ์บ์ ํ๋กํผํฐ
private let cachedImages = NSCache<NSURL, UIImage>() // key๊ฐ์ผ๋ก ํด๋์ค๋ฅผ ์๊ตฌํ๋ฏ๋ก URL๋์ NSURL์ฌ์ฉ
// ์บ์์ ์ด๋ฏธ์ง๊ฐ ์๋ ๊ฒฝ์ฐ, urlSession์ ํตํด ์ด๋ฏธ์ง๋ฅผ ์ป์ด์ค๊ธฐ ์ํด response๋ฅผ ๋ฐ์ ํ ๊ฒฐ๊ณผ๊ฐ์ ์ ๋ฌ๋ฐ๊ธฐ ์ํด ์ ์ธํ ๋์
๋๋ฆฌ
private var loadingResponses = [NSURL: [(ImageItem, UIImage?) -> Swift.Void]]()
// URL์ ์ธ์๋ก ๋ฐ์ ์บ์๋ ์ด๋ฏธ์ง๋ฅผ ํ๋ํ๋ ๋ฉ์๋
private final func getCachedImage(url: NSURL) -> UIImage? {
return cachedImages.object(forKey: url)
}
// ์ด๋ฏธ์ง๊ฐ ์๋ค๋ฉด ์บ์ฑ๋ ์ด๋ฏธ์ง๋ฅผ ๋ฐํํ๊ณ , ๊ทธ๋ ์ง ์์ผ๋ฉด ๋น๋๊ธฐ์ ์ผ๋ก ์ด๋ฏธ์ง๋ฅผ ๋ก๋ ๋ฐ ์บ์ํฉ๋๋ค.
// image(url:)์ ๊ฒฐ๊ณผ๋ก ์ด๋ฏธ์ง๊ฐ ์๋ค๋ฉด ๊ทธ ์ด๋ฏธ์ง๋ฅผ completion์ ์ ๋ฌ, ์์ผ๋ฉด URLSession์ ํตํด ๋ฐ์์์ completion์ ์ ๋ฌ
final func loadImage(url: NSURL, imageItem: ImageItem, completion: @escaping (ImageItem, UIImage?) -> Swift.Void) {
// ํด๋น ์ด๋ฏธ์ง๊ฐ ์บ์๋์ด ์๋ค๋ฉด ์ฐพ์ ์ด๋ฏธ์ง๋ฅผ ๋ฐํํฉ๋๋ค.
if let cachedImage = getCachedImage(url: url) {
DispatchQueue.main.async { // ์ ๋น๋๊ธฐ?
completion(imageItem, cachedImage)
}
return
}
if loadingResponses[url] != nil { // ํด๋น ์ด๋ฏธ์ง์ ๋ํด ๋ ์ด์์ ์์ฒญ์ด ์๋ ๊ฒฝ์ฐ, loadingResponses์ completion ๋ธ๋ก์ ์ถ๊ฐํฉ๋๋ค.
loadingResponses[url]?.append(completion)
return
} else { // ํด๋น ์ด๋ฏธ์ง์ ๋ํด ์ฒซ ์์ฒญ์ธ ๊ฒฝ์ฐ loadingResponses์ completion ๋ฑ๋ก
loadingResponses[url] = [completion]
}
// (URL๋ก๋ถํฐ) ์ด๋ฏธ์ง๋ฅผ ๋ฐ์์ต๋๋ค.
// URLSession(.ephimeral) : NSCache๋ฅผ ๋ฐ๋ก ์ฌ์ฉํ๋ฏ๋ก URLSession์ ์บ์๋ฅผ ์ฌ์ฉํ์ง ์๊ธฐ ์ํ ์ค์
let urlSession = URLSession(configuration: URLSessionConfiguration.ephemeral)
urlSession.dataTask(with: url as URL) { (data, response, error) in
// error์ data๋ฅผ ์ฒดํฌํ๊ณ ์ด๋ฏธ์ง ์์ฑ์ ์๋ํฉ๋๋ค.
guard let responseData = data,
let image = UIImage(data: responseData),
let responseBlocks = self.loadingResponses[url],
error == nil else { // ๋ท ์ค์ ํ๋๋ผ๋ nil์ด๋ฉด completion์ผ๋ก nil์ ์ ๋ฌํ๋ฉฐ ๋ฆฌํด
DispatchQueue.main.async {
completion(imageItem, nil)
}
return
}
// ๋ฐ์์จ ์ด๋ฏธ์ง๋ฅผ ์บ์์ ์ ์ฅํฉ๋๋ค.
self.cachedImages.setObject(image, forKey: url, cost: responseData.count)
// ํด๋น ์ด๋ฏธ์ง์ ์์ฒญ๋ค์ ๋ํด ๋ฐ๋ณตํด์ ์ด๋ฏธ์ง๋ฅผ ์ ๋ฌํฉ๋๋ค.
for block in responseBlocks {
DispatchQueue.main.async {
block(imageItem, image)
}
return
}
}.resume()
}
}
ImageItem
์ด๋ฏธ์ง์ UIImage, URL, id๋ฅผ ๊ฐ์ง๊ณ ์๋ ๊ฐ์ฒด
//
// ImageItem.swift
// starbuckst
//
// Created by Bibi on 2022/05/17.
//
import Foundation
import UIKit
class ImageItem { // struct?
var image: UIImage? = nil // ์ด๋ฏธ์ง
let url: URL // ์ด๋ฏธ์งURL
let identifier = UUID() // ์ด๋ฏธ์ง์ ID
init(image: UIImage? = nil, url: URL) {
self.image = image
self.url = url
}
}
ViewController
๋ทฐ์ปจ์์ ์ฌ์ฉํ ๋๋ ์๋์ ๊ฐ์ด ์ฌ์ฉ
imageCacheManager.loadImage(url: mainImageURL as NSURL, imageItem: mainImageItem) { (imageItem, image) in
DispatchQueue.main.async {
self.mainEventView.image = image
self.mainEventView.setNeedsDisplay()
}
}
'๐ฑ๐ iOS > Code Templates' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Swift] Stack ์คํ ๊ตฌํ (๋ฐฐ์ด / ์ฐ๊ฒฐ๋ฆฌ์คํธ๋ก ๊ตฌํํ๊ธฐ) (0) | 2022.12.20 |
---|---|
[Swift] Linked List - Singly Linked List ๊ตฌํ (0) | 2022.12.15 |
์ฝ๋๋ก UIView ๋ง๋ค๊ธฐ ๊ธฐ๋ณธ ํ ํ๋ฆฟ (0) | 2022.05.11 |
UIFont Extension - custom font (0) | 2022.05.11 |
UIColor extension (0) | 2022.05.11 |