Swift. How is it possible to get the name of an instance of a class within another class - swift

Could the Swift Jedi help me, I'm new to Swift.
There is class A and class B.
Is it possible to get the name (var name = Class A() ) of an instance of class A in the code of class B, which would then be added to the array.

You can get the class of an object, using type(of: ...). Example:
class A {
func test() {
print (type(of: self))
}
}
class B : A{
}
let a = A()
a.test()
let b = B()
b.test()
However, this information is available only at runtime and not at compile time. You may for example display the name of the class. But you cannot dynamically create an array or a variable of that class.
If you need this, you need to design your classes using some kind of polymorphism. If all the classes have a base class in common, you can just define an array of the base class. If not, you may consider letting the classes of your design share some common protocol:
protocol MySpecs {
func test()
}
class AA : MySpecs {
...
}
class BB : MySpecs {
...
}
var mylist = [ MySpecs ]()
mylist.append(AA())
mylist.append(BB())
for x in mylist {
x.test()
}
There are other possibilities. But unless you provide more details it'll be difficult to be more specific in the recommendations.

Related

Why can't I assign this protocol conforming class to a variable of the protocol type?

I've got a toy example here that I can't find any solutions for online.
protocol Tree {
associatedtype Element
// some nice tree functions
}
class BinaryTree<T> : Tree {
typealias Element = T
}
class RedBlackTree<T> : Tree {
typealias Element = T
}
enum TreeType {
case binaryTree
case redBlackTree
}
// Use a generic `F` because Tree has an associated type and it can no
// longer be referenced directly as a type. This is probably the source
// of the confusion.
class TreeManager<E, F:Tree> where F.Element == E {
let tree: F
init(use type: TreeType){
// Error, cannot assign value of type 'BinaryTree<E>' to type 'F'
switch(type){
case .binaryTree:
tree = BinaryTree<E>()
case .redBlackTree:
tree = RedBlackTree<E>()
}
}
}
I'm not sure what the problem here is or what I should be searching for in order to figure it out. I'm still thinking of protocols as I would interfaces in another language, and I view a BinaryTree as a valid implementation of a Tree, and the only constraint on F is that it must be a Tree. To confuse things even more, I'm not sure why the following snippet compiles given that the one above does not.
func weird<F:Tree>(_ f: F){ }
func test(){
// No error, BinaryTree can be assigned to an F:Tree
weird(BinaryTree<String>())
}
Any pointers or explanations would be greatly appreciated.
I don't understand the context of the situation this would be in. However, I have provided two solutions though:
1:
class Bar<F:Foo> {
let foo: FooClass.F
init(){
foo = FooClass.F()
}
}
2:
class Bar<F:Foo> {
let foo: FooClass
init(){
foo = FooClass()
}
}
What you are currently doing doesn't make logical sense, to whatever you are trying to achieve
I don't know what are you trying for, but of course it's not possible to do that. from your example, you attempt to create Bar class with generic. and that not the appropriate way to create a generic object, because the creation of generic object is to make the object to accept with any type.
Here's some brief explanation of the generic taken from Wikipedia.
On the first paragraph that says, "Generic programming is a style of computer programming in which algorithms are written in terms of types to-be-specified-later that are then instantiated when needed for specific types provided as parameters."
it's very clear about what are the meaning of to-be-specified-later, right :)
Back to your example :
class Bar<F: Foo> {
let foo: F
init() {
// Error, cannot assign value of type 'FooClass' to type 'F'
foo = FooClass()
}
}
From the above code, it is type parameter F which has a constraint to a type Foo. and, you try to create an instance for foo variable with a concrete implementation, that is FooClass. and that's not possible since the foo variable is a F type(which is abstract). of course we can downcast it, like this foo = FooClass() as! F, but then the foo is only limited to FooClass, so why even bother with generic then ?
Hope it help :)
Your approach to this is wrong. In your original example, you would have to specify the type of both the element (E) and the container (BinaryTree or RedBlackTree), in addition to the enum value. That make no sense.
Instead, you should construct the manager to take a tree as the constructor argument, allowing Swift to infer the generic arguments, i.e.
class TreeManager<E, F: Tree> where F.Element == E {
var tree: F
init(with tree: F) {
self.tree = tree
}
}
let manager = TreeManager(with: BinaryTree<String>())
Alternatively, you should look into using opaque return types in Swift 5.1 depending on what the final goal is (the example here is obviously not a real world scenario)
Something like this seems reasonable. The point was to try and have some piece of logic that would determine when the TreeManager uses one type of Tree vs another.
protocol TreePicker {
associatedtype TreeType : Tree
func createTree() -> TreeType
}
struct SomeUseCaseA<T>: TreePicker {
typealias TreeType = RedBlackTree<T>
func createTree() -> TreeType {
return RedBlackTree<T>()
}
}
struct SomeUseCaseB<T>: TreePicker {
typealias TreeType = BinaryTree<T>
func createTree() -> TreeType {
return BinaryTree<T>()
}
}
class TreeManager<T, Picker: TreePicker> where Picker.TreeType == T {
let tree: T
init(use picker: Picker){
tree = picker.createTree()
}
This introduces another protocol that cares about picking the tree implementation and the where clause specifies that the Picker will return a tree of type T.
I think all of this was just the result of not being able to declare the tree of type Tree<T>. Its a bit more verbose but its basically what it would have had to look like with a generic interface instead. I think I also asked the question poorly. I should have just posted the version that didn't compile and asked for a solution instead of going one step further and confusing everyone.
protocol Foo {
associatedtype F
}
class FooClass : Foo {
typealias F = String
}
class Bar<M:Foo> {
let foo: M
init(){
foo = FooClass() as! M
}
}

Cast T to generic superclass with unknown type

Let's say I have an array of type Foo with FooBar objects in it.
Bar <T> class is a generic class that inherits from Foo.
Then I have multiple FooBar classes, that inherits from Bar<T>, each with different generic type. (Bar<Int>, Bar<String>).
The problem is that I need to walk through the array of type Foo, check if na object is of type Bar<T>, cast it to Bar and set it's property.
Buty Swift won't allow me to cast to unknown type <T> and Bar<Any> doesn't match Bar<Int> or another...
I came up with two possible solutions - add new class to the hierarchy that inherits from Foo and is parent of Bar, but is not generic, and cast to this one or implement a protocol (which seems as much better solution).
What I'm really not sure about is if protocols are meant for this kind of stuff. In C-like lang, I would use an interface probably.
//Edit: Sample code
class Foo {
}
class Bar<T> : Foo {
var delegate : SomeDelegate
}
class FooBar: Bar<Int> {
}
class FooBar2: Bar<String> {
}
let arr : [Foo] = [ FooBar(), FooBar2(), Foo() ]
for item in arr {
// this is the problem - I need to match both String,Int or whathever the type is
if let b = item as? Bar<Any> {
b.delegate = self
}
}

inheritance and polymorphism in swift

I have the following problem:
Class A - super class.
Class A protocol:
has method ->
func test(params: GeneralParams, completionBlock: GeneralCompletionBlock)
GeneralParams is super class and has the following 2 subclasses: BParams, CParams.
now i have 2 more classes:
class B: A, A protocol
class C: A, A protocol
I want class B, C to use test function but with different class for their params for class B i want to use BParams and different completion block and for C the same thing. i want to have the same method for both with different parameters and implementation for both.
Whats the best solution for this situation?
Since Swift allows method overload, the best practice here would be to overload a method to suite your needs in a new class.
For instance:
class B: A{
func test(params: BParams, completionBlock: GeneralCompletionBlock){
...
}
}
class C: A{
func test(params: CParams, completionBlock: GeneralCompletionBlock){
...
}
}
I want class B, C to use test function but with different class for their params
The most important rule of subclassing is substitutability. If B is a kind of A, then everywhere an A can be used, it must be legal to use a B. So every subclass of A must support this method:
func test(params: GeneralParams, completionBlock: GeneralCompletionBlock)
It cannot restrict this method to other types (not even to subtypes of these). If it did, then there would be places that I could use A, but couldn't use B.
B is free to extend A and add new methods. So, as #tmac_balla suggests, you can add an overload that will be selected for subtypes. For example:
class AParam {}
class BParam: AParam {}
class A {
func f(param: AParam) {print("A")}
}
class B: A {
func f(param: BParam) {print("B")}
}
B().f(AParam()) // "A"
B().f(BParam()) // "B"
But B must still support being passed the superclass.
Most of the time in Swift, this is the wrong way to go about things. Subclassing introduces lots of complexity that you usually don't want. Instead you generally want protocols and generics in Swift. For example, rather than A, B, and C, you would just have a generic A and a protocol.
protocol Param {
var name: String { get }
}
struct AParam: Param {
let name = "A"
}
struct BParam: Param {
let name = "B"
}
struct S<P: Param> {
func f(param: P) {print(param.name)}
}
let a = S<AParam>()
a.f(AParam()) // "A"
// a.f(BParam()) // error; we've restricted the type it can take
let b = S<BParam>()
b.f(BParam()) // "B"

Check type of generic class without respecting generic type constraint

I have the following structure:
class A
class A<T: SomeInterface> : C {
}
class B
class B : SomeInterface {
func someFunc(param: C){
// check if param is of class A with `self.dynamicType` generic type
if let typedC = param as? A<self.dynamicType> { // does not work
}
}
}
I would like to check if param has a class of type B as a generic constraint. Is there a way to specify a wildcard generic type ? It is not of importance whether the generic constraint in fact is of type B or not, but this wouldn't be possible either:
class B : SomeInterface {
func someFunc(param: C){
// check if param is of type A
if let typedC = param as? A { // don't check generic constraint type
}
}
}
or (in case there is a wildcard):
class B : SomeInterface {
func someFunc(param: C){
// check if param is of type A with wildcard (Is there such a thing as a wildcard?)
if let typedC = param as? A<?> { // don't require specific generic type, just enter if param is of type A
}
}
}
EDIT - Playground sample
import UIKit
protocol SomeInterface{
func someFunc(param: C)
}
class C {
}
class A<T: SomeInterface> : C{
}
class B : SomeInterface {
func someFunc(param: C) {
// A is a subclass of C, but I want the typed generic subclass
if let typed = param as? A<self.dynamicType> {
}
}
}
let b = B()
let a = A<B>()
b.someFunc(a)
It is hard to express how complicated the world gets once you start allowing non-final subclasses. If you can possibly make these final classes or protocols, many little sharp edges will go away.
What you say you want and the code you're writing don't really line up (which is very common when dealing with subclasses). So it matters what you really want.
I would like to check if param has a class of type B as a generic constraint.
Sure. That's easy.
class B : SomeInterface {
func someFunc(param: C) {
if let typed = param as? A<B> {
print(typed)
} else {
print("NO")
}
}
}
This will work anytime we get an A<B>. It will not work if we receive A<SubClassOfB>, even if that's passed to SubclassOfB.someFunc(). (See what I meant about it being subtle?)
From your code, you seem to actually want "if I'm passed A<Self>" where "Self" means "exactly my class, not a superclass or subclass." The only way I've ever seen that done is with a protocol extension:
extension SomeInterface {
func someFunc(param: C) {
if let typed = param as? A<Self> {
print(typed)
} else {
print("NO")
}
}
}
This allows Self to be replaced with "my class." But note that this means someFunc(A<MySubclass>) won't work. A<Self> is not the same thing as A<Subclass>. Note that you can add a where clause to the extension to limit its scope:
extension SomeInterface where Self: B { ... }
If you mean "T is either Self or some subclass of Self", maybe it's possible, but in my experience it gets more and more tricky and fragile. You can't make generic types covariant on their parameters in Swift, so A<Subclass> is not itself a subtype of A<Superclass> (that's a much trickier problem than it sounds like, and you wouldn't always want it to be true). This is the kind of crazy corner case that goes away if you'll just mark B as final. Then most of the rat's nest goes away.
When you define:
class A<T: SomeInterface>{
}
you are not defining a class, and so a type, but a generic type A, that must be instantiated to become some type.
So, you cannot define inside another class a function in this way:
func someFunc(paramA: A)
This will give always a type error. Instead you should define:
func someFunc(paramA: A<SomeTypeSubtypeOfSomeInterface>)
For instance, since you have defined B as subtype of SomeInterface, you could define:
func someFunc(paramA: A<B>)

Swift generics complex inheritance

The following Swift code does not compile:
class Entity {
}
class EntityConverter<T: Entity> {
}
class GetEntityServerAction<T: Entity, C: EntityConverter<T>> {
}
class GetEntityListServerAction<T: Entity, C: EntityConverter<T>>: GetEntityServerAction<T, C> {
}
with the error:
Type 'C' does not inherit from 'EntityConverter<T>'
for the GetEntityListServerAction class definition.
For some reasons the compiler doesn't see that C parameter is defined like inheriting exactly the same type it wants.
The code should look rather simple for those who are used to complicated generic hierarchies in Java or C# but the Swift compiler doesn't like something indeed.
You might find that using protocols and associated types is the more Swift-like way of doing things:
class Entity { }
protocol EntityConversionType {
// guessing when you say conversion, you mean from something?
typealias FromEntity: Entity
}
class EntityConverter<FromType: Entity>: EntityConversionType {
typealias FromEntity = FromType
}
class GetEntityServerAction<T: Entity, C: EntityConversionType where C.FromEntity == T> { }
class GetEntityListServerAction<T: Entity, C: EntityConversionType where C.FromEntity == T>: GetEntityServerAction<T, C> { }
let x = GetEntityListServerAction<Entity, EntityConverter<Entity>>()
Possibly GetEntityServerAction can also be represented by just a protocol, and that you could convert Entity, EntityConverter and GetEntityListServerAction to structs, also.