So I am trying to follow standford's swift lecture series.. However, when I am trying to play the following code, it get 'lldb' error. Any help will be appreciated.. Many thanks
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var display: UILabel!
var userIsIntheMiddleOfTyping = false
#IBAction func touchDigit(_ sender: UIButton) {
let digit = sender.currentTitle!
if userIsIntheMiddleOfTyping {
let textCurrentInDisplay = display.text!
display.text = textCurrentInDisplay + digit
} else {
display.text = digit
}
userIsIntheMiddleOfTyping = true
}
}
It is to note that when the debugger opens the following line of code is highlighted,
let digit = sender.currentTitle!
In the line of code below, you are forcing unwrapping of optional value.
let digit = sender.currentTitle!
The compiler is trying to tell you that.
How to solve the problem?
Make sure that the all the values are connected and are not nil. In this specific case, the #IBOutlet weak var display: UILabel! outlet might not be connected.
If you are not sure that if the value is nil or not, use conditional statements and handle the nil case. For example:
if let digit = sender.currentTitle {
print("Great, its working")
} else {
print("error: currentTitle is nil")
}
Related
I am getting this error which I cannot explain:
#IBOutlet weak var licencePlateLabel: UILabel!
var editItem: CarValues? {
didSet {
// Update the view.
self.configureView()
}
}
func configureView() {
// Update the user interface for the detail item.
if let editedCar = self.editItem {
if let licencePlate = self.licencePlateLabel {
licencePlate.text? = editedCar.licencePlate!//this gives me nil
}
else {
print("value was nil")
}
print(editedCar.licencePlate!)//this is giving me the correct value
}
if I replace the
if let licencePlate = self.licencePlateLabel {
licencePlate.text! = editedCar.licencePlate!
}//this throws an error "found nil......"
even if I do this I m still getting the "found nil..."
func configureView() {
licencePlateLabel.text = "test"
[...]
}
BUT if I put the above on viewDidLoad then it works fine
override func viewDidLoad() {
licencePlateLabel.text = "test"
[...]
}
What is going on in this code?
EDIT
I am passing the value of the editItem from the detailView to the EditView like this:
#IBAction func editButtonTapped(sender: AnyObject) {
let storyBoard = UIStoryboard(name: "Main", bundle:nil)
let editScreen = storyBoard.instantiateViewControllerWithIdentifier("ID_EditViewController")
self.navigationController?.pushViewController(editScreen, animated: true)
let controller = EditViewController()
controller.editItem = detailItem
controller.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem()
controller.navigationItem.leftItemsSupplementBackButton = true
}
You don't unwrap properties to set them, only to read from them. So your code can simply be:
if let licencePlate = self.licencePlateLabel {
licencePlate.text = editedCar.licencePlate
}
Note that because licencePlate.text is an optional value anyway, there is also no need to unwrap editedCar.licencePlate. It's ok to use its value whether it is nil or contains a String.
I was just wondering how would I be able to use a searched barcode to fetch using Core Data in Swift. I'm basically passing a barcode to a static func method, but how would I be able to use that to fetch the data from the Core Data?
Here is the barcode when detected:
func barcodeDetected(code: String) {
// Let the user know we've found something.
let alert = UIAlertController(title: "Found a Barcode!", message: code, preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Search", style: UIAlertActionStyle.Destructive, handler: { action in
// Remove the spaces.
let trimmedCode = code.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet())
// EAN or UPC?
// Check for added "0" at beginning of code.
let trimmedCodeString = "\(trimmedCode)"
var trimmedCodeNoZero: String
if trimmedCodeString.hasPrefix("0") && trimmedCodeString.characters.count > 1 {
trimmedCodeNoZero = String(trimmedCodeString.characters.dropFirst())
// Send the doctored barcode
ProductDetailsViewController.searchCode(trimmedCodeNoZero)
} else {
// Send the doctored barcode
ProductDetailsViewController.searchCode(trimmedCodeString)
}
self.navigationController?.popViewControllerAnimated(true)
}))
self.presentViewController(alert, animated: true, completion: nil)
}
My Product Class:
import UIKit
import Foundation
import CoreData
class ProductDetailsViewController: UIViewController, NSFetchedResultsControllerDelegate {
#IBOutlet weak var productLabel: UILabel!
#IBOutlet weak var priceLabel: UILabel!
#IBAction func addProduct(sender: AnyObject) {
let AppDel = UIApplication.sharedApplication().delegate as? AppDelegate
let context:NSManagedObjectContext = (AppDel?.managedObjectContext)!
let ent = NSEntityDescription.entityForName("Products", inManagedObjectContext: context)
var newProduct = ProductItem(entity: ent!, insertIntoManagedObjectContext: context)
newProduct.title = productLabel.text
//newProduct.price = priceLabel.text
/*context.save(nil)
print(newProduct)
print("Object Saved")*/
}
private(set) var PRODUCT_NAME = ""
private(set) var PRODUCT_PRICE = ""
private var menuItems:[ProductItem] = []
static func searchCode(codeNumber: String) -> String{
let barcodeNumber = codeNumber
return barcodeNumber
}
deinit{
NSNotificationCenter.defaultCenter().removeObserver(self)
}
override func viewDidLoad() {
super.viewDidLoad()
productLabel.text = "Scan a Product"
priceLabel.text = ""
NSNotificationCenter.defaultCenter().addObserver(self, selector: "setLabels:", name: "ProductNotification", object: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
I already added the items into Core Data successfully and was able to load all items into a table in my app. Now with the barcode scanned I want to be able to just load the products with the barcode and i'm stuck on that part. As you can see my static fun searchCode is receiving the barcode from barcodeDetected but what should I do next to fetch it? Thanks.
EDIT:
Core Data Entity
import Foundation
import CoreData
#objc(ProductItem)
class ProductItem: NSManagedObject{
#NSManaged var barcodeNum:String?
#NSManaged var box_height:NSNumber?
#NSManaged var box_length:NSNumber?
#NSManaged var box_width:NSNumber?
#NSManaged var price:NSNumber?
#NSManaged var sku:String?
#NSManaged var weight:NSNumber?
#NSManaged var title:String?
}
To fetch the correct ProductItem, you need to use a predicate (see the Apple Documentation here). In your case, you could use something like this:
let AppDel = UIApplication.sharedApplication().delegate as? AppDelegate
let context:NSManagedObjectContext = (AppDel?.managedObjectContext)!
let fetchRequest = NSFetchRequest(entityName: "ProductItem")
fetchRequest.predicate = NSPredicate(format: "barcodeNum == %#",codeNumber)
let results = try! context.executeFetchRequest(fetchRequest) as! [ProductItem]
if results.count > 0 { // great, you found (at least one) matching item
let scannedProduct = results[0]
// from here you can access the attributes of the product
// such as title, price, sku, etc.
...
} else { // not found
...
}
Note that I've use try! for brevity, but in practice you should use proper do ... catch syntax and handle any errors.
I'm not clear why you are using a static func in the ProductDetailsViewController; a common approach would be to use the above fetch within your barcodeDetected method, and then to segue to the ProductDetailsViewController passing the relevant ProductItem for display/editing or whatever. Or to display an alert view if the product was not found.
I keep getting this error of value is never used. I understand this error pops up with Swift 2.2 often, and it is because the value that is initialized is not used. BUT, I do use this value, and this error pops up 3 other times on errors that I do use and I don't know why I still get it.
Below is the code. "Difficulty" is he variable that the compiler says is not used, but as you can see from my code, it is in fact used. Anyone know why this happens?
class SettingsController: UIViewController {
// MARK: Properties
// Preferences for difficulty level of questions
let preferences = NSUserDefaults.standardUserDefaults()
let difficultyKey = "Difficulty"
let questionnumKey = "QuestionNum"
var difficulty: String = "EASY"
#IBOutlet weak var Easy: DLRadioButton!
#IBOutlet weak var Medium: DLRadioButton!
#IBOutlet weak var Hard: DLRadioButton!
override func viewDidLoad() {
super.viewDidLoad()
readUserDefaults()
setDifficulty()
}
func readUserDefaults(){
let difficulty = preferences.stringForKey(difficultyKey) // <--Error
}
func setDifficulty(){
if difficulty == "HARD"{
Hard.selected = true
}
else if difficulty == "MEDIUM"{
Medium.selected = true
}
else{
Easy.selected = true
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
In readUserDefaults() it should be
difficulty = preferences.stringForKey(difficultyKey)
You need to remove the let: you already have created the difficulty variable earlier.
You also need to use ??, the "nil coalescing operator": preferences.stringForKey(difficultyKey) ?? "EASY", for example, to give a value even if the method call returns nil.
Note: answer made from the comments by #eric-d and #leo-dabus.
i´m writing an input text quiz app, and i have an array of Int where i can store if answer was correct or not with 0 or 1 and also i have 3 textfields where i write the answers, and i want to change the textfields back ground color to red or green depending on the answers variable ,, if index variable its 1 change color to green and if is 0 change color to red.. this is what i have
#IBOutlet var textField1: UITextField!
#IBOutlet var textField2: UITextField!
#IBOutlet var textField3: UITextField!
//change int to 1 if answer was correct (3, ea for each textfield)
var answers = [0,0,0]
#IBAction func button(sender: AnyObject) {
for (index, answer) in goodAnswers.enumerate() {
print (answer)
if answer != 0 {
print ("ok")
} else {
print("not ok")
}
}
}
thanks !
You need something like this:
var goodAnswers = ["One", "Two", "Three"]
var textFields:[Int:UITextField]!
override func viewDidLoad() {
super.viewDidLoad()
self.textFields = [0:textField, 1:textField2]
}
#IBAction func btnClick(sender: AnyObject) {
for(index, answer) in goodAnswers.enumerate() {
if textFields[index]?.text == answer {
textFields[index]?.backgroundColor = UIColor.greenColor()
}
else {
textFields[index]?.backgroundColor = UIColor.redColor()
}
}
}
UPDATE:
If you want to have another good answers simply change your code to like this:
Declare new property:
var collectionOfGoodAnswers: [Int : [String]]!
and in viewDidLoad() method:
self.collectionOfGoodAnswers = [0: ["Hello", "World"],
1: ["Welcome", "Friend"]]
and then implement the click action:
#IBAction func btnClick(sender: AnyObject) {
for(index, _) in collectionOfGoodAnswers.enumerate() {
guard let goodAnswer = collectionOfGoodAnswers[index] else { return }
guard let answer = textFields[index] else { return }
guard let text = answer.text else { return }
if goodAnswer.contains(text) {
textFields[index]?.backgroundColor = UIColor.greenColor()
}
else {
textFields[index]?.backgroundColor = UIColor.redColor()
}
}
}
hope it helps.
I am making a tiny quiz app to help my students study for tests. I have the questions on parse.com and successfully can query objects by their ID one at a time, but I end up having to hard code all the objectIds, and what I would like to do is grab the objectIds, put them into an array, and then pull a random question/objectID from that array as the students click through the questions.
I'm a novice, so ... while I may be able to understand the logic, I'm not sure how to write the code.
Here is the code I'm currently using ... but it doesn't include my failed attempts to put the object IDs in an array. I've been trying to add a function CallIDs() with a parse query to get them all, but so far ... no luck. Any ideas?
import UIKit
import Parse
class ViewController: UIViewController {
var ObjectIDs : [String]!
var Question : String!
var Answers : [String]!
var Answer : String!
#IBOutlet var QuestionLabel: UILabel!
#IBOutlet var Button1: UIButton!
#IBOutlet var Button2: UIButton!
#IBOutlet var Button3: UIButton!
#IBOutlet var Button4: UIButton!
#IBOutlet var AnswerResult: UILabel!
#IBOutlet var Next: UIButton!
#IBOutlet var QuizInstructions: UILabel!
var RandomID = 0
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
Hide()
CallData()
}
func GetRandomObjectID() {
ObjectIDs = ["jr92lfjbQc","r0C8oC4aJ6","XbTTX8xBRf","cjV2z4PSvV","wATbbu0JoX","9Y6HzfeeoD","mNHCMaao41","5qRcqyyXOL","JaLCoeyA1T","nrnifGOP1T","aDAQ6t3saJ","jKF0ZhmPxh"]
RandomID = Int (arc4random_uniform(UInt32(ObjectIDs.count)))
}
func CallData() {
GetRandomObjectID()
var query : PFQuery = PFQuery(className: "QuestionsandAnswers")
query.getObjectInBackgroundWithId(ObjectIDs[RandomID]) {
(ObjectHolder : PFObject!, error : NSError!) -> Void in
if (error == nil) {
self.Question = ObjectHolder ["Question"] as String!
self.Answers = ObjectHolder ["Answers"] as Array!
self.Answer = ObjectHolder ["Answer"] as String!
if (self.Answers.count > 0) {
self.QuestionLabel.text = self.Question
self.Button1.setTitle(self.Answers[0], forState: UIControlState.Normal)
self.Button2.setTitle(self.Answers[1], forState: UIControlState.Normal)
self.Button3.setTitle(self.Answers[2], forState: UIControlState.Normal)
self.Button4.setTitle(self.Answers[3], forState: UIControlState.Normal)
}
} else {
NSLog("Something is wrong, dude. Sorry.")
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func Hide() {
AnswerResult.hidden = true
Next.hidden = true
}
func UnHide() {
AnswerResult.hidden = false
Next.hidden = false
}
#IBAction func Button1Action(sender: AnyObject) {
UnHide()
if (Answer == "0") {
AnswerResult.text = "Woot! That's correct!"
} else {
AnswerResult.text = "Nope. Try Again."
}
}
#IBAction func Button2Action(sender: AnyObject) {
UnHide()
if (Answer == "1") {
AnswerResult.text = "Woot! That's correct!"
} else {
AnswerResult.text = "Nope. Try Again."
}
}
#IBAction func Button3Action(sender: AnyObject) {
UnHide()
if (Answer == "2") {
AnswerResult.text = "Woot! That's correct!"
} else {
AnswerResult.text = "Nope. Try Again."
}
}
#IBAction func Button4Action(sender: AnyObject) {
UnHide()
if (Answer == "3") {
AnswerResult.text = "Woot! That's correct!"
} else {
AnswerResult.text = "Nope. Try Again."
}
}
#IBAction func Next(sender: AnyObject) {
CallData()
Hide()
}
}
///// well ... here is the function I attempted to code, but it isn't working:
func CallIDs() {
var query = PFQuery(className: “QuestionsandAnswers”)
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]!, error: NSError!) -> Void in
for object in objects {
self.objectIdsArray.append(object.objectId)
}
}
}
I think the underlying misunderstanding in this question is that objects are to be retrieved using their ids. The requirement is for a random object, and that can be achieved better by knowing only the count of the objects stored on the server.
Getting the count we can select a random object by setting a random skip for a query. So, in the completion block of countObjectsInBackgroundWithBlock, get a random number up to that count like this:
let randomSkip = arc4random_uniform(count)
Now we're ready to do a query with no object id, setting query.skip = randomSkip, and query.limit = 1.
EDIT - If I were to build this in Objective-C, I would do it as follows:
- (void)randomQuestion:(void (^)(NSString *question, NSArray *answers))completion {
PFQuery *countQuery = [PFQuery queryWithClassName:#"QuestionsandAnswers"];
[countQuery countObjectsInBackgroundWithBlock:^(int count, NSError *error) {
NSInteger randomSkip = arc4random_uniform(count);
PFQuery *query = [PFQuery queryWithClassName:#"QuestionsandAnswers"];
query.skip = randomSkip;
query.limit = 1;
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
if (objects.count) {
PFObject *questionAndAnswerObject = objects[0];
NSString *question = questionAndAnswerObject[#"Question"];
NSArray *answers = questionAndAnswerObject[#"Answers"];
completion(question, answers);
} else {
NSLog(#"no error, but no Q&A objects found");
}
} else {
NSLog(#"there was an error %#", error);
completion(nil, nil);
}
}];
}];
}
I am not sure where you declare the array objectIdsArray. I was unable to find it. However the reason you may be having trouble replacing an existing array is because the function you are calling, findObjectsInBackgroundWithBlock does not run on the main thread. The code will continue to execute rather then waiting for the objectIds to download.
In order to make sure that you have downloaded the objectIds first before you try to use them, you may want to use the following code:
func CallIDs() {
var query = PFQuery(className: “QuestionsandAnswers”)
query.selectKeys(["objectId"])
self.objectIdsArray = query.findObjects()
}
Note: When run, you should see the following message:
Warning: A long-running operation is being executed on the main thread.
However I have not yet found this to be a problem. If the download is quick, it should not be noticeable. If it is taking a considerable amount of time, you can add in a UIActivityIndicatorView.