7주차 과제 (4)
Lv.4
Lv4. 위시 리스트 삭제
- 아래 방법 중 1가지를 택해 구현합니다.
- 목록을 스와이프하여 삭제 버튼을 노출하고 터치하면 삭제 -
UITableView의 기능
- 목록 Cell에 삭제 UIButton을 구성
- 길게 눌러 Alert를 띄우기
- 목록을 스와이프하여 삭제 버튼을 노출하고 터치하면 삭제 -
- 반드시 사용할 것 :
UITableView
orUIButton
예외 처리.
Lv.4 기능은 ContextualAction을 사용 해서 구현을 할 생각이다.
즉 Swipe해서 삭제를 처리 할 예정.
그전에 이전에 피드백에서도 단순히 print말고 좀 더 유져에게 보여주면 좋을 것 같다는 피드백을 들었기에
예외처리를 좀 더 해보려 한다.
1. 각 Button의 예외처리
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@IBAction func saveListBtn(_ sender: UIButton) {
let newItem = Lists(context: self.context)
newItem.id = Int64(list[0].id)
newItem.title = list[0].title
newItem.price = Int64(list[0].price)
newItem.discountPercentage = list[0].discountPercentage
do
{
try context.save()
} catch {
print(error.localizedDescription)
}
}
현재는 이렇게 되어있다.
단순히 콘솔로 에러코드가 출력되는 부분에 대해서 alert를 이용해서 하려고한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@IBAction func saveListBtn(_ sender: UIButton) {
let newItem = Lists(context: self.context)
newItem.id = Int64(list[0].id)
newItem.title = list[0].title
newItem.price = Int64(list[0].price)
newItem.discountPercentage = list[0].discountPercentage
do
{
try context.save()
} catch {
let alert = UIAlertController(title: "에러 발생", message: "\(error.localizedDescription)가 발생했습니다.", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "확인", style: .default))
self.present(alert, animated: true)
}
}
이렇게 해주었다.
showDBBtn에 대한 Action도 동일하게 해주었다.
2. DataLoad에서의 예외처리
기존에 난수를 만들때 배열을 썼는데 불필요한 메모리를 만드는 것같아 코드를 한줄로 줄인다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
func fetchRequest() {
let pageNumber = (1...100).randomElement() ?? 1 // modified
let url = "https://dummyjson.com/products/\(pageNumber)"
if let url = URL(string: url) {
let urlSession = URLSession(configuration: .default)
let task = urlSession.dataTask(with: url) { (data,response,error) in
if error != nil {
self.delegate?.sendList(data: [])
return
}
if let safeData = data {
let decodedData = self.decodingJson(data: safeData)
self.delegate?.sendList(data: decodedData)
}
}
task.resume()
}
}
현재 에러가 발생하게 되면 아무것도 없는 빈 배열을 delegate를 통해 전달하게 해두었다.
그래서 에러가 발생했을 경우를 생각하여 아래 isEmpty로 조건을 만들어 alert를 구현하여 유져에게 인폼을 주도록 했다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func sendList(data: [DataModel]) {
if !data.isEmpty { // new
list = data
DispatchQueue.main.async {
let price = self.numberFormatter.string(from: Double(self.list[0].price) * (100.00 - self.list[0].discountPercentage) as NSNumber)
self.imageView.load(url: URL(string: self.list[0].thumbnail)!)
self.titleLabel.text = self.list[0].title
self.bodyLabel.text = self.list[0].description
self.priceLabel.text = "\(price ?? "0")$"
}
} else {
let alert = UIAlertController(title: "에러 발생", message: "데이터 로드중 문제가 발생했습니다.", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "확인", style: .default))
self.present(alert, animated: true)
}
}
fetch request에 pagenumber를 0으로 줘서 예외 발생 처리를 테스트 해보자.
실제로 page가 0일때는
1
2
3
{
"message": "Product with id '0' not found"
}
이렇게 출력이된다. 즉 데이터가 없으므로 빈배열을 리턴하게 된다.
결과.
다음 버튼을 눌러도 똑같다.
예외처리가 잘 되었다.
Lv.4
1. 삭제기능 구현
위에 언급한대로 UIContextualAction을 사용해 swipe하면서 해당 내용을 지우는 것이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let deleteBtn = UIContextualAction(style: .normal, title: "Delete") { (UIContextualAction, UIView, success: @escaping (Bool) -> Void) in
let alert = UIAlertController(title: "삭제하기", message: "정말 삭제하시나요?.", preferredStyle: .alert)
let ok = UIAlertAction(title: "OK", style: .destructive, handler: { _ in
self.savedList.remove(at: indexPath.row)
tableView.beginUpdates()
tableView.deleteRows(at: [indexPath], with: .fade)
tableView.endUpdates()
})
let cancel = UIAlertAction(title: "Cancel", style: .cancel)
alert.addAction(ok)
alert.addAction(cancel)
self.present(alert,animated: false)
success(true)
}
deleteBtn.backgroundColor = .red
return UISwipeActionsConfiguration(actions: [deleteBtn])
}
일단 테이블뷰에 삭제되는 기능은 구현이 되었다
2. Context를 이용하여 삭제 구현.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let deleteBtn = UIContextualAction(style: .normal, title: "Delete") { (UIContextualAction, UIView, success: @escaping (Bool) -> Void) in
let alert = UIAlertController(title: "삭제하기", message: "정말 삭제하시나요?.", preferredStyle: .alert)
let ok = UIAlertAction(title: "OK", style: .destructive, handler: { _ in
self.context.delete(self.savedList[indexPath.row]) // new
self.savedList.remove(at: indexPath.row)
tableView.beginUpdates()
tableView.deleteRows(at: [indexPath], with: .fade)
tableView.endUpdates()
self.appDelegate.saveContext() // new
})
let cancel = UIAlertAction(title: "Cancel", style: .cancel)
alert.addAction(ok)
alert.addAction(cancel)
self.present(alert,animated: false)
success(true)
}
deleteBtn.backgroundColor = .red
return UISwipeActionsConfiguration(actions: [deleteBtn])
}
해당 위치의 값을 delete로 삭제해준다.
이대 saveContext()를 하지않으면 삭제된게 DB에 반영이 되지 않으므로 반드시 작성해준다.
그게 아니면
1
2
3
4
5
do {
try self.context.save()
} catch {
print(error)
}
이걸 사용해도 된다.
요지는 DB의 값이 변동이 되었을때 save를 해줘야 한다는것.
saveContext에는
1
2
3
4
5
6
7
8
9
10
11
12
13
func saveContext () {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
값이 변화하면 save를 하게 되어있다.
Lv.4 끝