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

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
}

Related

Swift type conversion example but using a class

I'm new to programming and I'm following a tutorial for fun to understand things in more detail. The tutorial I'm reading creates code that takes a Double and a Int type and multiply them. As we all know, they converted the Int to a Double to perform the operation. Here is the example code:
let hourlyRate: Double = 19.5
let hoursWorked: Int = 10
let totalCost: Double = hourlyRate * Double(hoursWorked)
After seeing this I said okay cool so let me see if I can do that in a passion project I'm working on. In the passion project code I use a UIStoryboard method that returns a UIViewController so I thought it would be interesting if I could do the same as the tutorial with my variable called controller in the following code:
class ItemListDataProviderTests: XCTestCase {
var controller: ItemListViewController!
var sut: ItemListDataProvider!
var tableView: UITableView!
override func setUp() {
super.setUp()
sut = ItemListDataProvider()
sut.itemManager = ItemManager()
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
controller = storyboard.instantiateViewController(withIdentifier: "ItemListViewController") as! ItemListViewController //compiles
controller = ItemListViewController(storyboard.instantiateViewController(withIdentifier: "ItemListViewController")) //causes error and code doesn't compile
_ = controller.view
tableView = controller.tableView
tableView.dataSource = sut
}
As you can see it didn't work out the way I hoped. I felt type casting was an extra step but I see that its necessary to make the code compile but I don't understand WHY that wouldn't work. What does the computer think I'm asking it to do which makes it not understand? I really want to know and understand why so any help is greatly greatly appreciated.
Double has an init method that takes an Int as an argument so you can write Double(intValue). You cannot do that with the ViewController classes above because there is no init that takes the other type, and in fact you do not want to create a new instance, you want to say 'this instance is actually this type', and you use 'as' for that.
With this line of code :
ItemListViewController(storyboard.instantiateViewController(withIdentifier: "ItemListViewController"))
you're trying to pass an argument of UIViewController into a constructor of ItemListViewController. No such constructor exists.
Also, by instantiating the view controller from the storyboard, you are given an instance of that view controller that's already loaded into memory. So there would be no need to instantiate another as you are doing
The correct way instead is to use the as! operator as you are doing on the line before.
I'm not sure what you're trying to do with controller = ItemListViewController(storyboard.instantiateViewController(withIdentifier: "ItemListViewController")), but this is both wrong and unnecessary. In the line just above you correctly instantiate the viewController and assign it to the controller variable. On this line you appear to be trying to do the exact same thing again, but in a different way. The reason it's failing is that I assume you don't have an initializer for ItemListViewController that takes a UIViewController as the only argument, which is what you're asking it to do. If I'm wrong, please post the code for that initializer so we can analyze. Either way, that line is completely unnecessary. Delete it and your code should compile.

Call method from other ViewController | Swift

I searched to solving this problem a lot of questions but no one helped me.
Have 2 viewControllers and I need to send array from one to another.
Calling method in the first viewController:
SubmitViewController.acceptContent(content)
Accepting in other:
var contentAccepted = [String]()
class func acceptContent(content: [String]){
contentAccepted = content
}
The problem is in taking the mistake on contentAccepted line: Instance member cannot be used on type UIViewController
You are referring to self within your type-method.
Here is what apple says on that matter:
Within the body of a type method, the implicit self property refers to
the type itself, rather than an instance of that type. For structures
and enumerations, this means that you can use self to disambiguate
between type properties and type method parameters, just as you do for
instance properties and instance method parameters.
More generally, any unqualified method and property names that you use
within the body of a type method will refer to other type-level
methods and properties. A type method can call another type method
with the other method’s name, without needing to prefix it with the
type name. Similarly, type methods on structures and enumerations can
access type properties by using the type property’s name without a
type name prefix.
More info on type-methods (class-methods) can be found here
Try to do it that way:
var contentAccepted = [String]()
class func acceptContent(content: [String]){
ViewController().contentAccepted = content
}
Make sure that you really need type-method.
The issue you are running into is that contentAccepted is an instance variable (as it should be) and acceptContent() is a class method. So, your method can't get to your variable. You will need to create an instance of SubmitViewController before you are able to access its variables.
let submitViewController = SubmitViewController()
submitViewController.contentAccepted = content
By first creating a SubmitViewController instance, you can now access that instance variable.
If you are passing information from one View Controller to another in the midst of a segue, you might consider using prepareForSegue.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "Submit" {
if let view = segue.destinationViewController as? SubmitViewController {
view.contentAccepted = content // If content is an instance variable
}
}
}

How to know the class of an object at runtime?

I don't understand why I could not find this question somewhere as I think it's a pretty common one so maybe I'm not well awake. Sorry for that if it's the case.
I have my prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) function and I cannot find out what class is sender. I don't want to try every classes of the Foundation framework so is there a way to know it at runtime.
When I use the debugger I only get (AnyObject!) sender = (instance_type = Builtin.RawPointer = ... which doesn't really help.
Instead of asking the object for its class, I find it more "Swifty" to use "if let" to check if it is what I am looking for.
func inputUnknown(sender : AnyObject) {
if let tableView = sander as? UITableView {
// now you have an object with a defined class
}
else {
// check for other classes or proceed with a default action
}
}
Every object has an underlying dynamicType property, which, in theory, should reveal the actual type of the object, when printed to the console. However, Swift doesn't yet have support for printable metatypes, so you'll get ExistentialMetatype for an instance's dynamicType and MetaType for a class's type. In order to get around this, you can cast your Type variable to AnyObject, which, when printed to your console will print the Objective C metatype.
if let object = sender {
println(object.dynamicType as AnyObject)
}

SWIFT: use of undeclared type of "detailViewController"

I have looked at every answer on the internet and yet it keeps giving the error. I know this might be a repeat but the solutions are not working for me.
I have made detailViewController a public class
I have added detailViewController.swift target membership as my test target.
import UIKit
class MainTableViewController: UITableViewController {
var items:[Item]=itemData
#IBAction func cancelToDetailViewController(segue:UIStoryboardSegue){
}
#IBAction func saveItemDetail(segue:UIStoryboardSegue){
let DetailTableViewController = segue.sourceViewController as DetailTableViewController;
}
Error says use of undeclared type of "detailViewController"
Assuming that by "detailViewController" you mean your "DetailTableViewController" class, the problem is entirely in your naming conventions. What you're doing is attempting to assign your source view controller to a variable (or constant, it doesn't matter) "DetailTableViewController" which is already the name of your class. This is not allowed.
You need to name the variable something else. The convention is to have classes start with a capital letter and instances with a lowercase letter, which you should adhere to because it will help keep this kind of thing from happening in the future.
That said, the following should work for you. Notice the only difference is that the "d" in the beginning of the variable is lowercase instead of uppercase.
#IBAction func saveItemDetail(segue: UIStoryboardSegue) {
let detailTableViewController = segue.sourceViewController as DetailTableViewController
}

Strange behaviour when naming variable in lowerCamelCase

I came across a strange behaviour in Swift while programming a Master-Detail application.
Here's the scenario:
It's a simple Task Manager application. I have two text controls (TaskName, TaskDescription) on the TaskDetailView and two string variables with the same name but in lowerCamelCase (taskName, taskDescription) declared in the TaskDetailViewController.
#IBOutlet var TaskName:UITextField! //UpperCamelCase
#IBOutlet var TaskDescription:UITextView! //UpperCamelCase
var taskName:String? //lowerCamelCase
var taskDescription:String? //lowerCamelCase
I am setting the values of Text controls on ViewDidLoad() as usual:
override func viewDidLoad() {
super.viewDidLoad()
TaskName.text = taskName
TaskDescription.text = taskDescription
}
And I am passing the data in prepareForSegue (from TaskListViewController) as usual:
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if(segue.identifier == "TaskListSegue"){
let detailViewController = segue.destinationViewController as ToDoTaskViewController
let (task, desc) = m_ToDoListManager.GetTask(TaskListView.indexPathForSelectedRow().row)
println("selected \(task) \(desc)")
detailViewController.taskName = task
detailViewController.taskDescription = desc
}
}
The way everything is implemented is correct.
But now when you run the application, the values of text controls are not set.
In fact, the values of the variables also are not set.
What must be happening here?
I have already investigated this problem and also came up with a solution (see my answer below). Please also see Martin R's answer below for a detailed explanation. I just wanted to share this with everyone. I am not sure if anyone has come across this issue.
Update:
Here's the actual code:https://github.com/Abbyjeet/Swift-ToDoList
Here is an explanation:
Your Swift class is (ultimately) a subclass of NSObject.
Therefore the properties are Objective-C properties with getter and setter method.
The name of the setter method for a property is built by capitalizing the first
letter of the property name, e.g. property "foo" has the setter method setFoo:
As a consequence, the setter method for both properties TaskName and taskName is called setTaskName:.
In an Objective-C file, you would get a compiler error
synthesized properties 'taskName' and 'TaskName' both claim setter 'setTaskName:' - use of this setter will cause unexpected behavior
but the Swift compiler does not notice the conflict.
A small demo of the problem:
class MyClass : NSObject {
var prop : String?
var Prop : String?
}
let mc = MyClass()
mc.prop = "foo"
mc.Prop = "bar"
println(mc.prop) // bar
println(mc.Prop) // nil
In your case
TaskName.text = ...
sets the "taskName" property, not the "TaskName". The properties have different type,
so that the behavior is undefined.
Note that the problem does only occur for "Objective-C compatible" properties. If you remove the
NSObject superclass in above example, the output is as expected.
Conclusion: You cannot have two Objective-C properties that differ only in the
case of the first letter. The Swift compiler should fail with an error here (as the
Objective-C compiler does).
The problem you were facing with was not connected to the swift language. Method prepareForSegue is called before loadView. That mean UITextField and UITextView are not initialized yet. That's why fields were not initialized.
You also asked: Why compiler doesn't show any error? That's because any selector performed on nil object doesn't throw an exception. So for example (sorry for obj-c):
UITextField *tf = nil;
[tf setText:#"NewText"];
Will not show any error.
As you said on your own answer to solve your problem you need to add additional fields to your destination controller (copy-paste):
var tAskName:String? //cUstomCamelCase
var tAskDescription:String? //cUstomCamelCase
Why is it happening?
I believe that internally Swift is using lowerCamelCase for text controls names which are not yet initialized and thus failing to set the values. But it is also strange that I didn't get any kind of error.
How did I solve it?
I know that the Swift is case-sensitive. So that was not the issue. So I just changed the case of one letter and named the variables as (tAskName, tAskDescription) and the values were set as expected.
#IBOutlet var TaskName:UITextField! //UpperCamelCase
#IBOutlet var TaskDescription:UITextView! //UpperCamelCase
var tAskName:String? //cUstomCamelCase
var tAskDescription:String? //cUstomCamelCase
So the conclusion is that if I have a control named TaskName, I cannot have a variable named as taskName

Resources