포스트

Todoey (5)

DB인식 문제 해결하기.

강의를 수강하기 전, Relationship을 하면서 DB에 입력은 되지만 TableView에 보이지 않는 현상이 생겼다.

이부분을 먼저 해결하고 다시 공부를 시작하도록 하겠다.

1
2
3
4
5
6
7
8
9
10
// wrong
override func numberOfSections(in tableView: UITableView) -> Int {
        return categories.count
    }

// correct    
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        
        return categories.count
    }

뭐가 잘못되었나 하나하나 확인하다가 어이없는 실수를 한걸 봤다.

segue로 화면 전환

우선 StoryBoard에 Segue가 있는지를 확인하고 작성하자

CleanShot 2024-04-11 at 18 30 10@2x

세그가 잘 있고 Identifier도 확실하게 체크해두자.

1
2
3
4
5
6
7
8
9
10
11
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        performSegue(withIdentifier: "goToItems", sender: self)
    }
    
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        let destinationVC = segue.destination as! TodoListViewController
        
        if let indexPath = tableView.indexPathForSelectedRow { 
            destinationVC.selectedCategory = categories[indexPath.row]
        }
    }

Didset이란?

  • 프로퍼티의 값이 변경된 직후에 호출되는 옵저버
  • 기존에 저장되어 있던 값이 매개상수 형태로 전달된다.
  • 프로퍼티 값이 변경된 후에 처리해야할 구문이 있다면 이 값을 이용해서 처리하면 된다.
  • 시스템에서 기본적으로 oldValue라는 상수명 제공
  • 새로 할당된 값이 필요할 때에는 프로퍼티 자체를 그냥 참조하면 된다.
  • 새로운 값은 이미 프로퍼티에 저장되어 있는 상태이기 때문이다.
1
2
3
4
5
6
7
8
9
10
11
var selectedCategory : Category? {
        didSet {
            loadItems()
        }
 }

let newItem = Item(context: self.context)
            newItem.title = textField.text!
            newItem.done = false
            newItem.parentCategory = self.selectedCategory // new
             

하지만 이렇게 해줘도 현재 category아무거나 클릭해도 똑같은 값이 나오게 된다.

왜냐하면 request에 어떠한 Query도 없기 때문이다.

즉 어떠한 카테고리를 눌러도 동일한 request를 호출한다는 것.

현재 TableView와 관련된 배열은 itemArray이다.

loadItems에 가게되면,

1
2
3
4
5
6
7
8
9
10
func loadItems (with request : NSFetchRequest<Item> = Item.fetchRequest()) {
        
        do {
            itemArray = try context.fetch(request)
        } catch {
            print("Error fetching data from context \(error)")
        }
        
        tableView.reloadData()
    }

Item에 모든 값을 request로하여 가져온다.

Predicate를 이용한 쿼리문 작성

우리는 이제 특정한 조건을 나타내는 쿼리문을 작성해야 한다.

let predicate = NSPredicate(format: "parentCategory.name MATCHES %@", selectedCategory?.name ?? "")

format: 정규식이 들어가서 parentCategory의 이름과 %@에 일치하는 카테코리를 가져온다. selectedCategory 우리가 선택한 카테고리의 이름

즉, 여러 카테고리중, 우리가 선택한 카테고리와 일치하는 값을 가져오는 쿼리

그후 request에 request.predicate = predicate 쿼리문을 요청한다.

parameter추가

그리고 이제 load할때 쿼리문도 같이 넣어주어야 그 쿼리문에 맞는 값에 대해 로드를 하게된다.

func loadItems (with request : NSFetchRequest<Item> = Item.fetchRequest(), predicate: NSPredicate) {

CleanShot 2024-04-15 at 10 03 06@2x

현재 검색을 하면 오름차순 정렬만 되기에, SearchBar를 구현한 함수부에도 수정을 좀 해준다.

1
2
3
4
5
6
7
8
9
10
11
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
        
        let request : NSFetchRequest<Item> = Item.fetchRequest()
        
        let predicate = NSPredicate(format: "title CONTAINS[cd] %@", searchBar.text!) // modified
        
        request.sortDescriptors = [NSSortDescriptor(key: "title", ascending: true)]

        loadItems(with: request, predicate: predicate) // modified
        
    }

쿼리와 일치하는 request를 요청한다.

그리고 다시 loadItems로 가서,

1
2
let compoundPredicate = NSCompoundPredicate(andPredicateWithSubpredicates: [categoryPredicate, predicate])
        request.predicate = compoundPredicate

하나더 만들어 준다.

NSCompoundPredicate는 말그대로 여러 predicate를 가지고 있는 모음이라고 생각하면 된다.

andPredicateWithSubpredicates는 배열안에 predicate가 들어가기에 저렇게 배열안에 담아주는 것이다.

함수 수정으로인한 에러 해결

loadItems에 parameter가 request하나 였지만, 이제는 predicate가 추가되었기에 그에 맞게 loadItems을 트리거하는 부분도 고쳐주어야 한다.

하지만 그전에 일반적으로 우리가 쿼리가 해당하지 않는 부분을 load 할 수도 있기에

func loadItems (with request : NSFetchRequest<Item> = Item.fetchRequest(), predicate: NSPredicate? = nil)

이렇게 default Value로 nil을 주면, 일반적으로 DB에서 가져올때는 쿼리문 없이 가져오게 된다.

1
2
3
4
5
if let additionalPredicate = predicate {
            request.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [categoryPredicate, additionalPredicate])
        } else {
            request.predicate = categoryPredicate
        }

아까 작성했던 부분을 Optional Binding을 사용하여 고쳐주었다.

작동해보면 이제는 검색도 잘 되는걸 알 수 있다.

Apr-15-2024 10-05-30

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