After the update of iOS 9.3 and the new update for Swift 2.2 (at least that was stated in the Xcode update changes) the code below doesn't work for me anymore.
if context[indexPath.row].dynamicType.className() == "MyClass"
it throws an exception which states:
Value of type 'AnyObject.Type' has no member 'className'
I am going through the changes which are described in swift.org but I can't find the answer of it.
Why bother with Strings when you have true compiler support in the type system itself? I recommend instead:
if context[indexPath.row].dynamicType == MyClass.Type.self {
// ...
}
Also, it's quite likely that you really mean:
if let cell = context[indexPath.row] as? MyClass {
// ...
}
unless you're intent on cutting out derived classes forever.
I don't know what className() does. It's not a known method on AnyClass.
There are a few ways you can fix this.
Use description() to get a String description of the class name:
if context[indexPath.row].dynamicType.description() == "MyClass" {
// ...
}
Or if you're using Objective-C types you can use NSClassFromString to return an AnyClass from a String:
if context[indexPath.row].dynamicType == NSClassFromString("MyClass") {
// ...
}
Swift 3:
dynamicType has been renamed to type(of:). Usage:
type(of: context[indexPath.row]) == MyClass.self
or
context[indexPath.row] is MyClass
Related
I'm trying to achieve the following in Swift - I want to pass in a type to a generic "get" function, and based on that type and the parameter, use different repositories to get back a class.
I recognize this is a bit strange to do in this way, but it would save me a lot of time and allow me to more properly refactor something later.
Here is the code but it compile errors on the Foo1Repository and Foo2Repository lines "Cannot convert the type of Foo1Repository to T".
Basically Foo1Repository should return a Foo1, which inherits from BaseEntity of course (and the same is true for Foo2)
class func Get<T: BaseEntity>(id: Int) -> T
{
if (T is Foo1) {
return Foo1Repository.Get(id)
}
else if (T == Foo2) {
return Foo2Repository.Get(id)
}
return T()
}
I was hoping to invoke this function by doing:
let foo = FactoryClass.Get<Foo1>(1)
I understand immediately you would ask "why not just call the appropriate repository, i.e."
let foo = Foo1Repository.Get(1)
and you're all set! No need for some weird factory pattern here.
Let's just say at the moment I need to try to do it above without a lot of refactoring of some code I inherited. I'll get back to planning a proper refactor later.
So I've tried a combination of things but still can't seem to get past the compiler errors. Is something like this possible, or do I need to just bite the bullet and go a different route? Thanks so much!
So I figured it out! I wanted to share the answer so others can see how to do this. Hopefully it will help.
func Get<T: BaseEntity>(id: Int) -> T
{
if (T.self == Foo1.self) {
return Foo1Repository.Get() as! T
}
else if (T.self == Foo2.self) {
return Foo2Repository.Get() as! T
}
...
return T()
}
So you can see, the whole as! T at the end was key. Also == instead of is for type checks.
Just like java's instanceOf keyword whats the equivalent in Swift?
java example:
A a = new A();
boolean isInstanceOfA = a instanceof A;
Here isInstanceOfA is true
So i need something similar in Swift
isKindOfClass() method, from NSObjectProtocol is the equivalent of java's instanceof keyword, in java it's a keyword but in swift it's a protocol method, but they behave similarly and are used in similar contexts.
isKindOfClass: returns YES if the receiver is an instance of the
specified class or an instance of any class that inherits from the
specified class.
Which is exactly what instanceof keyword does in Java related link
Example:
let a: A = A()
let isInstanceOfA: Bool = a.isKindOfClass(A) // returns true.
Also you can use the is keyword
let a: A = A()
let isInstanceOfA: Bool = a is A
The difference:
is works with any class in Swift, whereas isKindOfClass() works only with those classes that are subclasses of NSObject or otherwise implement NSObjectProtocol.
is takes a type that must be hard-coded at compile-time. isKindOfClass: takes an expression whose value can be computed at runtime.
So no is keyword doesn't work like instanceof
For swift3 and swift4 it's:
if someInstance is SomeClass {
...
}
if your class is extending NSObject you can also use:
if someInstance.isKind(of: SomeClass.self) {
...
}
let a = A()
let isInstanceOfA = a is A
For Swift 4.x it's:
func getOrElse<T>(defaultVal:T) -> T {
if let selfVal = self, selfVal is T {
return selfVal as! T
} else {
return defaultVal
}
}
Very short variant(by suggestion #Sulthan):
extension Optional {
func getOrElse<T>(defaultVal:T) -> T {
return (self as? T) ?? defaultVal
}
}
With objective-c it's isKindOfClass:[ClassName class].
With swift it's isKindOfClass(Classname.class()).
I'm trying to check if an object is of a given type and I'm getting an error:
'expectedClass' is not a type
My code below.
func testInputValue(inputValue: AnyObject, isKindOfClass expectedClass: AnyClass) throws {
guard let object = inputValue as? expectedClass else {
// Throw an error
let userInfo = [NSLocalizedDescriptionKey: "Expected an inputValue of type \(expectedClass), but got a \(inputValue.dynamicType)"]
throw NSError(domain: RKValueTransformersErrorDomain, code: Int(RKValueTransformationError.UntransformableInputValue.rawValue), userInfo: userInfo)
}
}
I'm trying to figure out what can be wrong here.
You should be able to do this with generics:
func testInputValue<T>(inputValue: AnyObject, isKindOfClass expectedClass: T.Type) throws {
guard let object = inputValue as? T else {
...
}
}
You should not do class comparisons with == as suggested in one of the other answers, unless you specifically want to test if the type of the object tested should exactly match the expected class and it is not allowed to be a subclass of the tested class.
You can use the instance method isKindOfClass to accomplish this, taking subclassing into account. See below for a code example.
NOTE: You may be surprised that this works on a pure Swift class type, given an instance method with the same name exists in NSObject / NSObjectProtocol. It does indeed work in pure Swift (as shown with the example code below – tested with Xcode 7.3, Swift 2.2.1), with no #objc types involved, as long as you import Foundation. I am presuming based on this that this instance method is added as an extension method in Foundation to all class types.
import Foundation
class A { }
class B { }
class C: B { }
enum ValueTestError: ErrorType {
case UnexpectedClass(AnyClass)
}
func testInputValue(inputObj:AnyObject, isKindOfClass expectedClass:AnyClass) throws {
guard inputObj.isKindOfClass(expectedClass) else {
throw ValueTestError.UnexpectedClass(inputObj.dynamicType)
}
}
let a = A()
let b = B()
let c = C()
do {
try testInputValue(c, isKindOfClass: B.self)
} catch {
print("This does not get printed as c is of type B (C is subclass of B).")
}
do {
try testInputValue(a, isKindOfClass: B.self)
} catch {
print("This gets printed as a is not of type B.")
}
Also, importantly although isKindOfClass is available as an instance method on AnyObject, trying to call it on an arbitrary Swift class typed object will only work if you first cast that object to AnyObject (which will always of course succeed). Example code illustrating this point is presented below, and there's more on this question over here.
import Foundation
class A {}
let a = A()
// the following compiles and returns 'true'
(a as AnyObject).isKindOfClass(A.self)
// the following fails to compile with "Value of type 'A' has no member 'isKindOfClass'"
a.isKindOfClass(A.self)
Not the greatest answer ever, but comparing with inputValue.dynamicType works:
if inputValue.dynamicType == expectedClass {
print("inputValue.dynamicType \(inputValue.dynamicType) equals expectedClass \(expectedClass)")
// ...
}
While trying and playing around with Typhoon DI, I realized that LazySingleton scope is not working as expected meaning lazy properties are injected even before they are used. Being more concrete I created a TyphoonAssembly as follow :
public class AppAssembly : TyphoonAssembly {
public dynamic func knight() -> AnyObject{
return TyphoonDefinition.withClass(Knight.self){
(definition) in
definition.injectProperty("name", with: "Dragon")
definition.injectProperty("horse")
definition.scope = TyphoonScope.LazySingleton
}
}
public dynamic func horse() -> AnyObject{
return TyphoonDefinition.withClass(Horse.self){
(definition) in
definition.injectProperty("color", with: "red")
definition.scope = TyphoonScope.LazySingleton
}
}
}
where Knight is NSObject and has validateProperties function
class Knight:NSObject {
var name:String?
var horse: Horse?
func validateProperties(){
if name != nil{
println("Name not nil")
}
if horse != nil{
println("Horse not nil")
}
}
}
After activating assembly and getting knight from it, calling validateProperties function always print Name not nil and Horse not nil even these properties are never used in my code. Am I missing something here or simply lazy injection does not work same as Swift lazy stored properties do?
Your interpretation of the term lazy singleton makes sense, but its not the correct one. TyphoonScopeLazySingleton means that Typhoon won't instantiate the whole object until its asked for. Once asked for all properties will be injected. There's no proxying to inject on demand - if you're interested in such a feature, would you mind raising an issue for us in Github?
You're right that such a feature would only work in Swift if the class extended NSObject and the properties were types compatible with Objective-C, as "pure" Swift uses C++ style static dispatch, and therefore runtime method interception/proxying is not possible.
Here's the user guide for Typhoon scopes.
I've had a fair few warnings and errors in my Swift code since updating to the latest Xcode 6 DP3. Most have been resolved by adopting the newly changed syntax however there is one error which seems strange.
The following code gives the error Type 'NSDictionary?' does not conform to protocol 'Equatable':
if (launchOptions != nil && launchOptions![UIApplicationLaunchOptionsRemoteNotificationKey] != nil) {
Does anyone have a solution? I'm probably overlooking something simple here..!
Thanks
There is a regression in Beta 3 causing that Optional<T> cannot be compared with nil if T is not Equatable or Comparable.
It's a bug caused by the removal of the _Nil type for which the equality operators were defined. nil is now a literal. The bug has been confirmed by Chris Lattner on Apple Dev Forums
Some workarounds:
You can still use .getLogicValue()
if launchOptions.getLogicValue() && ... {
or directly
if launchOptions && ... { //calls .getLogicValue()
or you can use the "Javascript object to boolean" solution
var bool = !!launchOptions
(first ! calls the getLogicValue and negates, the second ! negates again)
or, you can define those equality operators by yourself until they fix it:
//this is just a handy struct that will accept a nil literal
struct FixNil : NilLiteralConvertible {
static func convertFromNilLiteral() -> FixNil {
return FixNil()
}
}
//define all equality combinations
func == <T>(lhs: Optional<T>, rhs: FixNil) -> Bool {
return !lhs.getLogicValue()
}
func != <T>(lhs: Optional<T>, rhs: FixNil) -> Bool {
return lhs.getLogicValue()
}
func == <T>(lhs: FixNil, rhs: Optional<T>) -> Bool {
return !rhs.getLogicValue()
}
func != <T>(lhs: FixNil, rhs: Optional<T>) -> Bool {
return rhs.getLogicValue()
}
Example:
class A {
}
var x: A? = nil
if x == nil {
println("It's nil!")
}
else {
println("It's not nil!")
}
However, this workaround might cause other subtle problems (it probably works similarily to the _Nil type in Beta 2 which was removed because it was causing problems...).
The release notes of XCode 6 Beta 5 state the following:
Optionals no longer conform to the BooleanType (formerly LogicValue)
protocol, so they may no longer be used in place of boolean
expressions (they must be explicitly compared with v != nil). This
resolves confusion around Bool? and related types, makes code more
explicit about what test is expected, and is more consistent with the
rest of the language.
Note that ImplicitlyUnwrappedOptional still includes some BooleanType
functionality. This !issue will be resolved in a future beta.
(17110911)!
This means your previous approach should work now without any issues, just go back to it:
if (launchOptions != nil && launchOptions![UIApplicationLaunchOptionsRemoteNotificationKey] != nil) {
// some code
}
As #Sulthan figured out, this is a bug in the current beta release of the Swift compiler.
But note that an optional is itself a LogicValue that can be tested for its
boolean value. So you can simply write
if launchOptions && launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey] {
// ...
}
without comparing to nil.
This compiles for me but I'm not sure if it works as intended:
if launchOptions![UIApplicationLaunchOptionsRemoteNotificationKey] !== nil {}
This is most likely a side-effect by the change of nil to a literal in beta 3:
• nil is now a literal in the language, not a global constant of _Nil
type. This change resolved a number of problems with nil; e.g. nil
in a collection, nil converting to Any, etc. Types can now indicate
that they are nil compatible by conforming to the
NilLiteralConvertible protocol. (16951729)
For some reason it just complains when its an optional returned from indexing a dictionary, I have a feeling this will be fixed in the future. Submit a bug report though!