Is it possible to obtain a Swift type from a string? - swift

I wonder if it's possible to obtain a Swift type dynamically. For example, say we have the following nested structs:
struct Constants {
struct BlockA {
static let kFirstConstantA = "firstConstantA"
static let kSecondConstantA = "secondConstantA"
}
struct BlockB {
static let kFirstConstantB = "firstConstantB"
static let kSecondConstantB = "secondConstantB"
}
struct BlockC {
static let kFirstConstantC = "firstConstantBC"
static let kSecondConstantC = "secondConstantC"
}
}
It's possible to get value from kSeconConstantC from a variable). Like:
let variableString = "BlockC"
let constantValue = Constants.variableString.kSecondConstantC
Something akin to NSClassFromString, maybe?

No, it's not possible yet (as a language feature, at least).
What you need is your own type registry. Even with a type registry, you wouldn't be able to get static constants unless you had a protocol for that:
var typeRegistry: [String: Any.Type] = [:]
func indexType(type: Any.Type)
{
typeRegistry[String(type)] = type
}
protocol Foo
{
static var bar: String { get set }
}
struct X: Foo
{
static var bar: String = "x-bar"
}
struct Y: Foo
{
static var bar: String = "y-bar"
}
indexType(X)
indexType(Y)
typeRegistry // ["X": X.Type, "Y": Y.Type]
(typeRegistry["X"] as! Foo.Type).bar // "x-bar"
(typeRegistry["Y"] as! Foo.Type).bar // "y-bar"
A type registry is something that registers your types using a custom Hashable type (say a String or an Int). You can then use this type registry to reference registered types using custom identifiers (a String, in this case).
Since Any.Type by itself isn't all that useful, I constructed an interface Foo through which I could access a static constant bar. Because I know that X.Type and Y.Type both conform to Foo.Type, I forced a cast and read the bar property.

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

How to create an Single Class coexist with Generic in Swift

I have a Single Class like this:
class Single {
static let sharedInstance: Single = Single()
...
}
But I want use Generic in this Class like this:
class Single<T: Hashable> {
static let sharedInstance: Single = Single()
var dic: [T: Any] = [:] // Here use the Generic
}
I got this result from Xcode
Static stored properties not supported in generic types
I have search this error in stackoverflow, but all the answer for this is not suit for me. Like this one(How to define static constant in a generic class in swift?)
How can I solve this?
You can declare a generic type using a static computed property as follows:
class Single<T: Hashable> {
static var sharedInstance: Single? {
if self.sharedInstance != nil {
return self.sharedInstance
} else {
return Single()
}
}
var dic: [T: Any] = [:]
}
I take it you simply want the one singleton to be able to store any hashable key in your dictionary? If so, do the following:
class Single {
static let sharedInstance: Single = Single()
var dic: [AnyHashable : Any] = [:]
}
Single.sharedInstance.dic["Grimxn"] = 1
Single.sharedInstance.dic[1] = "Grimxn"
Single.sharedInstance.dic // [1: "Grimxn", "Grimxn": 1] as required

Swift Unable to access static variables in class using type(of: Instance)

I have multiple classes with the same static variable. I get the currentInstance of each class at runtime(AnyObject?). Then I am trying to access static variables by getting a class from instance using type(of: instance) method. But when trying to get the static variable it throws me an error - Value of type 'AnyObject.Type' has no member . Here is pseudo code.
public extension Reader {
open static var funcDictionary = [String: readerFuncs]() //ReaderFuncs is an enum of functions
}
public extension Library {
open static var funcDictionary = [String: libraryFuncs]()
}
public extension ReaderMenu {
open static var funcDictionary = [String: readerMenuFuncs]()
}
import Foundation
open class TestClass: NSObject {
open func executeFunction(currentInstance: AnyObject) { // I get current instance at runtime. And I have checked that I get the correct value
var count = type(of: currentInstance).functionDictionary.count // Error: Value of type 'AnyObject.Type' has no member funcDictionary
}
I would like to know how to access static variables when you only have the instance of the class available. I have used .classforCoder() too but it doesn't work. All the files have the same target membership too.
You should use generic types in your TestClass executeFunction method, but we need a common reference to all three of your classes Reader, Library and ReaderMenu. To do that, we will create a protocol that each class has to conform to.
protocol Funcable {
associatedtype FuncsEnum
static var funcDictionary: [String:FuncsEnum] { get set }
}
Now each class inherits from Funcable, they are all required to have a typealias that defines the type of enum in the funcDictionary.
class Reader: Funcable {
typealias FuncsEnum = Reader.Funcs
static var funcDictionary = [String:FuncsEnum]()
enum Funcs {
case A
case B
}
}
class Library: Funcable {
typealias FuncsEnum = Library.Funcs
static var funcDictionary = [String:FuncsEnum]()
enum Funcs {
case C
case D
}
}
class ReaderMenu: Funcable {
typealias FuncsEnum = ReaderMenu.Funcs
static var funcDictionary = [String:FuncsEnum]()
enum Funcs {
case E
case F
}
}
You can define your enums outside of the classes if you like, but I've moved them inside to make it more reusable. Anyway back at the TestClass, we can use Generic Type Token T which is a mirror of the class of the given currentInstance.
class TestClass: NSObject {
func executeFunction<T: Funcable>(currentInstance: T) {
print(T.funcDictionary.count)
}
}
To access the enums individually e.g. Reader.Funcs instead of readerFuncs
UPDATE
We can just instantiate the currentInstance on our own to make it work.
let testInstance = TestClass()
Reader.funcDictionary.updateValue(.A, forKey: "a")
testInstance.executeFunction(currentInstance: Reader()) // prints 1
Library.funcDictionary.updateValue(.C, forKey: "c")
Library.funcDictionary.updateValue(.D, forKey: "d")
testInstance.executeFunction(currentInstance: Library()) // prints 2

Enforce model unicity - Swift solution?

What is a good approach to ensure that a model representing an element identified by an id doesn't get instantiated twice? Are there some good libraries to do that in Swift? I could not find anything even though I suspect this to be a common problem.
I'd like something like this to happen:
let a = fetchElementWithId(123)
let b = fetchElementWithId(123)
// then a === b
I thought of doing something like
class Weak<T: AnyObject> {
weak var value : T?
init(value: T) {
self.value = value
}
}
class Foo {
private static var cache = [String: Weak<Foo>]()
var id: String
var bar: String
static func initialize(id: String, bar: String) -> Foo {
if let weakExistingModel = Foo.cache[id], let existingModel = weakExistingModel.value {
existingModel.bar = bar
return existingModel
}
return Foo(id: id, bar: bar)
}
private init(id: String, bar: String) {
self.id = id
self.bar = bar
Foo.cache[id] = Weak(value: self)
}
deinit() {
Foo.removeValueForKey(id)
}
}
And then call the static initializer instead of the normal one. But things can get messy if I start to get subclasses that represent the same document etc.
So before I dig into this, I'm wondering what smart people have done about it before! I guess the solution is not particularly swift related (even though for instance in swift you can't override static functions, which could be an issue), but since I'm using swift I'm more interested in a libraries in that language.

What is the correct way to create preset Structs?

In Swift classes we can use a class function to create preset instances. Like the calendar example below:
let calender = NSCalendar.currentCalendar()
Which will have a similar pattern as this :
class SomeClass {
var attribute : String
init(value:String) {
attribute = value
}
class func testClass() -> SomeClass {
return SomeClass(value: "test")
}
}
let test = SomeClass.testClass()
But there are no class functions in structs obviously. Xcode recommends using static instead. This is very close to the singleton pattern.
struct SomeStruct {
var attribute : String
init(value:String) {
attribute = value
}
static var testStruct = SomeStruct(value: "test")
}
Singleton pattern
class Singleton {
static let shared = Singleton()
private init() {
}
}
So is this an ok way to init a struct with preset values since structs are value types. If it is not ok, what is the correct way?
The equivalent of class func for struct types is static func:
static func testStruct() -> SomeStruct {
return SomeStruct(value: "foo")
}
and a static property (the "singleton-pattern") works identically
with both class and struct types:
static let singleStruct = SomeStruct(value: "foo")
testStruct() creates a value on each call, whereas singleStruct
creates the value once (on the first call).
In most cases that would make no difference because structures are
value types. The static property has advantages if creating the
value is "expensive". Also, as #Lance noticed in a comment,
this pattern is used by Apple frequently, such as CGRect.zero.
However, it makes a difference if the struct has properties which
are reference types (or pointers to unmanaged memory). Here is an example:
class MyClass {
var attribute : String
init(value : String) {
attribute = value
}
}
struct SomeStruct {
var ptr : MyClass
init(value : String) {
ptr = MyClass(value: value)
}
static func testStruct() -> SomeStruct {
return SomeStruct(value: "foo")
}
static let singleStruct = SomeStruct(value: "foo")
}
Using the static function:
let test1 = SomeStruct.testStruct()
print(test1.ptr.attribute) // foo
let test2 = SomeStruct.testStruct()
test2.ptr.attribute = "bar"
print(test1.ptr.attribute) // foo
Here test1 and test2 are separate values and we get the expected
output.
Using the static property:
let test1 = SomeStruct.singleStruct
print(test1.ptr.attribute) // foo
let test2 = SomeStruct.singleStruct
test2.ptr.attribute = "bar"
print(test1.ptr.attribute) // bar <--- What?
Here, test1 and test2 are set to the same value returned from
the static property. Changing test2.ptr does not mutate test2,
resulting in the somewhat unexpected output for test1.ptr.attribute
See Friday Q&A 2015-04-17: Let's Build Swift.Array for an interesting article on how this can be solved.
Btw, static can be used with class types as well, here static
is a shortcut for class final: a type method that cannot be overridden
in a subclass. Since there is no inheritance for struct types it makes
sense that type methods for struct types are written as static.