passing a let value between classes / viewcontrollers in swift - ios

I have a project with various class files. I have a barcode scanner which I have used from an online source which outputs the values in an alert controller. What I would like to do is take the barcode value and pass it back to a my main class and using a function parse it and display it in the relevant labels. For some reason I cannot get it to do this if anybody has any ideas that would be great. I have spent all day trying to figure this out without any luck.
barcodeScanner class relevant section
/* AVCaptureMetadataOutputObjectsDelegate */
func captureOutput(captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [AnyObject]!, fromConnection connection: AVCaptureConnection!) {
if alertController != nil {
return
}
if metadataObjects != nil && metadataObjects.count > 0 {
if let machineReadableCode = metadataObjects[0] as? AVMetadataMachineReadableCodeObject {
// get the barcode string
let type = machineReadableCode.type
let barcode = machineReadableCode.stringValue
I need to get the barcode let value above to the passengerInformation class where it will be passed through submitCodeAuto function to write the labels.
passengerInformation().self.submitCodeAuto(barcode)
My crack at it above which doesn't seem to work..
// display the barcode in an alert
let title = "Barcode"
let message = "Type: \(type)\nBarcode: \(barcode)"
displayAlert(title, message: message)
}
}
}
}
passengerInformation class
#IBOutlet weak var firstNameResponse: UILabel!
#IBOutlet weak var lastNameResponse: UILabel!
#IBAction func submitCodeAuto(sender: AnyObject!) {
firstNameResponse.text = barcodeProtocol(barcode).firstName
lastNameResponse.text = barcodeProtocol(barcode).lastName
}
Above the submitCodeAuto function also sends the incoming barcode through another function called barcodeProtocol which formats it allowing for first and last name to be retrieved.
I have currently tested the labels with a button running a textfield value through barcodeProtocol and displaying in the labels so that all is working.
I have also hooked up a button to a new viewcontroller with the scanner class that works fine. Showing the camera scanning and displaying the value.
but I just haven't been able to join them up. The app is returning fatal error: unexpectedly found nil while unwrapping an Optional value
Any help would be great thanks.

In your function:
#IBAction func submitCodeAuto(sender: AnyObject!) {
firstNameResponse.text = barcodeProtocol(barcode).firstName
lastNameResponse.text = barcodeProtocol(barcode).lastName
}
where did you get 'barcode' from?
Try replacing it with
#IBAction func submitCodeAuto(sender: AnyObject!) {
firstNameResponse.text = barcodeProtocol(sender as! String).firstName
lastNameResponse.text = barcodeProtocol(sender as! String).lastName
}
I am assuming that your method barcodeProtocol takes in any string and perfectly parses it into firstname and lastname

Your code is riddled with problems.
You create a new instance of your passengerInformation class from your barcodeScanner code, invoke the submitCodeAuto() method, and then forget about the newly created passengerInformation object. (presumably it's a view controller.)
It does not make sense to create a new view controller, send it a message, and then forget about it. That won't do anything. You probably want to instantiate your custom passengerInformation view controller from a storyboard, set a barcode string property in the view controller, and then present it modally using presentViewController:animated:completion:. In your passengerInformation view controller's viewWillAppear method, you should take the barcode property, extract the info you need from it, and install it into your text fields.
Your submitCodeAuto() method is declared as an IBAction even though you're not using it that way. It takes a parameter sender which you ignore, and instead you use a variable barcode which you don't show.
Your submitCodeAuto() function should probably take a parameter barcode of type String, since that's what it seems to be doing.
You have several classes who's class names start with lower-case letters. Class names should start with upper-case letters.

Related

iOS Delegate is returning nil (Swift)

I have a feeling there is more than one problem with this code, but my first issue is that my delegate returns nil and I do not know why. First, is my delegate:
import UIKit
//delegate to move information to next screen
protocol userEnteredDataDelegate {
func userDidEnterInformation(info:NSArray)
}
Next, I have a var defined for the delegate and I believe the ? makes it an optional variable? This is defined inside the class
var dataPassDelegate:userEnteredDataDelegate? = nil
Now, after my user has entered information into the fields in the view, I want to add those field values to an array and then pass that array on to the next view where it will be added to. I have pieced this code together from some YouTube examples but I think I am missing a needed part. When do I assign some kind of value to the dataPassDelegate var so it is not nil when the if statement comes? Do I even need that if statement?
if blankData != 1 {
//add code to pass data to next veiw controller
enteredDataArray = [enterDate.text, enterSeason.text, enterSport.text, enterDispTo.text]
//println(enteredDataArray)
self.appIsWorking ()
if (dataPassDelegate != nil) {
let information: NSArray = enteredDataArray
println(information)
dataPassDelegate!.userDidEnterInformation(information)
self.navigationController?.popViewControllerAnimated(true)
} else {
println ("dataPassDelegate = nil")
}
//performSegueWithIdentifier("goToDispenseScreenTwo", sender: self)
activityIndicator.stopAnimating()
UIApplication.sharedApplication().endIgnoringInteractionEvents()
}
blankData = 0
}
Your help is appreciated.
A delegate is a pointer to another object that conforms to a particular protocol. Often you use delegates to refine the behavior of your class, or to send back status information o the results of an async network request
When you set your dataPassDelegate delegate is up to you.
What is the object that has the dataPassDelegate property? What object will be serving as the delegate?
You need to create 2 objects (the object that will be serving as the delegate, and the object that has the dataPassDelegate property) and link them up.
We can't tell you when to do that because we don't know what you're trying to do or where these objects will be used.

How to find value of UIImage called in ViewDidLoad, pass to another function in Swift

I am using a simple function to call a random image when a view controller is loaded.
override func viewDidLoad() {
super.viewDidLoad()
var randomtest = ReturnRandom()
mainImage.image = UIImage(named: randomtest)
}
I want a button's action to be based on what image was displayed. For example
#IBAction func Button1(sender: AnyObject) {
switch randomtest {
case 1: //Do something here
case 2: //Do something different here
default:
}
However I can not figure out how to get the value of "randomtest" out of ViewDidLoad. I also can not seem to create the random number outside the ViewDidLoad then pass the variable in. Sorry I'm new to all this, iOS development is a long way away from php...
The reason I was having trouble declaring the result of my function as a instance variable is that my function is also an instance function. I can not call it, there has been no instance. So this
class ViewController : UIViewController {
var randomtest: Int = ReturnRandom();
Will return a "Missing argument for parameter #1 in call"
For more details please check out this very helpful thread on setting initial values. Since my function was so simple I just computed the property while setting the initial value, no need for an additional class level function.
dynamic var randomtest:String {
let imageNumber = arc4random_uniform(3)
var imageString = String(imageNumber)
return (imageString)}
Hope this helps someone.

Swift: exc_breakpoint (code=exc_arm_breakpoint subcode=0xdefe) on prepareForSegue

For some reason I am getting this error when the performSegueWithIdentifier line is reached.
I have this code:
if let storedAPIKeychain: AnyObject = dictionary.objectForKey("api_key") {
println(storedAPIKeychain)
//This is the line that causes the problems.
performSegueWithIdentifier("skipBrandSegue", sender: self)
}
The println() works fine and outputs the correct information.
I am trying to pass the storedAPIKeychain along with the segue:
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if segue.identifier == "skipBrandSegue" {
// Create a new variable to store the instance of the next view controller
let destinationVC = segue.destinationViewController as brandsViewController
destinationVC.storedAPIKey = storedAPIKeychain!
}
}
Which I thought might have been the problem. however when I changed that line to:
destinationVC.storedAPIKey = "someAPIplaceholder"
I also get the same error.
Can someone please advise me what this error is and how to resolve it. Thanks.
Edit: Screenshot of error:
The dynamic cast class unconditional indicates that a forced cast failed, because a variable cannot be cast to another type.
In your code I see one cast only at this line:
let destinationVC = segue.destinationViewController as brandsViewController
which means the destination view controller is not an instance of brandsViewController.
To fix the issue:
check in interface builder that the custom class property for the destination view controller is correctly set to brandsViewController
check that the segue is actually pointing to that view controller
If none of the above fixes the problem, set a breakpoint at that line and inspect the actual type of the destination view controller.
Side note: by convention, in swift all type names start with uppercase letter, whereas functions, variables and properties with lower case. If you want to make your code readable to other swift developers, I suggest you to stick with that convention (rename brandsViewController as BrandsViewController)
#antonios answer should solve your problem. The break is due to the object not being cast (found and assigned).
Just a side note: you're going to have a few issues with this line:
if let storedAPIKeychain: AnyObject = dictionary.objectForKey("api_key")
especially if you're expecting to get a String from it and pass that between ViewControllers?
Cast it as a String, Create a global scope variable and then assign it to that variable to use - Will be much easier to handle then.
var globalVariable = "" //add this line at the top, just before your class declaration.
if let storedAPIKeychain = dictionary.objectForKey("api_key") as? String {
self.globalVariable = storedAPIKeychain
}

Delegate Error in Swift

I am trying to send a double value from a UIView (which is loaded from a XIB) to a ViewController using a delegate
Here is my protocol, it is just sending a double to the main ViewController on a button click.
protocol HoursWorkedDelegate{
func sendHoursWorked(hoursWorked: Double);
}
class HoursWorkedView: UIView{
var delegate: HoursWorkedDelegate?;
#IBAction func calculateHoursWorked(sender: AnyObject){
// Do some calculations for the hoursWorked value
// Give value
delegate!.sendHoursWorked(hoursWorked);
}
}
// This class now wants that Double value
class ViewController: UIViewController, HoursWorkedDelegate{
// Conform to protocol
func sendHoursWorked(hoursWorked: Double){
// Lets say we just want to assign this value to a textField
hoursWorkedTextField.text = NSString(format: "%.4f", hoursWorked);
}
}
The error message I get is Thread 1: EXC_BAD_INSTRUCTION(code = EXC_I386_INVOP, subcode=0x0)
Any help would be much appreciated, Thank You!
As a start, change the exclamation point in this snippet to a question mark:
delegate!.sendHoursWorked(hoursWorked);
This is what's likely causing the crash, as you are force-unwrapping the optional delegate property. A question mark means we'll only call sendHoursWorked() on the delegate if the delegate exists.
That fix will now probably mean that your program is no longer crashing, but you still don't get the desired results, because sendHoursWorked() is never called. We have to tell our HoursWorkedView object who is delegating it.
Somewhere in your code, you might have something like this:
let hoursWorkedView = HoursWorkedView()
self.view.addSubview(hoursWorkedView)
It's right here where we should be setting the delegate:
let hoursWorkedView = HoursWorkedView()
hoursWorkedView.delegate = self
self.view.addSubview(hoursWorkedView)
Though if it's me, I probably add a constructor to HoursWorkedView that accepts the delegate property:
init(delegate: HoursWorkedDelegate) {
super.init()
self.delegate = delegate
}
And now we can just do this:
let hoursWorkedView = HoursWorkedView(delegate: self)
self.view.addSubview(hoursWorkedView)
I think you're getting your view and your viewcontroller mixed up: a ViewController controls things; a view just displays them. The viewController tells the view what to display.
So, you want to connect your button to the viewController -- not the view. And you don't need a custom view class or a delegate.
Set it up like this:
create a textField and a button
create an outlet for the textField
put calculateHoursWorked directly in your viewController
create an action to connect the button to calculateHoursWorked
in calculateHoursWorked, set self.textField.text to the result of the calculation (where "textField" is whatever you named your outlet)
You wouldn't use a delegate in this context because the viewController knows everything the view does. The delegate pattern is for cases where one object has no visibility into another.
EDIT:
That being said, the bug here is that the delegate isn't actually being set anywhere.
Swift Optionals (the ! and ?) help prevent cases like this. If you explicitly unwrap an optional using !, you have to make sure it's always defined. In this case, since delegate is defined as optional (?) you have to check it:
#IBAction func calculateHoursWorked(sender: AnyObject){
// Do some calculations for the hoursWorked value
// Give value
if let currentDelegate = self.delegate {
currentDelegate.sendHoursWorked(hoursWorked)
}
}

Accessing an IBOutlet from another class

I am new to Swift/iOS, so please bear with me:
I am trying to access a function in one class from another class, and update an UIImage name.
Within my viewcontroller class I have
class Documents: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet var UpdateImage: UIImageView
override func viewDidLoad() {
super.viewDidLoad()
UpdateImage()
}
func UpdateImage() {
UpdateImage.image = UIImage(named: "NewImage")
}
}
Everything works, the Image gets updated to "NewImage"
Question: I can access the UpdateImage func from another class, but why is it generating an error when trying to change the image in the Documents class?
class GetChanges {
var success = { operation:AFHTTPRequestOperation!, response:AnyObject!) -> Void in
var MakeChange = Documents()
MakeChange.UpdateImage()
}
}
This generates an error on the "UpdateImage.image = UIImage(named: "NewImage")" in the Documents Class; "fatal error: unexpectedly found nil while unwrapping an Optional value"
When you call it within the class itself, it is operating on itself and it has already been created from a nib/storyboard. This means that UpdateImage exists.
When you call the method from another class, when you call this line:
var MakeChange = Documents()
You are creating a new instance of Documents. This is not initialized through the nib/storyboard, and thus it never populated the IBOutlet value UpdateImage. Because this value doesn't exist, it unexpectedly finds nil and throws an error.
You need to somehow retain a reference to the instance of Documents you're trying to display. I'd need more information to tell you how to do that.
Also, because you mentioned that you're new, I'd like to point out a few issues I notice with your code that is making it very difficult to read.
Capitalized names are reserved for Types variable names should (almost) never begin with a capital letter.
Variable names should reflect the object they represent. UpdateImage sounds like it is an image. It would be better to name this updateImageView
Functions should be lowercase as well. It is strange to see capitalization this way and makes the code a bit uncomfortable to read.
Good luck!
Read about View Contoller's lifecycle, it's very important knowledge for iOS developer.
As Logan said:
You are creating a new instance of Documents. This is not initialized through the nib/storyboard, and thus it never populated the IBOutlet value UpdateImage
This means that after call init for ViewController (i.e. Documents()) nib isn't loaded. You can use outlets of viewController in another code only after viewDidLoad stage. Apple docs:
The nib file you specify is not loaded right away. It is loaded the first time the view controller's view is accessed. If you want to perform additional initialization after the nib file is loaded, override the viewDidLoad() method and perform your tasks there.
You can remove MakeChange.UpdateImage(), because it will be called in viewDidLoad. Or, if you want pass specific image name to view controller:
class Documents: UIViewController, UITableViewDataSource,
UITableViewDelegate {
#IBOutlet var UpdateImage: UIImageView
var imageName: String?
override func viewDidLoad() {
super.viewDidLoad()
updateImageView()
}
func updateImageView() {
if let imageName = imageName {
UpdateImage.image = UIImage(named: imageName)
}
}
}
After that, you can use
let documentsViewController = Documents
documentsViewController.imageName = "newImage"
When you load documentsViewController, newImage will be presented

Resources