Reputation: 55665
I would like to replace my global string constants with a nested enum for the keys I'm using to access columns in a database.
The structure is as follows:
enum DatabaseKeys {
enum User: String {
case Table = "User"
case Username = "username"
...
}
...
}
Each table in the database is an inner enum, with the name of the table being the enum's title. The first case in each enum will be the name of the table, and the following cases are the columns in its table.
To use this, it's pretty simple:
myUser[DatabaseKeys.User.Username.rawValue] = "Johnny"
But I will be using these enums a lot. Having to append .rawValue
to every instance will be a pain, and it's not as readable as I'd like it to be. How can I access the String value without having to use rawValue
? It'd be great if I can do this:
myUser[DatabaseKeys.User.Username] = "Johnny"
Note that I'm using Swift 2. If there's an even better way to accomplish this I'd love to hear it!
Upvotes: 36
Views: 14246
Reputation: 3038
You can use callAsFunction (New in Swift 5.2) on your enum that conforms to String.
enum KeychainKey: String {
case userId
case email
}
func callAsFunction() -> String {
return self.rawValue
}
usage:
KeychainKey.userId()
Upvotes: 15
Reputation: 1169
You can do this with custom class:
enum Names: String {
case something, thing
}
class CustomData {
subscript(key: Names) -> Any? {
get {
return self.customData[key.rawValue]
}
set(newValue) {
self.customData[key.rawValue] = newValue
}
}
private var customData = [String: Any]()
}
...
let cData = CustomData()
cData[Names.thing] = 56
Edit:
I found an another solution, that working with Swift 3:
enum CustomKey: String {
case one, two, three
}
extension Dictionary where Key: ExpressibleByStringLiteral {
subscript(key: CustomKey) -> Value? {
get {
return self[key.rawValue as! Key]
}
set {
self[key.rawValue as! Key] = newValue
}
}
}
var dict: [String: Any] = [:]
dict[CustomKey.one] = 1
dict["two"] = true
dict[.three] = 3
print(dict["one"]!)
print(dict[CustomKey.two]!)
print(dict[.three]!)
Upvotes: 5
Reputation: 13243
If you are able to use User
as dictionary key instead of String
(User
is Hashable
by default) it would be a solution.
If not you should use yours with a nested struct and static variables/constants.
Upvotes: 0
Reputation: 55665
While I didn't find a way to do this using the desired syntax with enums, this is possible using structs.
struct DatabaseKeys {
struct User {
static let identifier = "User"
static let Username = "username"
}
}
To use:
myUser[DatabaseKeys.User.Username] = "Johnny"
Apple uses structs like this for storyboard and row type identifiers in the WatchKit templates.
Upvotes: 38
Reputation: 21134
You can use CustomStringConvertible protocol for this.
From documentation,
String(instance) will work for an instance of any type, returning its description if the instance happens to be CustomStringConvertible. Using CustomStringConvertible as a generic constraint, or accessing a conforming type's description directly, is therefore discouraged.
So, if you conform to this protocol and return your rawValue through the description method, you will be able to use String(Table.User) to get the value.
enum User: String, CustomStringConvertible {
case Table = "User"
case Username = "username"
var description: String {
return self.rawValue
}
}
var myUser = [String: String]()
myUser[String(DatabaseKeys.User.Username)] = "Johnny"
print(myUser) // ["username": "Johnny"]
Upvotes: 19