๊ฐ๋ฐ ๊ธฐ๊ฐ: 2024.7.22 ~ 2024.7.28
๊ฐ๋ฐ ์ธ์: 1์ธ (๊ธฐํ/๋์์ธ/๊ฐ๋ฐ)
- Language: Swift 5.10
- Framework: UIKit
- Minimum Target: iOS 15.0
- Architecture: MVC
- Design Pattern: Singleton
- Local Storage: Realm + FileManager
- Network: Alamofire
- Image Caching: Kingfisher
- UI/Layout: SnapKit, Then
- Utility: Toast
- ์ต์ ์/๊ด๋ จ์ ์ ๋ ฌ ๊ธฐ๋ฅ
- 12๊ฐ์ง ์ปฌ๋ฌ ํํฐ๋ง (ํ๋ฐฑ/์ปฌ๋ฌ)
- Unsplash API ๊ธฐ๋ฐ ๊ฒ์ ๊ฒฐ๊ณผ ํ์
- ํ์ด์ง ๋ฒํธ ๊ธฐ๋ฐ ๋ฐ์ดํฐ ์์ฒญ ๊ด๋ฆฌ
- ์คํฌ๋กค ๊ธฐ๋ฐ ๋ค์ ํ์ด์ง ์๋ ๋ก๋ฉ
- Realm๊ณผ FileManager ์ฐ๋ ์ ์ฅ ์์คํ
- ์ต์ ์/๊ณผ๊ฑฐ์ ์ปฌ๋ ์ ์ ๋ ฌ
- 16๊ฐ์ง MBTI ์ ํ ์ ํ ๊ธฐ๋ฅ
- ๋๋ค์ ์ ํจ์ฑ ๊ฒ์ฆ ์์คํ
- ์กฐํ์, ๋ค์ด๋ก๋ ์ ํ์
- ์ ๋ก๋ ๋ ์ง, ์ด๋ฏธ์ง ํฌ๊ธฐ ์ ๋ณด
- ์๊ฐ ํ๋กํ ์ ๋ณด ์ฐ๋
- ํ์ด์ง ๋ฒํธ ๊ด๋ฆฌ ๋ฐ ๋ฐ์ดํฐ ์ค๋ณต ๋ฌธ์
- ์ ๋ ฌ ์ต์ ๋ณ๊ฒฝ ์ ํ์ด์ง ์ด๊ธฐํ ํ์
- ์คํฌ๋กค ์ ๋ฐ์ดํฐ ๋ก๋ฉ ์์ ์ ์ด ํ์
extension SearchViewController: UICollectionViewDataSourcePrefetching {
func collectionView(_ collectionView: UICollectionView, prefetchItemsAt indexPaths: [IndexPath]) {
for i in indexPaths {
if i.row == searchList.results.count-1 && page < searchList.total_pages {
page += 1
toggleCall()
}
}
}
}
func toggleCall() {
if toggleButton.isSelected {
callAPI(query: query, order_by: "latest")
} else {
callAPI(query: query, order_by: "relevant")
}
}
- prefetchItemsAt์ ํตํ ๋ค์ ํ์ด์ง ๋ก๋ ์์ ์ ์ด
- page ๋ณ์๋ก ์ค๋ณต ํธ์ถ ๋ฐฉ์ง
- ์ ๋ ฌ ๋ณ๊ฒฝ ์ page ์ด๊ธฐํ๋ก ๋ฐ์ดํฐ ์ ํฉ์ฑ ์ ์ง
- ๋์ฉ๋ ์ด๋ฏธ์ง ํ์ผ์ ํจ์จ์ ๊ด๋ฆฌ ํ์
- Realm๊ณผ FileManager ๊ฐ ๋ฐ์ดํฐ ๋๊ธฐํ ์ด์
- ์ด๋ฏธ์ง ์ญ์ ์ ์ ์ฅ์ ์ ๋ฆฌ ๋ฌธ์
func saveImageToDocument(image: UIImage, filename: String) {
guard let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
let fileURL = documentDirectory.appendingPathComponent("\(filename).jpg")
guard let data = image.jpegData(compressionQuality: 0.5) else { return }
do {
try data.write(to: fileURL)
} catch {
print("file save error", error)
}
}
func removeImageFromDocument(filename: String) {
guard let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
let fileURL = documentDirectory.appendingPathComponent("\(filename).jpg")
if FileManager.default.fileExists(atPath: fileURL.path) {
do {
try FileManager.default.removeItem(at: fileURL)
} catch {
print("file remove error", error)
}
}
}
- ์ด๋ฏธ์ง ์์ถ์ ํตํ ์ ์ฅ์ ๊ณต๊ฐ ์ต์ ํ
- ํ์ผ ์กด์ฌ ์ฌ๋ถ ํ์ธ์ผ๋ก ์์ ์ฑ ํ๋ณด
- ์๋ฌ ์ฒ๋ฆฌ๋ฅผ ํตํ ์์ธ ์ํฉ ๊ด๋ฆฌ
- ์ข์์ ์ํ ๋ณ๊ฒฝ ์ UI ์ ๋ฐ์ดํธ ์ง์ฐ
- ์ด๋ฏธ์ง ์ ์ฅ๊ณผ Realm ๋ฐ์ดํฐ ๋๊ธฐํ ํ์
- ๋ค์ค ํ๋ฉด์์์ ์ํ ๊ด๋ฆฌ ์ด์
@objc func likeButtonPressed(sender: UIButton) {
let index = searchList.results[sender.tag]
let imageID = index.id
if let existingLike = realmList.first(where: { $0.id == imageID }) {
try! realm.write {
existingLike.isLike.toggle()
if !existingLike.isLike {
removeImageFromDocument(filename: imageID)
removeImageFromDocument(filename: imageID + "_user")
}
sender.isSelected = existingLike.isLike
}
self.view.makeToast("์ข์์ ๋ชฉ๋ก์์ ์ ๊ฑฐ๋์ด์!")
} else {
// ์๋ก์ด ์ข์์ ํญ๋ชฉ ์ถ๊ฐ
let newLikeData = LikeList(
id: imageID,
date: Date(),
// ...์ด๊ธฐํ
)
try! realm.write {
realm.add(newLikeData)
}
self.view.makeToast("์ข์์ ๋ชฉ๋ก์ ์ถ๊ฐ๋์ด์!")
}
}
- Realm ํธ๋์ญ์ ๋ด์์ ์ํ ๋ณ๊ฒฝ ์ฒ๋ฆฌ
- FileManager ์ฐ๋์ผ๋ก ์ด๋ฏธ์ง ์๋ ๊ด๋ฆฌ
- Toast ๋ฉ์์ง๋ก ์ฆ๊ฐ์ ์ธ ์ฌ์ฉ์ ํผ๋๋ฐฑ ์ ๊ณต
- FileManager์ Realm์ ์ฐ๋ํ ํ์ด๋ธ๋ฆฌ๋ ์ ์ฅ์ ์ค๊ณ
- UICollectionViewDataSourcePrefetching์ ํ์ฉํ ํจ์จ์ ์ธ ํ์ด์ง๋ค์ด์
- ๋ค์ค ํํฐ ์ ์ฉ ์ ๊ฒ์ ์ฑ๋ฅ ์ต์ ํ
- ์ข์์ ์ํ ๋ณ๊ฒฝ ์ ๋์์ฑ ์ฒ๋ฆฌ ๊ฐ์
- ์ด๋ฏธ์ง ๋ฉํ๋ฐ์ดํฐ ์บ์ฑ ์ ๋ต ์ฌ์ค๊ณ
- ์ปฌ๋ฌ ๊ธฐ๋ฐ ์ด๋ฏธ์ง ๋ถ๋ฅ ์๊ณ ๋ฆฌ์ฆ ๊ฐ์
- FileManager ์ ์ฅ์ ๊ด๋ฆฌ ์๋ํ