Call method from other ViewController | Swift - ios

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
}
}
}

Related

Using the word self in a class or its methods instead of just using the property name [duplicate]

In a simple example like this, I can omit self for referencing backgroundLayer because it's unambiguous which backgroundLayer the backgroundColor is set on.
class SpecialView: UIView {
let backgroundLayer = CAShapeLayer()
init() {
backgroundLayer.backgroundColor = UIColor.greenColor().CGColor
}
}
But, just like in Objective-C, we can confuse things by adding local variables (or constants) named similarly. Now the backgroundColor is being set on the non-shape layer:
class SpecialView: UIView {
let backgroundLayer = CAShapeLayer()
init() {
var backgroundLayer = CALayer()
backgroundLayer.backgroundColor = UIColor.greenColor().CGColor
}
}
(this is resolved by using self.backgroundLayer.backgroundColor)
In Objective-C I always eschewed ivars for properties and properties were always prefixed with self for clarity. I don't have to worry about ivars in swift but are there other considerations for when I should use self in swift?
The only times self is required are when referencing a property inside a closure and, as you pointed out, to differentiate it from a local variable with the same name.
However, personally, I prefer to always write "self" because:
That is an instant and obvious sign that the variable is a property. This is important because it being a property means that its state can vary more widely and in different ways than a local variable. Also, changing a property has larger implications than changing a local variable.
The code does not need to be updated if you decide to introduce a parameter or variable with the same name as the property
Code can be easily copied in and out of closures that do require self
Most of the time we can skip self. when we access class properties.
However there is one time when we MUST use it: when we try to set self.property in a closure:
dispatch_async(dispatch_get_main_queue(), {
// we cannot assign to properties of self
self.view = nil
// but can access properties
someFunc(view)
})
one time when we SHOULD use it: so you don't mess a local variable with class property:
class MyClass {
var someVar: String = "class prop"
func setProperty(someVar:String = "method attribute") -> () {
print(self.someVar) // Output: class property
print(someVar) // Output: method attribute
}
}
other places where we CAN use self.
before property just to be expressive about were variable/constant comes from.
Looking at Ray Wenderlich's style guide
Use of Self
For conciseness, avoid using self since Swift does not require it to access an object's properties or invoke its methods.
Use self only when required by the compiler (in #escaping closures, or in initializers to disambiguate properties from arguments). In other words, if it compiles without self then omit it.
Swift documentation makes the same recommendation.
The self Property
Every instance of a type has an implicit property called self, which is exactly equivalent to the instance itself. You use the self property to refer to the current instance within its own instance methods.
The increment() method in the example above could have been written like this:
func increment() {
self.count += 1
}
In practice, you don’t need to write self in your code very often. If you don’t explicitly write self, Swift assumes that you are referring to a property or method of the current instance whenever you use a known property or method name within a method. This assumption is demonstrated by the use of count (rather than self.count) inside the three instance methods for Counter.
The main exception to this rule occurs when a parameter name for an instance method has the same name as a property of that instance. In this situation, the parameter name takes precedence, and it becomes necessary to refer to the property in a more qualified way. You use the self property to distinguish between the parameter name and the property name.
Here, self disambiguates between a method parameter called x and an instance property that is also called x:
struct Point {
var x = 0.0, y = 0.0
func isToTheRightOf(x: Double) -> Bool {
return self.x > x
}
}
let somePoint = Point(x: 4.0, y: 5.0)
if somePoint.isToTheRightOf(x: 1.0) {
print("This point is to the right of the line where x == 1.0")
}
// Prints "This point is to the right of the line where x == 1.0"
I'm going to go against the flow and not use self unless absolutely required.
The reason why is that two of the main reasons to use self is
When capturing self in a block
When setting self as a delegate
In both cases, self will be captured as a strong reference. This might be what you want, but in many cases, you actually want to use a weak one.
Therefor, forcing the developer to use self as an exception and not a rule will make this strong capture more conscious, and let him reflect on this decision.
As Apple documentation says in https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Methods.html
The self Property
Every instance of a type has an implicit property called self, which
is exactly equivalent to the instance itself. You use the self
property to refer to the current instance within its own instance
methods.
The increment() method in the example above could have been written
like this:
func increment() {
self.count += 1
}
In practice, you don’t need to write self in your code very often. If
you don’t explicitly write self, Swift assumes that you are referring
to a property or method of the current instance whenever you use a
known property or method name within a method. This assumption is
demonstrated by the use of count (rather than self.count) inside the
three instance methods for Counter.
The main exception to this rule occurs when a parameter name for an
instance method has the same name as a property of that instance. In
this situation, the parameter name takes precedence, and it becomes
necessary to refer to the property in a more qualified way. You use
the self property to distinguish between the parameter name and the
property name.
Here, self disambiguates between a method parameter called x and an
instance property that is also called x:
struct Point {
var x = 0.0, y = 0.0
func isToTheRightOf(x: Double) -> Bool {
return self.x > x
}
}
let somePoint = Point(x: 4.0, y: 5.0)
if somePoint.isToTheRightOf(x: 1.0) {
print("This point is to the right of the line where x == 1.0")
}
// Prints "This point is to the right of the line where x == 1.0"
Without the self prefix, Swift would assume that both uses of x
referred to the method parameter called x.
I would prefer to keep using self whenever I'm using a property to omit these misunderstandings.
As Nick said, in objective-c we had ivars + synthesized properties which gave the _internal variable names to delineate things. Eg.
#IBOutlet (nonatomic,strong) UITableView *myTableView;
resulting in _myTableView to be (preferably) referenced internally - and self.myTableView to be reference beyond the class. While this is pretty black and white, consider the exception when programmatically instantiating views, you can gain clarity/ simplicity / reduce boilerplate by removing self.
#interface CustomVC:UIViewController
{
UITableView *myTableView;
}
In swift, the public / internal properties clarify this scope.
If it's a public property that other classes will interact with err on self.
Otherwise if it's internal skip self and avoid the automatic repetition.
The compiler will catch you when it's needed.
// UIViewcontroller swift header
public var title: String? // Localized title for use by a parent controller.
public var navigationItem: UINavigationItem { get }
/// In your class
self.title = "Clarity"
self.navigationItem.leftBarButtonItem = UIBarButtonItem()
// In superclass
#property(nonatomic, copy) NSString *screenName // use self.screenName in swift subclass
#IBOutlet myTableView:UITableView // use self
public var myTableView:UITableView // use self
internal var myTableView:UITableView // skip self
var myTableView:UITableView // skip self

Generics type constraint vs inheritance

Is there a difference between these two function declarations?
func doSomething<T: UIViewController>(controller: T) {...}
vs.
func doSomething(controller: UIViewController) {...}
In Type Constraint Syntax section of the Apples Swift programming language book, there's this code sample:
func​ ​someFunction​<​T​: ​SomeClass​, ​U​: ​SomeProtocol​>(​someT​: ​T​, ​someU​: ​U​) {
// function body goes here
}
with this description:
The hypothetical function above has two type parameters. The first type parameter, T, has a type constraint that requires T to be a subclass of SomeClass. ...
So in which cases is it better to use generic function described above?
They are different, but in the way you are using them, they amount to pretty much exactly the same result.
The difference is that when you call the generic version, the compiler sets T statically to be whatever type is passed in as the argument. When calling methods on that argument, this makes almost no difference – either way the calls on its methods will be dynamically dispatched, and you can't touch any parts of T that aren't guaranteed to be available from the constraint.
But suppose you made a change to this method to not just take an argument, but also return one, of the same type:
// T here will take the type of whatever is going in/out of the function
// be that UIViewController or a subtype of it
func doSomethingGenerically<T: UIViewController>(controller: T) -> T {
// some logic that results in a new controller being returned
}
// here the return type is fixed to be UIViewController
func doSomethingViaBaseClass(controller: UIViewController) -> UIViewController {
// some logic that results in a new controller being returned
}
Now, suppose you had a subclass of UIViewController that you were passing in, like so:
let subClass: MyUIViewController = ...
let controller1 = doSomethingGenerically(subClass)
let controller2 = doSomethingViaBaseClass(subClass)
Here, the type of the variable controller1 will be MyUIViewController, because that is what was passed in to the function so that is what T is. But the type of the variable controller2 will be UIViewController because that is the fixed type that doSomethingViaBaseClass returns.
Note, this doesn't mean the object they reference will be different - that depends on what the body of the function implements. It's just the type of the variables referring to it that will change.
There are other subtle differences but this is the main one to know about. In the case of structs, however, there are more differences worth noting. As it happens I wrote an article about them yesterday that might help.

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: 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
}

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