我还是编码新手,我遇到了Realm Cloud的一些问题,无论我多么努力,似乎都无法解决。我正在尝试创建“点击并收集”订单跟踪器的示例,当orderState
对象属性在1-4(数字代表不同阶段)之间更改时,它将UI更改为相应的屏幕。我已经订阅了Results<Order>
对象的Realm观察,在该函数中,观察和通知发生的地方currentOrder
包含正确的Order
对象。但是,从观察到的changes
有一个开关,它调用一个函数来更新到正确的UI。在这个称为函数currentOrder
的内部突然不包含任何数据,currentOrder
是在全局范围内定义的,因此我无法理解为什么会这样。我过滤了Results<Order>
只查询匹配的ID(通过主键匹配并绕过Results
时,观察结果似乎对我没有任何作用)。
我将在此处添加整个VC,唯一重要的注意事项是currentOrderID
属性是从先前将对象写入Realm的VC传递过来的。如果向下滚动直到func prepareRealm
和func changeUIBasedOnStatus
,这就是问题所在,并且我还在末尾的inc中包含了控制台消息。打印报表结果。
//
// TrackerViewController.swift
// HG Demo
//
// Created by Adam Woodcock on 12/03/2019.
// Copyright © 2019 Adam Woodcock. All rights reserved.
//
import UIKit
import RealmSwift
import Lottie
import MapKit
import CoreLocation
class TrackerViewController: UIViewController {
//Lottie Views
@IBOutlet weak var orderPlacedAnimation: LOTAnimationView!
@IBOutlet weak var orderConfirmedAnimation: LOTAnimationView!
@IBOutlet weak var orderPickedAnimation: LOTAnimationView!
@IBOutlet weak var orderCompleteAnimation: LOTAnimationView!
//Outlets
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var headingLabel: UILabel!
@IBOutlet weak var bodyLabel: UILabel!
@IBOutlet weak var progressImage: UIImageView!
let config = SyncUser.current?.configuration()
var realm : Realm!
var currentOrder : Results<Order>!
var currentOrderID : String!
var subscription : SyncSubscription<Order>!
var subscriptionToken : NotificationToken?
var notificationToken : NotificationToken?
override func viewDidLoad() {
super.viewDidLoad()
realm = try! Realm(configuration: config!)
currentOrder = realm.objects(Order.self).filter("orderID = %@", currentOrderID!)
prepareRealm()
startOrderPlacedAnimation()
}
//Lottie functions
func startOrderPlacedAnimation() {
orderPlacedAnimation.setAnimation(named: "orderPlaced")
orderPlacedAnimation.play()
orderPlacedAnimation.loopAnimation = true
orderConfirmedAnimation.isHidden = true
orderCompleteAnimation.isHidden = true
headingLabel.text = "Thank you! Your order has been placed!"
bodyLabel.text = "Your order has been successfully placed, we'll notify you once this has been accepted!"
progressImage.image = UIImage(named: "singleCheck")
}
func startOrderConfirmedAnimation() {
orderConfirmedAnimation.isHidden = false
orderConfirmedAnimation.setAnimation(named: "undedited")
orderConfirmedAnimation.play()
orderConfirmedAnimation.loopAnimation = true
orderPlacedAnimation.isHidden = true
orderCompleteAnimation.isHidden = true
headingLabel.text = "It's Official! Your order is confirmed!"
bodyLabel.text = "A team member has confirmed your order, we'll start packing soon!"
progressImage.image = UIImage(named: "doubleCheck")
}
func startOrderPickedAnimation() {
orderPickedAnimation.isHidden = false
orderPickedAnimation.setAnimation(named: "orderPicked")
orderPickedAnimation.play()
orderPickedAnimation.loopAnimation = true
orderPlacedAnimation.isHidden = true
orderConfirmedAnimation.isHidden = true
orderCompleteAnimation.isHidden = true
headingLabel.text = "Woosh! Your order is being packed!"
bodyLabel.text = "A team member with extremely steady hands is currently packing your order!"
progressImage.image = UIImage(named: "tripleCheck")
}
func startOrderCompleteAnimation() {
orderCompleteAnimation.isHidden = false
orderCompleteAnimation.setAnimation(named: "orderComplete")
orderCompleteAnimation.play()
orderCompleteAnimation.loopAnimation = true
orderPlacedAnimation.isHidden = true
orderConfirmedAnimation.isHidden = true
orderPickedAnimation.isHidden = true
headingLabel.text = "Woohoo! Your order is ready to collect!"
bodyLabel.text = "We're as excited as you, so what're you waiting for? Come and grab it!"
progressImage.image = UIImage(named: "quadrupleCheck")
}
func startOrderHasBeenCollectedAnimation() {
}
func startErrorWithOrderAnimation() {
}
//Realm functions
//Assigning the current order to the Order object variable
func prepareRealm() {
subscription = currentOrder.subscribe(named: "current-order", limit: nil)
subscriptionToken = subscription.observe(\.state, options: .initial, { (state) in })
notificationToken = currentOrder.observe({ (changes) in
switch changes {
case .initial:
self.changeUIBasedOnStatus(sender: "Initial")
case .update :
self.changeUIBasedOnStatus(sender: "Update")
case .error(let error):
fatalError(error.localizedDescription)
}
})
print("Realm prepared, this is the object: \(currentOrder!)")
titleLabel.text = "\(String(currentOrder.first!.firstName))'s Order #\(currentOrder.first!.orderID!)"
}
func changeUIBasedOnStatus(sender: String) {
print("The switch realm object contains: \(currentOrder!), sender: \(sender)")
switch currentOrder.first!.orderStatus {
case 1:
startOrderPlacedAnimation()
case 2:
startOrderConfirmedAnimation()
case 3:
startOrderPickedAnimation()
case 4:
startOrderCompleteAnimation()
case 5:
startOrderHasBeenCollectedAnimation()
default:
startErrorWithOrderAnimation()
}
}
//IBActions
@IBAction func callUsTapped(_ sender: Any) {
guard let number = URL(string: "tel://+441522684865") else { return }
UIApplication.shared.open(number, options: [:], completionHandler: nil)
}
@IBAction func openingHoursTapped(_ sender: Any) {
}
@IBAction func directionsTapped(_ sender: Any) {
//Creating an action sheet to ask the user whether they'd like to use Apple Maps or Google Maps
let alert = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
//Adding the action and functionality to load Apple maps
alert.addAction(UIAlertAction(title: "Apple Maps", style: .default, handler: { (action) in
//Creating a placemark object to pass into the map item
let placemark = MKPlacemark(coordinate: CLLocationCoordinate2DMake(53.203498, -0.611785))
//Initialising a new map item object with the pre-made placemark object
let mapItem = MKMapItem(placemark: placemark)
mapItem.phoneNumber = "+44 (0) 1522 684865"
//Setting the launch options to default to driving directions
let launchOptions = [MKLaunchOptionsDirectionsModeKey:MKLaunchOptionsDirectionsModeDriving]
//Telling the map item object to open that specific location in maps
mapItem.openInMaps(launchOptions: launchOptions)
}))
alert.addAction(UIAlertAction(title: "Google Maps", style: .default, handler: { (action) in
//Add Google maps functionality
}))
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { (_) in
alert.dismiss(animated: true, completion: nil)
}))
present(alert, animated: true, completion: nil)
}
}
控制台消息:
2019-03-14 17:00:52.132718+0000 HG Demo[51949:3038807] Sync: Connection[1]: Connected to endpoint '3.121.59.66:443' (from '192.168.0.21:64953')
Realm prepared, this is the object: Results<Order> <0x7fdce8c2d370> (
[0] Order {
firstName = Adam;
lastName = Woodcock;
orderID = 4431295;
timestamp = 2019-03-14 17:00:54 +0000;
orderStatus = 1;
isFulfilled = 0;
}
)
The switch realm object contains: Results<Order> <0x7fdce8c2d370> (
), sender: Initial
(lldb)
致命错误出现在changeUIBased...
的switch语句上,特别是switch currentOrder.first!.orderStatus
会引发“意外发现nil ...”错误。
我知道这有点麻烦,所以在此先感谢您的帮助。
[编辑]
为了澄清起见,我从prepareRealm
函数中删除了所有与Realm通知有关的代码,我将currentOrder[0]
分配给一个名为thisOrder
的变量,以使其成为类型Object
而非Results
类型。然后,我打印thisOrder
的值,订单将正确地打印这些值。 las,然后我在计时器闭包内打印thisOrder
,现在将其打印为[无效对象]。从意义上说,计时器是象征性的,只要将值currentOrder
或thisOrder
传递到prepareRealm
函数之外或传递给闭包,对象就无效。我已经在不同的应用程序中多次执行此操作,甚至在单独的VC上的此应用程序中都执行了此操作,并且100%顺利运行,所以我真的不明白为什么会这样。
func prepareRealm() {
realm = try! Realm(configuration: config!)
currentOrder = realm.objects(Order.self).filter("orderID = %@", currentOrderID)
thisOrder = currentOrder[0]
print("This is thisOrder: \(thisOrder!)")
let timer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { (timer) in
print(self.thisOrder)
}
}
[编辑2] 我将Realm更新到最新版本,一切开始正常!我以为这是问题的根源,但是自那以后我一直在进行不同的元素构建和测试等工作,突然之间,每次它都开始再次执行此操作,我觉得这可能是Realm的问题,因此与他们一起提交错误。
答案 0 :(得分:0)
Order
必须具有用dynamic
修饰符标记的所有属性,Realm才能覆盖getter / setter。
因此您的订单将如下所示:
class Order: Object {
@objc dynamic var firstName = ""
@objc dynamic var lastName = ""
....
}
答案 1 :(得分:0)
这里的问题是,在后台发生了很多事情,这些事情可能以不一致的顺序发生,因为这涉及到与服务器的对话。可能引起问题的一系列可能的事件是:
Order
对象如果这些操作以略有不同的顺序发生(例如,服务器恰巧同时处理对象创建和订阅创建),则一切正常。
有几种方法可以解决此问题:
Results<Order>
并处理.first
暂时为nil
的情况.complete
状态,然后再设置对象观察器。