Creating Swift Properties - swift

I want to create a Swift property within a class or struct. I think the code would be really clean and beautiful if I can make a accessible anywhere within the class and in other classes. The problem I have is I want its type to be dynamic. Is this possible?
Code
class A {
var property: Any
init() {
switch B {
case 1:
property: String = "Hello World"
case 2:
property: Int = 1
}
}
}

Any is a legitimate type. But you are trying to do some sort of type redefinition. I think you are looking for something like this:
public class A {
public var someProperty: Any
init(isString: Bool) {
switch isString {
case true:
someProperty = "Hello World"
case false:
someProperty = 1
}
}
}
let foo = A(isString: true)
print(foo.someProperty)
let bar = A(isString: false)
print(bar.someProperty)
This produces:
Hello World
1
You can check type using is, the type check operator

Related

Can a Swift enum be referenced as a variable?

I am centralizing all my application strings in enums, and these strings are all namespaced to the application feature in which they are used (example below).
When I attempt to store the enum in a variable (like var strings = Strings.Feature.SubFeature) and call it like strings.someStringValue, I get a Expected member name or constructor call after name type error.
Declaration:
enum Strings {
enum Feature {
enum Subfeature {
static var someString: String { "some string".localizedLowerCase }
}
}
}
Callsite:
someLabel.text = Strings.Feature.Subfeature.string
Hoped-for behavior:
var strings = Strings.Feature.Subfeature
someLabel.text = strings.someString
Is it possible to store a reference to the containing enum, such that I will not have to reference the full path every time I use a given string? I would love to know if there are any alternative ways of going about this as well.
Joakim's answer looks like it answers your question, but another option (with potentially lower memory use?) would be using a typealias.
typealias SubFeatureStrings = Strings.Feature.Subfeature
and then
SubFeatureStrings.someString
The typealias could be nested inside the class/struct where you're calling it, to avoid conflicts across your app.
enum Strings {
enum Feature {
enum Subfeature {
static var someString: String { "some string".localizedLowerCase }
static var otherString: String { "some other string".localizedLowerCase }
}
}
}
var strings = Strings.Feature.Subfeature.self
someLabel.text = strings.someString
someLabel.text = strings.someOtherString
An enum can be enum <#name#> : String
enum Foo : String {
case bar = "bar"
}
usage: <#something#>.text = Foo.bar.rawValue
For chained purposes
enum SomeEnum {
enum Foo : String {
case bar = "bar"
}
}
usage: <#something#>.text = SomeEnum.Foo.bar.rawValue
Then you can typealias the chain:
typealias foo = SomeEnum.Foo
<#something#>.text = foo.bar.rawValue

Using an overriden static property during initialization

I would like to create a class with a static property that subclasses can override, which would be used to initialize instances. So far, I've tried to accomplish this like this:
import Cocoa
class A: NSObject {
class var staticProperty: String {
return "A"
}
var property: String = A.staticProperty
}
class B: A {
override class var staticProperty: String {
return "B"
}
}
This does not work, since B().property still returns "A". How could I change this code so that property contains the value specified by the subclass? Any help would be appreciated!
Edit
I would like to initialize property with the value of staticProperty, so this could also look like this:
var property: SomeClass = SomeClass(A.staticProperty)
But then, this initialization should still use "A" for class A, and "B" for class B.
Edit 2 (After #RakeshaShastri's comment)
For my specific use-case, I need property to be stored (so not computed) and non-lazy.
Edit 3
In short, I'm trying to build a Realm model class which has a few to-many relationships to other models. For these models (which are quite similar), I'm trying to create a superclass which contains the shared functionality, amongst which is also the inverse relationship. Therefore, I want to have a static property which contains the key in the first model to either of the other models, and then initialize a LinkingObjects property using this key name. Since Realm does not allow this to be lazy or computed, I cannot use these functionalities here.
If you inherit from NSObject why not using it ?
import Cocoa
class A: NSObject {
var property: String
public override init() {
let str = type(of: self).perform(#selector(getter: type(of: self).staticProperty))?.takeUnretainedValue() as! String
property = str
}
#objc class var staticProperty: String {
return "A"
}
}
class B: A {
override class var staticProperty: String {
return "B"
}
}
You can do this with this aproach
class A {
var prop: String{
return "A"
}
}
class B: A {
override var prop: String{
return "B"
}
}
print(A().prop) // "PRINTS A"
print(B().prop) // "PRINTS B"
A.staticProperty will use static dispatch and will always point to A's class property. You need dynamic dispatch here, aka type(of: self).
However, self needs an instance to work with, thus var property: String = type(of: self.staticProperty won't compile.
However, lazy properties can work around this limitation, so you could declare property as a lazy one:
class A: NSObject {
class var staticProperty: String {
return "A"
}
private(set) lazy var property: String = { type(of: self).staticProperty }()
}
class B: A {
override class var staticProperty: String {
return "B"
}
}
print(B().property) // B
P.S. the private(set) part is just something I usually do, I rarely allow extrinsic factors to change my object.
Update As #MartinR has pointed out, lazy is not a choice for the OP. An alternative solution that doesn't use a lazy var is to use a "shadowing" property:
class A: NSObject {
class var staticProperty: String {
return "A"
}
private var _property: String?
var property: String {
get {
return _property ?? type(of: self).staticProperty
}
set {
_property = newValue
}
}
}
class B: A {
override class var staticProperty: String {
return "B"
}
}
let b = B()
print(b.property) // B
b.property = "B'"
print(b.property) // B'

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

Can I initialize a class with a variable which is defined by a function inside that class in Swift 2?

Is it possible that the variable which initializes my class is computed by a function inside that class?
I first initialize MyClass:
let MyClass = MyClass()
Second, this would be the code in the class:
class MyClass {
var myString = computeRandomString()
func computeRandomString() -> String {
piece of code
return(randomString)
}
}
Whenever I create an instance of MyClass I want the myString to be a randomString. In order for that I made a function within the same class.
There are two options.
First, if computeRandomString does not rely on knowing about anything about specific instances of this class, it could be converted to a class function, or simply moved outside of the class entirely.
For example:
class MyClass {
var myString = MyClass.computeRandomString()
class func computeRandomString() -> String {
return "random string"
}
}
or
func computeRandomString() -> String {
return "random string"
}
class MyClass {
var myString = computeRandomString()
}
The second option is to set it in the initializers (rather than giving it a default value), but you'll only be able to do this after all values (including this one) have been assigned a value.
For example:
class MyClass {
var myString: String
init() {
myString = ""
myString = computeRandomString()
}
func computeRandomString() -> String {
return "random string"
}
}
The reason we can't use an instance method to assign a default value for an instance variable is better explained by the warning generated when we try to use the instance method within an initializer before first giving it a value:
class MyClass {
var myString: String
init() {
myString = computeRandomString()
}
func computeRandomString() -> String {
return "random string"
}
}
On the myString =... line in init, we see the following error:
Use of 'self' in method call 'computeRandomString' before all stored properties are initialized.
This error, unfortunately, does not show up when we use it as the property's default value, as you're trying to do, but it does accurately describe the actual problem.
We cannot use self before our class is fully initialized, and that includes calling methods on self. And until all of our stored properties have valid values, our instance is not fully initialized, so we can never use an instance method to give a non-optional stored property its first value.
A possible solution is a lazy computed property.
The string is created when the property is accessed the first time
class MyClass {
lazy var computeRandomString : String = {
let alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXZY0123456789"
let alphaLength = UInt32(alphabet.characters.count)
var randomString : String = ""
for _ in 0..<20 {
let random = Int(arc4random_uniform(alphaLength))
let index = alphabet.startIndex.advancedBy(random)
randomString += String(alphabet[index])
}
return randomString
}()
}
for _ in 0..<10 {
print(MyClass().computeRandomString)
}
You have to do it like this:
class MyClass {
var myString: String
init() {
self.myString = MyClass.computeRandomString()
}
static func computeRandomString() -> String {
piece of code
return(randomString)
}
}
This will set myString to the results of computeRandomString() on initialisation (creating) of a MyClass-object.
I changed computeRandomString() to static because otherwise it could not be used before initialisation has finished, thanks to #nhgrif.