안녕하세요!
계속해서 앱 기능을 구현한 부분을 설명하겠습니다.
오늘은 사실 간단한 내용입니다.
저번에 파일매니저로 로컬 기기에 앱 이미지를 저장하는 방법을 다뤘는데요,
맛집을 저장할때마다 기기에 사진이 하나씩 저장된다면
데이터 공간을 많이 차지하겠죠?
그래서 기본으로 제공되는 이미지가 아닌 경우(앨범에서 이미지를 가져온 경우)에만
파일 매니저를 통해 이미지를 저장하도록 코드를 짰습니다.
코드를 올리지만 제가 작성한 코드보다 더 좋은 코드가 있을거 같습니다!
***
이전 글에서 다뤘듯이
저는 코어 데이터에 이미지의 경로(저장한 날짜 데이터)를 저장하고
파일 매니저에 파일의 경로(저장한 날짜 데이터)를 만들고 그 경로에 이미지를 저장했습니다.
때문에 파일의 경로와 이미지의 경로는 일치하지만
둘은 각각 존재합니다.
이미지의 경로는 맛집이 저장되면서 함께 저장되는 데이터로 무조건 존재하지만
파일의 경로는 앨범의 이미지를 저장하지 않으면 생성되지 않습니다.
***
(Create) 맛집 데이터를 저장하는 경우입니다.
여러 과정을 거친 후 파일매니저 + 코어 데이터를 사용해서 저장합니다.
// 코어 데이터에 맛집 추가
func addResToCoreData(restaurantData: Document, catNameArray: [String], catTextArray: [String], resImage: UIImage) {
// 데이터 할당
let address = restaurantData.address ?? ""
let group = restaurantData.group ?? ""
// 번호 에러처리
let phone = if restaurantData.phone != "" {
restaurantData.phone
} else {
"번호 정보 없음"
}
let placeName = restaurantData.placeName ?? ""
let roadAddress = restaurantData.roadAddress ?? ""
let placeURL = restaurantData.placeURL ?? ""
let date = Date()
let formatter = DateFormatter()
formatter.dateFormat = "yyyyMMddHHmmss"
let dateString: String = formatter.string(from: date)
// 기본 이미지가 아니면 이미지 파일 생성
if !checkCommonImage(image: resImage) {
imageManager.createFile(urlPath: dateString, image: resImage)
}
guard catNameArray.count == catTextArray.count else {
fatalError("The length of catNameArray and catTextArray must be the same.")
}
coreDataManager.saveResToCoreData(address: address, group: group, phone: phone!, placeName: placeName, roadAddress: roadAddress, placeURL: placeURL, date: date, imagePath: dateString, categoryNameArray: catNameArray, categoryTextArray: catTextArray) {
}
}
저기서 !checkCommonImage(image: resImage) 함수를 통해
전달받은 이미지가 기본 이미지인지 확인합니다.
아래는 함수의 내용입니다.
private func checkCommonImage(image: UIImage) -> Bool {
// 13개
switch image {
case let image where image == RestaurantImages.korean :
return true
case let image where image == RestaurantImages.chicken :
return true
case let image where image == RestaurantImages.bakery :
return true
case let image where image == RestaurantImages.dessertCafe :
return true
default:
return false
}
}
여기서 false가 리턴된다면 앨범의 이미지를 따로 저장한 것이라고 볼 수 있게 됩니다.
파일 매니저를 통해 이미지 파일을 저장하고 이 때 파일의 경로가 생성됩니다.
반대로 true가 리턴된다면 기본 이미지를 저장한 것이라고 볼 수 있게 됩니다.
파일의 경로가 생성되지 않습니다.
(코어 데이터에는 어차피 이미지 자체가 아닌 이미지 경로만 저장합니다.)
(Read) 맛집 데이터를 불러올 때 입니다.
1 - 앨범의 이미지를 저장한 경우와 2 - 기본 이미지로 저장한 두가지 경우가 있습니다.
저는 파일 매니저에서 이미지를 불러올 때 경로가 없을 경우
UIImage(systemName: "person")을 리턴하도록 했습니다.
따라서 불러온 결과가 UIImage(systemName: "person") 일 때는
파일 경로가 없음 == 파일에 저장한 이미지가 없음이기 때문에
다시 에셋의 이미지를 할당해주도록 하였습니다.
반대로 불러온 결과과 UIImage(systemName: "person")가 아닐 경우에는
파일 경로와 해당 경로에 저장한 이미지가 있다는 것이기 때문에
해당 이미지를 사용하도록 하였습니다.
resImageView.image = if imageManager.readFile(urlPath: imagePath) != UIImage(systemName: "person") {
imageManager.readFile(urlPath: imagePath)
} else {
// 맛집의 group에 따라 에셋 이미지 할당
if let resGroup = restaurantCoreData.group {
switch resGroup {
case let groupString where groupString.contains("한식"):
RestaurantImages.korean
case let groupString where groupString.contains("치킨"):
RestaurantImages.chicken
case let groupString where groupString.contains("제과"):
RestaurantImages.bakery
default:
RestaurantImages.restaurant
}
}
}
**
하지만 에러처리에서 제 방식보다는 파일 매니저에서 리턴값을 옵셔널로 해서
옵셔널에 대한 에러 처리를 하는게 훨씬 깔끔한 코드가 나올 것 같습니다!
**
(Update) 저장된 맛집 데이터를 수정하는 경우입니다.
아까와 마찬가지로
1 - 앨범의 이미지를 저장한 경우와 2 - 기본 이미지로 저장한 두가지 경우가 있습니다.
그리고 다시 앨범의 이미지를 저장하는지 기본 이미지로 저장하는지 두가지 경우로 나뉩니다.
UIImage(systemName: "person")가 리턴되는지에 따라
기존에 파일 경로가 있는지 파악하고
파일 경로가 있고 앨범의 이미지를 저장할 경우 이미지를 업데이트 해줍니다.
반대로 파일 경로가 없지만 앨범의 이미지를 저장할 경우
파일의 경로를 생성하고 이미지 파일을 저장합니다.
// 이전에 기본 이미지로 저장한 경우(기존 경로 x)
if imageManager.readFile(urlPath: restaurantData.imagePath!) == UIImage(systemName: "person") {
// 앨범 이미지를 저장하는 경우(경로 생성)
if !checkCommonImage(image: resImage) {
imageManager.createFile(urlPath: restaurantData.imagePath!, image: resImage)
}
// 앨범의 이미지를 저장한 경우(기존 경로 o)
} else {
imageManager.createFile(urlPath: restaurantData.imagePath!, image: resImage)
}
}
(Delete) 마지막으로 맛집 데이터를 삭제하는 경우입니다.
다시 한번 UIImage(systemName: "person")가 리턴되는지에 따라
파일 경로가 있을 경우 파일 매니저를 통해 파일 경로를 삭제하고 코어 데이터를 삭제합니다.
결국 간단하게 말하자면 파일 경로의 유무에 따라
CRUD를 다르게 동작하도록 한 것인데 쓰다보니 길어졌네요.
++ 누적해서 정리한 내용은 깃허브에 올리고 있습니다. 링크