I am working on an app that involves the use of NSUserDefaults. I have successfully saved two strings and am currently trying to save an int.
This is the code
var number = Int(0)
override func viewDidLoad() {
super.viewDidLoad()
let numberOne = NSUserDefaults.standardUserDefaults()
numberOne.setValue(number, forKey: "num")
let numberTwo = numberOne.stringForKey("num")
if numberTwo > 1
{
//will insert code later
}
my error is that numberTwo is appearing as a string. This is my first time experimenting with NSUserDefault so, I am a novice but, any help is appreciated.
In case of you want to treat it as an integer value, you should set (saving) and get the value as an integer, by using setInteger:forKey: and integerForKey::
var number = Int(0)
override func viewDidLoad() {
super.viewDidLoad()
let numberOne = NSUserDefaults.standardUserDefaults()
numberOne.setInteger(number, forKey: "num")
let numberTwo = numberOne.integerForKey("num") // 0
if numberTwo > 1
{
//will insert code later
}
}
If your value is an Int you can retrieve it with (Swift 3)
let numberTwo = numberOne.integer(forKey: "num")
If Swift 2 I believe it was
let numberTwo = numberOne.integerForKey("num")
You can take a look at the UserDefaults API Reference for more details and more options (such as object(forKey:), string(forKey:)...)
Related
Screen in question I am trying to call the viewDidLoad() func here with the question() func a set number of times and only then call the code for the new view controller to be pushed. But, the code for both is getting executed at the same exact time such that as soon as the first round finishes, the view controller is pushed. I have tried using loops in different variations, but those attempts all led to similar results. Ideas? And thanks in advance!
var num1 = Int()
var num2 = Int()
var userInput = 0
#IBAction func numbers(_ sender: UIButton) {
answerLabel.text = answerLabel.text! + String(sender.tag - 1)
userInput = Int(answerLabel.text!)!
answer()
}
override func viewDidLoad() {
super.viewDidLoad()
// Views configuration
question()
}
func answer() {
let answer = num1 + num2
if userInput == answer {
answerLabel.text = ""
viewDidLoad()
let scoreVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(identifier: "ScoreViewController") as! ScoreViewController
self.navigationController?.pushViewController(scoreVC, animated: true)
}
}
func question() {
num1 = Int(arc4random_uniform(100))
num2 = Int(arc4random_uniform(100))
questionLabel.text = "\(num1) + \(num2)"
}
There are quit e few things that we can improve here. I'll start at basic.
As these numbers are unset at the beginning we don't have to assign them yet. So instead of settings them to a default value we can leave them as Optionals which allows us to just define the type and leave them be for an assignment later on.
var num1: Int?
var num2: Int?
Now we wanna show a Question. The thing is, the viewDidLoad shall only be used when - as the name suggests - the view loads. Because of this we simply create a Method and move our logic there. You already did that, I just renamed your function to a more speaking name
viewDidLoad() {
showNewQuestion()
}
showNewQuestion() { // previously question()
num1 = Int(arc4random_uniform(100))
num2 = Int(arc4random_uniform(100))
questionLabel.text = "\(num1) + \(num2)"
}
So far so good. Now we gotta check the input against the random value in the Button send function. Instead of force unwrapping (!) it is better practise to safely unwrap (?).
#IBAction func numbers(_ sender: UIButton) {
guard let userInput = answerLabel.text, let userInputAsInt = Int(userInput) else {
return
}
checkAnswer(userInput: userInputAsInt) // previously answere()
}
Lastly we improve your answer() function. The thing you left is to count how many questions have been answered. If we don't ever check that how should the program know when to show the score? To fix this problem we remember how many questions were asked (correctAnsweres ) and we define a threshold when to show the score (showScoreAfter)
var correctAnsweres = 0
var showScoreAfter = 5
func checkAnswer(userInputAsInt: Int) {
let answer = num1 + num2
if userInputAsInt != answers { // early exit if answered wrong
return
}
correctAnsweres += 1
answerLabel.text = ""
//either show score
if correctAnsweres % ShowScoreAfter == 0 {
let scoreVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(identifier: "ScoreViewController") as! ScoreViewController
self.navigationController?.pushViewController(scoreVC, animated: true)
}
// or new Question
else {
showNewQuestion()
}
}
I have an array of struct called displayStruct
struct displayStruct{
let price : String!
let Description : String!
}
I am reading data from firebase and add it to my array of struct called myPost which is initialize below
var myPost:[displayStruct] = []
I made a function to add the data from the database to my array of struct like this
func addDataToPostArray(){
let databaseRef = Database.database().reference()
databaseRef.child("Post").queryOrderedByKey().observe(.childAdded, with: {
snapshot in
let snapshotValue = snapshot.value as? NSDictionary
let price = snapshotValue?["price"] as! String
let description = snapshotValue?["Description"] as! String
// print(description)
// print(price)
let postArr = displayStruct(price: price, Description: description)
self.myPost.append(postArr)
//if i print self.myPost.count i get the correct length
})
}
within this closure if I print myPost.count i get the correct length but outside this function if i print the length i get zero even thou i declare the array globally(I think)
I called this method inside viewDidLoad method
override func viewDidLoad() {
// setup after loading the view.
super.viewDidLoad()
addDataToPostArray()
print(myPeople.count) --> returns 0 for some reason
}
I want to use that length is my method below a fucntion of tableView
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myPost.count --> returns 0
}
Any help would be greatly appreciated!
You making a asynchronous network request inside closure and compiler doesn't wait for the response, so just Reload Table when get post data. replace the code with below it work works fine for you. All the best.
func addDataToPostArray(){
let databaseRef = Database.database().reference()
databaseRef.child("Post").queryOrderedByKey().observe(.childAdded, with: {
snapshot in
let snapshotValue = snapshot.value as? NSDictionary
let price = snapshotValue?["price"] as! String
let description = snapshotValue?["Description"] as! String
// print(description)
// print(price)
let postArr = displayStruct(price: price, Description: description)
self.myPost.append(postArr)
print(self.myPost.count)
print(self.myPost)
self.tableView.reloadData()
//if i print self.myPost.count i get the correct length
})
}
Firebase observe call to the database is asynchronous which means when you are requesting for the value it might not be available as it might be in process of fetching it.
That's why your both of the queries to count returns 0 in viewDidLoad and DataSource delegeate method.
databaseRef.child("Post").queryOrderedByKey().observe(.childAdded, with: { // inside closure }
Inside the closure, the code has been already executed and so you have the values.
What you need to do is you need to reload your Datasource in main thread inside the closure.
databaseRef.child("Post").queryOrderedByKey().observe(.childAdded, with: {
// After adding to array
DispatchQueue.main.asyc {
self.tableView.reloadData()
}
}
var score = 0
var highscore = 0
var defaults = NSUserDefaults.standardUserDefaults()
#IBOutlet weak var scoreResult: UILabel!
#IBOutlet weak var highScoreResult: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
if let hscore = defaults.valueForKey("highscore") {
highScoreResult.text = String(hscore)
}
}
#IBAction func tapPressed(sender: AnyObject) {
score++
scoreResult.text = String(score)
if score > highscore {
highscore = score
highScoreResult.text = String(highscore)
defaults.setValue(highscore, forKey: "highscore")
defaults.synchronize()
}
}
When i press "tapPressed" button, score++ , then it saves as highscore, the problem is that when i restart app and i have highscore saved. When i press "tapPressed" highscore starts from zero again.
Change viewDidLoad to:
override func viewDidLoad() {
super.viewDidLoad()
if let hscore = defaults.valueForKey("highscore") {
highscore = hscore
highScoreResult.text = String(hscore)
}
}
That should load the highscore from the defaults once you're app launches.
In your viewDidLoad you need to change your statement to this:
if let hscore = defaults.valueForKey("highscore") {
highscore = hscore
highScoreResult.text = String(hscore)
}
The other answers are describing the reason for the error. In addition I'd like to describe also to use NSUserDefaults properly.
The recommended way is to register the key/value pairs to have reliable default values in case the preference file will be deleted or the app launches the first time.
In AppDelegate (applicationDidFinishLaunching or earlier) register highscore with a value of 0.
let defaultValues = ["highscore" : 0]
defaults.registerDefaults(defaultValues)
Then in viewDidLoad just write
let highscore = defaults.integerForKey("highscore")
highScoreResult.text = String(highscore)
There is no optional binding needed because the value in user defaults is always a non optional.
To save highscore back to disk use the dedicated method setInteger:forKey:
defaults.setInteger(highscore, forKey: "highscore")
valueForKey: is a KVC method with a special meaning and should not be used with NSUserDefaults.
I'm developing an app and using Stripe SDK and Card.io SDK. What i want to happen is populate the STPPaymentCardTextField Card Number,Expiry Month and Year with Card.io scanned credit card value. I Tried:
var paymentField = STPPaymentCardTextField()
func userDidProvideCreditCardInfo(cardInfo: CardIOCreditCardInfo!, inPaymentViewController paymentViewController: CardIOPaymentViewController!) {
var scanViewController: CardIOPaymentViewController = CardIOPaymentViewController(paymentDelegate: self)
paymentField.cardNumber = cardInfo.cardNumber
paymentField.expirationMonth = cardInfo.expiryMonth
paymentField.expirationYear = cardInfo.expiryYear
paymentViewController.dismissViewControllerAnimated(true, completion: nil)
}
I'm having an error Cannot assign to the result of this expression for each paymentField append.
What do you think i can do with this? Thanks!
STPPaymentCardTextField fields are read-only and you can only "get" from those properties.
STPPaymentCardTextField is used to collect credit card details. In your case you are already doing that using CardIOCreditCardInfo. Once you have the credit card details you can assemble the data into an STPCardParams object.
Once you've collected the card number, expiration, and CVC, package them up in an STPCardParams object and invoke the createTokenWithCard: method on the STPAPIClient class.
Now your method may look like this...
func userDidProvideCreditCardInfo(cardInfo: CardIOCreditCardInfo!, inPaymentViewController paymentViewController: CardIOPaymentViewController!) {
let card: STPCardParams = STPCardParams()
card.number = info.cardNumber
card.expMonth = info.expiryMonth
card.expYear = info.expiryYear
card.cvc = info.cvv
STPAPIClient.sharedClient().createTokenWithCard(card, completion: {(result, error) -> Void in
if error == nil {
// Either save the card token with your customer data or charge them right away
//createBackendChargeWithToken(token)
}
else {
//handleError(error)
}
})
}
paymentViewController?.dismissViewControllerAnimated(true, completion: nil)
}
Update May be the right answer to your question.
import Stripe
class PaymentCardEditorField: STPPaymentCardTextField {
func setExistingCard(card: STPCardParams) {
replaceField("numberField", withValue: card.number!)
replaceField("expirationField", withValue: String(format: "%02d/%02d", card.expMonth, (card.expYear % 100)))
replaceField("cvcField", withValue: card.cvc!)
}
func replaceField(memberName: String, withValue value: String) {
let field = self.valueForKey(memberName) as! UITextField
let delegate = self as! UITextFieldDelegate
let len = field.text?.characters.count
if delegate.textField?(field, shouldChangeCharactersInRange: NSMakeRange(0, len!), replacementString: value) ?? false {
field.text = value
}
}
}
And then call the setExistingCard
func userDidProvideCreditCardInfo(cardInfo: CardIOCreditCardInfo!, inPaymentViewController paymentViewController: CardIOPaymentViewController!) {
let card: STPCardParams = STPCardParams()
card.number = info.cardNumber
card.expMonth = info.expiryMonth
card.expYear = info.expiryYear
card.cvc = info.cvv
paymentTextField.setExistingCard(card)
}
Works like a charm.
Follow this thread for potential update to Stripe SDK for in built support in the future.
https://github.com/stripe/stripe-ios/issues/127
I got a much cleaner answer from here
let cardParams = STPCardParams()
cardParams.number = "4242424242424242"
cardParams.expMonth = 07 // this data type is UInt and *not* Int
cardParams.expYear = 19 // this data type is UInt and *not* Int
cardParams.cvc = "123"
let paymentField = STPPaymentCardTextField()
paymentField.cardParams = cardParams //the paymentTextField will now show the cc #, exp, and cvc from above
You can get other test cards numbers like Mastercard, Amex, and Discover from here from Stripe
So I am creating a fairly simple program using Realm as my database. I am fairly new to programing in Swift (or any OS X or iOS environment.) In my program when a button is pressed IBAction func createInvoice I want a few things to happen, I want to count the previous rows in the database and create an invoice number, I want to write new data to the database and I want to call a new view and view controller and pass along the invoice number. My code works except for one thing when using Realm the new view controller is called (override func prepareForSegue) before the invoice number is created so a 0 value is passed along to the new view controller.
If I create a dummy invoice number value such as let invoicenumber = 42 everything works perfectly. It seems that Realm is causing things to happen 'out of order' How can I make the veiwcontroller wait for a value before loading?
#IBAction func createInvoice(sender: AnyObject) {
let realm = Realm()
let invoicepull = Invoice()
let invoicecount = realm.objects(Invoice)
let invoicenraw = invoicecount.count
let a = 100
let invoicenumber = a + invoicenraw
var invoicefile = Invoice()
invoicefile.inumber = invoicenumber
invoicefile.cnumber = clientcombo.stringValue
invoicefile.cost = owed.doubleValue
invoicefile.paid = paid.doubleValue
invoicefile.sevicecode = service.stringValue
invoicefile.dateofservice = NSDate()
// Save your object
realm.beginWrite()
realm.add(invoicefile)
realm.commitWrite()
//Sent notification
performSegueWithIdentifier("cinvoiceseuge", sender: nil)
println("Inside Action")
println(invoicenumber)
dismissViewController(self)
}
override func prepareForSegue(segue: NSStoryboardSegue, sender: AnyObject!) {
if (segue.identifier == "cinvoiceseuge") {
//Checking identifier is crucial as there might be multiple
// segues attached to same view
var detailVC = segue.destinationController as! invociegenerator;
detailVC.toPass = invoicenumber
println("Inside Sugue")
println(invoicenumber)
}
}
If createInvoice is happening on a different thread than prepareForSegue, you'll have to refresh the realm (Realm().refresh()) before accessing your invoicenumber variable (which I assume is of type RealmSwift.Object).
I have solved this issue, thanks to the help of #Shmidt by using Realm's built in notification center. To use the notifications you can use this basic structure.
var notificationToken: NotificationToken?
deinit{
let realm = Realm()
if let notificationToken = notificationToken{
realm.removeNotification(notificationToken)
}
}
override func viewDidLoad() {
super.viewDidLoad()
let realm = Realm()
notificationToken = realm.addNotificationBlock { note, realm in
println("The realm is complete")
}
...
}
One small other error in my code was let invoicenumber = a + invoicenraw I needed to drop the let as it is a variable and not a constant.