Given a class:
class First<T> {
}
And a method of class First:
func second<U>(closure: (value: T) -> U) {
}
How could I store the closure passed as an argument to second so that I can call it at a later date?
You would need to declare U in the class instead, so that you have a type for the storage:
class First<T,U> {
var f : ((T) -> U)! = nil
func second(closure: #escaping (T) -> U) {
self.f = closure
}
}
If having the function second work for only one kind of type is good enough for you, then Matt's answer is good.
class First<T, U> {
typealias ClosureType = (value: T) -> U
var savedClosure: ClosureType? = nil
func second(closure: ClosureType) {
savedClosure = closure
}
}
That doesn't actually answer your question as stated!
The thing is: You can't store a value of an unknown type.
But! if the type conforms to a known protocol, then you can save it.
protocol P {}
class First<T> {
typealias ClosureType = (value: T) -> P
var savedClosure: ClosureType? = nil
func second<U: P>(closure: (value: T) -> U) {
savedClosure = closure
}
}
The protocol could even be protocol<> "no protocol at all", which is typealiased to the keyword Any.
class First<T> {
typealias ClosureType = (value: T) -> Any
var savedClosure: ClosureType? = nil
func second<U>(closure: (value: T) -> U) {
savedClosure = closure
}
}
But we don't really know what you want to do, so there are multiple answers to your question… for example, maybe you wanted to store a separate closure for each type?
class First<T> {
typealias ClosureType = (value: T) -> Any
var savedClosures = [String: ClosureType]()
func second<U>(closure: (value: T) -> U) {
savedClosures[String(U)] = closure
}
}
Anyway, the real question is: "Do you really need to do this? Is there some simple change you can do that obviates this need?"
Related
open class CheckItem<T, U: Equatable & CustomStringConvertible>: DataTableItem<T,U,Bool> {
public override init(_ data: T, getter: #escaping (T) -> U) {
super.init(data, getter: getter)
}
}
open class DataTableItem<T, U: Equatable & CustomStringConvertible, V: CustomStringConvertible>: TableItem{
let data: T
let getter: (T) -> U
public init(_ data: T, getter: #escaping (T) -> U) {
self.data = data
self.getter = getter
}
}
open class TableItem: NSObject {
public var title: String?
}
It is weird that, can't override the init in subclass CheckItem.
Compiler complains that Initializer does not override a designated initializer from its superclass. It complains that Overriding declaration requires an 'override' keyword if I remove the override keyword.
It drives me crazy anyone helps? Thanks in advance.
The more weird part is that it works in LabelItem
open class LabelItem<T, U: Equatable & CustomStringConvertible, V: CustomStringConvertible>: DataTableItem<T,U,V>{
public override init(_ data: T, getter: #escaping (T) -> U) {
super.init(data, getter: getter)
}
The full code is available here https://github.com/magerate/TableMaker
Edit
let checkItem = CheckItem<People, Bool>(people, getter: {(p: People) -> Bool in
p.isGirl
})
It compiles if don't try to create any instance of CheckItem. But complains
Cannot convert value of type 'People' to expected argument type 'Bool'
when try to create a new instance of CheckItem.
It seems that type inference is not correctly here.
Edit
It works when I deploy the code to swift framework. WTF
I am not sure what exactly goes wrong for you. When I run your code everything seems fine. Are you sure you're instantiating your instances correctly?
let checkItem = CheckItem("Check item data") { (string: String) -> String in
return string + "checkItem"
}
let labelItem = LabelItem<String, String, Int>("Label item") { (string: String) -> String in
return string + "labelItem"
}
let dataTableItem = DataTableItem<String, String, Int>("Data table item") { (string: String) -> String in
return string + "dataTableItem"
}
In LabelItem and DataTableItem you have a generic V which is not used anywhere and is not a parameter, so you need to be explicit with your types upon instantiation since you're not passing the V type in the init and the compiler can't infer the type. Thus <String, String, Int> or any other types that meet the constraints.
EDIT:
After looking into your project code (the project didn't run on my Xcode, I only copied relevant code into my project) I still see no problem - both initializers of CheckItem compile:
open class TableItem: NSObject {
public var title: String?
}
open class DataTableItem<T, U: Equatable & CustomStringConvertible, V: CustomStringConvertible>: TableItem{
let data: T
let getter: (T) -> U
public weak var host: TableItemHost?
public init(_ data: T, getter: #escaping (T) -> U) {
self.data = data
self.getter = getter
}
}
public protocol TableItemHost: class {}
open class CheckItem<T, U: Equatable & CustomStringConvertible>: DataTableItem<T,U,Bool> {
public init(_ data: T, host: TableItemHost, getter: #escaping (T) -> U) {
super.init(data, getter: getter)
self.host = host
}
public override init(_ data: T, getter: #escaping (T) -> U) {
super.init(data, getter: getter)
}
}
Creating instances:
let checkItem1 = CheckItem("Check item 1 ") { (string: String) -> String in
return string
}
class Host: TableItemHost {}
let host = Host()
let checkItem2 = CheckItem("Check item 2 ", host: host) { (string: String) -> String in
return string
}
print(checkItem1.data)
print(checkItem2.data)
Copy paste my code into a playground and see for yourself. Perhaps there is something other than the initializer causing the error.
For a test you can also try commenting out both initializers of CheckItem and instantiate it with the inherited initializer. That should work, because CheckItem will not have its own designated initializer anymore (https://stackoverflow.com/a/31459131/1433612)
protocol LOL {
var foo: ((_ lol: String) -> Bool)? { get set }
}
class LMAO: LOL {
internal var foo: ((String) -> Bool)? = { (_ lol: String) in
self.rofl()
return true
}
func rofl() {}
}
Why is self unresolved in the internal foo variable?
What am I doing wrong here?
The closure you are assigning to foo is independent of your LMAO class so it has no "self".
if you want to initialize your foo variable with code from the current instance's self, you need to use lazy initialization. You will also need to link to the current instance somehow (a plain closure will not do it ).
the easiest way would probably be to add a defaultFoo function to your class and assign it as the initial value:
class LMAO: LOL
{
lazy var foo: ((String) -> Bool)? = self.defaultFoo
func defaultFoo (_ lol: String) -> Bool
{
self.rofl()
return true
}
func rofl() {}
}
At this point you probably don't need foo to be an optional, but that's up to you.
Initializer of class A takes an optional closure as argument:
class A {
var closure: ()?
init(closure: closure()?) {
self.closure = closure
self.closure()
}
}
I want to pass a function with an argument as the closure:
class B {
let a = A(closure: action(1)) // This throws the error: Cannot convert value of type '()' to expected argument type '(() -> Void)?'
func action(_ i: Int) {
//...
}
}
Class A should execute the closure action with argument i.
I am not sure about how to write this correctly, see error in code comment above. What has to be changed?
Please make your "what-you-have-now" code error free.
Assuming your class A like this:
class A {
typealias ClosureType = ()->Void
var closure: ClosureType?
init(closure: ClosureType?) {
self.closure = closure
//`closure` would be used later.
}
//To use the closure in class A
func someMethod() {
//call the closure
self.closure?()
}
}
With A given above, you need to rewrite your class B as:
class B {
private(set) var a: A!
init() {
//initialize all instance properties till here
a = A(closure: {[weak self] in self?.action(1)})
}
func action(i: Int) {
//...
}
}
The problem is that closure()? is not a type. And ()? is a type, but it is probably not the type you want.
If you want var closure to have as its value a certain kind of function, you need to use the type of that function in the declaration, e.g.
var closure: (Int) -> Void
Similarly, if you want init(closure:) to take as its parameter a certain kind of function, you need to use the type of that function in the declaration, e.g.
init(closure: (Int) -> Void) {
Types as Parameters
In Swift, every object has a type. For example, Int, String, etc. are likely all types you are extremely familiar with.
So when you declare a function, the explicit type (or sometimes protocols) of any parameters should be specified.
func swallowInt(number: Int) {}
Compound Types
Swift also has a concept of compound types. One example of this is Tuples. A Tuple is just a collection of other types.
let httpStatusCode: (Int, String) = (404, "Not Found")
A function could easily take a tuple as its argument:
func swallowStatusCode(statusCode: (Int, String)) {}
Another compound type is the function type. A function type consists of a tuple of parameters and a return type. So the swallowInt function from above would have the following function type: (Int) -> Void. Similarly, a function taking in an Int and a String and returning a Bool would have the following type: (Int, String) -> Bool.
Function Types As Parameters
So we can use these concepts to re-write function A:
class A {
var closure: (() -> Void)?
init(closure: (() -> Void)?) {
self.closure = closure
self.closure()
}
}
Passing an argument would then just be:
func foo(closure: (Int) -> Void) {
// Execute the closure
closure(1)
}
One way to do what I think you're attempting is with the following code:
class ViewController: UIViewController {
override func viewDidLoad() {
let _ = A.init(){Void in self.action(2)}
}
func action(i: Int) {
print(i)
}
}
class A: NSObject {
var closure : ()?
init(closure: (()->Void)? = nil) {
// Notice how this is executed before the closure
print("1")
// Make sure closure isn't nil
self.closure = closure?()
}
}
I have a class with a property which should have as type what in other languages would be called a generic (or template) interface. When I try to mimic this behavior in Swift I cannot get protocols to work with the idea. For example:
protocol P {
typealias T
func returnT() -> T
}
class G<T>: P {
var t: T
init(t: T) {
self.t = t
}
func returnT() -> T {
return t
}
}
class C {
var p: P<Int> // Cannot specialize non-generic type 'P'
init(instanceOfP: P<Int>) { // Cannot specialize non-generic type 'P'
p = instanceOfP
}
func usePsT() -> Int {
return p.returnT() // 'P' does not have a member named 'returnT'
}
}
Errors from the compiler are reported as comments. It seems clear to me that such a situation should not be problematic: but since Swift's protocols cannot be generics (they use this obscure typealias syntax instead) C has no way to know that every class that implements P can be specialized for Int. Is there a Swift-y way to represent this situation correctly? Or is there any known workaround so that I don't have to force or de-generalize the class structure?
The generic isn't really needed in your protocol (use Any) - it's needed in your class G<T>. You could do this...
protocol P {
func returnT() -> Any
}
class G<T>: P {
var t: T
init(t: T) {
self.t = t
}
func returnT() -> Any {
return t
}
}
class C {
var p: P
init(instanceOfP: P) {
p = instanceOfP
}
func usePsT() -> Any {
return p.returnT()
}
}
let gi = G<Int>(t: 7) // {t 7}
let ci = C(instanceOfP: gi) // {{t 7}}
ci.usePsT() // 7
let gs = G<String>(t: "Hello")
let cs = C(instanceOfP: gs)
cs.usePsT() // "Hello"
My solution is not ideal but saves me a lot of complications, still code is readable...
Instead of interfaces I use base class with open empty functions.
Like this:
public class CSTableControllerFilter<Row: CSTableControllerRow, Data> {
open func filter(data: [Row]) -> [Row] { data }
open func onReloadDone(in controller: CSTableController<Row, Data>) {}
}
When creating a normal generic function without constraints it works as intended, i.e:
func select<T,U>(x:T, f:(T) -> U) -> U {
return f(x)
}
The type flows through into the closure argument where it lets me access it as the strong type, i.e:
var b1:Bool = select("ABC") { $0.hasPrefix("A") }
var b2:Bool = select(10) { $0 > 0 }
It continues to work when I add an Equatable constraint:
func selectEquatable<T : Equatable, U : Equatable>(x:T, f:(T) -> U) -> U {
return f(x)
}
var b3:Bool = selectEquatable("ABC") { $0.hasPrefix("A") }
But for some reason fails when using a Comparable constraint:
func selectComparable<T : Comparable, U : Comparable>(x:T, f:(T) -> U) -> U {
return f(x)
}
var b4:Bool = selectComparable("ABC") { $0.hasPrefix("A") }
Fails with the build error:
Could not find member 'hasPrefix'
But it does allow returning itself where the type flows through as a String
var b5:String = selectComparable("ABC") { $0 }
Looking at the API docs shows that String is Comparable:
extension String : Comparable {
}
and it even allows implicit casting from a String to a Comparable:
var str:Comparable = ""
So why can't I access it as a strong-typed String inside my Closure?
var b4:Bool = selectComparable("ABC") { $0.hasPrefix("A") } //build error
It's not the String. Your closure { $0.hasPrefix("A") } has the return type Bool, which is assigned to U. Bool is Equatable, but not Comparable.
You probably want the closure to return Bool, but selectComparable to return U.
Edit
Here's evidence that returning a String (which is Comparable) instead of a Bool (not Comparable) will compile:
func selectComparable<T: Comparable, U: Comparable>(x:T, f:(T) -> U) -> U {
return f(x)
}
var b4 = selectComparable("ABC") { (str: String) -> String in str }
Your selectComparable declaration is incorrect.
func selectComparable<T: Comparable, U: Comparable>(x:T, f:(T) -> U) -> U {
return f(x)
}
U: Comparable cannot hold for type Bool, it is not Comparable, only Equatable
This will work fine
func selectComparable<T: Comparable, U>(x:T, f:(T) -> U) -> U {
return f(x)
}
as will
func select<T:Comparable,U: Equatable>(x:T, f:(T) -> U) -> U {
return f(x)
}