My app have a e-store purchase function which use billing address. I also have a Billing Address List and tap on the address cell in the list will go inside to the edit address page. But after I run the purchase function and edit the address, and go back to the Billing Address List page, the app will stop running. (If I edit address without make purchase everything would be fine)
The error show:
Terminating app due to uncaught exception 'RLMException', reason: 'Object has been deleted or invalidated.'
And:
libc++abi.dylib: terminating with uncaught exception of type NSException
And also in AppDelegate.swift,
"Thread 1: signal SIGABRT"
error show in this line:
class AppDelegate: UIResponder, UIApplicationDelegate {
Here's the Billing Address List code (which contain many billing address cells, tap on cell will go inside to the edit address function:
import Foundation
import PKHUD
class ManageBillingAddressVC: _BaseViewController {
@objc var billingAddress: [UserBillingAddress] = [] { //= Address.getAllAddressLists()
didSet {
emptyView.isHidden = billingAddress.count != 0
}
}
let viewModel = BillingAddressViewModel()
@IBOutlet weak var tblBillingAddress: UITableView!
@IBOutlet weak var emptyView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
setUpNavBarWithAttributes(navtitle: "Manage Billing Address", setStatusBarStyle: .default, isTransparent: false, tintColor: .white, titleColor: Color.Tuna.instance(), titleFont: FontBook.AdobePro.navSize(), isBackHidden: false, isShadowIncluded: true, rightBarButton: [showGoldPlusBtn()])
viewModelCallBack()
initTableViews()
observeAdressUpdateNotification()
}
@objc func viewModelCallBack() {
viewModel.beforeApiCall = {
HUD.show(.systemActivity)
}
viewModel.afterApiCall = {
HUD.hide()
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.setNavigationBarHidden(false, animated: true)
fetchBillingAddress()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.navigationController?.setNavigationBarHidden(true, animated: true)
}
@objc func updateDeliveryAddressTable() {
fetchBillingAddress()
}
@objc func fetchBillingAddress() {
viewModel.getBillingAddress(success: {
self.billingAddress.removeAll()
for address in $0{
if address.crm_address_order != "postal_address"{
self.billingAddress.append(address)
}
}
self.tblBillingAddress.reloadData()
}) {
self.showAlert($0)
}
}
@objc func observeAdressUpdateNotification() {
NotificationCenter.default.addObserver(self, selector: #selector(updateDeliveryAddressTable), name: Constants.billingAddress, object: nil)
}
override func rightBarButtonClicked() {
viewModel.getBillingAddress(success: {
self.billingAddress.removeAll()
for address in $0{
if address.crm_address_order != "postal_address"{
self.billingAddress.append(address)
}
}
if self.billingAddress.count >= 3{
self.showAlert("Addresses are limited to a maximum of 3")
}else{
self.pushViewControllerWithSlideUpEffect(vc: UIStoryboard.AddBillingAddress())
}
self.tblBillingAddress.reloadData()
}) {
self.showAlert($0)
}
}
@objc func initTableViews() {
tblBillingAddress.register(UINib(nibName : "BillingAddressCell", bundle : nil), forCellReuseIdentifier: "BillingAddressCell")
tblBillingAddress.dataSource = self
tblBillingAddress.delegate = self
tblBillingAddress.separatorStyle = .none
tblBillingAddress.contentInset = UIEdgeInsetsMake(20, 0, 20, 0)
tblBillingAddress.rowHeight = UITableViewAutomaticDimension
tblBillingAddress.estimatedRowHeight = 165 //108
}
deinit {
NotificationCenter.default.removeObserver(self)
}
}
extension ManageBillingAddressVC: DeliveryAddressCellDelegate {
@objc func didSelectBillingAddress(address: UserBillingAddress, index: Int) {
guard let vc = UIStoryboard.AddBillingAddress() as? AddBillingAddressVC else { return }
vc.billingAddress = address
pushViewControllerWithSlideUpEffect(vc: vc)
}
}
extension ManageBillingAddressVC: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return billingAddress.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "BillingAddressCell" ) as! BillingAddressCell
cell.delegate = self
cell.billingAddress = billingAddress[indexPath.row]
//cell.address = billingAddress[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: false)
}
}
Here's my billing address cell in the address list code:
import UIKit
class BillingAddressCell: DeliveryAddresCell {
@objc var billingAddress: UserBillingAddress? {
didSet {
billingAddress.flatMap {
lblName.text = "\($0.salutation ?? ""). \($0.firstname ?? "") \($0.lastname ?? "")"
lblAddress.text = $0.street?.first
lblPhoneNo.text = $0.telephone
lblUnitNo.text = $0.unit_number
lblStateCountry.text = "\($0.region?.first?.region ?? ""), \($0.postcode ?? "")"
if let id = $0.country_id, let countryID = Int(id) {
lblCountry.text = CountryList.getCountryName(id: countryID)
}
}
}
}
override func btnEditTapped(_ sender: UIButton) {
delegate?.didSelectBillingAddress(address: billingAddress!, index: tag)
}
}
Here's the function to fetch/updating billing address:
class UserBillingAddress: UserBillingAddressInformation {
@objc var id : String?
@objc var customerID: String?
required convenience init?(map: Map){
self.init()
}
override func mapping(map: Map){
super.mapping(map: map)
id <- map["id"]
customerID <- map["customer_id"]
}
}
Here's the code for what happened after tap the "checkout" button in the purchase process:
@IBAction func btnCheckoutTapped(_ sender: UIButton) {
guard let card = defaultCreditCard, let address = defaultAddress else { return }
baseViewModel.checkCartLimit(success: { checkLimit in
if let limitErrors = checkLimit.limit_errors {
if limitErrors.count > 0 {
self.baseViewModel.handleCheckLimitError?(checkLimit)
} else {
guard let processingVC = UIStoryboard.ProcessingOrderVC() as? ProcessingOrderVC else { return }
// present processing view
processingVC.modalPresentationStyle = .overCurrentContext
processingVC.defaultAddress = address
processingVC.defaultCreditCard = card
processingVC.isSkyDollarPurchaseForMembership = self.isSkyDollarPurchaseForMembership
processingVC.isMembershipProduct = self.isMembershipProduct
processingVC.isFullSkyDollarPurchase = self.isFullSkyDollarPurchase
self.present(processingVC, animated: true)
}
} else {
guard let processingVC = UIStoryboard.ProcessingOrderVC() as? ProcessingOrderVC else { return }
// present processing view
processingVC.modalPresentationStyle = .overCurrentContext
processingVC.defaultAddress = address
processingVC.defaultCreditCard = card
processingVC.isSkyDollarPurchaseForMembership = self.isSkyDollarPurchaseForMembership
processingVC.isMembershipProduct = self.isMembershipProduct
self.present(processingVC, animated: true)
}
}, failure: { (errorMessage) in
self.showAlert(errorMessage)
})
}
And here's the code for the purchase processing after tap on checkout button:
import UIKit
class ProcessingOrderVC: UIViewController {
@IBOutlet weak var orderProcessingStackView: UIStackView!
@IBOutlet weak var orderProcessErrorStackView: UIStackView!
@IBOutlet weak var btnOkay: UIButton!
@IBOutlet weak var lblError: UILabel!
@objc var isMembershipProduct : Bool = false
@objc var isSkyDollarPurchaseForMembership : Bool = false
@objc var isFullSkyDollarPurchase: Bool = false
let checkoutViewModel = CheckoutStep2ViewModel()
@objc var defaultAddress = Address()
@objc var defaultCreditCard = CreditCardData()
override func viewDidLoad() {
super.viewDidLoad()
let attributedTitle = NSAttributedString(string: "TRY AGAIN", attributes: [NSAttributedStringKey.kern: 1.3, NSAttributedStringKey.font : FontBook.Bold.of(size: 13.0), NSAttributedStringKey.foregroundColor: UIColor.white])
btnOkay?.setAttributedTitle(attributedTitle, for: .normal)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if isSkyDollarPurchaseForMembership{
self.checkoutViewModel.placeOrderWithSkyDollarForMembership(address: defaultAddress, success: { orderID in
(UIStoryboard.CheckoutStep3VC() as? CheckoutStep3VC).flatMap {
$0.orderID = orderID
$0.hero.modalAnimationType = .fade
self.present($0, animated: false, completion: nil)
}
}) { (error) in
self.showError()
}
}else{
self.checkoutViewModel.placeOrder(address: defaultAddress, card: defaultCreditCard, isFullSkyDollarPurchase: isFullSkyDollarPurchase, success: { orderID in
(UIStoryboard.CheckoutStep3VC() as? CheckoutStep3VC).flatMap {
$0.orderID = orderID
$0.hero.modalAnimationType = .fade
self.present($0, animated: false, completion: nil)
}
}) { (error) in
self.showError()
}
}
}
@IBAction func btnCloseTapped(_ sender: UIButton) {
self.dismiss(animated: true, completion: nil)
}
@IBAction func btnOkayTapped(_ sender: UIButton) {
self.dismiss(animated: true, completion: nil)
}
@objc func showError() {
orderProcessErrorStackView.isHidden = false
orderProcessingStackView.isHidden = true
}
}
Here's the defaultAddress variable and class Address:
@objc var defaultAddress = Address()
class Address : Object, Mappable{
@objc dynamic var id: Int = 0
@objc dynamic var customerId: Int = 0
@objc dynamic var region: Region? = nil
@objc dynamic var region_id: Int = 0
@objc dynamic var countryId: String = ""
let streets = List<Street>()
@objc dynamic var telephone: String = ""
@objc dynamic var postcode: String = ""
@objc dynamic var city: String = ""
@objc dynamic var prefix: String = ""
@objc dynamic var company: String = ""
@objc dynamic var firstname: String = ""
@objc dynamic var lastname: String = ""
@objc dynamic var defaultShipping: Bool = false
@objc dynamic var defaultBilling: Bool = false
var addressAttributes = List<AddressAttributes>()
required convenience init?(map: Map){
self.init()
}
By right the purchase process will not update the address, but if I don't make purchase, then editing the address have no error. The error only happens after making purchase process. I really don't know what happened. Can anyone suggest what might be the problem?
I'm quite new in iOS so please be more precise on how to solve the issue. Thank you very much!