Consider the following code:
class Test {
func func1(arg1: Int) -> Void {
println(arg1)
}
var funcArr: Array< (Int) -> Void > = [func1] // (!) 'Int' is not a subtype of 'Test'
}
I'm trying to store the method func1in an array, but as you can see, this doesn't seem to work because func1supposedly only takes an argument of type Test. I assume this has something to do with methods needing to be associated with an object.
For some more clarification, have a look at the following code where I let swift infer the type of the array:
class Test {
func func1(arg1: Int) -> Void {
println(arg1)
}
var funcArr = [func1]
}
Test().funcArr[0](Test()) // Compiles, but nothing gets printed.
Test().funcArr[0](1) // (!) Type 'Test' does not conform to protocol 'IntegerLiteralConvertible'
Test().func1(1) // Prints '1'
A possible workaround for this problem is moving func1outside of the class like so:
func func1(arg1: Int) -> Void {
println(arg1)
}
class Test {
var funcArr = [func1]
}
Test().funcArr[0](1) // Prints '1'
This works fine for this simple example, but is less than ideal when I actually need to operate on an Object of type Test in the function. I can of course add another parameter to the function to pass an instance of Testto the function, but this seems clunky.
Is there any way I can store methods in an array?
Ultimately, what I want to be able to do is testObject.funcArr[someInt](someParam) and have that function work as a method belonging to testObject. Any clever workarounds are of course also welcome.
Instance methods in swift are just curried functions, and the first argument is implicitly an instance of the class (i.e. self). And that's why these two are equivalent:
Test().func1(0)
Test.func1(Test())(0)
So when you try to put that function in the array, you're reveling its real nature: the method func1 on Test is actually this class method:
class func1(self_: Test)(arg1: Int)
So when you refer to simply func1 (without an "instance context") it has type Test -> Int -> Void, instead of the expected Int -> Void, and that's why you get the error
Int is not a subtype of Test
So the real issue is that when you store the methods in funcArr the instance is not known yet (or if you will, you're referring the function at a class level). You can work around this using a computed property:
var funcArr: [Int -> Void] { return [func1] } // use a computed property
Or another valuable option could be simply to acknowledge the real type of func1 and explicitly passing the instance. E.g.
...
var funcArr = [func1]
...
let test = Test()
let func1 = test.funcArr[0]
func1(test)(0) // prints 0
update
Freely inspired by this other Q/A (Make self weak in methods in Swift) I came up with a similar solution that stores the method references.
func weakRef<T: AnyObject, A, B>(weakSelf: T, method: T -> A -> B) -> A -> B {
return { [unowned weakSelf] in { a in method(weakSelf)(a) } }()
}
class Test {
var methodRefs: [Int -> Void] = []
init() {
methodRefs.append(weakRef(self, Test.func1))
}
func func1(arg1: Int) {
println(arg1)
}
}
In order to store a method, you should remember that the method is invoked on a class instance. What's actually stored in the array is a curried function:
(Test) -> (Int) -> Void
The first function takes a class instance and returns another function, the actual (Int) -> Void method, which is then invoked on that instance.
To make it more explicit, the array type is:
var funcArr: [(Test) -> (Int) -> Void] = [Test.func1]
Then you can invoke the method with code like this:
var test = Test()
var method = test.funcArr[0]
method(test)(1)
Suggested reading: Curried Functions
Related
Given the following enum defined in an external api.
public enum Status {
COMPLETE,
RUNNING,
WAITING
}
I would like a way to add a int flag to each enum value. I know that I can extend the enum:
fun Status.flag(): Int {
when(this) {
RUNNING -> return 1;
WAITING -> return 2;
else -> return 0;
}
}
However I would like to define those int flag values as constants. Maybe a companion object, but I don't think I can extend an existing enum and add a companion object.
Any ideas?
Unless you are using a field that already exists in the original enum (like ordinal), you won't be able to do what you're asking without wrapping the external enum in your own enum.
Sure you could use ordinal, but a newer version of the external API may change the order of the items in the enum, so I wouldn't recommend it. But, if you REALLY want to, you could do something like this (again, this is NOT recommended):
val Status.flag: Int
get() = this.ordinal
But I'd definitely recommend wrapping it. That way you guarantee that the flag integers you define won't change.
enum class MyStatus(val status: Status, val flag: Int) {
COMPLETE(Status.COMPLETE, 0),
RUNNING(Status.RUNNING, 1),
WAITING(Status.WAITING, 2);
companion object {
private val STATUS_TO_MYSTATUS = values().associateBy { it.status }
fun fromStatus(status: Status): MyStatus {
return STATUS_TO_MYSTATUS[status] ?: throw Exception("No MyStatus found for status ${status.name}")
}
}
}
You can then convert Status to MyStatus by using MyStatus.fromStatus(...). Or you can add an extension function to Status to easily convert to MyStatus.
fun Status.toMyStatus() = MyStatus.fromStatus(this)
You can add extension properties/methods to the companion object of enum/class/etc. if one exists:
val Status.Companion.COMPLETE_INT = 0
val Status.Companion.RUNNING_INT = 1
but indeed you can't currently "create" the companion object if it doesn't. So just put the constants into your own non-companion object:
object StatusFlags {
const val COMPLETE_INT = 0
const val RUNNING_INT = 1
const val WAITING_INT = 2
}
fun Status.flag(): Int {
when(this) {
RUNNING -> return StatusFlags.RUNNING_INT
...
}
}
I am running into a compiler issue. It happens when I use SwiftTask, and Async, here is a sample:
//-- Generic Method
import Async
import SwiftTask
class AsyncTask {
func background<T>(job: (((Float -> Void), (T -> Void), (NSError -> Void), SwiftTask.TaskConfiguration) -> Void)) -> SwiftTask.Task<Float, T, NSError> {
return SwiftTask.Task<Float, T, NSError> { (progress: (Float -> Void), fulfill: (T -> Void), reject: (NSError -> Void), configure: SwiftTask.TaskConfiguration) -> Void in
Async.background {
job(progress, fulfill, reject, configure)
return
}
return
}
}
}
Now that compiles, but when I try to use the generic like so:
//-- Using the Generic Method
let task = AsyncTask.background<MyAwesomeObject> { progress, fulfill, reject, configure in
let obj = MyAwesomeObject()
//-- ... do work here
fulfill(obj)
return
}
I then get the following error Cannot explicitly specialize a generic function
The way you try to specialise a generic function is called explicit specialization.It’s not a syntax error, it’s a semantic error. At parse time, there’s no difference between
let x = foo<Int>()
and
let arr = Array<Int>()
But in the current version of Swift language which is 5.1 this is not permitted but in future versions it could be permited to use it.
Today, a type arguments of a generic function are always determined via type inference. For example, given:
func foo<T>()
let x = foo() as Int
// or
let x: Int = foo()
or T is determined via the argument's type. In that case an additional argument must be introduced into a method signature
func foo<T>(t: T)
let x = foo(Int.self)
Give the closure an explicit type to fix T:
let task = AsyncTask.background{ (progress: Float -> Void, fulfill: MyAwesomeObject -> Void, reject: NSError -> Void, configure: SwiftTask.TaskConfiguration) -> Void in
let obj = MyAwesomeObject()
//-- ... do work here
fulfill(obj)
}
I'd like to make an NSView subclass that handles drag and drop by redirecting it to its view controller. So I have to override NSDraggingDestination protocol methods in my NSView subclass. There I'd like to check if method is supported by view controller implementation and call it, or if not supported to call base class implementation. The first part seems easy by optional chaining, but how do I check if method implemented in super class? For example, this is what I came up for draggingEnded method. It gives a runtime crash inside if block, if the view controller does not implement draggingEnded method.
class ControllerChainedView: NSView {
#IBOutlet weak var chainedController: NSViewController!
override func draggingEnded(sender: NSDraggingInfo?) {
let destination = chainedController as! NSDraggingDestination
if !(destination.draggingEnded?(sender) != nil) {
super.draggingEnded(sender);
}
}
}
Changing the line inside if to super.draggingEnded?(sender); gives a compiler error. (Operand of postfix '?' should have optional type; type is '(NSDraggingInfo?) -> Void')
There is no problem with say similar draggingEntered method as it seems to be implemented in NSView.
So the question is how to detect if one can call method in super or not?
If your super is based on NSObject, you can still use respondsToSelector: in this way:
if super.respondsToSelector("draggingEnded:") {
super.draggingEnded(sender)
}
UPDATE - based on 1st Alex comment
Depends. Generally no, you can't do this with optional chaining without calling it.
But what you can do is to check if function was or wasn't called. But again, in specific cases only. Must be #objc protocol, must be based on NSObject and return value of optional function must not be Optional.
Check following examples.
#objc protocol Opt {
optional func implemented() -> Void
optional func notImplemented() -> Void
}
class Object: NSObject, Opt {
func implemented() {}
}
let o = Object() as Opt
let implementedCalled = o.implemented?() != nil // == true
let notImplementedCalled = o.notImplemented?() != nil // == false
In this particular case, implementedCalled is set to true. In other words, you can at least check if method was called or not. It's because of optional chaining and because o.implemented return type is Void, o.implemented?() returns Void?.
You can also do something like this ...
if o.implemented != nil {
// Function is implemented AND implemented WAS CALLED
} else {
// Function is not implemented
}
... but don't forget that it's not check if function is implemented, it's - if function is implemented, call it and return .Some(Void) or .None if it's not implemented.
In your particular case, you can do this:
override func draggingEnded(sender: NSDraggingInfo?) {
let destination = chainedController as! NSDraggingDestination
if destination.draggingEnded?(sender) == nil {
// .None returned - method not implemented in destination
// draggingEnded is called in super only if it's implemented
// ignore result of type Void?
super.draggingEnded?(sender)
}
}
If return type of optional function is not Void, but for example String, it behaves in the same way.
#objc protocol Opt {
optional func name() -> String
}
class Object: NSObject, Opt {
func name() -> String { return "Object" }
}
let o = Object() as Opt
let n: String? = o.name?() // n contains "Object"
If name is not implemented, n will contain None.
So far, so good. We can check if function was called for Void and for String (or any other type).
But what if return type is Optional? String? for example.
#objc protocol Opt {
optional func name() -> String?
}
class Object: NSObject, Opt {
func name() -> String? { return nil }
}
class Object2: NSObject, Opt {
}
let o = Object() as Opt
let n = o.name?()
let o2 = Object2() as Opt
let n2 = o2.name?()
Object implements name, but it returns nil and Object2 does not implement name at all. But both n and n2 contain nil. In other words, if return type is Optional, we can't check if function is or isn't implemented.
As you can see, it's tricky. Sometimes it suits your needs, sometimes not. If you are sure that your object is based on NSObject, stick with .respondsToSelector to be safe. You can then ignore all these conditions and cases.
If your goal is to just call function if it's implemented and you don't care if it is really implemented or not, if you're not interested in result, you can do it with ?.
Since the optional syntax ? is not working for super you'll have to check explicitly whether the superclass implements the method.
if NSView.instancesRespondToSelector("draggingEnded:") {
super.draggingEnded(sender)
}
In Swift you can define an enum and give it a property via an associated value, e.g.:
protocol SizeEnum {
var length : Double? { get } // Length should be >= 0 - has to be an Optional for errors
}
enum SizesEnum : SizeEnum {
case Short(length : Double) // 0 <= length <= maxShort
case Long(length : Double) // length > maxShort
private static let maxShort = 1.0
var length : Double? {
get {
switch self {
case let .Short(length):
if length >= 0 && length <= SizesEnum.maxShort { // Need to error check every access
return length
}
case let .Long(length):
if length > SizesEnum.maxShort { // Need to error check every access
return length
}
}
return nil // There was an error
}
}
}
SizesEnum.Short(length: 0.5).length // [Some 0.5]
SizesEnum.Short(length: 2).length // nil
SizesEnum.Long(length: 2).length // [Some 2.0]
SizesEnum.Long(length: -1).length // nil
However this is not ideal because:
The error checking for the length parameter can only be done on access, you cannot intercept the init
The length parameter is surprisingly long winded
An alternative, which seems better to me, is to use a static factory, e.g.:
protocol SizeStruct {
var length : Double { get } // Length should be >= 0 - is *not* an Optional
}
struct SizesStruct : SizeStruct {
static func Short(length : Double) -> SizeStruct? {
if length >= 0 && length <= maxShort { // Check at creation only
return SizesStruct(length)
}
return nil
}
static func Long(length : Double) -> SizeStruct? {
if length > maxShort { // Check at creation only
return SizesStruct(length)
}
return nil
}
let length : Double
private static let maxShort = 1.0
private init(_ length : Double) {
self.length = length
}
}
SizesStruct.Short(0.5)?.length // [Some 0.5]
SizesStruct.Short(2)?.length // nil
SizesStruct.Long(2)?.length // [Some 2.0]
SizesStruct.Long(-1)?.length // nil
Given that the static factory solution is neater, when would I actually use an enum with values? Am I missing something? Is there a killer use case?
In response to drewag
For Optional other languages, e.g. Java and Scala, you use factories, the Java version is described here: http://docs.oracle.com/javase/8/docs/api/java/util/Optional.html the factory is the of method.
In Swift you would do something like:
class Opt { // Note only Some stores the value, not None
//class let None = Opt() - class variables not supported in beta 4!
class Some<T> : Opt {
let value : T
init(_ value : T) {
self.value = value
}
}
private init() {} // Stop any other ways of making an Opt
}
Opt.Some(1).value // 1
This is probably the optimal example for enum since no error checking is required, but even so the factory version is competitive. The Optional example is so straightforward you don't even need a factory, you just create the Somes directly. Note how None doesn't use any storage.
The Barcode example shows how much better the factory technique is; in practice not all collections of 4 Ints are a valid UPCA and not all Strings are a valid QR Code, therefore you need error checking which is painful with enums. Here is the factory version:
class Barcode { // Note seperate storage for each case
class UPCABarcode : Barcode {
let type : Int, l : Int, r : Int, check : Int
private init(type : Int, l : Int, r : Int, check : Int) {
(self.type, self.l, self.r, self.check) = (type, l, r, check)
}
}
class func UPCA(#type : Int, l : Int, r : Int, check : Int) -> UPCABarcode? {
if ok(type: type, l: l, r: r, check: check) {
return UPCABarcode(type: type, l: l, r: r, check: check)
}
return nil
}
class func QRCode(#s : String) -> Barcode? { // Have not expanded this case; use same pattern as UPCA
return Barcode()
}
private init() {} // Prevent any other types of Barcode
class func ok(#type : Int, l : Int, r : Int, check : Int) -> Bool {
return true // In practice has to check supported type, range of L and R, and if check digit is correct
}
}
Barcode.UPCA(type: 0, l: 1, r: 2, check: 3)
If you use the enum version of Barcode then every time you use a Barcode you have to check its validity because there is nothing to stop invalid barcodes. Whereas the factory version does the checking at creation. Note how Barcode has no storage and UPCA has custom storage. I didn't code QRCode because it uses the same design pattern as UPCA.
My impression is that the enum version looks great in tutorials but soon becomes painful in practice because of error handling.
I believe the biggest killer use case is Optional. It is built into the language, but an optional is simply an enum:
enum Optional<T> {
case None
case Some(T)
}
In this case, a member variable like value would not make sense because in the None case, there is literally no value.
Also, right out of the Swift tour:
enum Barcode {
case UPCA(Int, Int, Int, Int)
case QRCode(String)
}
With a struct, there would be a lot of wasted member variables and a confusing interface to model this kind of data.
I don't believe it makes sense to use an associated value for an enum if the same type is used for every case. In that case a member variable is cleaner. The focus of associated values is to more accurately model certain types of data. The most useful cases are ones where different instances of a type can have different data associated with them. This could potentially be done with subclasses but then one would need to downcast to access more specific variables and the declarations would be much more verbose. Enums are a concise way to represent this type of data.
Another example could be web requests:
struct NetRequest {
enum Method {
case GET
case POST(String)
}
var URL: String
var method: Method
}
var getRequest = NetRequest(URL: "http://drewag.me", method: .GET)
var postRequest = NetRequest(URL: "http://drewag.me", method: .POST("{\"username\": \"drewag\"}"))
When I think of "enum" I don't think of "factory" at all. Normally factories are for larger more complex class structures. Enums are supposed to be very small pieces of data with little logic.
When compiling the following Swift code (in Sample.swift):
import Cocoa
class Sample {
func doSomething() {
var stringArray = Array<String>()
stringArray.append("AAA")
addToString(stringArray)
stringArray.append("CCC")
}
func addToString(myArray:Array<String>) {
myArray.append("BBB")
}
}
I get the following error on the line 'myArray.append("BBB")':
Immutable value of type 'Array<String>' only has mutating members named 'append'
How do I fix the code to allow a call to this mutable method?
Many thanks in advance
If you want to modify the array you have to specify it as in an inout parameter like this func addToString(inout myArray:Array<String>). Then when you call the function you have to add & in front of your argument to show that it can be modified by the function. Your code will look something like this:
class Sample {
func doSomething() {
var stringArray = Array<String>()
stringArray.append("AAA")
addToString(&stringArray)
stringArray.append("CCC")
}
func addToString(inout myArray:Array<String>) {
myArray.append("BBB")
}
}
You may want to take a look at in-out parameters on this page.