How can i pass class as a parameter to a function in Swift? - swift

Let us consider i have two different classes.
class A {
var something = "Hello"
}
class B {
var something = "World"
}
Now
class C {
func request() {
//Call with class A or B it can contain any class. I can call either class A or B depending on condition
update(myClass: A or B)
}
func update(myClass:A or B ) {
print(myClass.something) //Since both class have same varaible var something so this code should work either i pass class A or B through function
}
}
Plz help me achieve this using Swift

You cannot declare a function in Swift that could accept an input argument of several different types, so you cannot declare a type as A or B. However, you don't actually need this to solve your specific problem.
Since you want to access a common property of the two class instances, you should declare that property in a protocol, make both classes conform to that protocol, then make the function take an input argument of the protocol type.
protocol SomethingProtocol {
var something: String { get }
}
class A: SomethingProtocol {
let something = "Hello"
}
class B: SomethingProtocol {
let something = "World"
}
class C {
func request() {
//Call with class A or B it can contain any class. I can call either class A or B depending on condition
update(something: A())
update(something: B())
}
func update(something: SomethingProtocol) {
print(something.something) //Since both class have same varaible var something so this code should work either i pass class A or B through function
}
}

Use a protocol
protocol MyProtocol: class {
var something: String { get set }
}
class A: MyProtocol {
var something = "Hello"
}
class B: MyProtocol {
var something = "world"
}
class C {
func update(myClass:MyProtocol ) {
print(myClass.something) //Since both class have same varaible var something so this code should work either i pass class A or B through function
}
}
usage:
let a = A()
let b = B()
let c = C()
print(c.update(myClass: a))
print(c.update(myClass: b))
Output:
hello
world

Create a protocol that both A and B conforms to and use it as the parameter type in update()
protocol SomeProtocol {
var something: String {get set}
}
func update(_ o: SomeProtocol) {
print(o.something)
}

Let it be known that I think using a protocol is the cleanest option that will best solve your problem.
However, it is possible to use Any to pass any object as a parameter, this will require checking which class you are dealing with inside your update method.
Something like this...
class C {
func update(myClass: Any) {
if let a = myClass as? A {
print(a.something)
}
if let b = myClass as? B {
print(b.something)
}
}
}
This might be neater as a switch - ref
class C {
func update(myClass: Any) {
switch myClass {
case let a as A:
print(a.something)
case let b as B:
print(b.something)
default:
print("not a thing")
}
}
}

Related

Not Able to access values with .Type swift

Why am I not able to assign and read value from Type B in below code? B.self should be passed as a type and not an instance, so it should access static var in class B right?
class A{
}
class B:A{
static var a = 5
}
class c{
static func a(){
b(type: B.self)
}
static func b(type:B.Type){
print(type.a)
}
func takeObject<T>(type:T.Type){
print(type(of:String.self)) // String.Type
print(type) // B
print(type.a) // Value of type 'T' has no member 'a'
var a :type // Use of undeclared type 'type'
}
}
let objects : c = c()
objects.takeObject(object: B.self)
Correct me please, I am new on this topic and it seems quite interesting.
As i think you just want to add objects of type B so you can specify generic T of type B as below,
class A {}
class B: A {
static var a = 5
}
class c {
static func a() {
b(type: B.self)
}
static func b(type: B.Type){
print(type.a)
}
func takeObject<T: B>(type: T.Type){
print(type)
print(type.a)
var a : T
}
}
let objects : c = c()
objects.takeObject(type: B.self)

Why can't I declare generic protocol type in a similar way to generic class in Swift?

In Swift, this will give me compile error
class TestType {
}
protocol TestProtocol {
associatedtypes T: TestType
}
class TestClass<T: TestType> {
var x: TestProtocol<T>
}
It will give me a compiler error because TestProtocol "can only be used as a generic constraint". The correct way to do this which is much less clean (because it requires adding a generic parameter of TestProtocol everywhere when it is used)
class TestClass<T: TestProtocol> {
var x: T
}
So my question is, why doesn't Swift allow referring to a generic protocol simply as TestProtocol<T> when T is already a typed parameter as in the above example ?
There is a little "hack" to almost get what you want: You'll have to specify the alias in the where-clause of the template parameter list:
class TestType {
var value:Int = 0
func doTest() { print ("doing the test \(value)") }
}
protocol TestProtocol {
associatedtype T: TestType
func doProtocolForTest (t:T)
}
class TestClass<TT: TestType, TP:TestProtocol> where TP.T == TT {
var x:TP!
}
// the following demonstrats a sample usage, without much sense
// ------------------------------------------------------------
class TestProtocolImp : TestProtocol {
func doProtocolForTest(t: TestType) {
print ("running test...")
t.doTest()
}
}
let test = TestType()
test.value = 42
let tc = TestClass<TestType, TestProtocolImp>()
tc.x = TestProtocolImp()
tc.x.doProtocolForTest(t: test)

How to avoid using A.self?

Take this code:
protocol P: class {
static var hello: String { get }
}
class A: P {
class var hello: String {
return "Hello"
}
}
class B: A {
override static var hello: String {
return "Hello World"
}
}
class C: A {}
class D: C {
override static var hello: String {
return "Hello D"
}
}
func sayHello(elements: P.Type...) {
for p in elements {
print(p.hello)
}
}
func sayHelloAgain(elements: A.Type...) {
for p in elements {
print(p.hello)
}
}
func sayHelloThe3rd(elements: [A.Type]) {
for p in elements {
print(p.hello)
}
}
sayHello(A.self, B.self, C.self)
sayHelloAgain(A.self, B.self, C.self)
Compare it to this (taken from this presentation)
func register<T: UITableViewCell where T: ReusableView, T: NibLoadableView>(_: T.Type) { ... }
tableView.register(FoodTableViewCell)
Why do I have to use A.self in one case, but not in the other?
And also, don't need to use .self when calling with one argument.
sayHello(A)
sayHello(A, B) //doesn't compile
The .self is syntactic salt. It's not necessary from a technical perspective, but it exists to cause errors in code that's often a result of a typo, such as:
struct Foo { }
let foo = Foo
This code will give you a compiler error, telling you that either you need to complete the initializer/function/method call, or append .self if you meant to refer to the type.
In the latter example, the context deals exclusively with types and not values, so there's no chance of confusing one with the other, thus, .self isn't necessary.
Perhaps there's a way to modify the function declaration in your example so as to not require the .self, but I'm not aware of such a feature. I'd be interested to find out.

Change type of generic method in generic class

Please consider the following classes:
// Models:
class A {}
class B: A { }
// Parsers:
class AbstractParser<T> {}
class ParserB<T: B>: AbstractParser<T> {}
// Services:
class AbstractService<T> {
func parser() -> AbstractParser<T> {
fatalError("This method must be overridden")
}
}
class ServiceA<T: A>: AbstractService<T> {
}
class ServiceB<T: B>: ServiceA<T> {
private let _parser = ParserB()
override func parser() -> ParserB<B> {
return _parser
}
}
I'm getting an error Method doesn not override any method from it's superclasses at overriden parser function. I could easily fix this by changing
class ServiceB<T: B>: ServiceA<T>
to
class ServiceB<T: B>: ServiceA<B>
but this will break a solution from this question: A variable in generic class gets wrong type
Is there any workaround for this?
EDIT
Thanks, Kenneth Bruno, your approach works, but it again leads to another error with types.
I add class C:
class C {
var item = B()
}
and a simple method to ServiceB:
func doSomething() {
var entities = [T]()
let c = C()
entities.append(c.item)
}
This causes error: Cannot invoke 'append' method with an argument list of type '(B)'. It seems the compiler can't understand that B and T are the same thing?
Also please note that I can't define var entities = [B](), as I need to pass this array to another function in AbstractService method.
Just as in your other question you need to use the generic type instead of a specific type, then the method signatures will match to override the function.
class ServiceB<T: B>: ServiceA<T> {
private let _parser = ParserB<T>()
override func parser() -> ParserB<T> {
return _parser
}
}
From the question edit:
This causes error: Cannot invoke 'append' method with an argument list of type '(B)'. It seems the compiler can't understand that B and T are the same thing?
Just to clarify things. In the edit code example <T: B> and B are not the same thing. B is a regular type, while <T: B> is a generic type, which may represent a B type or any of it's subtypes.
Merging the question code with the code proposed by #Kenneth results in the following, which leads to a type error
class C {
var item = B()
}
class ServiceB<T: B>: ServiceA<T> {
private let _parser = ParserB<T>()
override func parser() -> ParserB<T> {
return _parser
}
func doSomething() {
var entities = [T]()
let c = C()
entities.append(c.item) // Error: Cannot invoke 'append' method with an argument list of type '(B)'
}
}
Now let's say in the future we add a new type D, subtype of B and instantiate a ServiceB<D>. This would cause the function doSomething() to try to append an instance of B in an array of D which is illegal, that's why the compiler raises an error.
With the code proposed in the comments by #Kenneth, the entities array would be filled in the ServiceB<B> case, but would always be empty in the ServiceB<D>.
class D: B { }
class ServiceB<T: B>: ServiceA<T> {
...
func doSomething() {
var entities = [T]()
let c = C()
if let item = c.item as? T { entities.append(item) }
}
}
let service = ServiceB<B>()
service.doSomething() // Creates an array of B and append a single B instance on it
let serviceD = ServiceB<D>()
serviceD.doSomething() // Creates an array of D, c.item of type B can't be cast to D, the array will be empty
While my answer doesn't really solves your problem, I think it should put you one step closer to a solution.

How can I override superclass property with different type

As title.
I know. I can use [AnyObject] instead of type variable, But I just want to prevent a lots of type checking in swift.
And now, I don't have any idea about this problem. Does anyone can help me. Thanks
class TypeA: NSObject {
var name: String
........
}
class TypeB: TypeA {
.........
}
class ObjectA {
var type = [TypeA]()
}
class ObjectB: ObjectA {
override var type = [TypeB]() <---Cannot override a property "type"
}
Question update: Thanks guys and reference to "R Menke"'s code
class TypeA: NSObject {
override init() {
print("typeA")
}
}
class TypeB: TypeA {
override init() {
print("typeB")
}
}
class ObjectA<T:TypeA> {
var type = [T]()
init(type:T) {
self.type.append(type)
print("objectA")
}
func addNewType() {
let newType = TypeA()
self.type.append(newType) <-- compiler complaints at here
}
}
class ObjectB<T:TypeA>: ObjectA<T> {
override init(type:T) {
super.init(type: type)
print("objectB")
}
}
You can make ObjectA and ObjectB generic classes.
class ObjectA<T:TypeA> {
var type = [T]()
Now the type attribute will always be a TypeA or a subclass of TypeA.
But you no longer need to downcast to find out what type it is.
class TypeA: NSObject {
override init() {
print("typeA")
}
}
class TypeB: TypeA {
override init() {
print("typeB")
}
}
class ObjectA<T:TypeA> {
var type = [T]()
init(type:T) {
self.type.append(type)
print("objectA")
}
}
class ObjectB<T:TypeA>: ObjectA<T> {
override init(type:T) {
super.init(type: type)
print("objectB")
}
}
let tA = TypeA()
let tB = TypeB()
let oA = ObjectA(type: tA) // ObjectA<TypeA>
let oB = ObjectB(type: tB) // ObjectB<TypeB>
Or just make ObjectA generic :
ObjectB will now work as a specialiser for ObjectA, it is not generic itself, but does put a constraint on it's superclass. Now you can use it like any other class but it's type property will have TypeB as type instead of TypeA
class ObjectA<T:TypeA> {
var type = [T]()
init(type:T) {
self.type.append(type)
print("objectA")
}
}
class ObjectB: ObjectA<TypeB> {
override init(type:TypeB) {
super.init(type: type)
print("objectB")
}
}
#RMenke is completely correct here, but it's also important to understand why it's true. This isn't a limitation of Swift. This is a fundamental issue of what subclasses mean.
Let's pretend for a moment that you could do what you're asking for. If that were true, the following code would be legal:
let objA: ObjectA = ObjectB()
objA.type.append(ObjectA())
I have to be allowed to treat ObjectB as if it were an ObjectA. That's what the Liskov Substitution Principle means for subtyping. So now I'm allowed to add an ObjectA to an [ObjectB]. That breaks the whole type system. Blam.
Generally when you want this, it's because you're misusing subclasses. Subclasses are seldom the right tool in pure Swift; they mostly creep in because of bridging to ObjC. But even in ObjC, subclassing should be avoided when possible. Swift brings us excellent new tools to help avoid subclasses, and you should prefer them. For example, rather than subclassing, just use protocols:
protocol A {}
struct TypeA: A {}
struct TypeB: A {}
class Object<T: A> {
var type = [T]()
}
You are not allowed to override a stored property in swift. However, you are allowed to change the value of an inherited property while initialising an Instance.
Example:
class TypeA: NSObject {
}
class TypeB: TypeA {
}
class ObjectA {
var type = [TypeA]()
}
class ObjectB: ObjectA {
override init() {
super.init()
type = [TypeB]()
}
}
Another option, would be to use one Class within the other instead of using inheritance. Something like this:
class TypeA: NSObject {
}
class TypeB: TypeA {
}
class ObjectA {
var type = [TypeA]()
}
class ObjectB: NSObject {
private var obj = ObjectA()
var type: [TypeB] {
get{
return obj.type as! [TypeB]
}
set {
obj.type = newValue
}
}
}
var obj1 = ObjectB()
obj1.type = [TypeB()]
let test: TypeB = obj1.type[0]