首页 > 解决方案 > 如果从已保存在数据库(领域)中更改,则观察 JSON 响应

问题描述

在. Alamofire_ Realm_ RxSwift一切正常,但当天的需要是防止总是刷新网络调用的视图。现在应用程序的行为就像我将 JSON 响应复制到数据库,然后从数据库更新视图。但是要始终获得最新的响应,应用程序需要在每个viewWillAppear. 但是我不想获取所有数据库数据并搜索新响应是否有任何更改然后显示它。如果数据与以前加载到数据库中的数据不同,那么我Swift是否可以观察到任何东西,然后只有应用程序会更新其视图。AlamofireRealm

                self?.notificationToken = Database.singleton.fetchStudentsForAttendence(byCLassId: classId).observe { [weak self] change in
                switch change {
                case .initial(let initial):
                    TestDebug.debugInfo(fileName: "", message: "INIT: \(initial)")
                    self!.viewModel.getStudentsData(id: classId)
                case .update(_, let deletions, let insertions, let modifications):
                    print(modifications)
                    TestDebug.debugInfo(fileName: "", message: "MODIFY: \(modifications)")
                    TestDebug.debugInfo(fileName: "", message: "MODIFY: \(insertions)")
                case .error(let error):
                    TestDebug.debugInfo(fileName: "", message: "ERROR:\(error)")
                }
            }

这就是我现在观察数据的方式,但是当我每次调用 API 时,我都会将响应保存到数据库中,并且我正在使用case .initial它来监视它,并且因为数据库总是被刷新并且每次都调用这个块。我需要一些东西来监控数据库中数据值的变化。领域中有什么东西吗?

链接到 GIF

好的,我正在这样做,有一个 viewController,其中我有一个容器视图,该容器视图将 Collection 视图作为子视图。

private lazy var studentsViewController: AttandenceView = {
    let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
    var viewController = storyboard.instantiateViewController(withIdentifier: "attendenceCV") as! AttandenceView
    self.add(asChildViewController: viewController, to: studentsView)
    return viewController
}()  //this way I am adding collectionView's container view.

这是我从中获取数据并为 CollectionView 创建可观察对象的 ViewModel 代码。

class AttendenceVM {

    //MARK: Properties
    let disposeBag = DisposeBag()
    let studentCells = BehaviorRelay<[StudentModel]>(value: [])

    var studentCell : Observable<[StudentModel]> {
        return studentCells.asObservable().debug("CELL")
    }

    var notificationToken : NotificationToken? = nil

    deinit {
        notificationToken?.invalidate()
    }
    func getStudentsData(id: Int) {
        let studentsData = (Database.singleton.fetchStudentsForAttendence(byCLassId: id))
        self.notificationToken = studentsData.observe{[weak self] change in
            TestDebug.debugInfo(fileName: "", message: "Switch:::: change")
            switch change {
            case .initial(let initial):
                TestDebug.debugInfo(fileName: "", message: "INIT: \(initial)")
                self!.studentCells.accept(Array(studentsData))
            case .update(_, let deletions, let insertions, let modifications):
                TestDebug.debugInfo(fileName: "", message: "MODIF::: \(modifications)")
                self!.studentCells.accept(Array(studentsData))
            case .error(let error):
                print(error)
            }
        }
        //self.studentCells.accept(studentsData)
    }
}

然后我通过这样做分别在其类中填充collectionView。

class AttandenceView: UIViewController, UICollectionViewDelegateFlowLayout {

    //MARK: - Outlets
    @IBOutlet weak var studentsView: UICollectionView!

    let studentCells = BehaviorRelay<[StudentModel]>(value: [])
    let scanStudentCells = BehaviorRelay<[ClassStudent]>(value: [])



    private let disposeBag = DisposeBag()

    override func viewDidLoad() {
        super.viewDidLoad()

        let flowLayout = UICollectionViewFlowLayout()
        let size = CGSize(width: 105, height: 135)
        flowLayout.itemSize = size
        studentsView.setCollectionViewLayout(flowLayout, animated: true)
        studentsView.rx.setDelegate(self).disposed(by: disposeBag)
        setupBinding()
    }

    func setupBinding() {

        studentsView.register(UINib(nibName: "StudentCVCell", bundle: nil), forCellWithReuseIdentifier: "studentCV")


            //Cell creation
            scanStudentCells.asObservable().debug("Cell Creation").bind(to: studentsView.rx.items(cellIdentifier: "studentCV", cellType: StudentCVCell.self)) {
                (row , element, cell) in
                if (element.attandance == 1 ) {
                    // update view accordingly
                } else if (element.attandance == 0) {
                    // update view accordingly
                } else if (element.attandance == 2) {
                     // update view accordingly  

                }
                cell.viewModel2 = element
                }.disposed(by: disposeBag)

            //Item Display
            studentsView.rx
                .willDisplayCell
                .subscribe(onNext: ({ (cell,indexPath) in
                    cell.alpha = 0
                    let transform = CATransform3DTranslate(CATransform3DIdentity, -250, 0, 0)
                    cell.layer.transform = transform
                    UIView.animate(withDuration: 1, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.5, options: .curveEaseOut, animations: {
                        cell.alpha = 1
                        cell.layer.transform = CATransform3DIdentity
                    }, completion: nil)
                })).disposed(by: disposeBag)

            // item selection with model details.
            Observable
                .zip(
                    studentsView
                        .rx
                        .itemSelected,
                    studentsView
                        .rx
                        .modelSelected(StudentModel.self))
                .bind { [weak self] indexPath, model in

                    let cell = self?.studentsView.cellForItem(at: indexPath) as? StudentCVCell
                    if (model.attandance == 0) {
                       // update view accordingly  

                    } else if (model.attandance == 1) {
                        // update view accordingly  

                    } else if (model.attandance == 2) {
                        // update view accordingly  
                    }

                }.disposed(by: disposeBag)

        } 

以下是主视图控制器的完整代码

class AttendanceViewController: MainViewController {
    let viewModel: AttendenceVM = AttendenceVM()
    private let disposeBag = DisposeBag()
    let appDelegate = (UIApplication.shared.delegate as! AppDelegate)
    let notificationCenter = NotificationCenter.default
    var students : Results<StudentModel>? = nil
    var notificationToken: NotificationToken? = nil

    private lazy var studentsViewController: AttandenceView = {
        let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
        var viewController = storyboard.instantiateViewController(withIdentifier: "attendenceCV") as! AttandenceView
        self.add(asChildViewController: viewController, to: studentsView)
        return viewController
    }()
     override func viewDidLoad() {
        super.viewDidLoad()

        if AppFunctions.getAssignedClassId(forKey: "assignedClassId") != 0 { // && pref id isAssigned == true
            let id = AppFunctions.getAssignedClassId(forKey: "assignedClassId")
            self.viewModel.getStudentsData(id: id)
        }

        bindViewModel()

    }
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        classNameLbl.text = "Attandence: Class \(AppFunctions.getAssignedClassName(forKey: "assignedClassName"))"
        students = Database.singleton.fetchStudents(byAttandence: 0, byclassId: AppFunctions.getAssignedClassId(forKey: "assignedClassId"))
        notificationToken = students?.observe {[weak self] change in
            self!.studentAbsentLbl.text = "Students Absent (\(String(describing: self!.students!.count)))"
        }
        if AppFunctions.getAssignedClassId(forKey: "assignedClassId") != 0 { // && pref id isAssigned == true
            let id = AppFunctions.getAssignedClassId(forKey: "assignedClassId")
            getAssignedClassData(classId: id)
        }
    }
    deinit {
        notificationToken?.invalidate()
    }
    func getAssignedClassData(classId: Int) {
        return APIService.singelton
            .getClassById(classId: classId)
            .subscribe({ [weak self] _ in
                TestDebug.debugInfo(fileName: "", message: "\(classId)")
//                self?.notificationToken = Database.singleton.fetchStudentsForAttendence(byCLassId: classId).observe { [weak self] change in
//                    switch change {
//                    case .initial(let initial):
//                        TestDebug.debugInfo(fileName: "", message: "INIT: \(initial)")
//                        //self!.viewModel.getStudentsData(id: classId)
//                    case .update(_, let deletions, let insertions, let modifications):
//                        print(modifications)
//                        TestDebug.debugInfo(fileName: "", message: "MODIFY: \(modifications)")
//                        TestDebug.debugInfo(fileName: "", message: "MODIFY: \(insertions)")
//                    case .error(let error):
//                        TestDebug.debugInfo(fileName: "", message: "ERROR:\(error)")
//                    }
//                }
            })
            .disposed(by: self.disposeBag)
    }

    func bindViewModel() {
        viewModel
            .studentCell
            .asObservable()
            .observeOn(MainScheduler.instance)
            .bind(to: studentsViewController.studentCells)
            .disposed(by: disposeBag)

    }
}

标签: swiftrealmalamofirerx-swift

解决方案


这个问题有点不清楚,所以这可能完全不合时宜。让我添加一些可能适合答案的信息。

领域结果对象是实时更新对象。例如,如果您加载了一些 Dog 对象

var dogResults: Results<DogClass>? = nil //define a class var
self.dogResults = realm.objects(Dog.self) //populate the class var

然后,如果您在代码中的其他位置更改狗的名称,dogResults 类 var 将包含该狗的更新名称。

因此,您不需要不断刷新该结果对象,因为它会自动完成。

如果您想收到这些更改的通知,您可以向 dogResults 类 var 添加一个观察者。

self.notificationToken = self.dogResults!.observe { (changes: RealmCollectionChange) in

当您第一次添加观察者时,.initial 块被调用一次且仅一次。之后的任何时候都不会调用它。这就是您要说的地方,刷新您的 tableView 以显示初始数据。

当数据更改时,会调用 .update 块。您可以选择再次重新加载 tableView,也可以根据更改的数据对 tableView 进行细粒度更改。

你的问题说明

但是要始终获得最新的响应,应用程序需要在每个 viewWillAppear 上调用网络 API。

这不是必需的,因为类 var dogResults 始终包含更新的信息。

沿着同样的路线

每次调用 API 时都将响应保存到数据库中

没有必要,因为您需要更新 UI 的唯一时间是在 .update 块内。

最后,这段代码似乎格格不入

self!.viewModel.getStudentsData(id: classId)

但是,问题中没有足够的代码来理解为什么在 .initial 中调用它 - 您可能需要考虑使用上面介绍的方法而不是轮询更新。


推荐阅读