swift - 如果从已保存在数据库(领域)中更改,则观察 JSON 响应
问题描述
在. Alamofire
_ Realm
_ RxSwift
一切正常,但当天的需要是防止总是刷新网络调用的视图。现在应用程序的行为就像我将 JSON 响应复制到数据库,然后从数据库更新视图。但是要始终获得最新的响应,应用程序需要在每个viewWillAppear
. 但是我不想获取所有数据库数据并搜索新响应是否有任何更改然后显示它。如果数据与以前加载到数据库中的数据不同,那么我Swift
是否可以观察到任何东西,然后只有应用程序会更新其视图。Alamofire
Realm
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
它来监视它,并且因为数据库总是被刷新并且每次都调用这个块。我需要一些东西来监控数据库中数据值的变化。领域中有什么东西吗?
好的,我正在这样做,有一个 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)
}
}
解决方案
这个问题有点不清楚,所以这可能完全不合时宜。让我添加一些可能适合答案的信息。
领域结果对象是实时更新对象。例如,如果您加载了一些 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 中调用它 - 您可能需要考虑使用上面介绍的方法而不是轮询更新。
推荐阅读
- python - 使用 Flask 通过 api_key POST 到 Elasticsearch 托管服务
- jpa - 没有正在进行的交易 - Karaf + Camel 3.3。0 + JPA
- mysql - 在透视 mysql 查询中获取百分比
- docker - GitHub Actions:如何在容器内运行测试
- javascript - 有没有办法知道一个方法是否从一个类中运行?
- java - java.lang.IndexOutOfBoundsException: Index: 1, Size: 1 on backpressed fragment
- javascript - 如何使用 laravel 在引导模式中为每个 id 获取动态数据?
- algorithm - 构造一个有效的最小生成树,使得 G 中给定的顶点子集是叶子 + 证明
- sql - 无论如何在搜索栏中使用逗号在 SQL Query 中显示多行
- javascript - 欢迎信息的问题 | 不和谐.js