Kakao API 검색을 통한 이미지 검색 앱
원하는 키워드로 검색하여 원하는 이미지를 다운받을 수 있습니다.
Xcode 12.4
CocoaPods 1.10.0
5.4
6.0
Home
Search
DetailImageView
검색 옵션
(정확도 순, 최신 순), 페이징 구현
(페이지당 이미지 80개)검색 완료 버튼 없이 사용자가 입력한 검색어를 검색어가 변경 될 때마다 그 결과를 화면에 뿌려주고자 했습니다. 하지만 검색어가 변경될 때 마다 api 를 호출하게 되면 호출 횟수가 매우 많아져 비효율적이라는 문제가 있었습니다.
빈번한 api호출을 막기 위해 Combine 의 Debounce(1초 간격) 를 이용해 api 호출 횟수를 최소화해서 검색을 수행했습니다.
가장 마지막 이벤트
를 수행 func addSearchControllerObserver(){
self.searchController.searchBar.searchTextField
.myDebounceSearchPublisher
.sink { receivedValue in
self.requestSearchTermToAPI(searchTerm: receivedValue)
}
.store(in: &mySubscription)
}
extension UISearchTextField {
var myDebounceSearchPublisher : AnyPublisher<String,Never>{
NotificationCenter.default.publisher(for: UISearchTextField.textDidChangeNotification, object: self)
// 노티피케이션 센터에서 UISearchTextField 가져옴
.compactMap { $0.object as? UISearchTextField }
//UISearchTextField 에서 text가져오기
.map{ $0.text ?? "" }
.debounce(for: .milliseconds(1000), scheduler: RunLoop.main)
.filter{ $0.count > 0 }
.print()
.eraseToAnyPublisher()
}
}
Kingfisher
등 이미지 라이브러리를 이용하면 이미지 캐싱, 이미지 리사이징 등의 기능을 제공해주지만,
이번 프로젝트를 통해 이미지를 캐싱하는 흐름을 익히고자 직접 이미지 캐싱을 구현했습니다.
api 호출 -> 이미지 URL을 받아옴
메모리 캐시에 이미지 이름을 키값으로 하는 이미지 파일이 존재하는지 체크,
존재한다면 이미지 로드
or 존재하지 않는다면 3
으로 이동
디스크 캐시에 이미지 이름으로 하는 키값으로 하는 이미지 파일이 존재하는지 체크,
존재한다면 이미지 로드
+ 메모리 캐시에 캐싱
or 존재하지 않는다면 4
으로 이동
이미지, 디스크 캐시
에 이미지가 존재하지 않는 경우이니 이미지 URL
을 통해 이미지 로드
로드한 이미지를 메모리
, 디스크
캐시에 캐싱
func configure(imageURL: String,newWidth: CGFloat){
// TODO: 1. 메모리 캐시에 이미지가 존재하는지 확인, 존재한다면 이미지 로드
// TODO: 2. 메모리 캐시에 이미지가 존재하지 않으므로 디스크 캐시에 존재하는지 확인, 존재한다면 이미지 로드
// TODO: 3. 메모리, 디스크 캐시에 이미지가 존재하지 않는다면 메모리 and 디스크 캐시에 이미지를 저장하고 이미지 load
if self.imageViewForCell.setCacheImageFromMemory(imageURL) == false {
if self.imageViewForCell.setCacheImageFromDisk(imageURL) == false {
self.imageViewForCell.setImageFromURL(imageURL)
}
}
}
extension UIImageView {
func setCacheImageFromMemory(_ url: String) -> Bool{
guard let url = URL(string: url) else { return false}
if let cacheImage = ImageCacheManager.shared.object(forKey: url.lastPathComponent as NSString){
print("메모리 캐시에서 이미지 로드 ===== ")
self.image = cacheImage
// 메모리 캐시에 존재하면 true return 으로 함수 종료
return true
}
return false // 캐시에 이미지가 존재하지 않았다면 false return
}
//TODO: 1. 기본적으로 제공되는 cache 폴더 경로 불러오기
//TODO: 2. 파일 이름으로 filePath의 path 설정
//TODO: 3. 해당 path에 파일이 존재하지 않는다면 retrun false / 존재한다면 캐시에서 이미지 불러오고 메모리에 캐싱, return true
func setCacheImageFromDisk(_ url: String) -> Bool {
if let url = URL(string: url){
if let path = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true).first {
var filePath = URL(fileURLWithPath: path)
let fileManager = ImageCacheManager.fileManager
filePath.appendPathComponent(url.lastPathComponent)
if !fileManager.fileExists(atPath: filePath.path) {
return false
} //if
else{
// 존재한다면 캐시에서 이미지 불러오기
guard let imageData = try? Data(contentsOf: filePath) else {
print("disk cache image data nil")
return false
}
guard let image = UIImage(data: imageData) else { return false }
ImageCacheManager.shared.setObject(image, forKey: url.lastPathComponent as NSString) //메모리에 캐싱
print("디스크 캐시에서 이미지 로드 ===== ")
self.image = image
return true
}
} //path
} //url
return false
}
메모리 캐싱
, 디스크 캐싱
func setImageFromURL(_ url: String){
if let url = URL(string: url) {
if let path = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true).first {
var filePath = URL(fileURLWithPath: path)
let fileManager = ImageCacheManager.fileManager
filePath.appendPathComponent(url.lastPathComponent)
DispatchQueue.global(qos: .background).async {
URLSession.shared.dataTask(with: url){ (data, res, err) in
if let _ = err{
print("error 처리")
return
}
DispatchQueue.main.async {
if let data = data, let image = UIImage(data: data){
// 메모리 캐싱
ImageCacheManager.shared.setObject(image, forKey: url.lastPathComponent as NSString)
// 디스크 캐싱
fileManager.createFile(atPath: filePath.path,
contents: image.jpegData(compressionQuality: 0.4),
attributes: nil)
print("URL 에서 이미지 로드 ===== ")
self.image = image
}
}
}.resume() //session
} //dispatch
} //path
} //url
}
위와 같은 과정을 통해 이미지를 load 하는 collectionView 의 Cell
부분에서 캐싱되어있는지 여부를 판단 하여 캐싱된 데이터
를 불러오거나, URL
을 통해 이미지를 불러왔습니다