I have a class whose property value I'd like to set at runtime although there is not a signature to set it.
eg:
public class CBCentralManager : NSObject {
public var state: CBCentralManagerState { get }
...
}
I want to be able to change this class's state at will because I'm creating a stub for unit tests.
stub:
class CBCentralManagerStub: CBCentralManager {
func changeState(newState: CBCentralManagerState) {
let val: NSValue = NSValue(&newState, withObjCType: &(CBCentralManagerState.rawValue))
self.setValue(val, forKey:"state")
}
}
In here, I was hoping to use obj-C runtime method,
setValue:forKey to accomplish this but I get the error:
Error: '&' used with non-inout argument of type 'UnsafePointer'
(aka 'UnsafePointer<()>')
Is there any way to set the value of this class at runtime to manipulate this for testing?
Related
I have read the Swift docs and searched here, but I still am not sure about how to implement a class hierarchy where each subclass sets custom value for an inherited static property; that is:
Base class defines a static property: all instances share the same value.
Subclass overrides the static property: all instances share the same value, which is different form that of the base class.
Can the property be stored?
Also, How should I access the value of the property from within an instance method (regardless of the particular class), and get the correct value everytime? will the following code work?
class BaseClass
{
// To be overridden by subclasses:
static var myStaticProperty = "Hello"
func useTheStaticProperty()
{
// Should yield the value of the overridden property
// when executed on instances of a subclass:
let propertyValue = self.dynamicType.myStaticProperty
// (do something with the value)
}
You are so close to being there, except that you can't override a static property in a subclass — that is what it means to be static. So you'd have to use a class property, and that means it will have to be a computed property — Swift lacks stored class properties.
So:
class ClassA {
class var thing : String {return "A"}
func doYourThing() {
print(type(of:self).thing)
}
}
class ClassB : ClassA {
override class var thing : String {return "B"}
}
And let's test it:
ClassA().doYourThing() // A
ClassB().doYourThing() // B
In messing around with Swift today I came across a strange thing. Here's the unit test I developed which shows some unexpected behaviours when using Swift's AnyObject.
class SwiftLanguageTests: XCTestCase {
class TestClass {
var name:String?
var xyz:String?
}
func testAccessingPropertiesOfAnyObjectInstancesReturnsNils() {
let instance = TestClass()
instance.xyz = "xyz"
instance.name = "name"
let typeAnyObject = instance as AnyObject
// Correct: Won't compile because 'xyz' is an unknown property in any class.
XCTAssertEqual("xyz", typeAnyObject.xyz)
// Unexpected: Will compile because 'name' is a property of NSException
// Strange: But returns a nil when accessed.
XCTAssertEqual("name", typeAnyObject.name)
}
}
This code is a simplification of some other code where there is a Swift function that can only return a AnyObject.
As expected, after creating an instance of TestClass, casting it to AnyObject and setting another variable, accessing the property xyz won't compile because AnyObject does not have such a property.
But surprisingly a property called name is accepted by the compiler because there is a property by that name on NSException. It appears that Swift is quite happy to accept any property name as long as it exists somewhere in the runtime.
The next unexpected behaviour and the thing that got all this started is that attempting to access the name property returns a nil. Watching the various variables in the debugger, I can see that typeAnyObject is pointing at the original TestClass instance and it's name property has a value of "name".
Swift doesn't throw an error when accessing typeAnyObject.name so I would expect it to find and return "name". But instead I get nil.
I would be interested if anyone can shed some light on what is going on here?
My main concern is that I would expect Swift to either throw an error when accessing a property that does not exist on AnyObject, or find and return the correct value. Currently neither is happening.
Similar as in Objective-C, where you can send arbitrary messages to id,
arbitrary properties and methods can be called on an instance of AnyObject
in Swift. The details are different however, and it is documented in
Interacting with Objective-C APIs
in the "Using Swift with Cocoa and Objective-C" book.
Swift includes an AnyObject type that represents some kind of object. This is similar to Objective-C’s id type. Swift imports id as AnyObject, which allows you to write type-safe Swift code while maintaining the flexibility of an untyped object.
...
You can call any Objective-C method and access any property on an AnyObject value without casting to a more specific class type. This includes Objective-C compatible methods and properties marked with the #objc attribute.
...
When you call a method on a value of AnyObject type, that method call behaves like an implicitly unwrapped optional. You can use the same optional chaining syntax you would use for optional methods in protocols to optionally invoke a method on AnyObject.
Here is an example:
func tryToGetTimeInterval(obj : AnyObject) {
let ti = obj.timeIntervalSinceReferenceDate // NSTimeInterval!
if let theTi = ti {
print(theTi)
} else {
print("does not respond to `timeIntervalSinceReferenceDate`")
}
}
tryToGetTimeInterval(NSDate(timeIntervalSinceReferenceDate: 1234))
// 1234.0
tryToGetTimeInterval(NSString(string: "abc"))
// does not respond to `timeIntervalSinceReferenceDate`
obj.timeIntervalSinceReferenceDate is an implicitly unwrapped optional
and nil if the object does not have that property.
Here an example for checking and calling a method:
func tryToGetFirstCharacter(obj : AnyObject) {
let fc = obj.characterAtIndex // ((Int) -> unichar)!
if let theFc = fc {
print(theFc(0))
} else {
print("does not respond to `characterAtIndex`")
}
}
tryToGetFirstCharacter(NSDate(timeIntervalSinceReferenceDate: 1234))
// does not respond to `characterAtIndex`
tryToGetFirstCharacter(NSString(string: "abc"))
// 97
obj.characterAtIndex is an implicitly unwrapped optional closure. That code
can be simplified using optional chaining:
func tryToGetFirstCharacter(obj : AnyObject) {
if let c = obj.characterAtIndex?(0) {
print(c)
} else {
print("does not respond to `characterAtIndex`")
}
}
In your case, TestClass does not have any #objc properties.
let xyz = typeAnyObject.xyz // error: value of type 'AnyObject' has no member 'xyz'
does not compile because the xyz property is unknown to the compiler.
let name = typeAnyObject.name // String!
does compile because – as you noticed – NSException has a name property.
The value however is nil because TestClass does not have an
Objective-C compatible name method. As above, you should use optional
binding to safely unwrap the value (or test against nil).
If your class is derived from NSObject
class TestClass : NSObject {
var name : String?
var xyz : String?
}
then
let xyz = typeAnyObject.xyz // String?!
does compile. (Alternatively, mark the class or the properties with #objc.)
But now
let name = typeAnyObject.name // error: Ambigous use of `name`
does not compile anymore. The reason is that both TestClass and NSException
have a name property, but with different types (String? vs String),
so the type of that expression is ambiguous. This ambiguity can only be
resolved by (optionally) casting the AnyObject back to TestClass:
if let name = (typeAnyObject as? TestClass)?.name {
print(name)
}
Conclusion:
You can call any method/property on an instance of AnyObject if that
method/property is Objective-C compatible.
You have to test the implicitly unwrapped optional against nil or
use optional binding to check that the instance actually has that
method/property.
Ambiguities arise if more than one class has (Objective-C) compatible
methods with the same name but different types.
In particular because of the last point, I would try to avoid this
mechanism if possible, and optionally cast to a known class instead
(as in the last example).
it has nothing with NSException!
from Apple documentation:
protocol AnyObject { ... }
The protocol to which all classes implicitly conform.
When used as a concrete type, all known #objc methods and properties are available, as implicitly-unwrapped-optional methods and properties respectively, on each instance of AnyObject
name is #objc property, xyz is not.
try this :-)
let typeAnyObject = instance as Any
or
#objc class TestClass: NSObject {
var name:String?
var xyz:String? }
let instance = TestClass() instance.xyz = "xyz" instance.name = "name"
let typeAnyObject = instance as AnyObject
typeAnyObject.name // will not compile now
I'd like to logically organize class properties to signify that they are one logical unit and to distinguish them from other class properties that are less tightly related.
I thought of doing this using a struct within my class. However, it seems that I can't call class methods from the struct property setters. I get what seems to be an inappropriate compile error: "missing argument for parameter #1 in call"
This seems to be different from calling method from struct in swift
where the function is within the struct. In my case, the methods are generic and don't just apply to my struct but to all class properties. Therefore, I don't want to move them within the struct.
Do you have ideas on how to organize properties into tight(er) logical units within classes?
class MyClass {
struct MyStruct {
static var aVarInMyStruct: String? {
didSet {
anotherVarInMyStruct = foo() // This gives compile error "missing argument for parameter #1 in call"
}
}
static var anotherVarInMyStruct: String?
}
func foo() {
println("I am foo()")
}
}
The inner type MyStruct knows nothing about its outer type MyClass. So there is no foo function to call from MyStruct. To better organize your code I suggest you to use // MARK: - whatever this section is comments. Types are not here to organize codes. Types are here to create right abstractions for the program.
I fix your bug:
class MyClass {
struct MyStruct {
static var aVarInMyStruct: String? {
didSet {
anotherVarInMyStruct = MyClass.foo() // This gives compile error "missing argument for parameter #1 in call"
}
}
static var anotherVarInMyStruct: String?
}
static func foo()->String {
println("I am foo()")
return "it is ok"
}
}
In my Swift library EVCloudKitDao I do a lot with reflection. Because of that I have set my base class of my data objects to NSObject. Now after the upgrade to Xcode 6.3 I get an error on the 2 functions for getting the hash and the description of the object. The description function was a nice to have, but i do need the hash to make my objects working with a Set.
Here is the code that I have
public class EVCloudKitDataObject : NSObject, NSCoding, Printable, Hashable, Equatable {
public func hash() -> Int {
return self.hashValue
}
public func description() -> String {
return EVReflection.description(self)
}
}
The errors that I get is:
/Users/evermeer/Desktop/dev/GitHub/EVCloudKitDao/AppMessage/AppMessage/CloudKit/EVCloudKitDataObject.swift:106:17:
Method 'hash()' with Objective-C selector 'hash' conflicts with getter
for 'hash' from superclass 'NSObject' with the same Objective-C
selector
/Users/evermeer/Desktop/dev/GitHub/EVCloudKitDao/AppMessage/AppMessage/CloudKit/EVCloudKitDataObject.swift:86:17:
Method 'description()' with Objective-C selector 'description'
conflicts with getter for 'description' from superclass 'NSObject'
with the same Objective-C selector
Does anyone know how I could solve this?
You can not use override.
As the error says, in both cases there's a naming conflict between a property and a method. The most obvious way to fix is by turning your 2 methods into properties:
public override var hash: Int {
return self.hashValue
}
public override var description: String {
return EVReflection.description(self)
}
which can also be written as:
public override var hash:Int {
get {
return self.hashValue
}
}
public override var description : String {
get {
return EVReflection.description(self)
}
}
The reason why it worked in the previous version is most likely because of this:
Swift now detects discrepancies between overloading and overriding in the Swift type system and the effective behavior seen via the Objective-C runtime.
Read more in the release notes (search for 18391046 and 18383574)
It seems that there are a few situations in which a required keyword on Swift class functions would be extremely beneficial, particularly due to the ability of class functions to return Self.
When returning Self from a class func, there are unfortunately two restrictions that make implementing said function very difficult/inhibitive:
You cannot use Self as a type check inside the function implementation, ie:
class Donut {
class func gimmeOne() -> Self {
// Compiler error, 'Self' is only available in a protocol or as the result of a class method
return Donut() as Self
}
}
You cannot return the actual type of the class itself, ie:
class Donut {
class func gimmeOne() -> Self {
// Compiler error, 'Donut' is not convertible to 'Self'
return Donut()
}
}
The reason for these compiler errors is valid. If you have a GlazedDonut subclass that does not override this class function, it is possible that calling GlazedDonut.gimmeOne() will give you back a Donut, which is not a Self.
It seems this situation could be alleviated by allowing classes to specify these functions with required. This would ensure that any subclasses override the method and encur their own round of type checking, making sure that a GlazedDonut returns itself in all cases, eliminating the possibility for a Donut to come back.
Is there a technical, authoritative reason why this has not been added? I'd like to suggest it as an improvement to the Swift team, but want to ensure there isn't an obvious reason why it has been omitted, or cannot be accomplished.
The idea for this question originates here:
https://stackoverflow.com/a/25924224/88111
required is generally only used on initializers, because initializers are not always inherited in Swift. Therefore, to allow you to call an initializer on a variable class (i.e. a value of metaclass type, say Foo.Type), you need to know that that class Foo, and all possible subclasses, have this initializer.
However, methods (both instance methods and class methods) are always inherited. Therefore, required is not necessary.
By the way, your assertion that "You cannot return the actual type of the class itself" is not true. (In fact, the error "'Self' is only available in a protocol or as the result of a class method" itself says you can return the type of the class itself.) Similar to in Objective-C, you can do:
class Donut {
required init() { }
class func gimmeOne() -> Self {
return self()
}
}
You could use a protocol to make the method 'required'
protocol IDonut{
class func gimmeOne()->Donut;
}
class Donut:IDonut {
class func gimmeOne() -> Donut {
return Donut();
}
}
class GlazedDonut: Donut, IDonut{
override class func gimmeOne()->Donut{
return GlazedDonut();
}
}