I need 4 file objects to upload. Few files have date, few files have image. What is the data structure should I use?
I tried to create an enum
enum File: String, CaseIterable {
case a = "A"
case b = "B"
case c = "C"
case d = "D"
var size: String//enum cannot contain stored properties
}
I tried to create a struct
struct File {
var name: String//for 4 files
var size: String//only for A and B
var image: UIImage// only for C and D
}
This could help you-
enum File: String, CaseIterable {
case a = "A"
case b = "B"
case c = "C"
case d = "D"
static let sizeMapper: [File: String] = [
.a: "Size A",
.b: "Size B"
]
static let nameMapper: [File: String] = [
.a: "Name A",
.b: "Name B",
.c: "Name C",
.d: "Name D"
]
static let imageMapper: [File: UIImage] = [
.c: UIImage(),
.d: UIImage()
]
var size: String {
return File.sizeMapper[self] ?? ""
}
var name: String {
return File.nameMapper[self] ?? ""
}
var image: UIImage {
return File.imageMapper[self] ?? UIImage()
}
}
call it like this-
let size = File.a.size
print(size)
let name = File.a.name
print(name)
let image = File.c.image
print(image)
The error says it all:
enum cannot contain stored properties
Ideally you're the one to provide values to your enum's properties. Meaning they should be read-only. You can however make a new instance of your enum.
Here's an example:
enum File: String, CaseIterable {
case a = "A"
case b = "B"
case c = "C"
case d = "D"
var size: String {
switch self {
case .a: return "Some size for A"
case .b: return "Some size for B"
case .c: return "Some size for C"
case .d: return "Some size for D"
}
}
}
let fileA = File(rawValue: "A")
let fileB = File(rawValue: "B")
let size = fileA.size //Some size for A
let size = fileB.size //Some size for B
If you answer is "How to create an enum with extra properties", then you can write something like this:
enum File {
case containingDate(fileName: String, date: Date)
case containingImage(fileName: String, image: UIImage)
}
Also, note that enum with raw types (in your case it is String) can't contain extra parameters.
PS: to be honest, I don't see any advantages of using enums in your situation
You should use both enum and struct here:
// Enum with associated values for differences
enum FileType {
case withDate(date: Date)
case withImage(image: UIImage)
}
// Struct to bind common
struct File {
var name: String
var fileType: FileType
}
And then you can have your files defined:
extension File {
// Files with date
static let a = File(name: "a", fileType: .withDate(date: Date()))
static let b = File(name: "b", fileType: .withDate(date: Date()))
// Files with image
static let c = File(name: "c", fileType: .withImage(image: UIImage()))
static let d = File(name: "d", fileType: .withImage(image: UIImage()))
}
And for simple way to access date and image you can declare those handy extensions:
extension FileType {
var date: Date? {
if case let .withDate(date) = self {
return date
} else {
return nil
}
}
var image: UIImage? {
if case let .withImage(image) = self {
return image
} else {
return nil
}
}
}
extension File {
var date: Date? {
return fileType.date
}
var image: UIImage? {
return fileType.image
}
}
And then use it like this:
let date = File.a.date
Related
I need your help to implement a custom JSON decoding. The JSON returned by the API is:
{
"zones": [
{
"name": "zoneA",
"blocks": [
// an array of objects of type ElementA
]
},
{
"name": "zoneB",
"blocks": [
// an array of objects of type ElementB
]
},
{
"name": "zoneC",
"blocks": [
// an array of objects of type ElementC
]
},
{
"name": "zoneD",
"blocks": [
// an array of objects of type ElementD
]
}
]
}
I don't want to parse this JSON as an array of zones with no meaning. I'd like to produce a model with an array for every specific type of block, like this:
struct Root {
let elementsA: [ElementA]
let elementsB: [ElementB]
let elementsC: [ElementC]
let elementsD: [ElementD]
}
How can I implement the Decodable protocol (by using init(from decoder:)) to follow this logic? Thank you.
This is a solution with nested containers. With the given (simplified but valid) JSON string
let jsonString = """
{
"zones": [
{
"name": "zoneA",
"blocks": [
{"name": "Foo"}
]
},
{
"name": "zoneB",
"blocks": [
{"street":"Broadway", "city":"New York"}
]
},
{
"name": "zoneC",
"blocks": [
{"email": "foo#bar.com"}
]
},
{
"name": "zoneD",
"blocks": [
{"phone": "555-01234"}
]
}
]
}
"""
and the corresponding element structs
struct ElementA : Decodable { let name: String }
struct ElementB : Decodable { let street, city: String }
struct ElementC : Decodable { let email: String }
struct ElementD : Decodable { let phone: String }
first decode the zones as nestedUnkeyedContainer then iterate the array and decode first the name key and depending on name the elements.
Side note: This way requires to declare the element arrays as variables.
struct Root : Decodable {
var elementsA = [ElementA]()
var elementsB = [ElementB]()
var elementsC = [ElementC]()
var elementsD = [ElementD]()
enum Zone: String, Decodable { case zoneA, zoneB, zoneC, zoneD }
private enum CodingKeys: String, CodingKey { case zones }
private enum ZoneCodingKeys: String, CodingKey { case name, blocks }
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
var zonesContainer = try container.nestedUnkeyedContainer(forKey: .zones)
while !zonesContainer.isAtEnd {
let item = try zonesContainer.nestedContainer(keyedBy: ZoneCodingKeys.self)
let zone = try item.decode(Zone.self, forKey: .name)
switch zone {
case .zoneA: elementsA = try item.decode([ElementA].self, forKey: .blocks)
case .zoneB: elementsB = try item.decode([ElementB].self, forKey: .blocks)
case .zoneC: elementsC = try item.decode([ElementC].self, forKey: .blocks)
case .zoneD: elementsD = try item.decode([ElementD].self, forKey: .blocks)
}
}
}
}
Decoding the stuff is straightforward
do {
let result = try JSONDecoder().decode(Root.self, from: Data(jsonString.utf8))
print(result)
} catch {
print(error)
}
the "zone" property is an array of Zone objects. so you can decode them like:
enum Zone: Decodable {
case a([ElementA])
case b([ElementB])
case c([ElementC])
case d([ElementD])
enum Name: String, Codable {
case a = "zoneA"
case b = "zoneB"
case c = "zoneC"
case d = "zoneD"
}
enum RootKey: CodingKey {
case name
case blocks
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: RootKey.self)
let zoneName = try container.decode(Name.self, forKey: .name)
switch zoneName {
case .a: try self = .a(container.decode([ElementA].self, forKey: .blocks))
case .b: try self = .b(container.decode([ElementB].self, forKey: .blocks))
case .c: try self = .c(container.decode([ElementC].self, forKey: .blocks))
case .d: try self = .d(container.decode([ElementD].self, forKey: .blocks))
}
}
}
Then you can filter out anything you like. For example you can pass in the array and get the result you asked in your question:
struct Root {
init(zones: [Zone]) {
elementsA = zones.reduce([]) {
guard case let .a(elements) = $1 else { return $0 }
return $0 + elements
}
elementsB = zones.reduce([]) {
guard case let .b(elements) = $1 else { return $0 }
return $0 + elements
}
elementsC = zones.reduce([]) {
guard case let .c(elements) = $1 else { return $0 }
return $0 + elements
}
elementsD = zones.reduce([]) {
guard case let .d(elements) = $1 else { return $0 }
return $0 + elements
}
}
let elementsA: [ElementA]
let elementsB: [ElementB]
let elementsC: [ElementC]
let elementsD: [ElementD]
}
✅ Benefits:
Retain the original structure (array of zones)
Handle repeating zones (if server sends more than just one for each zone)
I want to use enumeration with localized string, so I do like this, it works, but
the problem of this solution is : I can't get easily enum value from localized string, I must have the key to do it :
let option = DietWithoutResidueOption(rawValue: "NoDiet")
If not I must to call dietWithoutResidueOptionWith method to get enum value... :/
There are a better solution to store directly localizedString and not keys in enum ?
Thanks
Enumeration
enum DietWithoutResidueOption: String {
case NoDiet = "NoDiet"
case ThreeDays = "ThreeDays"
case FiveDays = "FiveDays"
private func localizedString() -> String {
return NSLocalizedString(self.rawValue, comment: "")
}
static func dietWithoutResidueOptionWith(#localizedString: String) -> DietWithoutResidueOption {
switch localizedString {
case DietWithoutResidueOption.ThreeDays.localizedString():
return DietWithoutResidueOption.ThreeDays
case DietWithoutResidueOption.FiveDays.localizedString():
return DietWithoutResidueOption.FiveDays
default:
return DietWithoutResidueOption.NoDiet
}
}
}
Localizable.strings
"NoDiet" = "NON, JE N'AI PAS DE RÉGIME";
"ThreeDays" = "OUI, SUR 3 JOURS";
"FiveDays" = "OUI, SUR 5 JOURS";
call
println(DietWithoutResidueOption.FiveDays.localizedString())
Try this, it's pretty simple and straight forward:
enum ChoicesTitle: String {
case choice1 = "Choice 1"
case choice2 = "Choice 2"
case choice3 = "Choice 3"
case choice4 = "Choice 4"
case choice5 = "Choice 5"
case choice6 = "Choice 6"
func localizedString() -> String {
return NSLocalizedString(self.rawValue, comment: "")
}
static func getTitleFor(title: ChoicesTitle) -> String {
return title.localizedString()
}
}
And you could use it like this:
let stringOfChoice1: String = ChoicesTitle.getTitleFor(title: .choice1)
Hope this works for you
You can use any StringLiteralConvertible, Equatable type for RawValue type of enum.
So, how about:
import Foundation
struct LocalizedString: StringLiteralConvertible, Equatable {
let v: String
init(key: String) {
self.v = NSLocalizedString(key, comment: "")
}
init(localized: String) {
self.v = localized
}
init(stringLiteral value:String) {
self.init(key: value)
}
init(extendedGraphemeClusterLiteral value: String) {
self.init(key: value)
}
init(unicodeScalarLiteral value: String) {
self.init(key: value)
}
}
func ==(lhs:LocalizedString, rhs:LocalizedString) -> Bool {
return lhs.v == rhs.v
}
enum DietWithoutResidueOption: LocalizedString {
case NoDiet = "NoDiet"
case ThreeDays = "ThreeDays"
case FiveDays = "FiveDays"
var localizedString: String {
return self.rawValue.v
}
init?(localizedString: String) {
self.init(rawValue: LocalizedString(localized: localizedString))
}
}
Using this, you can construct DietWithoutResidueOption by 3 ways:
let option1 = DietWithoutResidueOption.ThreeDays
let option2 = DietWithoutResidueOption(rawValue: "ThreeDays") // as Optional
let option3 = DietWithoutResidueOption(localizedString: "OUI, SUR 3 JOURS") // as Optional
and extract the localized string with:
let localized = option1.localizedString
this is a late answer, but I just had a chat with Apple Engineers about that topic they recommend to do it that way:
enum LocalizedStrings {
case title
var localized: String {
switch self {
case .title:
return NSLocalizedString("My Title", comment: "My Comment")
}
}
}
In your case the solution would be not much different from the original code:
enum DietWithoutResidueOption {
case NoDiet
case ThreeDays
case FiveDays
var localizedString: String {
switch self {
case .NoDiet:
return NSLocalizedString("NoDiet", comment: "Some comment")
case .ThreeDays:
return NSLocalizedString("ThreeDays", comment: "Some comment")
case .FiveDays:
return NSLocalizedString("FiveDays", comment: "Some comment")
}
}
static func dietWithoutResidueOptionWith(localizedString: String) -> DietWithoutResidueOption {
switch localizedString {
case DietWithoutResidueOption.ThreeDays.localizedString:
return DietWithoutResidueOption.ThreeDays
case DietWithoutResidueOption.FiveDays.localizedString:
return DietWithoutResidueOption.FiveDays
default:
return DietWithoutResidueOption.NoDiet
}
}
}
The reason is that they don't want you to pass variables into NSLocalizedString(). This has something to do with optimization and parsing the strings. Imagine Xcode generating the localizable.strings file on it's own at some point, but it could not find the strings, because they are passed as variables.
A nice approach is to create a struct of Localizable Strings with static variables like so:
LocalizableStrings.swift
struct LocalizableStrings {
static let noDiet = NSLocalizedString("NoDiet", comment: "")
static let threeDays = NSLocalizedString("ThreeDays", comment: "")
static let fiveDays = NSLocalizedString("FiveDays", comment: "")
}
Localizable.strings
"NoDiet" = "NON, JE N'AI PAS DE RÉGIME";
"ThreeDays" = "OUI, SUR 3 JOURS";
"FiveDays" = "OUI, SUR 5 JOURS";
And your enum would look like that:
Enum
enum DietWithoutResidueOption {
case NoDiet,
ThreeDays,
FiveDays
var description : String {
get {
switch(self) {
case .NoDiet:
return LocalizableStrings.noDiet
case .ThreeDays:
return LocalizableStrings.threeDays
case .FiveDays:
return LocalizableStrings.fiveDays
}
}
}
}
So, for instance, to get your description you can do like below:
DietWithoutResidueOption.NoDiet.description
The good thing about this approach is that you put the keys of your localizable strings on a single file. So, for instance, if you change the NoDiet key on your Localizable.strings file you only need to update the LocalizableStrings.swift file, instead of all the places where we have the NoDiet key as a string. Furthermore,
you take the risk of spelling wrong the NoDiet key in some file where it is being used and your code will compile with no errors, meanwhile using a static variable from LocalizableStrings.swift you can avoid that, as your code will not compile and you will see an error message saying where the error is.
Ohter alternative :
Enum
enum Title : String {
case CEO = "CEOKey"
case CTO = "CTOKey"
case CFO = "CFOKey"
private static let allTitles = [CEO, CTO, CFO]
var localizedString: String {
return NSLocalizedString(self.rawValue, comment: "")
}
init!(rawValue: String) {
var keys = Title.allTitles
var filtered = keys.filter { $0.rawValue == rawValue }
self = filtered.first!
}
init!(localizedString: String) {
var keys = Title.allTitles
var filtered = keys.filter { $0.localizedString == localizedString }
self = filtered.first!
}
}
Localizable.strings
"CEOKey" = "Chief Executive Officer";
"CTOKey" = "Chief Technical Officer";
"CFOKey" = "Chief Financial Officer";
Constract enum :
let option1 = Title.CFO
let option2 = Title(rawValue: "CTOKey") // init from key
let option3 = Title(localizedString: NSLocalizedString("CEOKey", comment: "")) // init from value
Extract the localized strings :
println("option1 localized string : \(option1.localizedString)")
println("option2 localized string : \(option2.localizedString)")
println("option3 localized string : \(option3.localizedString)")
Input
option1 localized string : Chief Financial Officer
option2 localized string : Chief Technical Officer
option3 localized string : Chief Executive Officer
This code will generate exception, if localized strings or keys not found
This is my example
enum Localization: String {
case appName = "app_name"
case appOk = "app_ok"
case appError = "app_error"
case placeholderNoContent = "placeholder_no_content"
case homeTitle = "home_title"
public func localized(args: CVarArg...) -> String {
let localizedString = NSLocalizedString(self.rawValue, comment: "")
return withVaList(args, { (args) -> String in
return NSString(format: localizedString, locale: Locale.current, arguments: args) as String
})
}
}
Usage
self.homeTitleLabel = Localization.homeTitle.localized()
This Localization enum can easily be used with string formats.
Try this protocol which I created, and you can import, use it like below.
https://github.com/Wei18/ZWExt/blob/master/ZWExt/Classes/Protocol/Localizable.swift
enum SomeKey: String, Localizable {
case MenuGreeting = "lb_menu_greeting"
case HaveBook = "I have %# books"
}
// Sample
let menuGreeting: String = SomeKey.MenuGreeting.localized()
let iHaveBoxes: String = SomeKey.HaveBook.localized([3])
/*
// You also can make it with html.
SomeKey.CustomCase.localizedHTML()
SomeKey.CustomCase.localizedHTML([])
*/
I am new in programming and swift. I have an enum like this
enum City : String {
case tokyo = "tokyo"
case london = "london"
case newYork = "new york"
}
Could I possibly get that city name to an array from enum raw value? I hope I can get something like this :
let city = ["tokyo","london","new york"]
Something like this.
let cities = [City.tokyo, .london, .newYork]
let names = cities.map { $0.rawValue }
print(names) // ["tokyo", "london", "new york"]
To get all enum values as an array see this.
Swift 4.0
If you want to iterate through enum you can do like this.
enum City : String {
case tokyo = "tokyo"
case london = "london"
case newYork = "new york"
static let allValues = [tokyo,london,newYork]
}
let values = City.allValues.map { $0.rawValue }
print(values) //tokyo london new york
Hope this might help. Please look into this https://stackoverflow.com/a/28341290/2741603 for more detail
enum City : String {
case tokyo = "tokyo"
case london = "london"
case newYork = "new york"
}
func iterateEnum<T: Hashable>(_: T.Type) -> AnyIterator<T> {
var k = 0
return AnyIterator {
let next = withUnsafeBytes(of: &k) { $0.load(as: T.self) }
if next.hashValue != k { return nil }
k += 1
return next
}
}
var cityList:[String] = []
for item in iterateEnum(City.self){
cityList.append(item.rawValue)
}
print(cityList)
Starting from the answer by https://stackoverflow.com/users/5991255/jaydeep-vora, you can also add conformity to CaseIterable protocol and then use the allCases method
enum City : String, CaseIterable {
case tokyo = "tokyo"
case london = "london"
case newYork = "new york"
}
let values = City.allCases.map { $0.rawValue }
print(values)
I'm trying to get enum type from raw value:
enum TestEnum: String {
case Name
case Gender
case Birth
var rawValue: String {
switch self {
case .Name: return "Name"
case .Gender: return "Gender"
case .Birth: return "Birth Day"
}
}
}
let name = TestEnum(rawValue: "Name") //Name
let gender = TestEnum(rawValue: "Gender") //Gender
But it seems that rawValue doesn't work for string with spaces:
let birth = TestEnum(rawValue: "Birth Day") //nil
Any suggestions how to get it?
Too complicated, just assign the raw values directly to the cases
enum TestEnum: String {
case Name = "Name"
case Gender = "Gender"
case Birth = "Birth Day"
}
let name = TestEnum(rawValue: "Name")! //Name
let gender = TestEnum(rawValue: "Gender")! //Gender
let birth = TestEnum(rawValue: "Birth Day")! //Birth
If the case name matches the raw value you can even omit it
enum TestEnum: String {
case Name, Gender, Birth = "Birth Day"
}
In Swift 3+ all enum cases are lowercased
Full working example:
enum TestEnum: String {
case name = "A Name"
case otherName
case test = "Test"
}
let first: TestEnum? = TestEnum(rawValue: "A Name")
let second: TestEnum? = TestEnum(rawValue: "OtherName")
let third: TestEnum? = TestEnum(rawValue: "Test")
print("\(first), \(second), \(third)")
All of those will work, but when initializing using a raw value it will be an optional. If this is a problem you could create an initializer or constructor for the enum to try and handle this, adding a none case and returning it if the enum couldn't be created. Something like this:
static func create(rawValue:String) -> TestEnum {
if let testVal = TestEnum(rawValue: rawValue) {
return testVal
}
else{
return .none
}
}
With Swift 4.2 and CaseIterable protocol it is not that hard at all!
Here is an example of how to implement it.
import UIKit
private enum DataType: String, CaseIterable {
case someDataOne = "an_awesome_string_one"
case someDataTwo = "an_awesome_string_two"
case someDataThree = "an_awesome_string_three"
case someDataFour = "an_awesome_string_four"
func localizedString() -> String {
// Internal operation
// I have a String extension which returns its localized version
return self.rawValue.localized
}
static func fromLocalizedString(localizedString: String) -> DataType? {
for type in DataType.allCases {
if type.localizedString() == localizedString {
return type
}
}
return nil
}
}
// USAGE EXAMPLE
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if let dataType = DataType.fromLocalizedString(localizedString: self.title) {
loadUserData(type: dataType)
}
}
You can easily modify it to return the DataType based on the rawValue. I hope it helps!
You can define enum like this -
enum TestEnum: String {
case Name, Gender, Birth
}
OR
enum TestEnum: String {
case Name
case Gender
case Birth
}
you can provide an init method which defaults to one of the member values.
enum TestEnum: String {
case Name, Gender, Birth
init() {
self = .Gender
}
}
In the example above, TestEnum.Name has an implicit raw value of "Name", and so on.
You access the raw value of an enumeration case with its rawValue property:
let testEnum = TestEnum.Name.rawValue
// testEnum is "Name"
let testEnum1 = TestEnum()
// testEnum1 is "Gender"
Display the rawvalue using Enum
import UIKit
enum car: String {
case bmw = "BMW"
case jaquar = "JAQUAR"
case rd = "RD"
case benz = "BENZ"
}
class ViewController: UIViewController {
#IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
label.text = car.bmw.rawValue
}
}
Here is example of more useable code in swift 4.1
import UIKit
enum FormData {
case userName
case password
static let array = [userName, password]
var placeHolder: String {
switch self {
case .userName:
return AppString.name.localized // will return "Name" string
case .password:
return AppString.password.localized // will return "Password" string
}
}
}
enum AppString: String {
case name = "Name"
case password = "Password"
var localized: String {
return NSLocalizedString(self.rawValue, comment: "")
}
}
I think this is a quick and clean solution for swift 4.2 (you can c&p to playground)
import UIKit
public enum SomeEnum: String, CaseIterable {
case sun,moon,venus,pluto
}
let str = "venus"
let newEnum = SomeEnum.allCases.filter{$0.rawValue == str}.first
// newEnum is optional
if let result = newEnum {
print(result.rawValue)
}
enum withdrawBalceTimeGenrateError : String , Error{
case insufficientBalance = "Plz Check Balance"
}
withdrawBalceTimeGenrateError.insufficientBalance.rawValue // Plz Check Balance
I want to use enumeration with localized string, so I do like this, it works, but
the problem of this solution is : I can't get easily enum value from localized string, I must have the key to do it :
let option = DietWithoutResidueOption(rawValue: "NoDiet")
If not I must to call dietWithoutResidueOptionWith method to get enum value... :/
There are a better solution to store directly localizedString and not keys in enum ?
Thanks
Enumeration
enum DietWithoutResidueOption: String {
case NoDiet = "NoDiet"
case ThreeDays = "ThreeDays"
case FiveDays = "FiveDays"
private func localizedString() -> String {
return NSLocalizedString(self.rawValue, comment: "")
}
static func dietWithoutResidueOptionWith(#localizedString: String) -> DietWithoutResidueOption {
switch localizedString {
case DietWithoutResidueOption.ThreeDays.localizedString():
return DietWithoutResidueOption.ThreeDays
case DietWithoutResidueOption.FiveDays.localizedString():
return DietWithoutResidueOption.FiveDays
default:
return DietWithoutResidueOption.NoDiet
}
}
}
Localizable.strings
"NoDiet" = "NON, JE N'AI PAS DE RÉGIME";
"ThreeDays" = "OUI, SUR 3 JOURS";
"FiveDays" = "OUI, SUR 5 JOURS";
call
println(DietWithoutResidueOption.FiveDays.localizedString())
Try this, it's pretty simple and straight forward:
enum ChoicesTitle: String {
case choice1 = "Choice 1"
case choice2 = "Choice 2"
case choice3 = "Choice 3"
case choice4 = "Choice 4"
case choice5 = "Choice 5"
case choice6 = "Choice 6"
func localizedString() -> String {
return NSLocalizedString(self.rawValue, comment: "")
}
static func getTitleFor(title: ChoicesTitle) -> String {
return title.localizedString()
}
}
And you could use it like this:
let stringOfChoice1: String = ChoicesTitle.getTitleFor(title: .choice1)
Hope this works for you
You can use any StringLiteralConvertible, Equatable type for RawValue type of enum.
So, how about:
import Foundation
struct LocalizedString: StringLiteralConvertible, Equatable {
let v: String
init(key: String) {
self.v = NSLocalizedString(key, comment: "")
}
init(localized: String) {
self.v = localized
}
init(stringLiteral value:String) {
self.init(key: value)
}
init(extendedGraphemeClusterLiteral value: String) {
self.init(key: value)
}
init(unicodeScalarLiteral value: String) {
self.init(key: value)
}
}
func ==(lhs:LocalizedString, rhs:LocalizedString) -> Bool {
return lhs.v == rhs.v
}
enum DietWithoutResidueOption: LocalizedString {
case NoDiet = "NoDiet"
case ThreeDays = "ThreeDays"
case FiveDays = "FiveDays"
var localizedString: String {
return self.rawValue.v
}
init?(localizedString: String) {
self.init(rawValue: LocalizedString(localized: localizedString))
}
}
Using this, you can construct DietWithoutResidueOption by 3 ways:
let option1 = DietWithoutResidueOption.ThreeDays
let option2 = DietWithoutResidueOption(rawValue: "ThreeDays") // as Optional
let option3 = DietWithoutResidueOption(localizedString: "OUI, SUR 3 JOURS") // as Optional
and extract the localized string with:
let localized = option1.localizedString
this is a late answer, but I just had a chat with Apple Engineers about that topic they recommend to do it that way:
enum LocalizedStrings {
case title
var localized: String {
switch self {
case .title:
return NSLocalizedString("My Title", comment: "My Comment")
}
}
}
In your case the solution would be not much different from the original code:
enum DietWithoutResidueOption {
case NoDiet
case ThreeDays
case FiveDays
var localizedString: String {
switch self {
case .NoDiet:
return NSLocalizedString("NoDiet", comment: "Some comment")
case .ThreeDays:
return NSLocalizedString("ThreeDays", comment: "Some comment")
case .FiveDays:
return NSLocalizedString("FiveDays", comment: "Some comment")
}
}
static func dietWithoutResidueOptionWith(localizedString: String) -> DietWithoutResidueOption {
switch localizedString {
case DietWithoutResidueOption.ThreeDays.localizedString:
return DietWithoutResidueOption.ThreeDays
case DietWithoutResidueOption.FiveDays.localizedString:
return DietWithoutResidueOption.FiveDays
default:
return DietWithoutResidueOption.NoDiet
}
}
}
The reason is that they don't want you to pass variables into NSLocalizedString(). This has something to do with optimization and parsing the strings. Imagine Xcode generating the localizable.strings file on it's own at some point, but it could not find the strings, because they are passed as variables.
A nice approach is to create a struct of Localizable Strings with static variables like so:
LocalizableStrings.swift
struct LocalizableStrings {
static let noDiet = NSLocalizedString("NoDiet", comment: "")
static let threeDays = NSLocalizedString("ThreeDays", comment: "")
static let fiveDays = NSLocalizedString("FiveDays", comment: "")
}
Localizable.strings
"NoDiet" = "NON, JE N'AI PAS DE RÉGIME";
"ThreeDays" = "OUI, SUR 3 JOURS";
"FiveDays" = "OUI, SUR 5 JOURS";
And your enum would look like that:
Enum
enum DietWithoutResidueOption {
case NoDiet,
ThreeDays,
FiveDays
var description : String {
get {
switch(self) {
case .NoDiet:
return LocalizableStrings.noDiet
case .ThreeDays:
return LocalizableStrings.threeDays
case .FiveDays:
return LocalizableStrings.fiveDays
}
}
}
}
So, for instance, to get your description you can do like below:
DietWithoutResidueOption.NoDiet.description
The good thing about this approach is that you put the keys of your localizable strings on a single file. So, for instance, if you change the NoDiet key on your Localizable.strings file you only need to update the LocalizableStrings.swift file, instead of all the places where we have the NoDiet key as a string. Furthermore,
you take the risk of spelling wrong the NoDiet key in some file where it is being used and your code will compile with no errors, meanwhile using a static variable from LocalizableStrings.swift you can avoid that, as your code will not compile and you will see an error message saying where the error is.
Ohter alternative :
Enum
enum Title : String {
case CEO = "CEOKey"
case CTO = "CTOKey"
case CFO = "CFOKey"
private static let allTitles = [CEO, CTO, CFO]
var localizedString: String {
return NSLocalizedString(self.rawValue, comment: "")
}
init!(rawValue: String) {
var keys = Title.allTitles
var filtered = keys.filter { $0.rawValue == rawValue }
self = filtered.first!
}
init!(localizedString: String) {
var keys = Title.allTitles
var filtered = keys.filter { $0.localizedString == localizedString }
self = filtered.first!
}
}
Localizable.strings
"CEOKey" = "Chief Executive Officer";
"CTOKey" = "Chief Technical Officer";
"CFOKey" = "Chief Financial Officer";
Constract enum :
let option1 = Title.CFO
let option2 = Title(rawValue: "CTOKey") // init from key
let option3 = Title(localizedString: NSLocalizedString("CEOKey", comment: "")) // init from value
Extract the localized strings :
println("option1 localized string : \(option1.localizedString)")
println("option2 localized string : \(option2.localizedString)")
println("option3 localized string : \(option3.localizedString)")
Input
option1 localized string : Chief Financial Officer
option2 localized string : Chief Technical Officer
option3 localized string : Chief Executive Officer
This code will generate exception, if localized strings or keys not found
This is my example
enum Localization: String {
case appName = "app_name"
case appOk = "app_ok"
case appError = "app_error"
case placeholderNoContent = "placeholder_no_content"
case homeTitle = "home_title"
public func localized(args: CVarArg...) -> String {
let localizedString = NSLocalizedString(self.rawValue, comment: "")
return withVaList(args, { (args) -> String in
return NSString(format: localizedString, locale: Locale.current, arguments: args) as String
})
}
}
Usage
self.homeTitleLabel = Localization.homeTitle.localized()
This Localization enum can easily be used with string formats.
Try this protocol which I created, and you can import, use it like below.
https://github.com/Wei18/ZWExt/blob/master/ZWExt/Classes/Protocol/Localizable.swift
enum SomeKey: String, Localizable {
case MenuGreeting = "lb_menu_greeting"
case HaveBook = "I have %# books"
}
// Sample
let menuGreeting: String = SomeKey.MenuGreeting.localized()
let iHaveBoxes: String = SomeKey.HaveBook.localized([3])
/*
// You also can make it with html.
SomeKey.CustomCase.localizedHTML()
SomeKey.CustomCase.localizedHTML([])
*/