Casting subclass from base class where subclass has generic in Swift - ios

I have a base class type and a subclass type, where the subclass includes a generic type. If I have the subclass stored in the form of the base class type but I would like to type cast it back to the subclass type, Swift won't seem to let me.
Below is an example of what I mean:
class Base {
}
class Next<T> : Base where T : UIView {
var view: T
init(view: T) {
self.view = view
}
}
let a: [Base] = [Next(view: UIImageView()), Next(view: UILabel())]
for item in a {
if let _ = item as? Next {
print("Hey!")
}
}
Why is "Hey!" never printed?
EDIT:
"Hey!" is printed if the cast reads:
if let _ item as? Next<UIImageView> ...
but only for the case of the UIImageView class.
and
"Hey!" is printed if one of the items in the array a is:
Next(view: UIView())
Ideally, I would like to not know what type the generic is when casting, but I realise this may not be possible.

The generic Next<T> is a template of sorts that creates unique separate classes. Next<UIView> and Next<UIImageView> are two completely unrelated types.
You can unite them with a protocol:
class Base {
}
class Next<T> : Base where T : UIView {
var view: T
init(view: T) {
self.view = view
}
}
protocol NextProtocol { }
extension Next: NextProtocol { }
let a: [Base] = [Next(view: UIImageView()), Next(view: UILabel()), Base()]
for item in a {
if item is NextProtocol {
print("Hey!")
} else {
print("Only a Base")
}
}
Output:
Hey!
Hey!
Only a Base
By defining a protocol such as NextProcotol that all classes derived from Next<T> conform to, you can refer to them as a group and distinguish them from other classes that derive from Base.
Note: To check if an item is of a type, use is instead of checking if the conditional cast as? works.

This is where protocols comes to help.
you need a dummy protocol to confirm Next to it
And use that dummy protocol to check your items types.
Therefore we create a dummy protocol confirm Next to it and use that protocol to compare items.
code would be something like this.
class Base {
}
class Next<T> : Base where T : UIView {
var view: T
init(view: T) {
self.view = view
}
}
protocol MyType {
}
extension Next: MyType {}
let a: [Base] = [Next(view: UILabel()), Next(view: UILabel())]
for item in a {
if let _ = item as? MyType {
print("Hey!")
}
}
UIView is super class for all the UIElements.
saying this will result into true,
if let _ = UILabel() as? UIView {print("yes") }
Next does not confirm to UIView but rather it requires something that confirm to UIView therefore you can't use the subs of UIView to check if they are Next, next have no exact type,
here we use the dummy protocol above !

When I saw your example I was expecting it to fail during compilation.
It seems that as long as you specify a concrete base class for your T (in this case UIView), then the compiler can infer that class in as or is statements. So when you type item as? Next the compiler understands item as?Next` because UIView is a concrete type.
This would not work if instead of UIView you would use a protocol. Also, simply using Next instead of Next<UIView> (or Next<concrete subclass of UIView>) will result in a compiler error outside an is or as statement.

Related

Swift Variable Constraints

Is it possible in the current iteration of Swift (2.1) to add constraints on a variable type?
If I have a class Element
class Element: NSObject {
var type: ElementType
init(type: ElementType) {
self.type = type
}
}
with an enum ElementType
enum ElementType {
case Cell
case Header
case Footer
case Decoration(kind: String)
}
in some other class if I have a variable of type Element, is it at all possible to put constraints like:
var element: Element? where Self == .Header
or instead would I have to override didSet
var element: Element? {
didSet {
if let value = element where value.type == Decoration {
element = Optional.None
}
}
}
I'm sure it's not possible, the generic system isn't as powerful as I would like (being able to only constrain extensions by protocol and class inheritance for example and no variadic generic parameters).
Type constraints and types checks are a compilation feature but you want to check the runtime value of an object.
I am pretty sure that's impossible. You will have to use a runtime check (e.g. willSet or didSet).
Or, instead of enforcing the type by the value of a type atrribute, enforce it with a real type, e.g.
subclassing
class Header: Element
or make Element a protocol implemented by four separate classes:
protocol Element {
}
class Header: Element
(you can use protocol extension for shared functions).

Send class type after "as!" as an argument

I need to "read" ViewController, which was sent as an argument to a function, as a VC of the specific class. So I need something like that (we get a class also from a function arguments):
let vc = vc_from_func_args as! type_from_func_args
I can pass a class to let's say isMemberOfClass() by doing that:
let klass: AnyClass = MyClass.self
vc.isMemberOfClass(klass)
But I can't do the same thing with "as" expression. It gives me an error:
klass is not a type
How can we pass class (type?) after "as" as a variable?
Given your comments, this is exactly what protocols are for. If you want a thing you can call pop on, then make that a requirement for your function. If it's easy to list all the things you need, then just put them in your protocol:
protocol Stackable {
var parent: UIViewController {get set}
var child: UIViewController {get set}
}
func push(vc: Stackable) {
// do the work
}
If you really need this to be a UIViewController that also happens to be Stackable, that's fine, too:
func pop<VC: UIViewController where VC: Stackable>(vc: VC) {
// do the work
}
Then just mark your view controllers as conforming to Stackable:
class SomeViewController: UIViewController, Stackable {
var parent: UIViewController
var child: UIViewController
...
}
If you find yourself doing a lot of as! or AnyClass, you're probably on the wrong track in Swift.
How about something like that...
if let checkedClass: MyFirstClass = vc_from_func_args as? MyFirstClass {
//It only hits here if it is MyFirstClass
}
if let checkedClass: MySecondClass = vc_from_func_args as? MySecondClass {
//It only hits here if it is MySecondClass
}
if let checkedClass: MyThirdClass = vc_from_func_args as? MyThirdClass {
//It only hits here if it is MyThirdClass
}
Also you are tying to instantiate a little bit wired :-)
Change this
let klass: AnyClass = MyClass.self
vc.isMemberOfClass(klass)
To something like this
vc.isMemberOfClass(MyClass)
You don't need to create an instance to check if another object is kind of a class :-) But just use my code from above... Its even better than this one
I discovered the same issue a little while ago, and ended up writing a downcast global function, and a protocol called Castable that includes the asType function:
protocol Castable: class {
func asType<T>(t: T.Type, defaultValue: T?, file: StaticString,
function: StaticString, line: UWord) -> T?
}
Basically, you can take any class object and write myObject.asType(newType) and it performs the cast. If the cast fails, it logs the failure to the console, reporting the types you were casting to and from, and the file name, method name, and line number where the method was called. I use it for debugging.
At any rate, the way that the asType and downcast functions are written, you can pass the type that you are casting to as a named variable, which is what your original question wanted to do.
The complete code for downcast, the Castable protocol, and the asType function are available at this link.

Identifying a subclass with Swift Generics works with custom class but not with UITapGestureRecognizer

There's something I wanted to do in swift, but I couldn't figure out how to achieve it, that is to remove gesture recognisers given a Class Type, here's my code (and example), i'm using swift 2.0 in Xcode 7 beta 5:
I have 3 classes that inherits from UITapGestureRecognizer
class GestureONE: UIGestureRecognizer { /*...*/ }
class GestureTWO: UIGestureRecognizer { /*...*/ }
class GestureTHREE: UIGestureRecognizer { /*...*/ }
Add them to a view
var gesture1 = GestureONE()
var gesture11 = GestureONE()
var gesture2 = GestureTWO()
var gesture22 = GestureTWO()
var gesture222 = GestureTWO()
var gesture3 = GestureTHREE()
var myView = UIView()
myView.addGestureRecognizer(gesture1)
myView.addGestureRecognizer(gesture11)
myView.addGestureRecognizer(gesture2)
myView.addGestureRecognizer(gesture22)
myView.addGestureRecognizer(gesture222)
myView.addGestureRecognizer(gesture3)
I print the object:
print(myView.gestureRecognizers!)
// playground prints "[<__lldb_expr_224.TapONE: 0x7fab52c20b40; baseClass = UITapGestureRecognizer; state = Possible; view = <UIView 0x7fab52d259c0>>, <__lldb_expr_224.TapONE: 0x7fab52d21250; baseClass = UITapGestureRecognizer; state = Possible; view = <UIView 0x7fab52d259c0>>, <__lldb_expr_224.TapTWO: 0x7fab52d24a60; baseClass = UITapGestureRecognizer; state = Possible; view = <UIView 0x7fab52d259c0>>, <__lldb_expr_224.TapTWO: 0x7fab52c21130; baseClass = UITapGestureRecognizer; state = Possible; view = <UIView 0x7fab52d259c0>>, <__lldb_expr_224.TapTWO: 0x7fab52e13260; baseClass = UITapGestureRecognizer; state = Possible; view = <UIView 0x7fab52d259c0>>, <__lldb_expr_224.TapTHREE: 0x7fab52c21410; baseClass = UITapGestureRecognizer; state = Possible; view = <UIView 0x7fab52d259c0>>]"
Have this extension I made with a generic function
extension UIView {
func removeGestureRecognizers<T: UIGestureRecognizer>(type: T.Type) {
if let gestures = self.gestureRecognizers {
for gesture in gestures {
if gesture is T {
removeGestureRecognizer(gesture)
}
}
}
}
}
Then I use it
myView.gestureRecognizers?.count // Prints 6
myView.removeGestureRecognizers(GestureTWO)
myView.gestureRecognizers?.count // Prints 0
Is removing all of the gestures D:
And here's an experiment with custom classes
//** TEST WITH ANIMALS*//
class Animal { /*...*/ }
class Dog: Animal { /*...*/ }
class Cat: Animal { /*...*/ }
class Hipo: Animal { /*...*/ }
class Zoo {
var animals = [Animal]()
}
var zoo = Zoo()
var dog1 = Dog()
var cat1 = Cat()
var cat2 = Cat()
var cat3 = Cat()
var hipo1 = Hipo()
var hipo2 = Hipo()
zoo.animals.append(dog1)
zoo.animals.append(cat1)
zoo.animals.append(cat2)
zoo.animals.append(cat3)
zoo.animals.append(hipo1)
zoo.animals.append(hipo2)
print(zoo.animals)
//playground prints "[Dog, Cat, Cat, Cat, Hipo, Hipo]"
extension Zoo {
func removeAnimalType<T: Animal>(type: T.Type) {
for (index, animal) in animals.enumerate() {
if animal is T {
animals.removeAtIndex(index)
}
}
}
}
zoo.animals.count // prints 6
zoo.removeAnimalType(Cat)
zoo.animals.count // prints 3
It's actually removing the classes it should :D
What am I missing with the UIGestureRecognizer's? I ended up with a workaround making a function that has no generics (boring) like this:
extension UIView {
func removeActionsTapGestureRecognizer() {
if let gestures = self.gestureRecognizers {
gestures.map({
if $0 is ActionsTapGestureRecognizer {
self.removeGestureRecognizer($0)
}
})
}
}
}
This works of course, but still I would like to have a real solution
I appreciate your help!!
Note: First question I ask here
TL;DR:
Use dynamicType to check the runtime type of each gesture recognizer against your type parameter.
Great question. It looks like you're encountering a scenario where the difference between Objective-C's dynamic typing and Swift's static typing becomes clear.
In Swift, SomeType.Type is the metatype type of a type, which essentially allows you to specify a compile-time type parameter. But this might not be the same as the type at runtime.
class BaseClass { ... }
class SubClass: BaseClass { ... }
let object: BaseClass = SubClass()
In the example above, object's compile-time class is BaseClass, but at runtime, it is SubClass. You can check the runtime class with dynamicType:
print(object.dynamicType)
// prints "SubClass"
So why does that matter? As you saw with your Animal test, things behaved as you expected: your method takes an argument whose type is a metatype type of an Animal subclass, and then you only remove animals that conform to that type. The compiler knows that T can be any particular subclass of Animal. But if you specify an Objective-C type (UIGestureRecognizer), the compiler dips its toes into the uncertain world of Objective-C dynamic typing, and things get a little less predictable until runtime.
I must warn you that I'm a bit wooly on the details here... I don't know the specifics of how the compiler/runtime treats generics when mixing the worlds of Swift & Objective-C. Perhaps somebody with some better knowledge of the subject could chip in and elucidate!
As a comparison, let's just quickly try a variation of your method where the compiler can steer a bit further clear of the Objective-C world:
class SwiftGesture: UIGestureRecognizer {}
class GestureONE: SwiftGesture {}
class GestureTWO: SwiftGesture {}
class GestureTHREE: SwiftGesture {}
extension UIView {
func removeGestureRecognizersOfType<T: SwiftGesture>(type: T.Type) {
guard let gestureRecognizers = self.gestureRecognizers else { return }
for case let gesture as T in gestureRecognizers {
self.removeGestureRecognizer(gesture)
}
}
}
myView.removeGestureRecognizers(GestureTWO)
With the above code, only GestureTWO instances will be removed, which is what we want, if only for Swift types. The Swift compiler can look at this generic method declaration without concerning itself with Objective-C types.
Fortunately, as discussed above, Swift is capable of inspecting the runtime type of an object, using dynamicType. With this knowledge, it only takes a minor tweak to make your method work with Objective-C types:
func removeGestureRecognizersOfType<T: UIGestureRecognizer>(type: T.Type) {
guard let gestureRecognizers = self.gestureRecognizers else { return }
for case let gesture in gestureRecognizers where gesture.dynamicType == type {
self.removeGestureRecognizer(gesture)
}
}
The for loop binds to the gesture variable only gesture recognizers whose runtime type is equal to the passed in metatype type value, so we successfully remove only the specified type of gesture recognizers.

access property from another class

I have a custom swift class like this
class NichedHelper: NSObject {
private var _theController:UIViewController? = nil
var theController:UIViewController? {
get {
return self._theController
}
set {
self._theController = newValue
}
}...
it has an implementation function like this and _theController passing a Lobb class that inherit UIViewController
func DoPump(from: String, theBoard: CGRect, overide: Bool) {
let abil:AnyObject = _theController!
abil.bottomConst.constant = -80
}
it throw error 'AnyObject' does not have a member named 'bottomConst'.
since i don't know what the english word for this kind of technique, so that will be my first question.
my second question, is it possible if i am sure Lobb class (or other class) have a variable called bottomConst, how can i access it from class NichedHelper?
you have declared the _theController as private , remove that just declare as
var _theController:UIViewController!
// this is how we roll in swift ;) bye bye Objective-C
I don't know exactly what you are trying to do and why you have two UIViewController instances. So I'm not able to answer your first question but regarding your second one, you have to cast the object to a UIViewController object:
func DoPump(from: String, theBoard: CGRect, overide: Bool) {
let abil:AnyObject = _theController as! UIViewController
abil.bottomConst.constant = -80
}
This at least should make the compiling error away, if you have the bottomConst attribute declared as a variable of UIViewControllers in an extension (since they do not have this variable normally.
Well, i change from passing the UIViewController to NSLayoutConstraint

What's the Swift equivalent of declaring `typedef SomeClass<SomeProtocol> MyType`?

I’m currently writing some Swift code in a project that is predominately Objective-C. In our ObjC code, we have a header that declares typedef GPUImageOutput<GPUImageInput> MyFilter;. We can then declare e.g. a #property that can only be a GPUImageOutput subclass that implements GPUImageInput.
(NOTE: GPUImageOutput and GPUImageInput are not defined by me; they are part of the GPUImage library)
Our Swift code doesn't seem to recognize this, even though the header is #imported in our Bridging Header. I’ve tried to replicate the declaration in Swift, but neither of these are proper syntax:
typealias MyFilter = GPUImageOutput, GPUImageInput
typealias MyFilter = GPUImageOutput : GPUImageInput
You can't declare typealias like that.
The best we can do is something like this:
class MyClass {
private var filter:GPUImageOutput
init<FilterType:GPUImageOutput where FilterType:GPUImageInput>(filter:FilterType) {
self.filter = filter
}
func setFilter<FilterType:GPUImageOutput where FilterType:GPUImageInput>(filter:FilterType) {
self.filter = filter
}
func someMethod() {
let output = self.filter
let input = self.filter as GPUImageInput
output.someOutputMethod()
input.someInputMethod()
}
}
In Swift 4 you can achieve this with the new & sign (Below an example of a parameter confirming to UIViewController and UITableViewDataSource:
func foo(vc: UIViewController & UITableViewDataSource) {
// access UIViewController property
let view = vc.view
// call UITableViewDataSource method
let sections = vc.numberOfSectionsInTableView?(tableView)
}
In Swift, something like the following should accomplish your task, but it's different than its ObjC counterpart:
typealias GPUImageOutput = UIImage
#objc protocol GPUImageInput {
func lotsOfInput()
}
class GPUImageOutputWithInput: GPUImageOutput, GPUImageInput
{
func lotsOfInput() {
println("lotsOfInput")
}
}
// ...
var someGpuImage = GPUImageOutput()
var specificGpuImage = GPUImageOutputWithInput()
for image in [someGpuImage, specificGpuImage] {
if let specificImage = image as? GPUImageInput {
specificImage.lotsOfInput()
} else {
println("the less specific type")
}
}
UPDATE: now that I understand where/why you have these types ...
GPUImage seems to have a swift example that does what you want, as Swift-ly as possible.
See here:
class FilterOperation<FilterClass: GPUImageOutput where FilterClass: GPUImageInput>: FilterOperationInterface {
...
The type constraint syntax can be applied to functions, too, and with a where clause, that's probably as good as you're going to get directly in Swift.
The more I tried to understand how to port this somewhat common objc trope, the more I realized it was the most Swift-way. Once I saw the example in GPUImage itself, I was convinced it was at least your answer. :-)
UPDATE 2: So, besides the specific GPUImage example I linked to above that uses Swift, the more and more I think about this, either using a where clause to guard the setter function, or using a computable property to filter the set functionality seems the only way to go.
I came up with this strategy:
import Foundation
#objc protocol SpecialProtocol {
func special()
}
class MyClass {}
class MyClassPlus: MyClass, SpecialProtocol {
func special() {
println("I'm special")
}
}
class MyContainer {
private var i: MyClass?
var test: MyClass? {
get {
return self.i
}
set (newValue) {
if newValue is SpecialProtocol {
self.i = newValue
}
}
}
}
var container = MyContainer()
println("should be nil: \(container.test)")
container.test = MyClass()
println("should still be nil: \(container.test)")
container.test = MyClassPlus()
println("should be set: \(container.test)")
(container.test as? MyClassPlus)?.special()
Outputs:
should be nil: nil
should still be nil: nil
should be set: Optional(main.MyClassPlus)
I'm special
(Optionally, you could also use precondition(newValue is SpecialProtocol, "newValue did not conform to SpecialProtocol") in place of the is check, but that will act like an assert() can crash the app if the case isn't met. Depends on your needs.)
#rintaro's answer is a good one, and is a good example of using a where clause as a guard (both nice functional-ly, and Swift-ly). However, I just hate to write a setFoo() function when computable properties exist. Then again, even using a computable property has code smell, since we can't seem to be able to apply a generic type-constraint to the set'er, and have to do the protocol conformance test in-line.
You can use typealias keyword. Here is how to do it:
typealias MyNewType = MyExistingType
It doesn't matter whether MyExistingType is protocol or function or enum. All it needs to be some type. And the best part is you can apply access control on it. You can say
private typealias MyNewType = MyExistingType
That makes MyNewType is only accessible the context that is defined in.

Resources