This is my simulator-
It shows - Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value in both the below scenarios:
i) - when I click on "card", it shows error as shown in the screenshot below:-
ii)when I click on "checkout" first it shows the following on the simulator
when I click on "confirm, , it shows error as shown in the screenshot below:-
This is my "CheckoutVC" -
import UIKit
import Stripe
import Firebase
class CheckoutVC: UIViewController {
var product = Cart()
lazy var quantitylabel = product.totalQuantity
var Pro = Product(imagename:#imageLiteral(resourceName: "blue"), price: 5,unit: 5)
#IBOutlet weak var selectCardView: UIView!
#IBOutlet weak var cardIcon: UIImageView!
#IBOutlet weak var cardEndingIn: UILabel!
#IBOutlet weak var selectBankView: UIView!
#IBOutlet weak var bankIcon: UIImageView!
#IBOutlet weak var bankEndingIn: UILabel!
#IBOutlet weak var activityIndicator: UIActivityIndicatorView!
var currentSelectedPaymentType: PaymentType?
var paymentContext: STPPaymentContext!
override func viewDidLoad() {
super.viewDidLoad()
setupStripe()
setupTapGestures()
setupUi()
setCheckoutLabelDetails()
}
func setCheckoutLabelDetails() {
let pricelabel = Pro.price.formatToCurrencyString()
let processingFee = FeesCalculator.calculateFeesForCard(subtotal: Pro.price)
let formatprocessingfees = "Processing Fees: \(processingFee.formatToCurrencyString())"
let total = processingFee + Pro.price
let totallabel = "Total: \(total.formatToCurrencyString())"
}
func setupUi() {
let producttitle = Pro
let productprice = Pro.price
let qunatity = "\(Pro.unit) night accomodations"
let price = Pro.price.formatToCurrencyString()
}
func setupTapGestures() {
let selectCardTouch = UITapGestureRecognizer(target: self, action: #selector(selectCardTapped))
selectCardView.addGestureRecognizer(selectCardTouch)
let selectBankTouch = UITapGestureRecognizer(target: self, action: #selector(selectBankTapped))
selectBankView.addGestureRecognizer(selectBankTouch)
}
#objc func selectBankTapped() {
setBankPaymentView()
}
func setBankPaymentView() {
if currentSelectedPaymentType == .bank { return }
currentSelectedPaymentType = .bank
selectBankView.layer.borderColor = UIColor(named: AppColor.BorderBlue)?.cgColor
selectBankView.layer.borderWidth = 2
selectCardView.layer.borderColor = UIColor.lightGray.cgColor
selectCardView.layer.borderWidth = 1
bankIcon.tintColor = UIColor(named: AppColor.BorderBlue)
cardIcon.tintColor = UIColor.lightGray
}
func setupStripe() {
guard (UserManager.instance.user?.stripeId) != nil else { return }
let config = STPPaymentConfiguration.shared
paymentContext = STPPaymentContext(customerContext: Wallet.instance.customerContext,
configuration: config(),
theme: .default())
paymentContext.hostViewController = self
paymentContext.delegate = self
}
// MARK: Select Card
#objc func selectCardTapped() {
setCardPaymentView()
}
func setCardPaymentView() {
if currentSelectedPaymentType == .card { return }
currentSelectedPaymentType = .card
selectCardView.layer.borderColor = UIColor(named: AppColor.BorderBlue)?.cgColor
selectCardView.layer.borderWidth = 2
selectBankView.layer.borderColor = UIColor.lightGray.cgColor
selectBankView.layer.borderWidth = 1
cardIcon.tintColor = UIColor(named: AppColor.BorderBlue)
bankIcon.tintColor = UIColor.lightGray
}
#IBAction func changeCardClicked(_ sender: Any) {
self.paymentContext.pushPaymentOptionsViewController()
}
#IBAction func changeBankClicked(_ sender: Any) {
}
#IBAction func payBtnClicked(_ sender: Any) {
let total = Pro.price + FeesCalculator.calculateFeesForCard(subtotal: Pro.price)
let confirmPayment = UIAlertController(title: "Confirm Payment", message: "Confirm payment for \(total.formatToDecimalCurrencyString())", preferredStyle: .alert)
let confirmAction = UIAlertAction(title: "Confirm", style: .default) { (action) in
self.paymentContext.requestPayment()
}
let cancel = UIAlertAction(title: "Cancel", style: .cancel)
confirmPayment.addAction(confirmAction)
confirmPayment.addAction(cancel)
present(confirmPayment, animated: true)
}
}
extension CheckoutVC: STPPaymentContextDelegate {
func paymentContextDidChange(_ paymentContext: STPPaymentContext) {
// Triggers when the content of the payment context changes, like when the user selects a new payment method or enters shipping information.
if let card = paymentContext.selectedPaymentOption {
cardEndingIn.text = card.label
} else {
cardEndingIn.text = "No Card Selected"
}
}
func paymentContext(_ paymentContext: STPPaymentContext, didFailToLoadWithError error: Error) {
simpleAlert(msg: "Sorry, but we are not able to load you credit cards at this time.")
}
func paymentContext(_ paymentContext: STPPaymentContext, didCreatePaymentResult paymentResult: STPPaymentResult, completion: #escaping STPPaymentStatusBlock) {
// Request Stripe payment intent, and return client secret.
guard let stripeId = UserManager.instance.user?.stripeId else { return }
let idempotency = UUID().uuidString.replacingOccurrences(of: "-", with: "")
let fees = FeesCalculator.calculateFeesForCard(subtotal: Pro.price)
let total = Pro.price + fees
let data: [String: Any] = [
"total": total,
"idempotency": idempotency,
"customer_id": stripeId
]
Functions.functions().httpsCallable("createPaymentIntent").call(data) { (result, error) in
if let error = error {
debugPrint(error)
self.simpleAlert(msg: "Sorry, but we are not able to complete your payment.")
return
}
guard let clientSecret = result?.data as? String else {
self.simpleAlert(msg: "Sorry, but we are not able to complete your payment.")
return
}
// Once the client secret is obtained, create paymentIntentParams
let paymentIntentParams = STPPaymentIntentParams(clientSecret: clientSecret)
paymentIntentParams.paymentMethodId = paymentResult.paymentMethod.stripeId
// Confirm the PaymentIntent
STPPaymentHandler.shared().confirmPayment(withParams: paymentIntentParams, authenticationContext: paymentContext) { (status, paymentIntent, error) in
switch status {
case .succeeded:
completion(.success, nil)
case .failed:
completion(.error, nil)
case .canceled:
completion(.userCancellation, nil)
}
}
}
}
func paymentContext(_ paymentContext: STPPaymentContext, didFinishWith status: STPPaymentStatus, error: Error?) {
// Take action based on return status: error, success, userCancellation
switch status {
case .success:
let successAlert = UIAlertController(title: "Payment Success!", message: "\nYou will receive an email with all the travel details soon! \n\n Bon Voyage!", preferredStyle: .alert)
let ok = UIAlertAction(title: "Ok", style: .default) { (action) in
self.navigationController?.popToRootViewController(animated: true)
}
successAlert.addAction(ok)
present(successAlert, animated: true)
case .error:
simpleAlert(msg: "Sorry, something went wrong during checkout. You were not charged and can try again.")
case .userCancellation:
return
}
}
}
enum PaymentType {
case card
case bank
}
what am I doing wrong ?
Related
I've built a likert quiz and I'm trying to create a results page that looks like the second view controller. So far all I've been able to return is the score and personality ranked (first view controller).
I'm not sure how I can show the quiz results in order. I basically would want to rank them and adjust the cells, while showing the point total but I have no clue what to do.
class SurveyResultsViewController: UITableViewController {
#IBOutlet var lblSortedScores : [UILabel]!
#IBOutlet var sortedTitle : [UILabel]!
#IBOutlet weak var cellFinishButton : UITableViewCell!
var survey: LikertSurvey?
var points = [0, 0, 0, 0]
var results: [(Int, Int)] = []
override func viewDidLoad() {
super.viewDidLoad()
UserDefaults.standard.setValue(points, forKey: "points")
self.setResults()
self.initUI()
}
private func setResults() {
for (index, point) in points.enumerated() {
results.append((index, point))
}
results.sort { (result1, result2) -> Bool in
return result1.1 > result2.1
}
}
private func initUI() {
for i in 0 ..< results.count {
let title = survey!.questionCategories[results[i].0]
lblSortedScores[i].text = "\(title) = \(results[i].1) points"
sortedTitle[i].text = "\(title)"
}
let finishButtonTap = UITapGestureRecognizer(target: self, action: #selector(self.finishButtonTapped(_:)))
cellFinishButton.addGestureRecognizer(finishButtonTap)
self.navigationItem.hidesBackButton = false
}
#objc func finishButtonTapped(_ sender: UITapGestureRecognizer) {
self.survey?.completed = true
let alertController = UIAlertController(title: "Congratulations! You earned 100 XP from completing this quest!", message: "", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "Ok",
style: .default) { action in
self.performSegue(withIdentifier: "unwindToSectionTableView", sender: self)
})
self.present(alertController, animated: true, completion: nil)
}
}
Try restructuring your code like so
var results: [(index: Int, points: Int)] = []
...
private func setResults() {
for (index, point) in points.enumerated() {
results.append((index, point))
}
results.sort{ $0.points > $1.points }
self.initUI()
}
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()
}
}
I am making a budgeting app for the purposes of learning and I have a few questions about storing and fetching entities in CoreData.
I have two entities "Budget" and "Expense".
Every Budget has its own Expenses. As an example I can have an 'Entertainment' budget and it can have expenses such as 'Bowling' and 'Movies' etc.
I can create a Budget and save it. And then add expenses to it.
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let expense = Expense(context: context)
.
. // Filling out the expense here
.
budget?.addToExpense(expense)
(UIApplication.shared.delegate as! AppDelegate).saveContext()
I then retrieve the collection of Expenses and display the store name in a TableView
// Inside cellForRowAt
let cell = UITableViewCell(style: .default, reuseIdentifier: "cell")
let myArray = Array((budget?.expense)!)
cell.textLabel?.text = (myArray[indexPath.row] as! Expense).store
return cell
So far so good. My issue is that when I store an expense it is stored in a Set. Which means the order is random when I retrieve that set and typecast it into an Array.
What I want is to store the Expenses and retrieve them in such a way that I can display the expenses in a FIFO order in the TableView. In other words the first expense I add in the budget should be the first element in the table view and so on and so forth.
There could be several ways to achieve that. The most straightforward would be to use Ordered relation for expense.
To do that,
Open expense relationship properties in DataModel editor.
Check Ordered option
Then budget.expense will be not Set, but OrderedSet, and you won't need to convert it to Array, but access it directly by index.
VIEWCONTROLLER 1:
=====================>
import CoreData
class ViewController: UIViewController {
#IBOutlet weak var txt_user: UITextField!
#IBOutlet weak var txt_password: UITextField!
var result = NSArray()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
txt_password.isSecureTextEntry = true
}
#IBAction func login_action(_ sender: Any)
{
if(txt_user.text == "" || txt_password.text == "")
{
let alert = UIAlertController(title: "info", message: "fields are empty", preferredStyle: .alert)
let ok = UIAlertAction(title: "OK", style: .default, handler: nil)
alert.addAction(ok)
self.present(alert, animated: true, completion: nil)
}
else
{
self.CheckForUserNameAndPasswordMatch(empName: txt_user.text! , empPwd: txt_password.text!)
}
}
func CheckForUserNameAndPasswordMatch(empName:String,empPwd:String)
{
let app = UIApplication.shared.delegate as! AppDelegate
let context = app.persistentContainer.viewContext
let fetchdata = NSFetchRequest<NSManagedObject>(entityName: "Employee")
let predicate = NSPredicate(format: "empName = %#",empName)
fetchdata.predicate = predicate
do
{
self.result = try context.fetch(fetchdata as! NSFetchRequest<NSFetchRequestResult>)as NSArray
if result.count>0
{
let objcetEntity = result.firstObject as! Employee
if objcetEntity.empName == empName && objcetEntity.empPwd == empPwd
{
print("Login Successfully")
// Entered Username & password matched
}
else
{
print("Wrong password/username")
//Wrong password/username
}
}
}
catch let error as NSError
{
print("error",error.localizedDescription)
}
}
#IBAction func unwindToVC1(sender:UIStoryboardSegue)
{
if sender.source is ViewController2
{
let secvc = sender.source as! ViewController2
txt_password.text = secvc.str1! as String
}
}
VIEWCONTROLLER 2:
=====================>
class ViewController2: UIViewController ,UITextFieldDelegate{
#IBOutlet weak var txt_name: UITextField!
#IBOutlet weak var txt_mail: UITextField!
#IBOutlet weak var txt_pwd: UITextField!
#IBOutlet weak var txt_cpwd: UITextField!
#IBOutlet weak var txt_phone: UITextField!
#IBOutlet weak var err: UILabel!
var str1:NSString!
var str2:NSString!
//var update:NSManagedObject!
override func viewDidLoad() {
super.viewDidLoad()
/*if(update != nil)
{
txt_name.text = update.value(forKey: "empName") as? String
txt_mail.text = update.value(forKey: "empMail") as? String
txt_pwd.text = update.value(forKey: "empPwd") as? String
txt_cpwd.text = update.value(forKey: "empCpwd") as? String
txt_phone.text = update.value(forKey: "empPhone") as? String
}*/
txt_pwd.isSecureTextEntry = true
txt_cpwd.isSecureTextEntry = true
}
override func viewWillAppear(_ animated: Bool)
{
txt_name.becomeFirstResponder()
}
#IBAction func regis_clicked(_ sender: Any)
{
if(txt_name.text == "" || txt_mail.text == "" || txt_pwd.text == "" || txt_cpwd.text == "" || txt_phone.text == "" )
{
let alert = UIAlertController(title: "information", message: "fields are empty", preferredStyle: .alert)
let ok = UIAlertAction(title: "OK", style: .default, handler:
{
(actionsheet) in
if(self.txt_name.text == "")
{
self.txt_name.becomeFirstResponder()
}
if(self.txt_mail.text == "")
{
self.txt_mail.becomeFirstResponder()
}
if(self.txt_pwd.text == "")
{
self.txt_pwd.becomeFirstResponder()
}
if(self.txt_cpwd.text == "")
{
self.txt_cpwd.becomeFirstResponder()
}
if(self.txt_phone.text == "")
{
self.txt_phone.becomeFirstResponder()
}
})
let cancel = UIAlertAction(title: "cancel", style: .default, handler: nil)
alert.addAction(ok)
alert.addAction(cancel)
self.present(alert, animated: true, completion: nil)
}
else if(txt_pwd.text != txt_cpwd.text)
{
let alert1 = UIAlertController(title: "information", message: "password mismatched", preferredStyle: .alert)
let ok = UIAlertAction(title: "OK", style: .default, handler:nil)
let cancel = UIAlertAction(title: "cancel", style: .default, handler: nil)
alert1.addAction(ok)
alert1.addAction(cancel)
self.present(alert1, animated: true, completion: nil)
}
else
{
let app = UIApplication.shared.delegate as! AppDelegate
let context = app.persistentContainer.viewContext
let newuser = NSEntityDescription.insertNewObject(forEntityName: "Employee", into: context)
newuser.setValue(txt_name.text, forKey: "empName")
newuser.setValue(txt_mail.text, forKey: "empMail")
newuser.setValue(txt_pwd.text, forKey: "empPwd")
newuser.setValue(txt_cpwd.text, forKey: "empCpwd")
newuser.setValue(txt_phone.text, forKey: "empPhone")
do
{
try context.save()
}
catch let error as NSError
{
print("error",error.localizedDescription)
}
}
// self.navigationController?.popViewController(animated: true)
performSegue(withIdentifier: "unwind", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
if let newlabel = txt_pwd.text
{
str1 = newlabel as NSString
}
}
I have this code below that when run I'd like it to check and see if the fields itemName, itemNote, etc are empty and if so display UIAlertController and prevent the user from continuing. The code that I'm attempting to use is under the saveTapped IBAction function. Currently, in runtime it seems to bypass all the checking and goes directly to the "if(item != nil) {" check.
Any insight would be appreciated.
import UIKit
import CoreData
class AddEditViewController: UIViewController, NSFetchedResultsControllerDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
var item : Item? = nil
#IBOutlet weak var itemName: UITextField!
#IBOutlet weak var itemNote: UITextField!
#IBOutlet weak var hoursPlayed: UITextField!
#IBOutlet weak var imageHolder: UIImageView!
let moc = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
override func viewDidLoad() {
super.viewDidLoad()
if item != nil {
itemName.text = item?.name
itemNote.text = item?.note
hoursPlayed.text = item?.hoursPlayed
imageHolder.image = UIImage(data: (item?.image)!)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func AddImage(sender: AnyObject) {
let pickerController = UIImagePickerController()
pickerController.delegate = self
pickerController.sourceType = UIImagePickerControllerSourceType.PhotoLibrary
pickerController.allowsEditing = true
self.presentViewController(pickerController, animated: true, completion: nil)
}
#IBAction func addImageFromCamera(sender: AnyObject) {
let pickerController = UIImagePickerController()
pickerController.delegate = self
pickerController.sourceType = UIImagePickerControllerSourceType.Camera
pickerController.allowsEditing = true
self.presentViewController(pickerController, animated: true, completion: nil)
}
func imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage, editingInfo: [String : AnyObject]?) {
self.dismissViewControllerAnimated(true, completion: nil)
self.imageHolder.image = image
}
#IBAction func saveTapped(sender: AnyObject) {
var nameMissing = ""
if itemName == nil {
nameMissing = "name"
print("item missing")
}
else if itemNote == nil {
nameMissing = "note"
print("note missing")
}
else if hoursPlayed == nil {
nameMissing = "hours played"
print("hours played missing")
}
else if imageHolder == nil {
nameMissing = "image"
print("image missing")
}
if (itemName == nil || itemNote == nil || hoursPlayed == nil || imageHolder == nil) {
let missingDetailsAlertController = UIAlertController(title: "Alert", message: "Complete field for \(nameMissing).", preferredStyle: UIAlertControllerStyle.Alert)
missingDetailsAlertController.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(missingDetailsAlertController, animated: true, completion: nil)
return
}
if(item != nil) {
editItem()
} else {
createNewItem()
}
dismissViewController()
}
#IBAction func cancelTapped(sender: AnyObject) {
dismissViewController()
}
func dismissViewController() {
navigationController?.popViewControllerAnimated(true)
}
func createNewItem() {
let entityDescription = NSEntityDescription.entityForName("item", inManagedObjectContext: moc)
let item = Item(entity: entityDescription!, insertIntoManagedObjectContext: moc)
item.name = itemName.text
item.note = itemNote.text
item.hoursPlayed = hoursPlayed.text
item.image = UIImagePNGRepresentation(imageHolder.image!)
do {
try moc.save()
} catch {
print("Create Save Failed.")
return
}
}
func editItem() {
item?.name = itemName.text
item?.note = itemNote.text
item?.hoursPlayed = hoursPlayed.text
item!.image = UIImagePNGRepresentation(imageHolder.image!)
do {
try moc.save()
} catch {
print("Edit Save Failed.")
return
}
}
}
You should check for text property of your textFields not for nil.
Try this:
if let name = itemName.text where name.characters.count > 0 {
//show your alert
} else if let note = itemNote.text where note.characters.count > 0 {
//show your alert
} else if let hours = hoursPlayed.text where hours.characters.count > 0 {
//show your alert
} else if let image = imageHolder.image {
//show your alert
}
And if you want to access the value without alert then you could try this also for UITextFields:
let name = itemName.text ?? ""
let note = itemNote.text ?? ""
let hours = hoursPlayed.text ?? ""
No need to do extra code every time
Use this simple code for check empty string
let bananaName = " " //Empty String
let mango = "Mango"
//Make file for this function and call like my example below return Boolean value for check the value
//Function
struct Checker {
static func isEmptyfor(content:String) -> Bool? {
guard content.trimString.isEmpty else {
print("Not Empty")
return true
}
print("Empty")
return false
}
}
// String Extenion here for Validate trim String
extension String {
var trimString:String {
return self.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())
}
}
//***Final Code here for use this Only for check your string
//Call Function here Like This
//##exp 1
if Checker.isEmptyfor(bananaName)! {
print("Not Empty")
//Do something here
}else{
print("Empty")
}
//##exp 2
if Checker.isEmptyfor(mango)! {
print("Not Empty")
//Do something here
}else{
print("Empty")
}
Output of the code inside Playground
Hi whenever i enter the wrong login information details which is checked at the Firebase database, i want to display an alert controller to stop it from entering the app. But i get an error saying the alertviewcontroller is not the window hierarchy. I don't get that error when i'm calling the function outside of the firebase data block. Could someone explain how to fix this issue?
{
import UIKit
import Firebase
class LoginViewController: UIViewController {
let BASE_REF = Firebase(url: "https://cal-sap.firebaseio.com/?page=Auth")
let USER_REF = Firebase(url: "https://cal-sap.firebaseio.com/?page=Auth/users")
let gradientLayer = CAGradientLayer()
#IBOutlet weak var Email: UITextField!
#IBOutlet weak var Password: UITextField!
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
/*let defaults = NSUserDefaults.standardUserDefaults()
let name = defaults.stringArrayForKey("uid")
if name != nil && USER_REF.authData.uid != nil {
self.performSegueWithIdentifier("LoggedinView", sender: self)
print(name);
}
*/
if NSUserDefaults.standardUserDefaults().valueForKey("uid") != nil && DataService.dataService.CURRENT_USER_REF.authData != nil {
self.performSegueWithIdentifier("LoggedinView", sender: nil)
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.grayColor()
gradientLayer.frame = self.view.bounds
let color1 = UIColor(red: (69/255.0), green: (69/255.0), blue:(69/255.0), alpha: 0.05).CGColor as CGColorRef
let color2 = UIColor(red: (71/255.0), green: (71/255.0), blue:(71/255.0), alpha: 0.05).CGColor as CGColorRef
let color3 = UIColor(red: (200/255.0), green: (200/255.0), blue:(200/255.0), alpha: 0.05).CGColor as CGColorRef
let color4 = UIColor.whiteColor().CGColor as CGColorRef
gradientLayer.colors = [color1,color2,color3,color4]
gradientLayer.locations = [0.0,0.25,0.75,1.0]
self.view.layer.addSublayer(gradientLayer)
let userimageView = UIImageView();
let userimage = UIImage(named: "Email.png");
userimageView.image = userimage;
Email.leftView = userimageView
Email.leftViewMode = UITextFieldViewMode.Always
userimageView.frame = CGRectMake(20, 10, 15, 20)
let passimageView = UIImageView();
let passimage = UIImage(named: "lock.png");
passimageView.image = passimage;
Password.leftView = passimageView
Password.leftViewMode = UITextFieldViewMode.Always
passimageView.frame = CGRectMake(15, 10, 15, 20)
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func LoginButtonTapped(sender: AnyObject) {
if(Email != "" || Password != ""){
/*USER_REF.authUser(Email.text, password:Password.text) {
error, authData in
if error != nil {
self.displayAlertMessage("Information not valid. Please enter again.");
print("user not defined");
} else {
NSUserDefaults.standardUserDefaults().setValue(authData.uid, forKey: "uid")
}
}
*/
DataService.dataService.BASE_REF.authUser(Email.text, password: Password.text, withCompletionBlock: { error, authData in
if error != nil {
print(error)
self.displayAlertMessage("Information invalid. Please enter again.")
} else {
// Be sure the correct uid is stored.
NSUserDefaults.standardUserDefaults().setValue(authData.uid, forKey: "uid")
// Enter the app!
self.performSegueWithIdentifier("CurrentlyLoggedIn", sender: nil)
}
})
}
else{
self.displayAlertMessage("Please fill all the fields")
}
}
func displayAlertMessage(usermessage: String)
{
let MyAlert = UIAlertController(title: "Alert", message: usermessage, preferredStyle: UIAlertControllerStyle.Alert)
let okAction = UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: nil)
MyAlert.addAction(okAction)
self.presentViewController(MyAlert, animated: true, completion: nil)
}
}
}
#DataService.swift File
{
import Foundation
import Firebase
class DataService {
static let dataService = DataService()
private var _BASE_REF = Firebase(url: "https://cal-sap.firebaseio.com/?page=Auth")
private var _USER_REF = Firebase(url: "https://cal-sap.firebaseio.com/?page=Auth/users")
private var _EVENT_REF = Firebase(url: "https://cal-sap.firebaseio.com/?page=Auth/users/events")
var BASE_REF: Firebase {
return _BASE_REF
}
var USER_REF: Firebase {
return _USER_REF
}
var CURRENT_USER_REF: Firebase {
let userID = NSUserDefaults.standardUserDefaults().valueForKey("uid") as! String
let currentUser = Firebase(url: "\(BASE_REF)").childByAppendingPath("users").childByAppendingPath(userID)
return currentUser!
}
var EVENT_REF: Firebase {
return _EVENT_REF
}
}
}
If you have to deal with UI elements from an async block you have to do it in the main queue:
dispatch_async(dispatch_get_main_queue(), { [weak self] () -> Void in
self?.presentViewController(MyAlert, animated: true, completion: nil)
})
I think this will solve your problem, but for alerts I always use these utility vars:
public var APP_KEY_WINDOW: UIWindow? { get { return UIApplication.sharedApplication().keyWindow } }
public var APP_ROOT_VC: UIViewController? {
get {
guard let root = APP_KEY_WINDOW?.rootViewController else { return nil }
guard root.isViewLoaded() && root.view.window != nil else {
guard let presentedVC = root.presentedViewController else { return nil }
return presentedVC
}
return root
}
}
as follow:
dispatch_async(dispatch_get_main_queue(), { () -> Void in
APP_ROOT_VC?.presentViewController(MyAlert, animated: true, completion: nil)
})