Read NSArray of CGPoints in Swift - ios

I have a function in an Objective-C class that returns an NSArray:
- (NSArray *) myAwesomeFunction {
// Good stuff here
return array
}
Now I know that the members of this array are all NSValue objects with a CGPoint inside. This information is not available to Swift however, which infers the return type (correctly) as [AnyObject]!
Now when I try to cast the members of the array into an NSValue I get a cast error as I should.
let myArray = AnObjectiveCClass.myAwesomeFunction()
let myValue = myArray[0] as NSValue // Cast error since AnyObject cannot be cast to NSValue
This leaves me with a problem though, how can I access the contents of the array in my Swift class? In the end I want to retrieve the CGPoint values inside of the array.

In my opinion, you can cast the the whole array to a [NSValue] array:
if let downcastNSValueArray = myArray as? [NSValue] {
let myValue = downcastNSValueArray[0]
}

You can simply add an Objective-C generics annotation to your method:
+ (NSArray<NSValue *> *) myAwesomeMethod {
// Good stuff here
return array;
}
Now Swift automatically infers the type:
let myArray = MyClass.myAwesomeMethod()
let myValue = myArray[0]
let myPoint = myValue.pointValue
Note that myArray will be an implicitly-unwrapped optional i.e. an [NSValue]!, so you could also add nullability annotations if you know myAwesomeMethod won't ever return nil. myValue will be considered non-optional. So the code above doesn't need any additional type handling to satisfy the compiler, but may still trigger runtime exceptions if myArray == nil or if myArray.count < 1.

Related

Cannot downcast from ... to a more optional type

First of all I've got "ApiModel" protocol, which is simply empty. Then I've got model struct "Pokemon", which implements ApiModel protocol. Now I'm trying to convert array of ApiModel ( [ApiModel] ) to array of Pokemon [Pokemon], so I'm using this function:
if let pokemonsList = objects as! [Pokemon]? {
self.pokemons = pokemonsList
}
objects - is non optional let variable, which holds [ApiModel].
But I've got following error:
Cannot downcast from '[ApiModel]' to a more optional type '[Pokemon]?'
When I'm trying to do that on single model it's working fine:
if let poke = objects.first as! Pokemon? {
self.pokemon = poke
}
Also, if objects will be optional - again it's working fine.
Can you help me to understand why arrays doesn't works?
You had mistake in if let syntax. Instead of force cast you want to have optional cast. And since you already have this array you don't want get optional array of pokemons
if let pokemonsList = objects as? [Pokemon] {
self.pokemons = pokemonsList
}

Casting Syntax to Loop Through Array of Arrays in Swift

I have an NSArray consisting of NSArrays of strings created in Objective-C.
I now want to loop through the items in the array in a swift class and am having trouble with the syntax.
The original Objective-C Array of arrays looks like the following:
NSArray* shapes =#[#[#"square",#"square.png"],#[#"circle",#"circle.png"],#[#"square",#"square.png"]];
I am able to get and print the Array from the Objective-C class using:
let shapes:Array = Utilities.sharedInstance().getShapes
The following to loop through the array, however, is not compiling:
var term : String = ""
var pic : String = ""
for shape in shapes {
term = shape[1] //ERROR HERE
pic = shape[2] //SAME ERROR HERE
}
It gives the error: Type 'Any' has no subscript members
What is the proper syntax to loop through the elements?
You can try
let shapes = Utilities.sharedInstance().getShapes as! [[String]]
Your Array elements are of type Any so you can't use [] with them until you cast , it's always the case when you use bridged code from objective-c , so you have to be specific about the actual type you use , also i encourage
struct Item {
let term,pic:String
}
Then
let res:[Item] = shapes.map { Item(term:$0[0],pic:$0[1]) }
an irrelevant note but important you can do
NSArray* shapes = #[#"square",#"circle",#"square"];
then the matter of appending .png is simple instead of having an [[String]] directly it's [String]

How to pass a Dictionary to Objective-C method in Swift

I need to pass a Dictionary to a Objective-C method in Swift. In Swift the code is like this:
let modelData: Dictionary<String, [Double]> = getModelData()
result = myChilitags.estimate3D(configAt: configFilePath, forModel: modelData);
(The configure file has nothing to do with this problem, just ignore it.)
I used a .h file:
#interface myChilitags : NSObject
+ (nonnull UIImage *)estimate3D:configAt:(NSString *)configFilePath forModel:(nonnull NSDictionary*) modelData;
#end
The question is that I need to do something with the modelData in the Objective-C method estimate3D but I don't know what to do after I passed the Dictionary value modelData to the method.
I tried to just print the modelData value but all that came out was:
1
I also tried to print the value in the Dictionary like:
std::cout << modelData["face001"] << std::endl;
I am pretty sure that there is a key "face001" in the dictionary but the result was still:
1
I know it must have something to do with NSDictionary and Dictionary but I just don't know what to do.
First of all, a Dictionary in Swift is struct, an NSDictionary is class.
Objective-C is not type-safe so it doesn't show an error.
If you try to do the same in Swift, it will tell you
Cannot assign value of type '[String : [Double]]' to type 'NSDictionary'
let swiftDictionary = [String: [Double]]()
var nsDictionary = NSDictionary()
nsDictionary = swiftDictionary //shows error
So you have to convert the Swift dictionary to an NSDictionary.
let modelData: Dictionary<String, [Double]> = getModelData()
let nsModelData = NSDictionary(dictionary: modelData)
result = myChilitags.estimate3D(configAt: configFilePath, forModel: nsModelData);

Cannot assign a value of type '[Thing]' to a value of type '[Any]'

I'm struggling with what seems like a simple Swift problem.
I've declared a struct with a static function that returns some instances:
struct Thing {
static func allTheThings() -> [Thing] {
...
}
}
I've got a CustomViewController with an property declared:
var objects = [Any]()
In the subclass of that controller in viewDidLoad(), I'm trying to assign the objects property.
objects = Thing.allTheThings()
But I'm getting a compiler error
Cannot assign a value of type '[Thing]' to a value of type '[Any]'
Isn't that the whole point of Any?
This works:
objects = Thing.allTheThings().map { $0 }
But this doesn't
let things = Thing.allTheThings().map { $0 }
objects = things
Any ideas what's going on here?
It seems Swift can convert Thing to Any but not [Thing] to [Any].
The reason this works
objects = Thing.allTheThings().map { $0 }
is that the compiler can infer that the type of $0 is Any, but in the second example
let things = Thing.allTheThings().map { $0 }
it infers $0 to be of type Thing. You end up with the variable things being of type [Thing], which means that the assignment
objects = things
will mean a conversion from [Thing] to [Any] which does not work.

Error trying to access members of Array of AnyObject within a Dictionary - Is there a way around unwrapping?

I have a dictionary set up as:
var jDict = Dictionary<String, AnyObject[]>()
Where the arrays are either a collection of custom buttons (JunkButton) or Labels (JunkLabels).
I am having an issue when trying to access the members of the arrays contained in the Dictionary as follows:
let thisArray = jDict[key]
var aButton = thisArray[0] //Gives error: 'AnyObject[]? does not have a member named 'subscript'
I can get around this by downcasting the whole array as follows:
if let aArray = thisArray as? JunkButton[]{
var aButton = aArray[0]
}
This seems very cumbersome especially if I am sure I know what type the array is made up of beforehand. Is there a way to cast thisArray when it is created that would allow me to extract its elements without unwrapping them each time?
Dictionary always give you Optional value.
Your code is like this
let thisArray : Optional<AnyObject[]> = jDict[key]
You need to unwrap it to get non-optional value
let thisArray = jDict[key]! // thisArray is AnyObject[]
You really shouldn't use a dictionary for this. Swift makes it very easy to use custom little structs or classes instead of dictionaries:
struct JunkItems {
var buttons: [JunkButton] = []
var labels: [JunkLabel] = []
}
Then you can access those items like this without downcasting:
for button in junkItems.buttons {
// ...
}
Or:
if let button = junkItems.buttons[0] {
// ...
}
Btw, the array notation [JunkButton] is new in beta 3.

Resources