Cannot express tuple conversion '([Subclass], Int, String)' to '([Superclass], Int, String)' - ios

Original
In my project I have a Iterator class, which has a function:
func iterateItems<T: Items>(iterationItems: [T], removeItem: (T) -> Void, addItem: (T) -> Void, calculateEfficiency: () -> Void) -> [T] {
...
return bestComposition as! [T]
}
And in its subclass WPCalculator I run it this way:
func iterateWPItems() -> [Items] {
return iterateItems(iterationItems: WeaponItems.weaponItems, removeItem: removeWeaponItem, addItem: addWeaponItem, calculateEfficiency: calcWeaponDemage)
}
New code
Everything worked fine that way. Now I want to change the iterateItems function to this:
func iterateItems<T: Items>(iterationItems: [T], removeItem: (T) -> Void, addItem: (T) -> Void, calculateEfficiency: () -> Void) -> ([T], Int, String) {
...
return (bestComposition as! [T], bestBuildCost, customMessage)
}
Then I updated WPCalculator accordingly:
func iterateWPItems() -> ([Items], Int, String) {
return iterateItems(iterationItems: WeaponItems.weaponItems, removeItem: removeWeaponItem, addItem: addWeaponItem, calculateEfficiency: calcWeaponDemage)
}
Now I get an error: Cannot express tuple conversion '([WeaponItems], Int,
String)' to '([Items], Int, String)'
The argument passed to iterateWPItems is an array of type WeaponItems, which is a subclass of Items, it worked fine in the original version, where Swift seems to have inferred the subclass-to-superclass conversion, but when I put it in a tuple in the new code, it doesn't work.
Why is that? How do I solve this problem?
Edit:
WeaponItems:
class WeaponItems: Items {
var weaponPower, attackSpeed, criticalChance, criticalDamage, armorPierce: Double
init(name: String, index: Int, price: Int, weaponPower: Double = 0, attackSpeed: Double = 0, criticalChance: Double = 0, criticalDamage: Double = 0, armorPierce: Double = 0, image: UIImage){
self.weaponPower = weaponPower
self.attackSpeed = attackSpeed
self.criticalChance = criticalChance
self.criticalDamage = criticalDamage
self.armorPierce = armorPierce
super.init(name: name, index: index, price: price, image: image)
}
...
}
Items:
class Items {
let name: String
let index: Int
let price: Int
let image: UIImage
init(name: String, index: Int, price: Int, image: UIImage) {
self.name = name
self.index = index
self.price = price
self.image = image
}
...
}

The problem has indeed to do with the fact that although Swift can implicitly convert [WeaponItems] into [Items], it fails to do so when these types come as the components of tuples. E.g. see this: Tuple "upcasting" in Swift
There are several ways to "solve" this problem.
Easiest is:
func iterateWPItems() -> ([Items], Int, String) {
let (composition, cost, message) = iterateItems(iterationItems: WeaponItems.weaponItems, removeItem: removeWeaponItem, addItem: addWeaponItem, calculateEfficiency: calcWeaponDemage)
return (composition, cost, message)
}
Alternatively, you can change iterateItems to return just the tuple you expect, that is ([Items], Int, String):
func iterateItems<T: Items>(iterationItems: [T], removeItem: (T) -> Void, addItem: (T) -> Void, calculateEfficiency: () -> Void) -> ([Items], Int, String) {
...
return (bestComposition as! [Items], bestBuildCost, customMessage)
}
Better still, from the way it looks I do not see why iterateItems has to be a generic method. If that's indeed the case, then simply changing it to:
func iterateItems(iterationItems: [Items], removeItem: (Items) -> Void, addItem: (Items) -> Void, calculateEfficiency: () -> Void) -> ([Items], Int, String) {
...
return (bestComposition as! [Items], bestBuildCost, customMessage)
}
.. should also help.

Related

How to pass a function to another class in Swift

I need some help passing a function to my second class. I have tried many things but it still not work.
class Main {
func one() {
let test = Sub(function: two)
}
func two(val: Int, completion: ((Int, String)?)->()) { }
}
class Sub {
var function: (Int, ((Int, String)?)->())
init(function: (Int, ((Int, String)?)->())) {
self.function = function
}
}
Why is it that i get error on this line
let test = Sub(function: two)
which says: Cannot convert value of type '(Int, ((Int, String)?) -> ()) -> ()' to expected argument type '(Int, ((Int, String)?) -> ())'
What is the reason?
Function has a return value plus the completion , you need to change syntax of function var inisde Sub and the init also
class Main {
func one() {
let test = Sub(function: two)
}
func two(val: Int, completion: ((Int, String)?)->()) { }
}
class Sub {
var function: ((Int, ((Int, String)?)->())) -> ()
init(function:#escaping ((Int, ((Int, String)?)->())) -> ()) {
self.function = function
}
}
#escaping need to be added
class Main {
func one() {
let test = Sub(function: two)
}
func two(val: Int, completion: (Int, String)?)->() { }
}
class Sub {
var function: (Int, (Int, String)?)->()
init(function: #escaping (Int, (Int, String)?)->()) {
self.function = function
}
}

How to cast parameter of a function to Any in a function given as method's argument?

How to cast parameter of a function to Any in a function given as method's argument?
struct InitializationStaticData: Decodable {}
func method1(responseListener: #escaping (_ status:Int, _ data: InitializationStaticData?) -> Void)
{
let dsa = responseListener as! (Int, Any?) -> Void // EXC_BREAKPOINT
let asd = responseListener as! (Int, Decodable?) -> Void // EXC_BREAKPOINT
method2(responseListener: sdd)
}
I tried to cast it like this:
func method2<T>(responseListener: #escaping (_ status: Int, _ data: Any?) -> Void)
{
}
Basically the generic approach is right.
Change the type of data to T and constrain the generic to Decodable
func method2<T : Decodable>(responseListener: #escaping (Int, T) -> Void)
{
}
The underscore characters and the parameter labels are Swift 2 legacy, they are unused in Swift 3+

Passing functions as arguments error: Cannot convert value of type "someType.type" to expected argument type "someType"

I have a function, which takes several other functions as arguments:
class Iterator {
func iterateItems(itemArray: [Items], removeItem: (Items) -> Void, addItem: (inout Items) -> Void, calculateEfficiency: () -> Void) -> [Items] {
// function body
}
}
And I call it in its class' subclass like this:
class WPCalculator: Iterator {
func removeWeaponItem(item: WeaponItems) { ... }
func addWeaponItem(item: inout WeaponItems) { ... }
func calcWeaponDamage() { ... }
func iterateWPItems() {
iterateItems(itemArray: WeaponItems.weaponItems, removeItem: removeWeaponItem(item: WeaponItems), addItem: addWeaponItem(item: &WeaponItems), calculateEfficiency: calcWeaponDemage())
}
}
Then Xcode says error on removeItem and addItem parameter:
Cannot convert value of type "WeaponItems.type" to expected argument type "WeaponItems"
Also the WeaponItems class is a subclass of Items class:
class WeaponItems: Items { ... }
Why is that error message?
You are passing a class WeaponItems instead of class objects. Following is more correct version:
func iterateWPItems() {
let itemsToRemove = WeaponItems() //initialize object somehow
var itemsToAdd = WeaponItems() //initialize object somehow
iterateItems(itemArray: WeaponItems.weaponItems, removeItem: removeWeaponItem(item: itemsToRemove), addItem: addWeaponItem(item: &itemsToAdd), calculateEfficiency: calcWeaponDemage())
}
EDIT: Sorry, I've got your problem. Than instead of calling these methods you should just pass them as arguments, so you don't have to put parentheses to a trail of method name:
func iterateWPItems() {
iterateItems(itemArray: WeaponItems.weaponItems, removeItem: removeWeaponItem, addItem: addWeaponItem, calculateEfficiency: calcWeaponDemage)
}
Within your invocation of iterateItems, removeWeaponItem(item: WeaponItems), WeaponItems is a type, since it is the same as the class name WeaponItems. If you create variables of type WeaponItems, and name them differently (e.g., weaponItems) instead of WeaponItems, you probably will not run into this problem.
I found the problem: the way I passed functions as arguments is wrong, it should be like this:
func iterateWPItems() {
iterateItems(itemArray: WeaponItems.weaponItems, removeItem: removeWeaponItem as! (Items) -> Void, addItem: addWeaponItem as! (inout Items) -> Void, calculateEfficiency: calcWeaponDemage())
}

What is return type " () "?

I have a function, which takes several other functions as arguments:
class Iterator {
func iterateItems(itemArray: [Items], removeItem: (Items) -> Void, addItem: (inout Items) -> Void, calculateEfficiency: () -> Void) -> [Items] {
// function body
}
}
And I call it in its class' subclass like this:
class WPCalculator: Iterator {
func removeWeaponItem(item: WeaponItems) { ... }
func addWeaponItem(item: inout WeaponItems) { ... }
func calcWeaponDamage() { ... }
func iterateWPItems() {
return iterateItems(itemArray: WeaponItems.weaponItems, removeItem: removeWeaponItem as! (Items) -> Void, addItem: addWeaponItem as! (inout Items) -> Void, calculateEfficiency: calcWeaponDemage)
}
}
Also the WeaponItems class is a subclass of Items class:
class WeaponItems: Items { ... }
Xcode gives me a warning on addWeaponItem:
Cast from (inout WeaponItems) -> () to unrelated type (inout Items) -> Void always fails
I can't understand the -> () return type, clearly its not what my function does, the other functions passed as arguments without inout parameters just worked fine
Your function iterateItems has removeItem addItem calculateEfficiency parameter is function type. So you must use function name as params when call function.
like this:
iterateItems(itemArray: WeaponItems.weaponItems, removeItem: removeWeaponItem, addItem: addWeaponItem, calculateEfficiency: calcWeaponDamage)
I think () same as Void
The warning occurs because WeaponItems and Items do not match
http://ericasadun.com/2015/05/11/swift-vs-void/

How to make a closure optional with a default value

I have a bunch of functions that I would like to be able to specify a default closure if one is not provided. I can't seem to figure out how to do it without some ugly code.
So for example, I would like the perform function to accept an optional parameter called closure that is executed when provided. Otherwise it will default to executing myClosure. How can I make this better so I don't have to repeat the function calls?
class MyClas {
typealias closureType = ((number: Int) -> Int)?
func myClosure (number: Int) -> Int {
return number * 2
}
func perform(number: Int, closure: closureType = nil) -> Int {
if closure == nil {
return myClosure(number)
} else {
return closure!(number: number)
}
}
}
Ideally, I could do this!
class MyClass {
typealias closureType = ((number: Int) -> Int)?
func myClosure (number: Int) -> Int {
return number * 2
}
func perform(number: Int, closure: closureType = myClosure) -> Int {
return closure(number: number)
}
}
Your problem is that you've made made myClosure a method (or member function), which means it doesn't have the signature you want (it's instead a curried function, of type MyClass->Int->Int).
Either pull it out of the class, or make it a static (or rather "class" in the case of a class) method:
class MyClass {
typealias closureType = (number: Int) -> Int
class func myClosure (number: Int) -> Int {
return number * 2
}
func perform(number: Int, closure: closureType = MyClass.myClosure) -> Int {
return closure(number: number)
}
}
P.S. once you do this, it doesn't need to be optional any more
Just to show it compiling as a non-static method:
class MyClass {
typealias closureType = MyClass -> (number: Int) -> Int
func myClosure (number: Int) -> Int {
return number * 2
}
func perform(number: Int, closure: closureType = myClosure) -> Int {
return closure(self)(number: number)
}
}
let c = MyClass()
println(c.perform(5)) // prints 10
Closure is first-class citizen in Swift. So you can provide default value for it.
class MyClass {
func perform(number: Int, closure: Int -> Int = { $0 * 2 }) -> Int {
return closure(number)
}
}

Resources