How to define objects in if-else statements in Swift? - swift

I want to define an object a certain way if a condition is met, or define it as a different object of the condition is not met. However, when I try to use that object later in my code, it doesn't recognize it as being defined.
I want something like this
if condition {
object = Class1()
} else {
object = Class2()
}

You just have to declare your variable earlier:
let object: RootClass
if condition {
object = Class1()
} else {
object = Class2()
}
where RootClass is a type that can represent both Class1 and Class2.
Swift's definitive initialization rules will ensure that object gets exactly one value on every path where it is used.

If for some odd reason your Class1 and Class2 are not related you can do something like this
var object: Any
if condition {
object = "something"
} else {
object = 32
}
and later use it as
if let some = object as? Int {
//do int stuff
} else if let some = object as? String {
//do string stuff
}

there are multiple ways to do it, In Swift Type has to match, so can't assign two different type of classes based on condition for same object, so either use Protocol(more swifty) or Inheritance
1. Use protocols: lets declare a protocol MyProtocol, both Class1 and Class2 both confirms to
protocol MyProtocol { }
class Class1: MyProtocol { }
class Class2: MyProtocol { }
Now, when initialising object:
var object: MyProtocol
if condition {
object = Class1()
} else {
object = Class2()
}
or:
let object = condition ? Class1() : Class2()
2. Use Inheritance
class MainClass() { }
class Class1: MainClass { }
class Class2: MainClass { }
let object = condition ? Class1() : Class2()
3. Use Any type
let object: Any = condition ? Class1() : Class2()
if object is MyClass1 {
} else {
}

You can do this,
let classsObject = condition ? Class1() : Class2()
Here after ? your condtion is right and after : your else part

You can try this
let object = condition ? Class1() : Class2()
You can alternatively declare the object as an optional and var & you can assign a value to it later.

Related

"Use of undeclared type " error when iterating over an array of Type

Given an array containing instances of several SubClasses (all having the same SuperClass), I want to check if all the elements of another array of required subClass Types are represented by the instances.
I came up with what I think is a valid solution, but I'm plagued by the error: Use of undeclared type 'requiredType' in the closure for requiredTypes.allSatisfy { ... }
class MyClass { }
class SubClass1: MyClass { }
class SubSubClass1: SubClass1 { }
class SubClass2: MyClass { }
class SubClass3: MyClass { }
let requiredTypes = [SubClass1.self, SubClass2.self]
let instances = [SubSubClass1(), SubClass2()]
let meetsRequirements = requiredTypes.allSatisfy { (requiredType) -> Bool in
return instances.map({$0 is requiredType}).contains(true)
}
is operator requires a type that is known at compile-time. You cannot use a variable there.
One possibility is to use type(of:), e.g.:
let meetsRequirements = requiredTypes.allSatisfy { requiredType in
instances.contains { type(of: $0) == requiredType }
}
Although that will be a problem with SubSubClass1.
If your classes inherit from NSObject, you can use:
let meetsRequirements = requiredTypes.allSatisfy { requiredType in
instances.contains { $0.isKind(of: requiredType) }
}
Checking whether a class inherits from a type which is stored in a variable is not currently supported in Swift. Usually it's a code smell.

In Swift, is it possible to use a parameter value in a default parameter value?

I wanna do something like that:
func test(parameter1: Class1, parameter2: Class2 = Class2(class1: parameter1)) {
}
Is there any way for doing that in Swift?
Make the second parameter an Optional with a nil default, and do what you are doing in the body of the function if it is in fact nil.
That's not possible, but you can make parameter2 optional and do something like this:
func test(parameter1: Class1, parameter2: Class2? = nil) {
let param2 = parameter2 ?? Class2(class1: parameter1)
}
That's not possible yet
but you can use the optional option as Rob says.
I tried this in a playground:
class Class1 {
var name = ""
}
class Class2 {
var class1:Class1?
init(class1: Class1) {
self.class1 = class1
}
func printNameClass1() {
print(class1?.name ?? "No name")
}
}
func test(parameter1: Class1, parameter2: Class2? = nil ) {
parameter1.name = "Camilo"
let object2 = parameter2 ?? Class2(class1: parameter1)
object2.printNameClass1()
}
let param1 = Class1()
test(parameter1: param1)

Pass a class type for use inside a method

In order to reduce cut-and-paste code in this app, I'm trying to pass class names around in order to tell a method which way it should process some data. I have something like the following:
class MyClass : NSObject {
var name : String = ""
}
class OneClass : MyClass {
override init() {
super.init()
self.name = "One"
}
}
class TwoClass : MyClass {
override init() {
super.init()
self.name = "Two"
}
}
class Thing : NSObject {
func doStuff(withClass cls: AnyClass) -> String {
let x = cls.init()
return x.name
}
}
let z = Thing()
print(z.doStuff(withClass: OneClass))
print(z.doStuff(withClass: TwoClass))
Passing withClass cls: AnyClass the parser pushed me to change let x = cls() to let x = cls.init(). But I've got an Expected member name or constructor call after type name error for the last two lines. The recommended fixes both cause other problems.
The first suggestion, adding the () constructor after the class name, causes new errors on those lines: Cannot convert value of type 'OneClass' to expected argument type 'AnyClass' (aka 'AnyObject.Type')
Taking the second suggestion and changing them to OneClass.self and TwoClass.self gets rid of the parser errors, but when I execute the code it just runs forever.. never erroring out, and never completing.
I found a recommendation elsewhere that suggests I should change the Thing.doStuff() parameters to expect MyClass instead of AnyClass, but that causes another set of new problems.
First, the parser starts complaining about the cls.init() call, and the series of fixes it suggests eventually lead to something that makes no sense: let x = cls.type(of:;; init)(). The parser ends up in a suggestion loop where it keeps adding more semi-colons in the middle of the statement.
Second, I'm back to type mismatch errors on the calls to doStuff() in the last two lines: Cannot convert value of type 'OneClass.Type' to expected argument type 'MyClass'.
There's obviously something I'm not getting here about passing types as arguments, but none of the googling I've done has landed me on something that explains the problems I'm seeing.
How about the generic Swift way.
The code constrains the generic type T to MyClass since it must have a name property.
class MyClass : NSObject {
var name : String
override required init() {
self.name = ""
super.init()
}
}
class OneClass : MyClass {
required init() {
super.init()
self.name = "One"
}
}
class TwoClass : MyClass {
required init() {
super.init()
self.name = "Two"
}
}
class Thing : NSObject {
func doStuff<T : MyClass>(withClass cls: T.Type) -> String {
let x = cls.init()
return x.name
}
}
let z = Thing()
print(z.doStuff(withClass: OneClass.self))
print(z.doStuff(withClass: TwoClass.self))
Or use a protocol.
protocol Nameable {
var name : String { get }
init()
}
class MyClass : NSObject, Nameable { ...
...
class Thing : NSObject {
func doStuff<T : Nameable>(withClass cls: T.Type) -> String {
let x = cls.init()
return x.name
}
}
To get this working, you must call init on cls after typecasting it to NSObject.Type. Also, x.name only works if cls Class type contains that particular property. This is the reason x is then typecasted to MyClass.
class Thing : NSObject
{
func doStuff(withClass cls: AnyClass) -> String?
{
let x = (cls as? NSObject.Type)?.init()
if let x = x as? MyClass
{
return x.name
}
return nil
}
}
Call doStuff with ClassType.self
print(z.doStuff(withClass: OneClass.self))
print(z.doStuff(withClass: TwoClass.self))
Let me know if you still face any issues.

Selecting class depending on variable

I have two classes ClassOne and ClassTwo. I want to initialize a different one depending on a variable value, i want to do something like:
if(a == "0") {
let b = ClassOne();
}else{
let b = ClassTwo();
}
without having to write it everytime I need it. Something like:
let b = MainClass()
and gets called ClassOne() or ClassTwo() depending on the case, a is a global variable.
In order for this to work the two types should be related either by extending a common base class or by implementing the same protocol. Further, subsequent operations on b would be restricted to these the two classes have in common.
If you are fine with that restriction, you can do it like this:
protocol CommonProtocol {
func foo() -> Double
var bar : Int { get }
}
class ClassOne : CommonProtocol {
...
}
class ClassTwo : CommonProtocol {
...
}
func MainClass() -> CommonProtocol {
if(a == "0") {
return ClassOne()
} else {
return ClassTwo()
}
}
...
let b = MainClass()
b.foo()
print(b.bar)
Note: You could forego all of the above in favor of a completely dynamic approach by following matt's advise.
You can do it, but it isn't going to be useful without further effort. Consider the following:
class ClassOne {}
class ClassTwo {}
Now we proceed to initialize a variable as an instance of one of these classes. To do so, we must type the variable as AnyObject:
var which : Bool { return true /* or false */}
let obj : AnyObject
switch which {
case true:
obj = ClassOne()
case false:
obj = ClassTwo()
}
You now have obj as either a ClassOne instance or a ClassTwo instance. But there's a problem. You don't know which it is. The AnyObject typing preserves the real underlying type (polymorphism), but it also hides the type. Every time you use obj, you will have to test whether it is a ClassOne or a ClassTwo and cast it to that type in order to use it.
if obj is ClassOne {
(obj as! ClassOne).doSomethingClassOneKnowsHowToDo()
}
The question is: is the pain worth the gain? I would suggest that your desire to do this in the first place is probably a Bad Smell and you should revise your intended architecture. Strict static typing is the point of Swift; you are wrong to want to throw it away.
You could use the ternary operator to do it quickly, however you do need to do it every time:
let b = (a == 0) ? ClassOne() : ClassTwo() //If a==0 let b = ClassOne if not let b= ClassTwo.
#dasblinkenlight solution is great but you can also do like this
protocol MyProto {
var x: Int { get }
}
class A: MyProto {
var x = 10
var y = 10
}
class B: MyProto {
var x = 20
}
class Demo {
var type: MyProto!
init(str: String) {
if str == "0" {
type = A()
} else {
type = B()
}
}
}
....
let obj = Demo(str: "0").type
print(obj.x)

Get object type from optional?

Is it possible to get the object type from an optional?
For example, if I have a class that has a property that is an optional string, can I somehow just get back the string type?
The exact use case I have is I have many custom classes all of which have a property that is storing another custom class as an optional value. I would like to write a generic function that will create an instance of the object class stored in the optional.
Here is an example of what I am looking for, although .dynamicType does not work since it is an optional:
class Class1 {
}
class Class2 {
var myOp: Class1?
}
var c = Class2()
c.myOp = c.myOp.dynamicType()
Since you wanted to use this with Generics I tried it for you. It works, but it may not be so useful.
First some setup:
This is a helper protocol to make sure our Generic type will have a known init method.
protocol ZeroParameterInit {
init()
}
This is an extension to get the type from an optional:
extension Optional {
var dynamicWrappedType : Wrapped.Type {
return Wrapped.self
}
}
Implemented in your code:
class Class1 : ZeroParameterInit {
required init() {}
}
class Class2 {
var myOp: Class1?
}
var c = Class2()
c.myOp = c.myOp.dynamicWrappedType.init()
Generic implementation:
class Class1 : ZeroParameterInit {
required init() {}
}
class Class2<T where T : ZeroParameterInit> {
var attribute: Optional<T>// used long syntax to remind you of : Optional<Wrapped>
init(attr:T) {
attribute = attr
attribute = nil
}
}
The function to create the instance:
func myFunc<T>(instance: Class2<T>) -> T {
return instance.attribute.dynamicWrappedType.init()
}
Some tests:
let alpha = Class1()
let beta = Class2(attr: alpha)
beta.attribute = myFunc(beta)
The issue:
You can't create an instance of Class2 without informing it about the type of it's generic attribute. So you need to pass it some object/type and that complicates things again.
Some extra methods that might improve how it all works:
init() {
}
let delta = Class2<Class1>()
delta.attribute = myFunc(delta)
init(type:T.Type) {
}
let epsilon = Class2(type: Class1.self)
epsilon.attribute = myFunc(epsilon)
You just need to check if the optional exist:
func myFunc(c: Class2) -> Class1? {
if let c1 = c.myOp{
return c1.dynamicType()
}
return nil
}
OR
func myFunc(c: Class2) -> Class1? {
if c.myOp != nil{
return c.myOp!.dynamicType()
}
return nil
}
Note the your return type need to be optional as well.
Tried this in simulator, seems like doing the right thing, if I understood you
class Class1 {
}
class Class2 {
var myOp: Class1?
}
func myFunc(c: Class2) -> AnyObject {
if let c1 = c.myOp{
return c1.self
}
return c
}
var object = Class2()
object.myOp = Class1()
myFunc(object) // Class1