How to save a users progress in a Quiz game - ios

I am wondering how I should save the users progress in my quiz game. Every time the user closes the app it resets. Here's my code:
import UIKit
import GoogleMobileAds
class ViewController: UIViewController, GADBannerViewDelegate {
//Place your instance variables here
let allQuestions = QuestionBank()
var pickedAnswer : Bool = false
var questionNumber : Int = 0
var score : Int = 0
let googleAdTestID: String = "ca-app-pub-3940256099942544/2934735716"
#IBOutlet weak var questionLabel: UILabel!
#IBOutlet weak var scoreLabel: UILabel!
#IBOutlet weak var bannerView: GADBannerView!
#IBOutlet weak var progressLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
print("Google Mobile Ads SDK version: " + GADRequest.sdkVersion() )
bannerView.adUnitID = googleAdTestID
bannerView.rootViewController = self
bannerView.load(GADRequest())
nextQuestion()
}
#IBAction func skipButton(_ sender: Any) {
skip()
}
func skip() {
if score <= 4 {
}
else if score >= 5 {
questionNumber = questionNumber + 1
nextQuestion()
score = score - 5
scoreLabel.text = "Coins: \(score)"
}
}
#IBAction func answerPressed(_ sender: AnyObject) {
if sender.tag == 1 {
pickedAnswer = true
}
else if sender.tag == 2 {
pickedAnswer = false
}
checkAnswer()
questionNumber = questionNumber + 1
nextQuestion()
}
func updateUI() {
scoreLabel.text = "Coins: \(score)"
progressLabel.text = "Question Number: \(questionNumber + 1)"
}
func nextQuestion() {
if questionNumber <= 37 {
questionLabel.text = allQuestions.list[questionNumber].questionText
updateUI()
}
else {
let alert = UIAlertController(title: "Awesome", message: "You have finished all the questions!! Do you want to start over?", preferredStyle: .alert)
let restartAction = UIAlertAction(title: "Restart", style: .default, handler: { (UIAlertAction) in
self.startOver()
})
alert.addAction(restartAction)
present(alert, animated: true, completion: nil)
}
}
func checkAnswer() {
let correctAnswer = allQuestions.list[questionNumber].answer
if correctAnswer == pickedAnswer {
print("Correct!")
ProgressHUD.showSuccess("Correct!!")
// varible += 1 same as varible = varible + 1
score += 1
}
else {
print("wrong!")
ProgressHUD.showError("Wrong!!")
}
}
func startOver() {
questionNumber = 0
nextQuestion()
score = 0
scoreLabel.text = "Score: 0"
}
}

Save
// save necessory information using UserDefaults
// Add this code at your business logic
let database = UserDefaults.standard
let questionNumber = 2
database.set(questionNumber, forKey: "LAST_ANSWERED_QUESTION_NUMBER")
let score = 23
database.set(score, forKey: "QUIZ_SCORE")
let sync = database.synchronize()
if sync{
print("userdefaults - sync done")
}else{
print("userdefaults - failed to sync")
}
Fetch
//access stored values from UserDefaults
// Add this code at your business logic
let database = UserDefaults.standard
if let lastAnswredQuestion = database.value(forKey: "LAST_ANSWERED_QUESTION_NUMBER") as? Int{
print("LAST_ANSWERED_QUESTION_NUMBER \(lastAnswredQuestion)")
}
if let scored = database.value(forKey: "QUIZ_SCORE") as? Int{
print("QUIZ_SCORE \(scored)")
}
NOTE: The data will be stored until the app is installed, if the user deletes the app then whole data will vanish from UserDefaults.

Related

Table View Remains Blank Despite Core Data "Successfully Saved!"

My first Screen
Then I go on to login screen by clicking "Sign In" button.
My Login Screen
Next I go on to the screen with the table view... This is how it looks when I successfully login:
Table View Screen
Next I click on the add resident icon(the small icon with the person and the plus sign) and I get this page in which I can register a resident.
Register Resident Screen
Next, what should happen is, when I click the add button, it should "successfully save" my core data and it should automatically dismiss this view and got back to the "Table View Screen" (Previous Picture). But it doesn't! When it goes to the Table View Screen, it should register the new resident with the name "Bob" and the Age "45" saved in my core data successfully. Please help me fix these 2 problems...
This is the view controller in which my data is saved using core data. I get "successfully saved" when I click the "Add" button.
// ResidentViewController.swift
// 77 Safety Updater
//
// Created by Tejas Ravishankar on 17/04/20.
// Copyright © 2020 Tejas Ravishankar. All rights reserved.
//
//MARK:- Import Modules
import UIKit
import CoreData
let appDelegate = UIApplication.shared.delegate as? AppDelegate
class ResidentViewController: UIViewController {
//MARK:- Variables
var residentCount : Int = 0
var travelSwitchIsOn : Bool = false
var movedInSwitchIsOn : Bool = false
var stateColor : UIColor = .systemGreen
var name : String = ""
var age : Int = 0
var type : Int = 0
var risk : String = ""
//MARK:- IB Outlets
#IBOutlet weak var nameTextField: UITextField!
#IBOutlet weak var riskLabel: UILabel!
#IBOutlet weak var ageTextField: UITextField!
#IBOutlet weak var workingSwitch: UISwitch!
#IBOutlet weak var movedInSwitch: UISwitch!
#IBOutlet weak var movedInLabel: UILabel!
#IBOutlet weak var yesLabel: UILabel!
#IBOutlet weak var noLabel: UILabel!
//MARK:- Add Button Action
#IBAction func addButtonPressed(_ sender: UIButton)
{
residentCount += 1
let ageText = ageTextField.text
if ageText!.count >= 4
{
ageTextField.text = ""
ageTextField.placeholder = "Invalid Age!"
}
name = nameTextField.text!
age = Int(ageText!)!
travelSwitchIsOn = workingSwitch.isOn
movedInSwitchIsOn = movedInSwitch.isOn
if movedInSwitchIsOn == true || travelSwitchIsOn == true
{
risk = "Medium"
}
else if movedInSwitchIsOn == true && travelSwitchIsOn == true
{
risk = "High"
}
else
{
risk = "Low"
}
if age <= 4
{
stateColor = .systemRed
}
if age <= 20
{
stateColor = .systemGreen
}
else if age >= 55 && age <= 90
{
stateColor = .systemRed
}
else if age > 90
{
stateColor = .systemPurple
}
if nameTextField.text != "" && ageTextField.text != ""
{
saveData { (complete) in
if complete
{
self.dismiss(animated: true, completion: nil)
}
}
}
}
//MARK:- Core Data Save Function
func saveData(completion:(_ finished:Bool) -> ())
{
guard let managedContext = appDelegate?.persistentContainer.viewContext
else
{
return
}
let data = UserData(context: managedContext)
data.name = name
data.age = Int32(age)
data.risk = risk
do {
try managedContext.save()
print("Successfully Saved!")
completion(true)
}
catch {
debugPrint("Save Failed: \(error.localizedDescription)")
}
}
//MARK:- Resident Type Action
#IBAction func residentTypeChanged(_ sender: UISegmentedControl)
{
let type = sender.selectedSegmentIndex
if type == 0
{
movedInSwitch.isHidden = true
movedInLabel.isHidden = true
yesLabel.isHidden = true
noLabel.isHidden = true
}
if type == 1
{
movedInSwitch.isHidden = false
movedInLabel.isHidden = false
yesLabel.isHidden = false
noLabel.isHidden = false
}
}
//MARK:- When App Screen Loads Up
override func viewDidLoad()
{
super.viewDidLoad()
movedInSwitch.isHidden = true
movedInLabel.isHidden = true
riskLabel.text = ""
ageTextField.delegate = self
yesLabel.isHidden = true
noLabel.isHidden = true
}
}
extension ResidentViewController : UITextFieldDelegate
{
#IBAction func ageTextField(_ sender: UITextField)
{
let age = Int(ageTextField.text!)
if age! <= 4
{
riskLabel.textColor = UIColor.systemGreen
stateColor = .systemRed
riskLabel.text = "High Risk Host!"
}
if age! <= 20
{
riskLabel.textColor = UIColor.systemGreen
stateColor = .systemGreen
riskLabel.text = "Low Risk Host!"
}
else if age! >= 55 && age! <= 90
{
riskLabel.textColor = UIColor.systemRed
stateColor = .systemRed
riskLabel.text = "High Risk Host!"
}
else if age! > 90
{
riskLabel.textColor = UIColor.systemPurple
stateColor = .systemPurple
riskLabel.text = "Very High Risk Host!"
}
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
ageTextField.endEditing(true)
return true
}
func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
if ageTextField.text != ""
{
return true
}
else
{
textField.placeholder = "Type Something!"
return false
}
}
}
This is the table view controller which contains the configuration of the cell and the table! I have also fetched the data in this view controller. In the output: I get "successfully fetched!" Ignore the login view controller.swift as it just contains my authentication using user defaults... There are no parts of CoreData in that file...
//
// StatisticsViewController.swift
// 77 Safety Updater
//
// Created by Tejas Ravishankar on 17/04/20.
// Copyright © 2020 Tejas Ravishankar. All rights reserved.
//
//MARK:- Import Modules
import UIKit
import CoreData
class StatisticsViewController: UIViewController
{
var details : [UserData] = []
#IBOutlet weak var informationTableView: UITableView!
override func viewDidLoad()
{
super.viewDidLoad()
informationTableView.delegate = self
informationTableView.dataSource = self
informationTableView.isHidden = false
informationTableView.register(UINib(nibName: "InformationTableViewCell", bundle: nil), forCellReuseIdentifier: "informationCell")
}
func fetchData(completion:(_ complete:Bool) -> ())
{
guard let managedContext = appDelegate?.persistentContainer.viewContext
else
{
return
}
let fetchRequest = NSFetchRequest<UserData>(entityName: "UserData")
do {
try managedContext.fetch(fetchRequest) as![UserData]
print("Successfully Fetched!")
completion(true)
}
catch
{
debugPrint("Failed To Fetch: \(error.localizedDescription)")
completion(false)
}
}
override func viewWillAppear(_ animated: Bool)
{
super.viewWillAppear(animated)
self.fetchData { (complete) in
if complete
{
if details.count >= 1
{
informationTableView.isHidden = true
}
else
{
informationTableView.isHidden = false
}
}
}
}
}
extension StatisticsViewController : UITableViewDelegate,UITableViewDataSource
{
func numberOfSections(in tableView: UITableView) -> Int
{
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
details.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = informationTableView.dequeueReusableCell(withIdentifier: "informationCell") as! InformationTableViewCell
let userDetail = details[indexPath.row]
cell.configureCell(userDetail)
return cell
}
}
This is my table view cell cocoa touch class in which I configure the data stored in the cells.
//
// InformationTableViewCell.swift
// 77 Safety Updater
//
// Created by Tejas Ravishankar on 26/04/20.
// Copyright © 2020 Tejas Ravishankar. All rights reserved.
//
import UIKit
import CoreData
class InformationTableViewCell: UITableViewCell
{
#IBOutlet weak var ageLabel: UILabel!
#IBOutlet weak var nameLabel: UILabel!
override func awakeFromNib()
{
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool)
{
super.setSelected(selected, animated: animated)
}
func configureCell(_ detail : UserData)
{
self.nameLabel.text = detail.name
self.ageLabel.text = String(describing: detail.age)
}
}
Thanks A Lot!!!

Is there a way to save my firebase model from query in a dictionary in Swift?

first I want to tell you what's my goal:
It should be possible for my app users to tap the + or - Stepper in each ProductTableViewCell to add or remove products to/from Cart.
In the NewOrderViewController where the productTableView is embedded there should be a label for showing the user how many products are in Cart at all. I named it totalProductCountLabel.
Also there should be a label named totalProductPriceLabel, to show the price for all products in Cart. The Cart should be updated every time the user taps the stepper inside a cell, and when the product.counter value is 0, then the product should be automatically removed from Cart.
Yesterday I've had a solution which works perfect:
ViewController
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, CartSelection {
var totalProductCount = 0
var timerEnabled = true
var timerCount = 0
#IBOutlet weak var testTableview: UITableView!
#IBOutlet weak var totalProductCountLabel: UILabel!
#IBOutlet weak var totalProductPriceLabel: UILabel!
var productArray = [Product]()
var cartDictionary = [String: Product]()
override func viewDidLoad() {
super.viewDidLoad()
testTableview.delegate = self
testTableview.dataSource = self
testTableview.allowsSelection = false
productArray.append(Product(name: "Bananen", count: 0, price: 1.09, uuid: UUID().uuidString))
productArray.append(Product(name: "Äpfel", count: 0, price: 0.81, uuid: UUID().uuidString))
productArray.append(Product(name: "Kirschen", count: 0, price: 6.34, uuid: UUID().uuidString))
productArray.append(Product(name: "Tomaten", count: 0, price: 2.68, uuid: UUID().uuidString))
productArray.append(Product(name: "Weintrauben", count: 0, price: 1.48, uuid: UUID().uuidString))
productArray.append(Product(name: "Schokolade", count: 0, price: 0.67, uuid: UUID().uuidString))
testTableview.reloadData()
timerEnabled = true
timerCount = 0
self.totalProductCountLabel.text = "Anzahl Produkte: 0 Stück"
self.totalProductPriceLabel.text = "Preis Produkte: 0.00 €"
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
timerEnabled = false
timerCount = 0
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
timerEnabled = false
timerCount = 5
}
func startTimer() {
_ = Timer.scheduledTimer(withTimeInterval: 10, repeats: true) { timer in
if self.timerEnabled != false && self.timerCount < 4 {
self.testTableview.reloadData()
print("Die Produktliste wurde aktualisiert!")
self.timerCount += 1
} else {
timer.invalidate()
self.timerEnabled = true
self.timerCount = 0
}
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return productArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! TestTableViewCell
cell.product = productArray[indexPath.row]
cell.productNameLabel.text = "\(cell.product.name)"
cell.productPriceLabel.text = "\(cell.product.price)" + " € / kg"
cell.productCountLabel.text = "\(cell.product.count)"
cell.cartSelectionDelegate = self
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 188
}
func addProductToCart(product: Product, uuid: String) {
timerEnabled = false
cartDictionary[uuid] = product
if cartDictionary[uuid]?.count == 0 {
cartDictionary.removeValue(forKey: uuid)
}
calculateTotal()
}
func calculateTotal() {
var totalProductCount = 0
var totalProductPrice = 0.00
for (_,value) in cartDictionary {
totalProductCount += value.count
totalProductPrice += value.totalPrice
}
self.totalProductCountLabel.text = "Anzahl Produkte: \(totalProductCount) Stück"
self.totalProductPriceLabel.text = "Preis Produkte: \(String(format: "%5.2f", totalProductPrice)) €"
self.totalProductCount = totalProductCount
if cartDictionary.isEmpty {
print("Der Einkaufswagen ist leer")
timerEnabled = true
timerCount = 0
startTimer()
}
}
#IBAction func orderButtonTapped(_ sender: UIButton) {
if totalProductCount != 0 {
showOrderConfirmation()
var priceLabel = 0.00
print("Im Einkaufwagen sind folgende Produkte:")
for (key,value) in cartDictionary {
priceLabel += value.totalPrice
print("Produkt: \(value.name) --> \(value.count) __ Produktkennung: \(key)")
}
print("Gesamtpreis: \(priceLabel)")
}
}
func showOrderConfirmation() {
let alert = UIAlertController(title: "Bestellung aufgeben?", message: nil, preferredStyle: .alert)
let okayAction = UIAlertAction(title: "Bestellung aufgeben", style: .default)
let cancelAction = UIAlertAction(title: "Bestellung bearbeiten", style: .destructive)
alert.addAction(okayAction)
alert.addAction(cancelAction)
present(alert, animated: true, completion: nil)
}
}
Product Model
import UIKit
struct Product {
var name = "Produkt"
var count = 0
var price = 0.00
var totalPrice: Double {
return Double((count)) * (price)
}
var uuid = "Example: 20EE8409-230C-4E69-B366-C2EEE03998AF"
}
protocol CartSelection {
func addProductToCart(product : Product, uuid : String)
}
TableViewCell
import UIKit
class TestTableViewCell: UITableViewCell {
var cartSelectionDelegate: CartSelection?
#IBOutlet weak var productCountLabel: UILabel!
#IBOutlet weak var productNameLabel: UILabel!
#IBOutlet weak var productPriceLabel: UILabel!
#IBOutlet weak var totalProductPriceLabel: UILabel!
#IBOutlet weak var stepper: UIStepper!
#IBAction func cellTrashButtonTapped(_ sender: UIButton) {
resetCell()
}
#IBAction func changeProductCountStepper(_ sender: UIStepper) {
productCountLabel.text = Int(sender.value).description
product.count = Int(sender.value)
let totalPriceForProduct = Double(product.count) * product.price
totalProductPriceLabel.text = "Produktgesamtpreis: \(String(format: "%5.2f", totalPriceForProduct)) €"
updateChanges()
}
func updateChanges() {
cartSelectionDelegate?.addProductToCart(product: product, uuid: product.uuid)
}
func resetCell() {
productCountLabel.text = "0"
product.count = 0
totalProductPriceLabel.text = "Produktgesamtpreis: 0.00 €"
stepper.value = 0
updateChanges()
}
override func awakeFromNib() {
super.awakeFromNib()
stepper.autorepeat = true
}
var product : Product!
var productIndex = 0
}
Now my products are stored in Firebase Firestore, so I don't need the append method anymore and using a query instead.
All works as before, my products are shown in the table.
First Problem:
My Outlets from the ViewController are all nil, when pressing the stepper, so I can't see my label counting anymore. (The one in the ViewController), the labels in the cell are working.
Second Problem:
In the first code I am using a dictionary to store all the products in it ("cartDictionary").
This works in the second code, too. But only for one product. The counter in the dictionary is updating if I only tap one cells stepper, but if I tap on another cells stepper, the whole dictionary is empty and it starts with the other product again.
I can't find a solution for this problem, I know the problem is something with my ViewController is not initialized when using my outlets, but if I initialize it my dataSource crashes.
It would be very nice if there's someone helping me, because I can't go on working the last days.
Thank you very much!
My code that's not working:
Product Model
import UIKit
import FirebaseFirestore
protocol CartSelection {
func addProductToCart(product : Product, uuid : String)
}
struct Product {
var documentID: String
var shopID: String
var productName: String
var productPrice: Double
var counter: Int
var uuid: String
var totalPrice: Double {
return Double((counter)) * (productPrice)
}
}
// MARK: - Firestore interoperability
extension Product: DocumentSerializable {
/// Initializes a shop with a documentID auto-generated by Firestore.
init(shopID: String,
productName: String,
productPrice: Double,
counter: Int,
uuid: String) {
let document = Firestore.firestore().shops.document()
self.init(documentID: document.documentID,
shopID: shopID,
productName: productName,
productPrice: productPrice,
counter: counter,
uuid: uuid)
}
/// Initializes a shop from a documentID and some data, from Firestore.
private init?(documentID: String, dictionary: [String: Any]) {
guard let shopID = dictionary["shopID"] as? String,
let productName = dictionary["productName"] as? String,
let productPrice = dictionary["productPrice"] as? Double,
let counter = dictionary["counter"] as? Int,
let uuid = dictionary["uuid"] as? String else { return nil }
self.init(documentID: documentID,
shopID: shopID,
productName: productName,
productPrice: productPrice,
counter: counter,
uuid: uuid)
}
init?(document: QueryDocumentSnapshot) {
self.init(documentID: document.documentID, dictionary: document.data())
}
init?(document: DocumentSnapshot) {
guard let data = document.data() else { return nil }
self.init(documentID: document.documentID, dictionary: data)
}
/// The dictionary representation of the restaurant for uploading to Firestore.
var documentData: [String: Any] {
return [
"shopID": shopID,
"productName": productName,
"productPrice": productPrice,
"counter": counter,
"uuid": uuid
]
}
}
Product TableViewCell
import UIKit
import FirebaseFirestore
class ProductTableViewCell: UITableViewCell {
var cartSelectionDelegate: CartSelection?
#IBOutlet weak var productCountLabel: UILabel!
#IBOutlet weak var productNameLabel: UILabel!
#IBOutlet weak var productPriceLabel: UILabel!
#IBOutlet weak var totalProductPriceLabel: UILabel!
#IBOutlet weak var stepper: UIStepper!
#IBAction func cellTrashButtonTapped(_ sender: UIButton) {
resetCell()
}
#IBAction func changeProductCountStepper(_ sender: UIStepper) {
productCountLabel.text = Int(sender.value).description
product.counter = Int(sender.value)
let totalPriceForProduct = Double(product.counter) * product.productPrice
totalProductPriceLabel.text = "Produktgesamtpreis: \(String(format: "%5.2f", totalPriceForProduct)) €"
updateChanges()
}
func updateChanges() {
cartSelectionDelegate?.addProductToCart(product: product, uuid: product.uuid)
}
func resetCell() {
productCountLabel.text = "0"
product.counter = 0
totalProductPriceLabel.text = "Produktgesamtpreis: 0.00 €"
stepper.value = 0
updateChanges()
}
override func awakeFromNib() {
super.awakeFromNib()
stepper.autorepeat = true
}
var product : Product!
var productIndex = 0
}
Product TableViewDataSource
import UIKit
import FirebaseFirestore
#objc class ProductTableViewDataSource: NSObject, UITableViewDataSource {
private let products: LocalCollection<Product>
var sectionTitle: String?
public init(products: LocalCollection<Product>) {
self.products = products
}
public convenience init(query: Query, updateHandler: #escaping ([DocumentChange]) -> ()) {
let collection = LocalCollection<Product>(query: query, updateHandler: updateHandler)
self.init(products: collection)
}
public func startUpdates() {
products.listen()
}
public func stopUpdates() {
products.stopListening()
}
subscript(index: Int) -> Product {
return products[index]
}
public var count: Int {
return products.count
}
// MARK: - UITableViewDataSource
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sectionTitle
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: Constants.TableView.productCell, for: indexPath) as! ProductTableViewCell
cell.product = products[indexPath.row]
cell.productNameLabel.text = "\(cell.product.productName)"
cell.productPriceLabel.text = "\(cell.product.productPrice)" + " € / kg"
cell.productCountLabel.text = "\(cell.product.counter)"
cell.cartSelectionDelegate = NewOrderViewController()
return cell
}
}
NewOrderViewController
import UIKit
import FirebaseFirestore
class NewOrderViewController: UIViewController, UITableViewDelegate, CartSelection {
#IBOutlet var productTableView: UITableView!
#IBOutlet weak var totalProductCountLabel: UILabel!
#IBOutlet weak var totalProductPriceLabel: UILabel!
private var shop: Shop!
var totalProductCount = 0
var timerEnabled = true
var timerCount = 0
var cartDictionary = [String: Product]()
// MARK: - View Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
productTableView.dataSource = dataSource
productTableView.delegate = self
query = baseQuery
productTableView.allowsSelection = false
productTableView.reloadData()
timerEnabled = true
timerCount = 0
totalProductCountLabel.text = "Anzahl Produkte: 0 Stück"
totalProductPriceLabel.text = "Preis Produkte: 0.00 €"
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.setNeedsStatusBarAppearanceUpdate()
dataSource.startUpdates()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
dataSource.stopUpdates()
timerEnabled = false
timerCount = 0
}
// MARK: - DataSource and Query
private func dataSourceForQuery(_ query: Query) -> ProductTableViewDataSource {
return ProductTableViewDataSource(query: query) { [unowned self] (changes) in
self.productTableView.reloadData()
}
}
lazy private var dataSource: ProductTableViewDataSource = {
return dataSourceForQuery(baseQuery)
}()
lazy private var baseQuery: Query = {
Firestore.firestore().products.whereField("shopID", isEqualTo: shop.documentID)
}()
fileprivate var query: Query? {
didSet {
dataSource.stopUpdates()
productTableView.dataSource = nil
if let query = query {
dataSource = dataSourceForQuery(query)
productTableView.dataSource = dataSource
dataSource.startUpdates()
}
}
}
// MARK: - Delegate Method
func addProductToCart(product: Product, uuid: String) {
timerEnabled = false
cartDictionary[uuid] = product
if cartDictionary[uuid]?.counter == 0 {
cartDictionary.removeValue(forKey: uuid)
}
calculateTotal()
}
// MARK: - Actions
#IBAction func orderButtonTapped(_ sender: UIButton) {
if totalProductCount != 0 {
showOrderConfirmation()
var priceLabel = 0.00
print("Im Einkaufwagen sind folgende Produkte:")
for (key,value) in cartDictionary {
priceLabel += value.totalPrice
print("Produkt: \(value.productName) --> \(value.counter) __ Produktkennung: \(key)")
}
print("Gesamtpreis: \(priceLabel)")
}
}
// MARK: - Functions
func showOrderConfirmation() {
let alert = UIAlertController(title: "Bestellen?", message: nil, preferredStyle: .alert)
let okayAction = UIAlertAction(title: "Bestellung aufgeben", style: .default)
let cancelAction = UIAlertAction(title: "Bestellung bearbeiten", style: .destructive)
alert.addAction(okayAction)
alert.addAction(cancelAction)
present(alert, animated: true, completion: nil)
}
func calculateTotal() {
var totalProductCount = 0
var totalProductPrice = 0.00
for (_,value) in cartDictionary {
totalProductCount += value.counter
totalProductPrice += value.totalPrice
}
self.totalProductCountLabel.text = "Anzahl Produkte: \(totalProductCount) Stück"
self.totalProductPriceLabel.text = "Preis Produkte: \(String(format: "%5.2f", totalProductPrice)) €"
if cartDictionary.isEmpty {
print("Der Einkaufswagen ist leer")
timerEnabled = true
timerCount = 0
startTimer()
}
}
func startTimer() {
_ = Timer.scheduledTimer(withTimeInterval: 10, repeats: true) { timer in
if self.timerEnabled != false && self.timerCount < 4 {
self.productTableView.reloadData()
print("Die Produktliste wurde aktualisiert!")
self.timerCount += 1
} else {
timer.invalidate()
self.timerEnabled = true
self.timerCount = 0
}
}
}
// MARK: - TableView Delegate
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 188
}
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
timerEnabled = false
timerCount = 5
}
// MARK: - Navigation
static func fromStoryboard(_ storyboard: UIStoryboard = UIStoryboard(name: Constants.StoryboardID.main, bundle: nil),
forShop shop: Shop) -> NewOrderViewController {
let controller = storyboard.instantiateViewController(withIdentifier: Constants.StoryboardID.newOrderViewController) as! NewOrderViewController
controller.shop = shop
return controller
}
}
All problems fixed by making a static instance of my NewOrderViewController!
class NewOrderViewController: UIViewController, UITableViewDelegate, CartSelection {
#IBOutlet var productTableView: UITableView!
#IBOutlet weak var totalProductCountLabel: UILabel!
#IBOutlet weak var totalProductPriceLabel: UILabel!
private var shop: Shop!
var totalProductCount = 0
var timerEnabled = true
var timerCount = 0
var cartDictionary = [String: Product]()
static var instance: NewOrderViewController?
// MARK: - View Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
NewOrderViewController.instance = self
productTableView.dataSource = dataSource
productTableView.delegate = self
query = baseQuery
productTableView.allowsSelection = false
productTableView.reloadData()
timerEnabled = true
timerCount = 0
totalProductCountLabel.text = "Anzahl Produkte: 0 Stück"
totalProductPriceLabel.text = "Preis Produkte: 0.00 €"
}

How to manage counting priority calculation in a calculator app ? (Swift)

I'm doing a calculator for school I finished everything but in the end the priority of calculation is not respected for example when I do : 2 + 2 * 2 it should be 6 but in my app it tells me 8, anyone have an idea how can I do that ? you will find in my code my model and my controller :
class viewControllerUtilities: UIViewController {
var stringNumbers: [String] = [String()]
var operators: [String] = ["+"]
var formerResult: Double?
var index = 0
var isExpressionCorrect: Bool{
if let stringNumber = stringNumbers.last{
if stringNumber.isEmpty{
if stringNumbers.count == 1 {
return false
}
return false
}
}
return true
}
var canAddOperator: Bool {
if let stringNumber = stringNumbers.last{
if stringNumber.isEmpty && formerResult == nil{
return false
}
}
return true
}
var canAddDecimal: Bool{
if let strings = stringNumbers.last{
if strings.contains(".") || strings.isEmpty{
return false
}
}
return true
}
func addDecimal(){
if let stringNumber = stringNumbers.last{
var stringNumberDecimal = stringNumber
stringNumberDecimal += "."
stringNumbers[stringNumbers.count-1] = stringNumberDecimal
}
}
func calculateTotal() -> Double{
var total : Double = 0
for (i, stringNumber) in stringNumbers.enumerated(){
if let number = Double(stringNumber){
switch operators[i]{
case "+":
total += number
case "-":
total -= number
case "x":
total *= number
case "/":
total /= number
default:
break
}
}
}
formerResult = total
clear()
return total
}
func clear(){
stringNumbers = [String()]
operators = ["+"]
index = 0
}
func allClear(){
clear()
formerResult = nil
}
func sendOperand(operand: String, number: String) {
operators.append(operand)
stringNumbers.append(number)
}
func addNewNumber(_ newNumber: Int){
if let stringNumber = stringNumbers.last{
var stringNumberMutable = stringNumber
stringNumberMutable += "\(newNumber)"
stringNumbers[stringNumbers.count-1] = stringNumberMutable
}
}
func roundResult(_ result: Double?){
if roundEvaluation(result!){
let rounded = Int(result!)
stringNumbers = ["\(rounded)"]
formerResult = nil
}
}
func roundEvaluation(_ result: Double) -> Bool{
if result.truncatingRemainder(dividingBy: 1) == 0{
return true
}
return false
}
}
and my controller :
class ViewController: UIViewController {
// MARK: - Properties
var CountOnMeU = viewControllerUtilities()
// MARK: - Outlets
#IBOutlet weak var textView: UITextView!
#IBOutlet var numberButtons : [UIButton]!
#IBOutlet var operators: [UIButton]!
#IBOutlet weak var point: UIButton!
// MARK: - Action
#IBAction func tappedNumberButton(_ sender: UIButton) {
for (i, numberButton) in numberButtons.enumerated() where sender == numberButton{
CountOnMeU.addNewNumber(i)
updateDisplay()
}
}
#IBAction func tappedPointButton(_ sender: Any){
if CountOnMeU.canAddDecimal{
CountOnMeU.addDecimal()
updateDisplay()
} else {
showAlert(message: "Vous ne pouvez pas mettre 2 points")
}
}
#IBAction func equal() {
if !CountOnMeU.isExpressionCorrect{
showAlert(message: "opération invalide")
} else {
let total = CountOnMeU.calculateTotal()
textView.text! += "\n =\(total)"
}
}
#IBAction func operandButtonTapped(_ sender: UIButton){
performOperation(operand: (sender.titleLabel?.text!)!)
}
#IBAction func allClear(_ sender: UIButton) {
CountOnMeU.allClear()
textView.text = "0"
}
// MARK: - Methods
func addNewNumber(message: String){
let alertVC = UIAlertController(title: "Erreur", message: message, preferredStyle: .alert)
alertVC.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil))
self.present(alertVC, animated: true, completion: nil)
}
func updateDisplay() {
var text = ""
let stack = CountOnMeU.stringNumbers.enumerated()
for (i, stringNumber) in stack {
// Add operator
if i > 0 {
text += CountOnMeU.operators[i]
}
// Add number
text += stringNumber
}
textView.text = text
}
func showAlert(message: String){
let AlertVC = UIAlertController(title: "Erreur", message: message, preferredStyle: .alert)
AlertVC.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil))
self.present(AlertVC,animated: true, completion: nil)
}
func performOperation(operand: String){
if CountOnMeU.canAddOperator{
let result = CountOnMeU.formerResult
if result != nil {
CountOnMeU.roundResult(result)
updateDisplayForResultReuse(operand: operand)
} else {
CountOnMeU.sendOperand(operand: operand, number: "")
updateDisplay()}
} else {
self.showAlert(message: "Expression incorrecte")
}
}
func updateDisplayForResultReuse(operand: String){
updateDisplay()
CountOnMeU.sendOperand(operand: operand, number: "")
updateDisplay()
}
}

Thread 1: EXC_BAD_ACCESS (code=2, address=0x7ffeeaa26f48)

I have no clue what this error means
Thread 1: EXC_BAD_ACCESS (code=2, address=0x7ffeeaa26f48)
which means I have no clue how to fix my code. Please help me. Here is my code below if you need it. If you need more code ask me please. I hope you guys can help me. Edit: I included all of my code. I hope you guys can use it and sorry for not defining the variables that were necessary for this question. Again I hope you guys can solve this.
var score = 0
var randomAmountOfTime = Double(arc4random_uniform(5) + 2)
var randomAmountOfTime2 = Double(arc4random_uniform(5) + 2)
var randomAmountOfTime3 = Double(arc4random_uniform(5) + 2)
var randomAmountOfTime4 = Double(arc4random_uniform(5) + 2)
var randomAmountOfTime5 = Double(arc4random_uniform(5) + 2)
override func viewDidLoad() {
super.viewDidLoad()
if X != nil {
X.text = ""
}
if Puppy5 != nil {
Puppy5.isHidden = true
}
if Puppy4 != nil {
Puppy4.isHidden = true
}
if Puppy3 != nil {
Puppy3.isHidden = true
}
if Puppy2 != nil {
Puppy2.isHidden = true
}
if Puppy1 != nil {
Puppy1.isHidden = true
}
score = 0
if Score != nil {
Score.text = "Score - \(score)"
}
loadingProcess()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBOutlet var Score: UILabel!
#IBOutlet var X: UILabel!
#IBOutlet var Puppy5: UIButton!
#IBAction func puppy5(_ sender: Any) {
Puppy5.isHidden = true
score += 1
Score.text = "Score - \(score)"
}
#IBOutlet var Puppy4: UIButton!
#IBAction func puppy4(_ sender: Any) {
Puppy4.isHidden = true
score += 1
Score.text = "Score - \(score)"
}
#IBOutlet var Puppy3: UIButton!
#IBAction func puppy3(_ sender: Any) {
Puppy3.isHidden = true
score += 1
Score.text = "Score - \(score)"
}
#IBOutlet var Puppy2: UIButton!
#IBAction func puppy2(_ sender: Any) {
Puppy2.isHidden = true
score += 1
Score.text = "Score - \(score)"
}
#IBOutlet var Puppy1: UIButton!
#IBAction func puppy1(_ sender: Any) {
Puppy1.isHidden = true
score += 1
Score.text = "Score - \(score)"
}
func loadingProcess() {
if self.Puppy1 != nil && self.Puppy1.isHidden == true {
let when = DispatchTime.now() + randomAmountOfTime
DispatchQueue.main.asyncAfter(deadline: when) {
self.Puppy1.isHidden = false
self.loadingProcess()
}
} else if self.Puppy1 != nil && self.Puppy1.isHidden == false {
let when = DispatchTime.now() + 3
DispatchQueue.main.asyncAfter(deadline: when) {
self.Puppy1.isHidden = true
if self.X.text == "X" {
self.X.text = "X X"
} else if self.X.text == "" {
self.X.text = "X"
} else if self.X.text == "X X" {
self.X.text = "X X X"
}
}
}
self.loadingProcess()
}
You're calling self.loadingProcess() recursively without any condition. This infinite loop is certainly causing the crash.

iOS app freezes after dismiss view in swift

My issue is this I have two viewControllers connected with a modal segue like normal A--->B, A has the controls like textFields, switches, buttons and a mapView where I get the userLocation. B hast only a button, and a mapView at the moment, but when I tap the exit button it does successfully dismisses viewController B and shows A only controls are frozen, can't tap anything anymore, I don't know why. Any help?
B code
import UIKit
import MapKit
import Parse
import CoreLocation
class MapaMososViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
#IBOutlet weak var mapMozosFollow: MKMapView!
var totlaAutomozo: String!
var fechaRegistro: String!
override func viewDidLoad() {
super.viewDidLoad()
mapMozosFollow.delegate = self
mapMozosFollow.showsUserLocation = true
mapMozosFollow.showsTraffic = false
mapMozosFollow.showsScale = false
print(mapMozosFollow.userLocation.location)
}
override func viewDidAppear(_ animated: Bool) {
self.displayError(error: "Exito", message: "Tu pago ha sido procesado, en unos momentos atenderemos tu orden. Total es de $\(totlaAutomozo!) la fecha registrada \(fechaRegistro!)")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func viewWillDisappear(_ animated: Bool) {
}
#IBAction func salirTapped(_ sender: UIButton) {
self.dismiss(animated: true, completion: {
print("here dismissing")
})
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print(locations.last!.coordinate)
centerMapOnLocation(locations.last!)
}
let regionRadius: CLLocationDistance = 2000
func centerMapOnLocation(_ location: CLLocation) {
let coordinateRegion = MKCoordinateRegionMakeWithDistance(location.coordinate,
regionRadius * 2.0, regionRadius * 2.0)
mapMozosFollow.setRegion(coordinateRegion, animated: true)
}
}
EDIT 1: A ViewController code.
import UIKit
import Parse
import MapKit
import BraintreeDropIn
import Braintree
class ViewController: UIViewController, PayPalPaymentDelegate, PayPalFuturePaymentDelegate, PayPalProfileSharingDelegate, CLLocationManagerDelegate, UITextFieldDelegate, MKMapViewDelegate, BTDropInViewControllerDelegate {
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var mapaLugar: MKMapView!
#IBOutlet weak var numeroExteriorTextField: UITextField!
#IBOutlet weak var telefonoTextField: UITextField!
#IBOutlet weak var lavadoSwitch: UISwitch!
#IBOutlet weak var lavadoYAspiradSwitch: UISwitch!
#IBOutlet weak var numeroCarrosTextField: UITextField!
#IBOutlet weak var numeroMinivanTextField: UITextField!
#IBOutlet weak var numeroPickUpsTextField: UITextField!
#IBOutlet weak var numeroVansTextField: UITextField!
#IBOutlet weak var numeroAspiradoCarrosTextField: UITextField!
#IBOutlet weak var numeroAspiradoMinivanTextField: UITextField!
#IBOutlet weak var numeroAspiradoPickUpsTextField: UITextField!
#IBOutlet weak var numeroAspiradoVansTextField: UITextField!
#IBOutlet weak var botonRealizarPedido: UIButton!
#IBOutlet weak var botonInstrucciones: UIButton!
#IBOutlet weak var totalLabel: UILabel!
var showAlertFirstTime: Bool = true
var locationManager: CLLocationManager = CLLocationManager()
var ubicacion: CLLocationCoordinate2D!
var environment:String = PayPalEnvironmentSandbox {
willSet(newEnvironment) {
if (newEnvironment != environment) {
PayPalMobile.preconnect(withEnvironment: newEnvironment)
}
}
}
var braintreeClient: BTAPIClient?
let defaults = UserDefaults.standard
var resultText = "" // empty
var payPalConfig = PayPalConfiguration() // default
var total: NSDecimalNumber!
var vistaDeMozos: Bool = false
var fechaRegistro: String!
var totalToSend: String!
#IBOutlet weak var constrainSizeMap: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
botonInstrucciones.backgroundColor = UIColor(colorLiteralRed: (200.0/255.0), green: 0.0, blue: 0.0, alpha: 1.0)
botonInstrucciones.layer.cornerRadius = 3
botonInstrucciones.layer.borderWidth = 2
botonInstrucciones.layer.borderColor = UIColor.clear.cgColor
botonInstrucciones.layer.shadowColor = UIColor(colorLiteralRed: (100.0/255.0), green: 0.0, blue: 0.0, alpha: 1.0).cgColor
botonInstrucciones.layer.shadowOpacity = 1.0
botonInstrucciones.layer.shadowRadius = 1.0
botonInstrucciones.layer.shadowOffset = CGSize(width: 0, height: 3)
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.requestAlwaysAuthorization()
self.locationManager.startUpdatingLocation()
NotificationCenter.default.post(name: Notification.Name(rawValue: "keyPressed"), object: nil)
numeroCarrosTextField.delegate = self
numeroMinivanTextField.delegate = self
numeroPickUpsTextField.delegate = self
numeroVansTextField.delegate = self
numeroAspiradoCarrosTextField.delegate = self
numeroAspiradoMinivanTextField.delegate = self
numeroAspiradoPickUpsTextField.delegate = self
numeroAspiradoVansTextField.delegate = self
do {
if defaults.object(forKey: "clientId") == nil || clientId == "000" {
let idTest = try PFCloud.callFunction("newCutomer", withParameters: nil)
print(idTest)
clientId = idTest as! String
defaults.set(clientId, forKey: "clientId")
} else {
print(self.clientId)
}
} catch let error {
print(error)
}
if defaults.object(forKey: "clientId") == nil {
} else {
clientId = defaults.string(forKey: "clientId")!
print(clientId)
}
fetchClientToken()
NotificationCenter.default.addObserver(
self,
selector: #selector(ViewController.keyboardWillShow(notification:)),
name: NSNotification.Name.UIKeyboardWillShow,
object: nil
)
NotificationCenter.default.addObserver(
self,
selector: #selector(ViewController.keyboardWillHide(notification:)),
name: NSNotification.Name.UIKeyboardWillHide,
object: nil
)
scrollView.keyboardDismissMode = UIScrollViewKeyboardDismissMode.interactive
let touch = UITapGestureRecognizer(target: self, action: #selector(ViewController.singleTapGestureCaptured(gesture:)))
scrollView.addGestureRecognizer(touch)
}
func singleTapGestureCaptured(gesture: UITapGestureRecognizer){
self.view.endEditing(true)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if showAlertFirstTime {
showAlertFirstTime = false
} else {
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
PayPalMobile.preconnect(withEnvironment: environment)
print("stop test")
}
#IBAction func realizarPedidoTapped(_ sender: UIButton) {
let numeroExterior = numeroExteriorTextField.text!
let numeroMotosLavado = numeroCarrosTextField.text!
let numeroDeportivosLavado = numeroMinivanTextField.text!
let numeroCarroLavado = numeroPickUpsTextField.text!
let numeroCamionLavado = numeroVansTextField.text!
let numeroMotoLavadoAspirado = numeroAspiradoCarrosTextField.text!
let numeroDeportivoLavadoAspirado = numeroAspiradoMinivanTextField.text!
let numeroCarroLavadoAspirado = numeroAspiradoPickUpsTextField.text!
let numeroCamionLavadoAspirado = numeroAspiradoVansTextField.text!
let numeroTelefono = telefonoTextField.text!
if numeroExterior == "" || numeroTelefono == "" {
displayError("Error", message: "Te falto llenar tu numero exterior y/o telefono")
} else {
//Braintree init
self.braintreeClient = BTAPIClient(authorization: clientToken)
let items = [item1]
let subtotal = PayPalItem.totalPrice(forItems: items)
//var subtotal = PayPalItem.totalPrice(forItems: items)
let shipping = NSDecimalNumber(string: "0.00")
let tax = NSDecimalNumber(string: "0.00")
//details ???
let paymentDetails = PayPalPaymentDetails(subtotal: subtotal, withShipping: shipping, withTax: tax)
self.total = subtotal.adding(shipping).adding(tax)
let payment = PayPalPayment(amount: total, currencyCode: "MXN", shortDescription: "Automozo inc", intent: .sale)
payment.items = items
payment.paymentDetails = paymentDetails
print("\(payment.localizedAmountForDisplay)")
self.showDropIn(clientTokenOrTokenizationKey: clientToken)
if (payment.processable) {
let paymentViewController = PayPalPaymentViewController(payment: payment, configuration: payPalConfig, delegate: self)
}
else {
print("Payment not processalbe: \(payment.description)")
print("payment not processable \(payment)")
displayError("Error", message: "Hubo un error al procesar tu pago, por favor intenta de nuevo.")
}
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
if segue.identifier == "MapaMozosSegue" {
let mapaMozosVC = segue.destination as! MapaMososViewController
mapaMozosVC.totlaAutomozo = totalToSend!
mapaMozosVC.fechaRegistro = fechaRegistro
locationManager.stopUpdatingLocation()
}
}
func textFieldDidEndEditing(_ textField: UITextField) {
textField.resignFirstResponder()
print("test text field ended")
if textField.text == "" {
textField.text = "0"
}
var numeroCarrosLavadoVar = numeroCarrosTextField.text!
var numeroMinivansLavadoVar = numeroMinivanTextField.text!
var numeroPickUpsLavadoVar = numeroPickUpsTextField.text!
var numeroVansLavadoVar = numeroVansTextField.text!
var numeroCarrosLavadoAspiradoVar = numeroAspiradoCarrosTextField.text!
var numeroMinivansLavadoAspiradoVar = numeroAspiradoMinivanTextField.text!
var numeroPickUpsLavadoAspiradoVar = numeroAspiradoPickUpsTextField.text!
var numeroVansLavadoAspiradoVar = numeroAspiradoVansTextField.text!
if numeroCarrosLavadoVar == "" {
numeroCarrosLavadoVar = "0"
numeroCarrosTextField.text = "0"
}
if numeroMinivansLavadoVar == "" {
numeroMinivansLavadoVar = "0"
numeroMinivanTextField.text = "0"
}
if numeroPickUpsLavadoVar == "" {
numeroPickUpsLavadoVar = "0"
numeroPickUpsTextField.text = "0"
}
if numeroVansLavadoVar == "" {
numeroVansLavadoVar = "0"
numeroVansTextField.text = "0"
}
if numeroCarrosLavadoAspiradoVar == "" {
numeroCarrosLavadoAspiradoVar = "0"
numeroAspiradoCarrosTextField.text = "0"
}
if numeroMinivansLavadoAspiradoVar == "" {
numeroMinivansLavadoAspiradoVar = "0"
numeroMinivanTextField.text = "0"
}
if numeroPickUpsLavadoAspiradoVar == "" {
numeroPickUpsLavadoAspiradoVar = "0"
numeroAspiradoPickUpsTextField.text = "0"
}
if numeroVansLavadoAspiradoVar == "" {
numeroVansLavadoAspiradoVar = "0"
numeroVansTextField.text = "0"
}
let priceOfLavadoCarro = Int(numeroCarrosLavadoVar)! * pricesLavado["LavadoCarro"]!
let priceOfLavadoMinivan = Int(numeroMinivansLavadoVar)! * pricesLavado["LavadoMinivan"]!
let priceOfLavadoPickUp = Int(numeroPickUpsLavadoVar)! * pricesLavado["LavadoPickUp"]!
let priceOfLavadoVan = Int(numeroVansLavadoVar)! * pricesLavado["LavadoVan"]!
//Lavado y Aspirado
let priceOfLavadoYAspiradoCarro = Int(numeroCarrosLavadoAspiradoVar)! * pricesLavadoYAspirado["LavadoYAspiradoCarro"]!
let priceOfLavadoYAspiradoMinivan = Int(numeroMinivansLavadoAspiradoVar)! * pricesLavadoYAspirado["LavadoYAspiradoMinivan"]!
let priceOfLavadoYAspiradoPickUp = Int(numeroPickUpsLavadoAspiradoVar)! * pricesLavadoYAspirado["LavadoYAspiradoPickUp"]!
let priceOfLavadoYAspiradoVan = Int(numeroVansLavadoAspiradoVar)! * pricesLavadoYAspirado["LavadoYAspiradoVan"]!
let totalAutomozo = priceOfLavadoCarro + priceOfLavadoMinivan + priceOfLavadoPickUp + priceOfLavadoVan + priceOfLavadoYAspiradoCarro + priceOfLavadoYAspiradoMinivan + priceOfLavadoYAspiradoPickUp + priceOfLavadoYAspiradoVan
print(totalAutomozo)
totalLabel.text = "\(totalAutomozo).00"
textField.resignFirstResponder()
}
func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
if (textField.text?.characters.count)! == 1 {
print("text quota meet")
textField.resignFirstResponder()
self.view.endEditing(true)
return true
} else {
textField.text = "0"
}
return true
}
let regionRadius: CLLocationDistance = 100
func centerMapOnLocation(_ location: CLLocation) {
let coordinateRegion = MKCoordinateRegionMakeWithDistance(location.coordinate,
regionRadius * 2.0, regionRadius * 2.0)
mapaLugar.setRegion(coordinateRegion, animated: true)
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
centerMapOnLocation(locations.last!)
ubicacion = locations.last!.coordinate
}
// Mark - Braintree methods
func showDropIn(clientTokenOrTokenizationKey: String) {
var value: Bool = false
var totlaAutomozo = self.totalLabel.text
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MMM dd YYYY HH:mm"
dateFormatter.timeZone = NSTimeZone.local
fechaRegistro = dateFormatter.string(from: Date())
let request = BTDropInRequest()
request.amount = "\(total)"
request.currencyCode = "MXN"
print(request.description)
BTUIKAppearance.darkTheme()
BTUIKAppearance.sharedInstance().activityIndicatorViewStyle = UIActivityIndicatorViewStyle.gray
// request.
let dropIn = BTDropInController(authorization: clientTokenOrTokenizationKey, request: request)
{ (controller, result, error) in
if (error != nil) {
print("ERROR")
} else if (result?.isCancelled == true) {
print("CANCELLED")
} else if let result = result {
// Use the BTDropInResult properties to update your UI
print(result.paymentOptionType)
print("payment method: \(result.paymentMethod?.nonce)")
print("ppayment desc \(result.paymentDescription)")
print(result.paymentIcon.description)
value = self.postNonceToServer(paymentMethodNonce: (result.paymentMethod?.nonce)!)
}
controller.dismiss(animated: true, completion: nil)
if value {
self.locationManager.stopUpdatingLocation()
self.performSegue(withIdentifier: "MapaMozosSegue", sender: self)
self.vistaDeMozos = true
} else {
self.displayError("Alerta", message: "El pedido ha sido cancelado exitosamente.")
//top row
self.numeroCarrosTextField.text = "0"
self.numeroMinivanTextField.text = "0"
self.numeroPickUpsTextField.text = "0"
self.numeroVansTextField.text = "0"
//bottom row
self.numeroAspiradoCarrosTextField.text = "0"
self.numeroAspiradoMinivanTextField.text = "0"
self.numeroAspiradoPickUpsTextField.text = "0"
self.numeroAspiradoVansTextField.text = "0"
//data
self.telefonoTextField.text = ""
self.telefonoTextField.text = ""
}
}
self.present(dropIn!, animated: true, completion: nil)
}
func userDidCancelPayment() {
self.dismiss(animated: true, completion: nil)
}
func postNonceToServer(paymentMethodNonce: String) -> Bool {
var val = true
do {
var response = try PFCloud.callFunction("checkout", withParameters: ["payment_method_nonce":paymentMethodNonce, "amount":"\(total!).00", "customerId": clientId])
print(response)
} catch let error {
print(error.localizedDescription)
}
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MMM. dd, YYYY HH:mm"
dateFormatter.timeZone = NSTimeZone.local
fechaRegistro = dateFormatter.string(from: Date())
let usuarioPagado: PFObject = PFObject(className: "Ordenes")
let location: PFGeoPoint = PFGeoPoint(latitude: ubicacion.latitude, longitude: ubicacion.longitude)
usuarioPagado["Ubicacion"] = location
usuarioPagado["NumeroExterior"] = numeroExteriorTextField.text!
usuarioPagado["NumeroDeTelefono"] = telefonoTextField.text!
usuarioPagado["LavadoCarro"] = numeroCarrosTextField.text!
usuarioPagado["LavadoMiniVan"] = numeroMinivanTextField.text!
usuarioPagado["LavadoPickUp"] = numeroPickUpsTextField.text!
usuarioPagado["LavadoDeVan"] = numeroVansTextField.text!
usuarioPagado["LavadoAspiradoCarro"] = numeroAspiradoCarrosTextField.text!
usuarioPagado["LavadoAspiradoMiniVan"] = numeroAspiradoMinivanTextField.text!
usuarioPagado["LavadoAspiradoPickUp"] = numeroAspiradoPickUpsTextField.text!
usuarioPagado["LavadoAspiradoDeVan"] = numeroAspiradoVansTextField.text!
usuarioPagado["Monto"] = totalLabel.text!
usuarioPagado["NumeroDeTelefono"] = telefonoTextField.text!
usuarioPagado["TipoDeCelular"] = "iPhone"
usuarioPagado["FechaDeOrden"] = fechaRegistro
self.totalToSend = self.totalLabel.text!
usuarioPagado.saveInBackground() {
(success: Bool, error: Error?) -> Void in
if error == nil {
//done
print("saved object")
val = false
} else {
//not done
print("not saved because \(error?.localizedDescription)")
}
}
numeroCarrosTextField.text = "0"
numeroMinivanTextField.text = "0"
numeroPickUpsTextField.text = "0"
numeroVansTextField.text = "0"
numeroAspiradoCarrosTextField.text = "0"
numeroAspiradoMinivanTextField.text = "0"
numeroAspiradoPickUpsTextField.text = "0"
numeroAspiradoVansTextField.text = "0"
totalLabel.text = "00.00"
self.lavadoSwitch.isOn = false
self.lavadoYAspiradSwitch.isOn = false
self.numeroExteriorTextField.text = ""
self.telefonoTextField.text = ""
self.numeroCarrosTextField.isHidden = true
self.numeroMinivanTextField.isHidden = true
self.numeroPickUpsTextField.isHidden = true
self.numeroVansTextField.isHidden = true
self.numeroAspiradoCarrosTextField.isHidden = true
self.numeroAspiradoMinivanTextField.isHidden = true
self.numeroAspiradoPickUpsTextField.isHidden = true
self.numeroAspiradoVansTextField.isHidden = true
return val
}
func drop(inViewControllerDidLoad viewController: BTDropInViewController) {
print("did load view drop")
}
func drop(inViewControllerDidCancel viewController: BTDropInViewController) {
print("did cancel drop payment")
}
func drop(inViewControllerWillComplete viewController: BTDropInViewController) {
print("drop will complete payment")
}
func drop(_ viewController: BTDropInViewController, didSucceedWithTokenization paymentMethodNonce: BTPaymentMethodNonce) {
var totlaAutomozo = totalLabel.text
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MMM dd YYYY HH:mm"
dateFormatter.timeZone = NSTimeZone.local
fechaRegistro = dateFormatter.string(from: Date())
print("did succeeded with tokenization")
print(" \(paymentMethodNonce.nonce)")
var value = postNonceToServer(paymentMethodNonce: paymentMethodNonce.nonce)
self.dismiss(animated: true, completion: nil)
if value {
displayError("Exito", message: "Tu pago ha sido procesado, en unos momentos atenderemos tu orden. Total es de $\(totlaAutomozo ?? "00.00") la fecha registrada \(fechaRegistro)")
} else {
self.displayError("Error", message: "Hubo un error al guardar tu informacion, ponte en contacto con nosotros.")
}
}
func fetchClientToken() {
do {
let response = try PFCloud.callFunction("generateToken", withParameters: ["clientId": clientId])
self.clientToken = response as! String
} catch let error {
print(error)
}
}
func keyboardWillShow(notification:NSNotification){
var userInfo = notification.userInfo!
var keyboardFrame:CGRect = (userInfo[UIKeyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue
keyboardFrame = self.view.convert(keyboardFrame, from: nil)
var contentInset:UIEdgeInsets = self.scrollView.contentInset
contentInset.bottom = keyboardFrame.size.height
self.scrollView.contentInset = contentInset
}
func keyboardWillHide(notification:NSNotification){
let contentInset:UIEdgeInsets = UIEdgeInsets.zero
self.scrollView.contentInset = contentInset
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
self.view.endEditing(true)
}
func showFullMap() {
if vistaDeMozos {
self.telefonoTextField.isHidden = true
self.botonRealizarPedido.isHidden = true
self.lavadoSwitch.isHidden = true
self.lavadoYAspiradSwitch.isHidden = true
self.botonRealizarPedido.isHidden = true
self.numeroExteriorTextField.isHidden = true
self.numeroCarrosTextField.isHidden = true
self.numeroMinivanTextField.isHidden = true
self.numeroPickUpsTextField.isHidden = true
self.numeroVansTextField.isHidden = true
self.numeroAspiradoCarrosTextField.isHidden = true
self.numeroAspiradoMinivanTextField.isHidden = true
self.numeroAspiradoPickUpsTextField.isHidden = true
self.numeroAspiradoVansTextField.isHidden = true
self.view.layoutSubviews()
} else {
}
}
}
Did you try pausing the app with the debugger to inspect the stack frames? Sometimes that can help you spot deadlocks or infinite loops. You'll find the pause button in the bar between the code editor and the debugger (or at the bottom if the debugger is hidden):
Look at the stack frames in the Debug Navigator on the left-hand side of the Xcode window. Can you see anything suspicious? Here is a stack frame that indicates the app is idling on the main run loop:
That's usually perfectly normal as the app is waiting for user input. If you see something like the following you are likely dealing with a deadlock:
The semaphore stops the thread from continuing until some other thread opens the semaphore again. You are dealing with a deadlock if two or more threads are stopped waiting for each other. If the main thread is involved the app will be frozen.
The third possibility I can think of is an infinite loop on the main thread:
Of course this one is pretty easy to spot :)
The darker entries in stack frames are from your own code. You can click on those entries to find the exact locations and use the debugger to inspect variables. The other entries will show you assembly.
If you could not find anything suspicious by pausing the debugger I would start to remove those parts of code bit by bit, trying to reproduce the problem each time. There are two possible results:
You remove some harmless looking code and suddenly it works as expected. You can then take a closer look at the code you removed, making it easier to figure the issue out.
You end up with a minimal project exhibiting the issue. This is now much easier to debug and reason about because there is nothing else distracting you from the problem. You also have a much higher probability of getting help from coworkers or here on Stack Overflow when your code sample is as small as possible.
A third attack vector is checking the View Debugger:
Are there any transparent views above the frozen view controller blocking user events?
Is user interaction enabled on the controls and views involved? Select a view and check the Object Inspector on the right-hand side:
Try to remove DispatchQueue.main.async or the A viewController won't get notified. It's interaction has been disabled because the segue
//DispatchQueue.main.async {
self.dismiss(animated: true, completion: {
print("here dismissing")
})
//}
Make sure to check if any NSDelayedPerforming methods are blocking the main thread (Foundation > NSRunloop). i.e. performSelector afterDelay...
In my case (Objective-C) I had to use cancelPreviousPerformRequestsWithTarget in the viewDidDisappear instance method of the ViewController.
[NSObject cancelPreviousPerformRequestsWithTarget:self];
I think the same applies when using NSTimer.scheduledTimerWithTimeInterval (Swift) without invalidating the timer in the proper life cycle method.

Resources