[swift] IGListDiffKit을 사용한 히스토리 관리

IGListDiffKit은 iOS 앱의 히스토리 관리를 쉽게 구현할 수 있도록 도와주는 라이브러리입니다. 이 라이브러리를 사용하면 뷰 컨트롤러의 이전 상태와 현재 상태를 비교하여 변경된 부분만 업데이트할 수 있으므로 앱의 성능을 향상시킬 수 있습니다.

IGListDiffKit의 장점

IGListDiffKit 사용 예시

다음은 IGListDiffKit을 사용하여 히스토리 관리를 구현하는 간단한 예제입니다.

import IGListDiffKit

// 이전 상태와 현재 상태를 나타내는 모델
struct Item: ListDiffable {
    let name: String
    let value: Int
    
    func diffIdentifier() -> NSObjectProtocol {
        return name as NSObjectProtocol
    }
    
    func isEqual(toDiffableObject object: ListDiffable?) -> Bool {
        guard let object = object as? Item else {
            return false
        }
        
        return self.value == object.value
    }
}

// 히스토리 관리를 위한 뷰 컨트롤러
class HistoryViewController: UIViewController, ListAdapterDataSource {
    var items: [Item] = []
    var adapter: ListAdapter!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())
        collectionView.backgroundColor = .white
        collectionView.frame = view.bounds
        view.addSubview(collectionView)
        
        adapter = ListAdapter(updater: ListAdapterUpdater(), viewController: self)
        adapter.collectionView = collectionView
        adapter.dataSource = self
        
        items.append(Item(name: "Item 1", value: 0))
        
        Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(updateItems), userInfo: nil, repeats: true)
    }
    
    @objc func updateItems() {
        // 새로운 아이템 생성
        let newItem = Item(name: "Item \(items.count + 1)", value: Int.random(in: 0...100))
        
        // 현재 아이템 목록에 새로운 아이템 추가
        items.append(newItem)
        
        // 이전 상태와 현재 상태를 비교하여 변경된 부분만 업데이트
        let diffResult = ListDiffPaths(fromSection: 0, toSection: 0, oldArray: adapter.objects() as? [Item], newArray: items)
        adapter.performUpdates(animated: true, completion: nil)
    }
    
    // 컬렉션 뷰에 표시할 아이템 목록 반환
    func objects(for listAdapter: ListAdapter) -> [ListDiffable] {
        return items
    }
    
    // 아이템과 셀을 매핑해서 반환
    func listAdapter(_ listAdapter: ListAdapter, sectionControllerFor object: Any) -> ListSectionController {
        let sectionController = ListSingleSectionController()
        sectionController.configureCell = { (_, _, _, _) in
            return UICollectionViewCell()
        }
        sectionController.sizeForItem = { (_, _, _) in
            return CGSize(width: 100, height: 100)
        }
        return sectionController
    }
    
    // 섹션 컨트롤러와 헤더, 푸터를 매핑해주는 메소드
    func emptyView(for listAdapter: ListAdapter) -> UIView? {
        return nil
    }
}

위의 예제에서는 Item이라는 모델을 IGListDiffKit의 ListDiffable 프로토콜을 따르도록 설계했습니다. 이 모델은 diffIdentifier() 메소드를 통해 고유 식별자를 반환하고, isEqual(toDiffableObject:) 메소드를 통해 동등성 비교를 수행합니다.

HistoryViewControllerListAdapterDataSource 프로토콜을 구현하여 IGListDiffKit의 ListAdapter를 사용하도록 설정합니다. objects(for:) 메소드에서는 현재 상태의 아이템 목록을 반환하고, listAdapter(_:sectionControllerFor:) 메소드에서는 섹션 컨트롤러와 셀을 매핑합니다.

updateItems() 메소드에서는 매 초마다 새로운 아이템을 생성하고, ListAdapterperformUpdates(animated:completion:) 메소드를 호출하여 변경된 부분만 업데이트합니다.

위의 예제를 실행하면 매 초마다 새로운 아이템이 추가되는 것을 볼 수 있습니다. IGListDiffKit은 이전 상태와 현재 상태를 비교하여 변경된 부분만 업데이트하므로 성능이 향상됩니다.

참고 자료

IGListDiffKit GitHub 리포지토리