I've got an issue where I'm calling a method with generic a generic parameter that works properly (at runtime, the parameter is visible to the method), however in the Xcode debugger, the parameter is "not there").
The definition of the method looks like this:
func updateCacheObject<T: PLObjectCacheable>(newCacheObject: T) throws -> T {
NSLog("CacheObject.cacheId: \(newCacheObject.cacheId)")
let cachedObject: T? = try self.getCacheObject(id: newCacheObject.cacheId)
...
Where the PLObjectCacheable is defined as:
protocol PLObjectCacheable {
var cacheId: String { get }
var isFullObject: Bool { get }
func mergeWithOldObject(_ oldObject: Self?) -> Self
static func getCacheIdForString(id: String) -> String
var cacheDate: Date { get }
}
and an implementation of a class that implements PLObjectCacheable is this:
struct FullTrack: PLObjectCacheable {
let id: String
var cacheId: String {
return self.id
}
var isFullObject: Bool {
return true
}
...
}
So, going back to the updateCacheObject method, when it's called, the first line dumps out the newCacheObject parameter's cacheId member variable.
This works fine.
However, if I place a breakpoint in the Xcode debugger anywhere in the method, Xcode can't "see" the newCacheObject parameter.
See the image:
Note that the Blue/Cyan square shows that the newCacheObject.cacheId was successfully logged.
The Red and purple squares show that the debugger can't find the newCacheObject variable.
Is it because it's a generic parameter? I'm confused.
Thanks.
Related
I'm using Xcode 9.4.1 with Swift 4.1 on iOS 11.4.1.
I've got a handful of protocols, like this:
protocol Bla {
// some stuff
}
protocol Gorp {
// some other stuff
}
And I've got some structs that conform to both of those protocols, like this:
typealias MyType = Bla & Gorp
struct Hello: MyType {
var ID: Int
var name: String
}
struct Goodbye: MyType {
var percentage: Double
var hairbrush: String
}
Then I've got a func that takes an argument, theType, which conforms to both Bla & Gorp. In this example, I'm just printing a description -- like this:
func doSomething<T: MyType>(_ theType: T.Type) {
// some stuff
print("Got called... \(theType)")
}
And, I can call this function passing each of the two struct types (Hello and Goodbye), like this:
doSomething(Hello.self)
doSomething(Goodbye.self)
This works great and I get the following output, as expected:
Got called... Hello
Got called... Goodbye
However, what I'd really like to do is to iterate over a bunch of these, rather than calling them individually.
This way gives me an error "note: expected an argument list of type '(T.Type)'":
for thingy in [Hello.self, Goodbye.self] {
doSomething(thingy)
}
If I add an as! [MyType.Type] or as! [MyType], I get the same error. I've also tried this:
for thingy in [Hello.self as MyType.Type, Goodbye.self as MyType.Type] {
doSomething(thingy)
}
Same error as the rest.
I've also tried without the typealias.
If I start typing, the autocomplete says that doSomething is expecting an argument of type (Bla & Gorp).Protocol. So, I also tried this:
for thingy in [Hello.self, Goodbye.self] as! [(Bla & Gorp).Protocol] {
doSomething(thingy)
}
In this case, I get the message:
In argument type '(Bla & Gorp).Protocol', 'Bla & Gorp' does not conform to expected type 'Bla'
Also tried this sort of thing, which gave an error, "Cannot invoke 'doSomething' with an argument list of type '(MyType.Type)'":
struct AnyBlaGorp {
let blaGorp: MyType.Type
init<T: MyType>(_ theType: T.Type) {
self.blaGorp = theType
}
}
for thingy in [AnyBlaGorp(Hello.self), AnyBlaGorp(Goodbye.self)] {
doSomething(thingy.blaGorp)
}
Pointers to the magical correct syntax would be greatly appreciated. :)
You can make the doSomething method non-generic and accept a MyType.Type. You can only do this if your protocols don't have Self or associated types.
func doSomething(_ theType: MyType.Type) {
// some stuff
print("Got called... \(theType)")
}
Next, you cast the array to a [MyType.Type]:
for thingy in [Hello.self, Goodbye.self] as [MyType.Type] {
doSomething(thingy)
}
This makes use of the fact that A.self can be converted to the type B.Type if A inherits from/conforms to B.
I have some code that looks something like this:
class Calculator {
private let estimateChanged:(newEstimate:Double) -> Void
init(_ estimateChanged:(newEstimate:Double) -> Void) {
self.estimateChanged = estimateChanged
}
}
Which is used by another class. Currently I'm trying this to set a property containing a calculator:
class AnotherClass {
private lazy var calculator = Calculator({ [unowned self] in
self.loadEstimate()
})
func loadEstimate() {}
}
And I'm getting errors. Firstly where it says [unowned self] is giving me the error: 'unowned' cannot be applied to non-class type 'AnotherClass -> () -> AnotherClass'
Secondly on self.loadEstimate() I get: value of type 'AnotherClass -> () -> AnotherClass' has no member 'loadEstimate'
Everything I've read suggests to me that I've got this right, however I've not seen any example of setting a var with an instance of a class which takes a closure as an init argument.
Has anyone done this or knows what to try next? Or is there a better way to do this?
As a side note, the goal of this code was to effectively provide a way for the Calculator class to notify AnotherClass when a value has changed. I've looked around, but I'm not sure what techniques are best suited for doing this. Any suggestions?
Here is as close as I could get to what you wanted by fiddling around in a playground. I had to change the types to match what you were passing to Calculator
class Calculator {
private let estimateChanged:() -> Void
init(_ estimateChanged:() -> Void) {
self.estimateChanged = estimateChanged
}
}
class AnotherClass {
lazy var callback: () -> Void = { [unowned self] in
self.loadEstimate()
}
var calculator: Calculator?
// viewDidLoad in the case of a view controller
init() {
calculator = Calculator(callback)
}
func loadEstimate() {}
}
It is obviously not exactly what you wanted but it compiles. There seem to be issues when trying to refer to self in an un-initialized object (even when it seems you should be able to because you specify lazy and weak or unowned etc)
I'm trying to create a protocol in Swift I can use for object construction. The problem I'm running into is that I need to store the type information so the type can be constructed later and returned in a callback. I can't seem to find a way to store it without either crashing the compiler or creating build errors. Here's the basics (a contrived, but working example):
protocol Model {
init(values: [String])
func printValues()
}
struct Request<T:Model> {
let returnType:T.Type
let callback:T -> ()
}
We have a simple protocol that declares a init (for construction) and another func printValues() (for testing). We also define a struct we can use to store the type information and a callback to return the new type when its constructed.
Next we create a constructor:
class Constructor {
var callbacks: [Request<Model>] = []
func construct<T:Model>(type:T.Type, callback: T -> ()) {
callback(type(values: ["value1", "value2"]))
}
func queueRequest<T:Model>(request: Request<T>) {
callbacks.append(request)
}
func next() {
if let request = callbacks.first {
let model = request.returnType(values: ["value1", "value2"])
request.callback(model)
}
}
}
A couple things to note: This causes a compiler crash. It can't figure this out for some reason. The problem appears to be var callbacks: [Request<Model>] = []. If I comment out everything else, the compiler still crashes. Commenting out the var callbacks and the compiler stops crashing.
Also, the func construct works fine. But it doesn't store the type information so it's not so useful to me. I put in there for demonstration.
I found I could prevent the compiler from crashing if I remove the protocol requirement from the Request struct: struct Request<T>. In this case everything works and compiles but I still need to comment out let model = request.returnType(values: ["value1", "value2"]) in func next(). That is also causing a compiler crash.
Here's a usage example:
func construct() {
let constructor = Constructor()
let request = Request(returnType: TypeA.self) { req in req.printValues() }
//This works fine
constructor.construct(TypeA.self) { a in
a.printValues()
}
//This is what I want
constructor.queueRequest(request)
constructor.next() //The callback in the request object should be called and the values should print
}
Does anyone know how I can store type information restricted to a specific protocol to the type can later be constructed dynamically and returned in a callback?
If you want the exact same behavior of next I would suggest to do this:
class Constructor {
// store closures
var callbacks: [[String] -> ()] = []
func construct<T:Model>(type:T.Type, callback: T -> ()) {
callback(type(values: ["value1", "value2"]))
}
func queueRequest<T:Model>(request: Request<T>) {
// some code from the next function so you don't need to store the generic type itself
// **EDIT** changed closure to type [String] -> () in order to call it with different values
callbacks.append({ values in
let model = request.returnType(values: values)
request.callback(model)
})
}
func next(values: [String]) {
callbacks.first?(values)
}
}
Now you can call next with your values. Hopefully this works for you.
EDIT: Made some changes to the closure type and the next function
Unfortunately there is no way to save specific generic types in an array and dynamically call their methods because Swift is a static typed language (and Array has to have unambiguous types).
But hopefully we can express something like this in the future like so:
var callbacks: [Request<T: Model>] = []
Where T could be anything but has to conform to Model for example.
Your queueRequest method shouldn't have to know the generic type the Request it's being passed. Since callbacks is an array of Request<Model> types, the method just needs to know that the request being queued is of the type Request<Model>. It doesn't matter what the generic type is.
This code builds for me in a Playground:
class Constructor {
var callbacks: [Request<Model>] = []
func construct<T:Model>(type:T.Type, callback: T -> ()) {
callback(type(values: ["value1", "value2"]))
}
func queueRequest(request: Request<Model>) {
callbacks.append(request)
}
func next() {
if let request = callbacks.first {
let model = request.returnType(values: ["value1", "value2"])
request.callback(model)
}
}
}
So I found an answer that seems to do exactly what I want. I haven't confirmed this works yet in live code, but it does compile without any errors. Turns out, I needed to add one more level of redirection:
I create another protocol explicitly for object construction:
protocol ModelConstructor {
func constructWith(values:[String])
}
In my Request struct, I conform to this protocol:
struct Request<T:Model> : ModelConstructor {
let returnType:T.Type
let callback:T -> ()
func constructWith(values:[String]) {
let model = returnType(values: values)
callback(model)
}
}
Notice the actual construction is moved into the Request struct. Technically, the Constructor is no longer constructing, but for now I leave its name alone. I can now store the Request struct as ModelConstructor and correctly queue Requests:
class Constructor {
var callbacks: [ModelConstructor] = []
func queueRequest(request: Request<Model>) {
queueRequest(request)
}
func queueRequest(request: ModelConstructor) {
callbacks.append(request)
}
func next() {
if let request = callbacks.first {
request.constructWith(["value1", "value2"])
callbacks.removeAtIndex(0)
}
}
}
Note something special here: I can now successfully "queue" (or store in an array) Request<Model>, but I must do so indirectly by calling queueRequest(request: ModelConstructor). In this case, I'm overloading but that's not necessary. What matters here is that if I try to call callbacks.append(request) in the queueRequest(request: Request<Model>) function, the Swift compiler crashes. Apparently we need to hold the compiler's hand here a little so it can understand what exactly we want.
What I've found is that you cannot separate Type information from Type Construction. It needs to be all in the same place (in this case it's the Request struct). But so long as you keep construction coupled with the Type information, you're free to delay/store the construction until you have the information you need to actually construct the object.
The following code produces a compile error of "Generic parameter "T" cannot be bound to non-#objc protocol type 'AAA' on the fail line. When I use a class instead of a protocol, it works ok. Also, if I add an #objc to the protocol it also works, but only in 6.4 beta. Any suggestions would be helpful.
protocol AAA {
var id: String { get set }
}
class BBB: AAA {
var id: String = ""
}
class BBBService {
func getAll<T:AAA>() -> [T] {
var returnArray:[T] = [T]()
return returnArray
}
}
class TestIt
{
func myTest() {
var service = BBBService()
var fail:[AAA] = service.getAll() // fails with Generic parameter "T" cannot be bound to non-#objc protocol type AAA
var succeed:[BBB] = service.getAll()
}
}
this also fails:
<T where T:AAA>
Update - from a practical perspective, adding the #objc causes other problems in my app. So, that is not an option at this point and time.
The trouble is with this line:
getAll<T: AAA>() -> [T]
you are declaring that T must be a concrete type that implements the protocol AAA. It’s important to distinguish between the protocol AAA, i.e. code like this:
func getAll() -> [AAA] {
var returnArray: [AAA] = [BBB()]
return returnArray
}
which works fine (returns an array of references to AAA-conforming objects, which could be of type BBB, or type CCC), and this:
func getAll<T: AAA>() -> [T] {
var returnArray: [T] = [] // what is T? No idea.
return returnArray
}
in which you are saying to the compiler “write me a version of getAll, in which T can be replaced by any specific type that implements AAA”.
This is why your second version compiles - you’re fixing T to be the actual type BBB.
Bear in mind, T might be a struct. In which case the array returned must be sized specifically for whatever struct is being held, right there as a value within the array. As opposed to if the protocol was #objc in which case it would at least be known to only be a class reference of a fixed size.
If what you actually want is an array of protocols, you should remove the generic placeholder and just return an array of protocols:
func getAll() -> [AAA] {
var returnArray: [AAA] = []
return returnArray
}
I am having an issue with calling an instance method within the class itself. If someone can provide me some insight it would be greatly appreciated.
My current code looks like this:
class Rect
{
func printthis() -> String {
return "this is working or what"
}
var toPrint:String = self.printthis()
}
The error I am getting in Xcode is: Use of unresolved identifier 'self'.
What am I missing here?
You can't call an instance method without an instance. The class is merely the template for instances. So i don't what you are trying to do here...
But the answer is no, you cannot call an instance method form the class definition because there is no instance yet.
Perhaps you want to delcare a class method and use that to set an instance variable on creation? If so, you might do that like this:
class Rect {
class func printthis() -> String {
return "this is working or what"
}
var toPrint:String
init() {
toPrint = Rect.printthis()
}
}
var r = Rect()
println(r.toPrint) //-> this is working or what
An instance of a class is not initialized and able to be referenced (even as 'self') until all of its variables have been assigned values.
An option that may work for you is to declare your variable as an implicitly-unwrapped optional, which is assigned nil by default. Then in the class's init method, since all of the variables have been assigned values, you are able to start calling methods on your instance.
class Rect {
var toPrint: String!
init() {
toPrint = printthis()
}
printthis() -> String {
return "this will work"
}
}
the problem is that swift is strict about initing all properties.
you may as a workaround
class Rect
{
func printthis() -> String {
return "this is working or what"
}
var toPrint:String = ""
init() {
toPrint = printthis()
}
}