Converting some of my code from Objective-C to Swift. Got stuck in a combination of typecasting statement:
if (![[array objectAtIndex:0] isKindOfClass:[BaseView class]]) {
//Throw an exception
}
Since the branching should throw an exception, I am using guard statement instead of if-else. So far I have done the following:
guard NSArray(array: array).objectAtIndex(0) else {
//throw SomeException
}
Please guide the remaining part, how to use the isKindOfClass, also I doubt the correctness of NSArray typecasting
The more correct method is to use is, as its meant to replace isKindOfClass.
guard array.first is BaseView else {
// Throw exception if its not a BaseView subclass
}
The following works in your case:
guard let arr0 = array.first as? BaseView else {
return // or throw exception
}
You try to cast the value in array.first to BaseView and if that conversion fails you will enter the else block and can throw an exception if you wish. Additionally (thanks to #Adam) that will return/throw if the array is empty. If you have to check for the 3rd element to be of a certain type, you should check that the array actually contains at least 3 values.
Consider the following example:
func t(arr : [AnyObject]) {
guard let arr0 = arr.first as? String else {
return
}
print(arr0)
}
t(["asd", 123]) // prints something
t([123, "asd"]) // returns without printing anything
t([]) // returns without printing anything
Related
I'm trying to run this line:
guard let spriteComponent = entity?.componentForClass(SpriteComponent.self) else {
return
}
and receiving this:
Value of type 'GKEntity' has no member 'componentForClass'
Apparently apple has removed this method from GamePlayKit GkEntity class. So which method should I use instead?
In Swift 3 this has changed to component(ofType:). So your expression will look like;
guard let spriteComponent = entity?.component(ofType: SpriteComponent.self) else {
return
}
My application code gets JSON data from a server, converts it to a Dictionary and then uses that to hydrate and attempt to save a RealmSwift object with a matching schema.
I ran into a crash caused by a Float value coming into a field that was declared as Int in the model. RLMException was thrown.
As suggested in this thread, one doesn't simply try to catch RLMException.
My question is, is that answer correct? And, if so, what is the correct way to prevent a crash when an unexpected value is found? Isn't there some sort of validation mechanism that I can run on all of the values before attempting to set them?
You can use third party frameworks for mapping JSON to Realm objects, like ObjectMapper and use it's failable initializer to validate your JSON.
This failable initializer can be used for JSON validation prior to object serialization. Returning nil within the function will prevent the mapping from occuring. You can inspect the JSON stored within the Map object to do your validation.
You can use this to see if everything is ok with json parameters
static func createFromJSONDictionary(json: JSONDictionary) throws -> MyObject {
guard let
property1 = json["property1"] as? Int,
property2 = json["property1"] as? String else {
// Throw error.... or create with default values
throw NSError(domain: "", code: 0, userInfo: nil)
}
// Everything is fine, create object and return it
let object = MyObject()
object.something = property1
return object
}
I ended up writing an extension to handle this. At the moment I'm only validating numbers and dates, but the switch statement could check every single property type easily.
extension Object {
public convenience init( withDictionary rawData: [String:AnyObject] ) {
var sanitizedData = rawData
let dynamicSelf = self.dynamicType
let schema = dynamicSelf.sharedSchema()
for property in schema.properties {
guard let value = sanitizedData[property.name] else { continue }
var isValid = true
switch property.type {
case .Double:
let val = Double(String(value))
if val == nil || val!.isNaN {
isValid = false
}
case .Float:
let val = Float(String(value))
if val == nil || val!.isNaN {
isValid = false
}
case .Int:
if Int(String(value)) == nil {
isValid = false
}
case .Date:
if Int64(String(value)) == nil {
isValid = false
} else {
sanitizedData[property.name] = NSDate(timeIntervalSince1970: value.doubleValue / 1000)
}
default:
break
}
if !isValid {
log.error( "Found invalid value: \(value) for property \(property.name) of \(dynamicSelf)" )
sanitizedData.removeValueForKey(property.name)
}
}
self.init( value: sanitizedData )
}
}
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)")
// ...
}
I'm using PINCache (https://github.com/pinterest/PINCache) to cache some objects into my app. So, using objC it is perfect, but when I wanna cast with swift I had EXC_BAD_ACCESS
When I call objectForKey
- (id <NSCoding>)objectForKey:(NSString *)key;
And I have nothing cached for that key, but Swift understand that the object is an NSConding. So, if I have values into a key, I have no problems.
So, how can I cast this NSCoding result and compare with nil value. I only need to know if the object return from PINCache is nil (never cached).
I've already tried:
private func setupTrucks() {
let trucksCached: AnyObject? = PINDiskCache.sharedCache().objectForKey(TruckCache.key)
if let _truck = trucksCached as? [Truck] { //EXC_BAD_ACCESS here
print(_truck)
}
}
private func setupTrucks() {
let trucksCached = PINDiskCache.sharedCache().objectForKey(TruckCache.key) as [Truck] //EXC_BAD_ACCESS here
if trucksCached.count > 0 {
print(_truck)
}
}
It looks like PINCache is not defining the return types of the synchronous objectForKey: methods as __nullable. Without that Swift doesn't know that the return type is optional.
I submitted a PR, which has already been accepted, that should fix the problem. You should now be able to do something like:
let trucksCached: AnyObject? = PINDiskCache.sharedCache().objectForKey("truck_key")
if let _truck = trucksCached as? Truck {
print(_truck)
}
A'right guys, I'm stumped.
So I'm working on a functional json implementation along the lines of this article. Things are going fairly well, but I have this one issue that persists in both current and beta versions of Xcode and Swift.
My decode logic takes an argument from the JSON dictionary on the left, and uses a decode function provided on the right, composing them with the 'bind' operator:
return AdUnitDictionary.create <^>
d["adRepeats"] >>> _JSONInt <*>
d["adStartsAfter"] >>> _JSONInt <*>
d["advertisingOn"] >>> _JSONInt <*>
d["numberOfCards"] >>> _JSONInt <*>
d["adUnitIdNonRetina"] >>> _JSONString <*>
d["adUnitIdRetina"] >>> _JSONString
Bind is defined like this:
func >>><A, B>(a: Result<A>, f: A -> Result<B>) -> Result<B> {
switch a {
case let .Value(x): return f(x.value)
case let .Error(error): return .Error(error)
}
}
So, originally, this method tries to cast the string and returns a safe, empty string in the event of failure. I thought that I was getting EXC_BAD_ACCESS from my if-let block:
func _JSONString(object: JSON) -> String {
if let object = object as? String { return object } // EXC_BAD_ACCESS
return ""
}
Because that 'if-let' line is where the debugger shows the exception to be thrown. However! Eliminating that block, and using a function that ignores its arguments ENTIRELY, causes the exception to be thrown on the return!!
func _JSONString(object: JSON) -> String {
return "" // EXC_BAD_ACCESS
}
The only sense I can make of this is that the function being applied is nil, since the argument itself is checked to exist in the implementation of bind. This doesn't make sense, however, because the debugger makes it right into my target function before throwing its exception.
Another mystery is why the numeric decodes succeed without any issue, even though they're just carbon-copies of the string decoder with a different type specialization.
Any ideas?
check you pattern!
let object: Int? = nil
if let object = object as? String {
print("a")
} else {
print("nil")
}
// prints nil
or
let object: Int = 1
if let object = object as? String {
print("a")
} else {
print("nil")
}
// prints nil