포스트

7주차 과제 (4)

Lv.4

Lv4. 위시 리스트 삭제

  • 아래 방법 중 1가지를 택해 구현합니다.
    • 목록을 스와이프하여 삭제 버튼을 노출하고 터치하면 삭제 - UITableView의 기능
    • 목록 Cell에 삭제 UIButton을 구성
    • 길게 눌러 Alert를 띄우기
  • 반드시 사용할 것 : UITableView or UIButton

예외 처리.

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"
}

이렇게 출력이된다. 즉 데이터가 없으므로 빈배열을 리턴하게 된다.

결과.

Simulator Screenshot - iPhone 15 Pro - 2024-04-11 at 01 26 11

다음 버튼을 눌러도 똑같다.

예외처리가 잘 되었다.

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])
    }

일단 테이블뷰에 삭제되는 기능은 구현이 되었다

Apr-11-2024 01-39-27

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 끝

Apr-11-2024 02-43-59

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.