Skip to content

Commit

Permalink
feat: main 썸네일 caching 구현 (#55)
Browse files Browse the repository at this point in the history
  • Loading branch information
eeeesong committed Apr 28, 2021
1 parent 1ac3f10 commit 38502c2
Show file tree
Hide file tree
Showing 11 changed files with 145 additions and 46 deletions.
4 changes: 4 additions & 0 deletions ios/sidedish/sidedish.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
E41EC954262EBA2E0043CC44 /* SideDishes.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41EC953262EBA2E0043CC44 /* SideDishes.swift */; };
E41EC97D263119E90043CC44 /* EndPoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41EC97C263119E90043CC44 /* EndPoint.swift */; };
E41EC98B263172760043CC44 /* CustomToaster.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41EC98A263172760043CC44 /* CustomToaster.swift */; };
E43A3C242639538C00BACB07 /* ImageDownloadManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = E43A3C232639538C00BACB07 /* ImageDownloadManager.swift */; };
E4896DFF26353AB000AA4DD7 /* NetworkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4896DFE26353AB000AA4DD7 /* NetworkManager.swift */; };
E4896E0226353F1400AA4DD7 /* AFNetworkManagable.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4896E0126353F1400AA4DD7 /* AFNetworkManagable.swift */; };
E4896E092635420000AA4DD7 /* RequestManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4896E082635420000AA4DD7 /* RequestManager.swift */; };
Expand Down Expand Up @@ -78,6 +79,7 @@
E41EC953262EBA2E0043CC44 /* SideDishes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SideDishes.swift; sourceTree = "<group>"; };
E41EC97C263119E90043CC44 /* EndPoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EndPoint.swift; sourceTree = "<group>"; };
E41EC98A263172760043CC44 /* CustomToaster.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomToaster.swift; sourceTree = "<group>"; };
E43A3C232639538C00BACB07 /* ImageDownloadManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageDownloadManager.swift; sourceTree = "<group>"; };
E4896DFE26353AB000AA4DD7 /* NetworkManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkManager.swift; sourceTree = "<group>"; };
E4896E0126353F1400AA4DD7 /* AFNetworkManagable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AFNetworkManagable.swift; sourceTree = "<group>"; };
E4896E082635420000AA4DD7 /* RequestManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestManager.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -285,6 +287,7 @@
E4896E082635420000AA4DD7 /* RequestManager.swift */,
E4896E0126353F1400AA4DD7 /* AFNetworkManagable.swift */,
E4896DFE26353AB000AA4DD7 /* NetworkManager.swift */,
E43A3C232639538C00BACB07 /* ImageDownloadManager.swift */,
);
path = Network;
sourceTree = "<group>";
Expand Down Expand Up @@ -437,6 +440,7 @@
E4896E0E2635422300AA4DD7 /* AFRequestManagable.swift in Sources */,
E4896E0226353F1400AA4DD7 /* AFNetworkManagable.swift in Sources */,
8208FC5926315CCA009504EE /* TableViewTapToasterGesture.swift in Sources */,
E43A3C242639538C00BACB07 /* ImageDownloadManager.swift in Sources */,
82A971D72632478E005E419A /* CustomHeaderFooter.swift in Sources */,
82680CC526354BC400009775 /* CoreDataStorage.swift in Sources */,
E41EC97D263119E90043CC44 /* EndPoint.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import CoreData


extension SaveSideDish: SideDishManageable {

@nonobjc public class func fetchRequest() -> NSFetchRequest<SaveSideDish> {
return NSFetchRequest<SaveSideDish>(entityName: Properties.entity)
}
Expand All @@ -24,6 +24,7 @@ extension SaveSideDish: SideDishManageable {
@NSManaged public var salePrice: Int16
@NSManaged public var subtitle: String
@NSManaged public var title: String
@NSManaged public var thumbnailPath: String?

enum Properties {
static let entity = "SaveSideDish"
Expand All @@ -35,16 +36,25 @@ extension SaveSideDish: SideDishManageable {
static let salePrice = "salePrice"
static let deliveryTypes = "deliveryTypes"
static let badges = "badges"
static let thumbnailPath = "thumbnailPath"
}

func getID() -> String {
self.id
}

func getTitle() -> String {
return self.title
}

func getimage() -> String {
func getImageURL() -> String {
return self.image
}

func getThumbnailPath() -> String? {
return self.thumbnailPath
}

func getdescription() -> String {
return self.subtitle
}
Expand All @@ -64,6 +74,10 @@ extension SaveSideDish: SideDishManageable {
func getbadge() -> [String]? {
return self.badges
}

func updateThumbnailPath(_ path: String) {
self.thumbnailPath = path
}
}

extension SaveSideDish : Identifiable {
Expand Down
53 changes: 53 additions & 0 deletions ios/sidedish/sidedish/Data/DishRepository.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,17 @@ final class DishRepository {
private var cancelBag = Set<AnyCancellable>()

private let networkManager: AFNetworkManagable!
private let imageDownloadManager: ImageDownloadManager!

init(with url: String) {
self.context = coreData.persistentContainer.viewContext
self.sideDishEntity = NSEntityDescription.entity(forEntityName: SaveSideDish.Properties.entity, in: context)
self.categoryEntity = NSEntityDescription.entity(forEntityName: SaveSideDishes.Properties.entity, in: context)
self.networkManager = NetworkManager(with: url)
self.imageDownloadManager = ImageDownloadManager()
}

//MARK: - category
func getCategories(completionHandler: @escaping (Just<[SideDishesCategoryManageable]>) -> ()) {

let categories = networkManager.get(decodingType: [SideDishesCategory].self,
Expand Down Expand Up @@ -60,6 +63,8 @@ final class DishRepository {
return publisher
}


//MARK: - sidedishes
func getSideDishes(endPoint: String, completionHandler: @escaping (Just<[SideDishManageable]>) -> ()) {

let mainSideDishes = networkManager.get(decodingType: [SideDish].self, endPoint: endPoint)
Expand Down Expand Up @@ -110,6 +115,54 @@ final class DishRepository {
return fetchRequest
}


//MARK: - thumbnail path
func getSideDishThumbnailPath(from url: String, id: String, completionHandler: @escaping (Just<String?>) -> ()) {

imageDownloadManager.download(from: url, fileName: id) { (thumbnailPath) in
self.updateThumbnailPath(of: id, with: thumbnailPath)
self.save()
completionHandler(self.loadThumbnailPath(of: id))
}
}

private func updateThumbnailPath(of id: String, with path: String) {
let fetchRequest = findSideDishForHash(id)

guard let objectToUpdate = try! self.context.fetch(fetchRequest).first,
let targetDish = objectToUpdate.sideDish?.first(where: { (dish) -> Bool in
dish.getID() == id
}) else { return }

targetDish.setValue(path, forKeyPath: SaveSideDish.Properties.thumbnailPath)
}

private func findSideDishForHash(_ hash: String) -> NSFetchRequest<SaveSideDishes> {
let fetchRequest: NSFetchRequest<SaveSideDishes> = SaveSideDishes.fetchRequest()
fetchRequest.predicate = NSPredicate(format: "ANY sideDish.id == %@", hash)
return fetchRequest
}

private func loadThumbnailPath(of id: String) -> Just<String?> {
let fetchRequest = findSideDishForHash(id)

guard let targetSideDish = try? context.fetch(fetchRequest).first else {
return Just(nil)
}

var thumnailPath: String?

targetSideDish.sideDish?.forEach({ (sideDish) in
if id == sideDish.id {
thumnailPath = sideDish.thumbnailPath ?? nil
}
})

let publisher = Just(thumnailPath)
return publisher
}

//MARK: - delete
func deleteAllInCoreData(){
let saveSideDishesRequest: NSFetchRequest<SaveSideDishes> = SaveSideDishes.fetchRequest()
let saveSideDishRequest: NSFetchRequest<SaveSideDish> = SaveSideDish.fetchRequest()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
<attribute name="price" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="salePrice" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="subtitle" attributeType="String"/>
<attribute name="thumbnailPath" optional="YES" attributeType="String"/>
<attribute name="title" attributeType="String"/>
</entity>
<entity name="SaveSideDishes" representedClassName="SaveSideDishes" syncable="YES">
Expand All @@ -17,7 +18,7 @@
<attribute name="sideDish" optional="YES" attributeType="Transformable" valueTransformerName="NSSecureUnarchiveFromData" customClassName="[SideDish]"/>
</entity>
<elements>
<element name="SaveSideDish" positionX="21.19749450683594" positionY="5.541259765625" width="128" height="149"/>
<element name="SaveSideDish" positionX="21.19749450683594" positionY="5.541259765625" width="128" height="164"/>
<element name="SaveSideDishes" positionX="-63.9647216796875" positionY="-126.251708984375" width="128" height="89"/>
</elements>
</model>
36 changes: 6 additions & 30 deletions ios/sidedish/sidedish/Domain/Entity/SideDish.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,44 +9,20 @@ import Foundation

protocol SideDishManageable {
func getTitle() -> String
func getimage() -> String
func getImageURL() -> String
func getdescription() -> String
func getPrice() -> Int
func getSalePrice() -> Int
func getDeliveryTypes() -> [String]?
func getbadge() -> [String]?
func getID() -> String
func updateThumbnailPath(_ path: String)
func getThumbnailPath() -> String?
}


struct SideDish: Decodable, SideDishManageable {
func getTitle() -> String {
return self.title
}

func getimage() -> String {
return self.image
}

func getdescription() -> String {
return self.description
}

func getPrice() -> Int {
return self.price
}

func getSalePrice() -> Int {
return self.salePrice
}

func getDeliveryTypes() -> [String]? {
return self.deliveryTypes
}

func getbadge() -> [String]? {
return self.badges
}

struct SideDish: Decodable {

let id: String
let image: String //library/cache의 파일명
let title: String //"[소중한식사] 골뱅이무침 195g"
Expand Down
10 changes: 10 additions & 0 deletions ios/sidedish/sidedish/Domain/UseCase/TurnonAppUsecase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ protocol ManufactureDataforViewModel {
func manufactureForMainViewSideDishes(endPoint: String,
completionHandler: @escaping (Just<[SideDishManageable]>) -> ())

func thumbnailForMainiewSideDishes(url: String, id: String,
completionHandler: @escaping (Just<String?>) -> ())

}

class TurnonAppUsecase: ManufactureDataforViewModel {
Expand Down Expand Up @@ -43,4 +46,11 @@ class TurnonAppUsecase: ManufactureDataforViewModel {
completionHandler(publisher)
}
}

func thumbnailForMainiewSideDishes(url: String, id: String,
completionHandler: @escaping (Just<String?>) -> ()) {
return repository.getSideDishThumbnailPath(from: url, id: id) { (publisher) in
completionHandler(publisher)
}
}
}
1 change: 1 addition & 0 deletions ios/sidedish/sidedish/Network/EndPoint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ import Foundation

enum EndPoint {
static let categories = "/categories"
static let detail = "/detail"
static var sideDishes = [String]()
}
32 changes: 32 additions & 0 deletions ios/sidedish/sidedish/Network/ImageDownloadManager.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//
// ImageDownloadManager.swift
// sidedish
//
// Created by Song on 2021/04/28.
//

import Foundation
import Alamofire

class ImageDownloadManager {

private let cacheURL = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0]

func download(from imageURL: String, fileName: String, completionHandler: @escaping (String) -> ()){
let request = downloadRequest(of: imageURL, fileName: "\(fileName).png")
request.responseURL { response in
if response.error == nil, let filePath = response.fileURL?.path {
completionHandler(filePath)
}
}
}

private func downloadRequest(of imageURL: String, fileName: String) -> DownloadRequest {
let destination: DownloadRequest.Destination = {_,_ in
let fileURL = self.cacheURL.appendingPathComponent(fileName)
return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
}
return AF.download(imageURL, to: destination)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class MenuCell : UITableViewCell {

func updateMenu(image: UIImage, titleText: String, subTitle: String, price: Int, reducedPrice: Int, badge: [String]){
selectionStyle = .none
self.menuImage?.image = image
self.menuTitle.text = titleText
self.menuSubTitle.text = subTitle

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,19 +51,32 @@ class MenuCellViewModel {

private func loadSideDishes(count: Int) {
self.dishes = Array(repeating: [], count: count)
for i in 0..<count {
turnonAppUsecase.manufactureForMainViewSideDishes(endPoint: EndPoint.sideDishes[i]) { (publisher) in
for section in 0..<count {
turnonAppUsecase.manufactureForMainViewSideDishes(endPoint: EndPoint.sideDishes[section]) { (publisher) in
publisher.sink { (result) in
if case .failure(let error) = result {
self.errorMessage = error.localizedDescription
}
} receiveValue: { (sideDish) in
self.dishes[i] = sideDish
self.dishes[section] = sideDish
for (row, dish) in sideDish.enumerated() {
self.loadThumbnails(for: dish, section: section, row: row)
}
}.store(in: &self.cancelBag)
}
}
}

private func loadThumbnails(for sideDish: SideDishManageable, section: Int, row: Int) {
turnonAppUsecase.thumbnailForMainiewSideDishes(url: sideDish.getImageURL(), id: sideDish.getID()) { (publisher) in
publisher.sink { (thumbnailPath) in
if let thumbnailPath = thumbnailPath {
self.dishes[section][row].updateThumbnailPath(thumbnailPath)
}
}.store(in: &self.cancelBag)
}
}

func sideCategoryCount() -> Int {
if dishesCategory == nil {
return 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,22 +29,16 @@ class MainTableViewDataSource: NSObject, UITableViewDataSource {

let dishProperty = menuCellViewModel.dishes[indexPath.section][indexPath.row]

cell.updateMenu(image: UIImage(),
let thumbnailPath = dishProperty.getThumbnailPath() ?? ""
let image = UIImage(contentsOfFile: thumbnailPath)

cell.updateMenu(image: image ?? UIImage(),
titleText: dishProperty.getTitle(),
subTitle: dishProperty.getdescription(),
price: dishProperty.getPrice(),
reducedPrice: dishProperty.getSalePrice(),
badge: dishProperty.getbadge() ?? ["",""])

DispatchQueue.global().async {
let url = URL(string: dishProperty.getimage())!
let data = try? Data(contentsOf: url)
DispatchQueue.main.async {
if let tempdata = data {
cell.menuImage.image = UIImage(data: tempdata)
}
}
}
return cell
}
}

0 comments on commit 38502c2

Please sign in to comment.