I'm trying to play a little bit with swift and iOS 8.
The question is, I'm trying to create this setter in a view controller:
- (void)setViewController:(UIViewController *)viewController
{
_viewController = viewController;
method do something
}
This code below is in Objective-C so I'd like to create it in swift.
I think, I should use willSet, but I know how do it if it's defined when you have your own variable, how can I define that willSet method if that property is a ViewController property.
Thanks
Something like this:
var viewController: UIViewController? {
willSet {
//method to do something
}
}
You can actually access what viewController will be set to with the variable "newValue" if you need that in the method call.
Note: I made the property optional here just to get rid of compiler warnings about needing initialization
An alternate way different from #Eric's answer is by using a computed property, which mimics objective-c properties more closely, consisting on defining a pseudo-private data member and a computed property implementing a getter and a setter:
class MyClass {
var _viewController: UIViewController?
var viewController : UIViewController? {
get {
return self._viewController
}
set {
self._viewController = newValue
// Do something else
}
}
}
I defined the property pseudo-private because swift doesn't have access modifiers (yet), so everything is public - but when I see an underscore prefixing a variable or property name, that to me means private (It's a convention I use it a lot in javascript for instance).
Related
Consider code like this:
protocol SomeProtocol {
var something: Bool { get set }
}
class SomeProtocolImplementation: SomeProtocol {
var something: Bool = false {
didSet {
print("something changed!")
}
}
}
protocol MyProtocol {
var myProperty: SomeProtocol { get }
}
class MyClass: MyProtocol {
var myProperty: SomeProtocol = SomeProtocolImplementation() {
didSet {
print("myProperty has changed")
}
}
}
var o: MyProtocol = MyClass()
o.myProperty.something = true
This code doesn't compile with error:
error: cannot assign to property: 'myProperty' is a get-only property
o.myProperty.something = true
~~~~~~~~~~~~ ^
Why? My property is of type of SomeProtocolImplementation, which is class type so it should be possible to modify it's inner property using reference to myProperty.
Going further, after modifying myProperty definition so that it looks like that:
var myProperty: SomeProtocol { get set }
something weird happens. Now the code compile (not a surprise), but the output is:
something changed!
myProperty has changed
So at this point SomeProtocolImplementation starts behaving like a value type - modyifing it's internal state causes that the "didSet" callback for myProperty is triggered. Just as SomeProtocolImplementation would be struct...
I actually find the solution, but I want also understand what's going on. The solution is to modify SomeProtocol definition to:
protocol SomeProtocol: class {
var something: Bool { get set }
}
It works fine, but I'm trying to understand why it behaves like this. Anybody able to explain?
First read what Class Only Protocol is. Concentrate on the note section that says:
Use a class-only protocol when the behavior defined by that protocol’s requirements assumes or requires that a conforming type has reference semantics rather than value semantics.
Above quote should get you the idea.
You are trying to get the behavior of reference type for your SomeProtocol's conforming class (i.e. SomeProtocolImplementation). You want to be able to change the value of something in future. So basically you are directing to the above quoted sentence.
If you need more clarification please consider the following more meaningful design where I changed the naming for convenience:
protocol Base: class {
var referenceTypeProperty: Bool { get set }
// By now you are assuming: this property should be modifiable from any reference.
// So, instantly make the protocol `Class-only`
}
class BaseImplementation: Base {
var referenceTypeProperty: Bool = false {
didSet {
print("referenceTypeProperty did set")
}
}
}
protocol Child {
var valueTypeProperty: Base { get }
// This property shouldn't be modifiable from anywhere.
// So, you don't need to declare the protocol as Class-only
}
class ChildImplementation: Child {
var valueTypeProperty: Base = BaseImplementation() {
didSet {
print("valueTypeProperty did set")
}
}
}
let object: Child = ChildImplementation()
object.valueTypeProperty.referenceTypeProperty = true
Any class that can provide behavior useful to other classes may declare a programmatic interface for vending that behavior anonymously. Any other class may choose to adopt the protocol and implement one or more of its methods, thereby making use of the behavior. The class that declares a protocol is expected to call the methods in the protocol if they are implemented by the protocol adopter.
Protocol Apple Documentation
When you try to 'set' value to a variable that is read-only - you are trying to change the protocol's implementation. Conforming classes can only consume information from protocol. In Swift we can write protocol extensions where we can have alternative methods for the protocol.
In short think of computed variables as functions. You are technically trying to change a function in this case.
I actually find the solution, but I want also understand what's going on.
I was just about to tell you to make SomeProtocol a class protocol, but you already figured that out. — So I'm a little confused as to what you don't understand.
You understand about reference types and value types, and you understand about class protocols and nonclass protocols.
Well, as long as SomeProtocol might be adopted by a struct (it's a nonclass protocol), then if you are typing something as a SomeProtocol, it is a value type. The runtime isn't going to switch on reference type behavior just because the adopter turns out to be a class instance; all the decisions must be made at compile time. And at compile time, all the compiler knows is that this thing is a SomeProtocol, whose adopter might be a struct.
what is the suggested approach when I want to add a functionality to UIView so all views inside my app get those? As a matter of fact I need to add some stored properties too so an Extension is not possible. Since I need to deal with Textfields, ImageViews, Views (and who knows what else will come) I dont want to subclass every each of the too add that functionality, so the goal would be to make a subclass of UIView and all my controls (if its possible) get that functionality out of the box.
With an extension it would be easy, but as I said, I need to store some stuff too, so is this goal achievable with a subclass? Or what would be the right approach (maybe there is a third option)
Thanks
Why don't you define a protocol and provide default implementations in the protocol extension, then have UIView conform to that protocol? Here is an example:
protocol MyProto {
var someVar: Bool { get set }
func someFunc() -> Void
}
extension MyProto {
var someVar: Bool {
get {
// provide default implementation
return true
}
set {
}
}
func someFunc() -> Void {
// provide common implementation
}
}
extension UIView: MyProto {}
You can also use the where clause to constrain the default behaviour for a type.
extension MyProto where Self: UIControl {
var someVar: Bool {
get {
return isUserInteractionEnabled
}
set {
isUserInteractionEnabled = newValue
}
}
}
extension MyProto where Self: UITextField {
var someVar: Bool {
get {
return isFirstResponder
}
set {
newValue ? becomeFirstResponder() : resignFirstResponder()
}
}
}
TLDR; You can't do this and you will need to subclass each UI element that you want to introduce new properties to.
You can't do this (without access to the source code) as you would effectively be changing the class inheritance tree by injecting your own class between UIView and its subclasses.
Consider the implications if a language allowed this:
Class A defines a property, a
Class Binherits from Class A and defines a property b, which is fine because Class A does not have this property.
Class C inherits from Class B and has both a and b properties.
Now, what could happen if you could 'inject' Class A1 somehow 'below' Class A?
Class A1 could define a property, b, which is fine because Class A does not have this property
Class B now has a problem though, because its b clashes with the superclass b
Class C has a multiple-inheritance diamond-problem with property b
Of course, you only intend to add properties that don't clash (although you can't know this because you don't know of all possible subclass implementations) and don't need the subclasses to access your property, so the multiple inheritance
isn't an issue, but if such a feature were in a language, these potential issues would need to be addressed because you can't rely on everyone having the same intentions as you.
I would like to set a willSet statement to isResting property. Whenever isResting is changed some function is called. This is what I mean:
starNode.physicsBody?.isResting: Bool = false{
willSet{
if newValue == true{
print("GOOGOO")
}else{
print("STOP")
}
}
}
This is not working. Can you assign a willSet or didSet to an already defined iOS property? If not, how do I get similar effect?
This is the documentation:
https://developer.apple.com/library/mac/documentation/SceneKit/Reference/SceneKit_Framework/index.html#//apple_ref/doc/uid/TP40012283
https://developer.apple.com/library/prerelease/ios/documentation/SceneKit/Reference/SCNPhysicsBody_Class/index.html#//apple_ref/occ/instp/SCNPhysicsBody/isResting
If not read-only:
One option would be to subclass the library object and override the property you'd like to set. Then, you can implement willSet within your subclass:
class Object {
var property: Float = 0.0
}
class ChildObject: Object {
override var property: Float {
willSet {
print("Set in child class")
}
}
}
This is the way that Apple recommends in the documentation (see "Overriding Property Observers"):
You can use property overriding to add property observers to an
inherited property. This enables you to be notified when the value of
an inherited property changes, regardless of how that property was
originally implemented.
If read-only:
You might be able to use Key-Value Observing, if the object inherits from NSObject, or if it has been marked dynamic in Swift.
Is this property an IBInspectable property?
In Objetive-C this is automatically if this is a property trough getter and setter, but in Swift it isn't. You can make this property as private and create methods for set this property and inside this method, add a call for another method.
func setResting(resting:Bool) {
self.isResting = resting
//call another method here
}
This is my inheritance structure
Protocols
protocol BaseProtocol {
}
protocol ChildProtocol: BaseProtocol {
}
Classes
class BaseClass: NSObject {
var myVar: BaseProtocol!
}
class ChildClass: BaseClass {
override var myVar: ChildProtocol!
}
I'm receiving a compiler error:
Property 'myVar' with type 'ChildProtocol!' cannot override a property with type 'BaseProtocol!'
What is the best approach to achieve this?
UPDATE
I updated the question trying to implement the solution with generics but it does not work :( This is my code (now the real one, without examples)
Protocols
protocol TPLPileInteractorOutput {
}
protocol TPLAddInteractorOutput: TPLPileInteractorOutput {
func errorReceived(error: String)
}
Classes
class TPLPileInteractor<T: TPLPileInteractorOutput>: NSObject, TPLPileInteractorInput {
var output: T!
}
And my children
class TPLAddInteractor<T: TPLAddInteractorOutput>: TPLPileInteractor<TPLPileInteractorOutput>, TPLAddInteractorInput {
}
Well, inside my TPLAddInteractor I can't access self.output, it throws a compiler error, for example
'TPLPileInteractorOutput' does not have a member named 'errorReceived'
Besides that, when I create the instance of TPLAddInteractor
let addInteractor: TPLAddInteractor<TPLAddInteractorOutput> = TPLAddInteractor()
I receive this other error
Generic parameter 'T' cannot be bound to non-#objc protocol type 'TPLAddInteractorOutput'
Any thoughts?
#tskulbru is correct: it can't be done, and this has nothing to do with your protocols. Consider the example below, which also fails…this time with Cannot override with a stored property 'myVar':
class Foo {
}
class Goo: Foo {
}
class BaseClass: NSObject {
var myVar: Foo!
}
class ChildClass: BaseClass {
override var myVar: Foo!
}
To understand why, let's reexamine the docs:
Overriding Properties
You can override an inherited instance or class property to provide
your own custom getter and setter for that property, or to add
property observers to enable the overriding property to observe when
the underlying property value changes.
The implication is that if you are going to override a property, you must write your own getter/setter, or else you must add property observers. Simply replacing one variable type with another is not allowed.
Now for some rampant speculation: why is this the case? Well, consider on the one hand that Swift is intended to be optimized for speed. Having to do runtime type checks in order to determine whether your var is in fact a Foo or a Bar slows things down. Then consider that the language designers likely have a preference for composition over inheritance. If both of these are true, it's not surprising that you cannot override a property's type.
All that said, if you needed to get an equivalent behavior, #tskulbru's solution looks quite elegant, assuming you can get it to compile. :)
I don't think you can do that with protocols
The way i would solve the problem you are having is with the use of generics. This means that you essentially have the classes like this (Updated to a working example).
Protocols
protocol BaseProtocol {
func didSomething()
}
protocol ChildProtocol: BaseProtocol {
func didSomethingElse()
}
Classes
class BaseClass<T: BaseProtocol> {
var myProtocol: T?
func doCallBack() {
myProtocol?.didSomething()
}
}
class ChildClass<T: ChildProtocol> : BaseClass<T> {
override func doCallBack() {
super.doCallBack()
myProtocol?.didSomethingElse()
}
}
Implementation/Example use
class DoesSomethingClass : ChildProtocol {
func doSomething() {
var s = ChildClass<DoesSomethingClass>()
s.myProtocol = self
s.doCallBack()
}
func didSomething() {
println("doSomething()")
}
func didSomethingElse() {
println("doSomethingElse()")
}
}
let foo = DoesSomethingClass()
foo.doSomething()
Remember, you need a class which actually implements the protocol, and its THAT class you actually define as the generic type to the BaseClass/ChildClass. Since the code expects the type to be a type which conforms to the protocol.
There are two ways you can go with your code, depending what you want to achieve with your code (you didn't tell us).
The simple case: you just want to be able to assign an object that confirms to ChildProtocol to myVar.
Solution: don't override myVar. Just use it in ChildClass. You can do this by design of the language Swift. It is one of the basics of object oriented languages.
Second case: you not only want to enable assigning instances of ChildProtocol, you also want to disable to be able to assign instances of BaseProtocol.
If you want to do this, use the Generics solution, provided here in the answers section.
If you are unsure, the simple case is correct for you.
Gerd
I've tried to declare IBOutlet property on extension of class. But it give error as
'var' declaration without getter/setter method not allowed here
class ExampleView : UIView
{
}
extension ExampleView
{
#IBOutlet var btn1, btn2 : UIButton // here I got error.
}
Please any one suggest me correct way to do it?
From Extensions -> Computed Properties in The Swift Programming Language
NOTE
Extensions can add new computed properties, but they cannot add stored
properties, or add property observers to existing properties.
Addition in response to twlkyao's comment: Here is my implementation of the absoluteValue property of a Double
extension Double {
var absoluteValue: Double {
if self >= 0 {
return self
} else {
return -self
}
}
}
// Simple test -> BOTH println() should get called.
var a = -10.0
if (a < 0) {
println("Smaller than Zero")
}
if (a.absoluteValue > 5) {
println("Absolute is > 5")
}
From The Swift Programming Language:
Extensions in Swift can:
Add computed properties and computed static properties
Define instance methods and type methods
Provide new initializers
Define subscripts
Define and use new nested types
Which means you can't add IBOutlets and other stored properties.
If you really want to cheat, you can create global vars or a bookkeeping object which would allow you to query these vars or the object in order to add those properties (and have them be computed properties).
But it seems like it would go against the best practices. I would only do it if there's absolutely no other way.