首页 > 解决方案 > 领域丢失函数外的数据

问题描述

我仍然是编码新手,我在使用 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这就是问题所在,我还在结尾处包含了控制台消息。打印报表结果。

//
//  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)

致命错误出现在 switch 语句中,changeUIBased...特别switch currentOrder.first!.orderStatus是它抛出“意外发现 nil ...”错误的地方。

我知道这有点啰嗦,所以提前感谢您的帮助。

[编辑]

为澄清起见,我已经从prepareRealm函数中删除了与 Realm 通知有关的所有代码,我将 分配给了currentOrder[0]一个被调用的变量thisOrder,以使 this 成为 typeObject而不是 type Results。然后我打印thisOrder订单正确打印值的值。唉,然后我thisOrder在计时器闭包内打印,它现在打印为 [invalid object]。计时器在某种意义上是象征性的,每当值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 的问题向他们提交错误。

标签: iosswiftrealm

解决方案


Order必须将所有属性标记为dynamic修饰符,Realm 才能覆盖 getter/setter。

所以你的订单看起来像这样:

class Order: Object {
    @objc dynamic var firstName = ""
    @objc dynamic var lastName = ""
    ....
}

推荐阅读