Создание приложения, которое использует Alamofire
и Realm
для сетевых вызовов и хранения данных соответственно с помощью RxSwift
. Все работает нормально, но необходимость дня в том, чтобы постоянно обновлять представления о сетевых вызовах. На данный момент поведение приложения похоже на то, что я копирую ответ JSON в БД, а затем обновляю представления из БД. Но чтобы всегда получать последние ответы, приложение должно вызывать сетевой API на каждом 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
для мониторинга этого, и поскольку база данных всегда обновлялась, и этот блок вызывается каждый раз. Мне нужно что-то, что контролирует, что значение данных изменилось в БД. Есть ли что-то в Realm для этого?
Ссылка на GIF
Хорошо, я делаю это так, есть viewController, в котором у меня есть контейнерное представление, которое имеетКоллекция смотреть как ребенок.
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.
вот код ViewModel, из которого я получаю свои данные и создаю наблюдаемое для CollectionView.
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)
}
}