let me start by saying that I have already implemented Decodable which decodes JSON into several objects with these two Integer values:
public class ARBufferData: DecoderUpdatable {
private var previousStation: Int
private var numberOfElements: Int
func update(from decoder: Decoder) throws {
//Still needs work
}
}
What I am now trying to achieve is making the created objects updatable so that when a value in the JSON changes (e.g. numberOfElements) only the value is changed in the corresponding object. I believe this guide can enable me to do it, but I am having trouble implementing it: Understanding and Extending Swift 4’s Codable
This is the extension of KeyedDecodingContainer:
extension KeyedDecodingContainer {
func update<T: DecoderUpdatable>(_ value: inout T, forKey key: Key, userInfo: Any) throws {
let nestedDecoder = NestedDecoder(from: self, key: key)
try value.update(from: nestedDecoder)
}
}
The reason this would be helpful is that I can then set a property observer on that value and trigger a redraw of the visualisation.
I would be very grateful, if anyone can help or point me in the right direction.
Thank you!
Cheers
There are two ways to update the class. One, you decode each int by itself and compare. Two, you implement DecoderUpdatable for Int and call container.update with them as argument.
public class ARBufferData: NSObject, Decodable, DecoderUpdatable {
init(previousStation: Int, numberOfElements: Int) {
self.previousStation = previousStation
self.numberOfElements = numberOfElements
}
#objc dynamic var previousStation: Int
#objc dynamic var numberOfElements: Int
private enum CodingKeys: String, CodingKey {
case previousStation, numberOfElements
}
public func update(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
try container.update(&previousStation, forKey: .previousStation)
try container.update(&numberOfElements, forKey: .numberOfElements)
}
}
extension Int: DecoderUpdatable {
public mutating func update(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let result = try container.decode(Int.self)
guard result != self else { return }
self = result
}
}
I do not know whether the blogpost-writer intended it this way though. If he did, then generating the DecoderUpdatable conformances for the basic types could be a use case for Sourcery, but that's off topic here.
In Swift4 there is an interesting way to observe which you may also be interested in:
let token = buffer.observe(\.numberOfElements, options: [.new, .old]) {
object, change in
if change.oldValue != change.newValue {
// Act on change of buffer.numberOfElements.
}
}
Source
Related
I’m setting up my Settings class which gets/sets values from UserDefaults. I wish for as much of the code to be generic to minimise effort involved whenever a new setting is introduced (I have many as it is, and I expect many more in the future too), thereby reducing the probability of any human errors/bugs.
I came across this answer to create a wrapper for UserDefaults:
struct UserDefaultsManager {
static var userDefaults: UserDefaults = .standard
static func set<T>(_ value: T, forKey: String) where T: Encodable {
if let encoded = try? JSONEncoder().encode(value) {
userDefaults.set(encoded, forKey: forKey)
}
}
static func get<T>(forKey: String) -> T? where T: Decodable {
guard let data = userDefaults.value(forKey: forKey) as? Data,
let decodedData = try? JSONDecoder().decode(T.self, from: data)
else { return nil }
return decodedData
}
}
I’ve created an enum to store all the setting keys:
enum SettingKeys: String {
case TimeFormat = "TimeFormat"
// and many more
}
And each setting has their own enum:
enum TimeFormat: String, Codable {
case ampm = "12"
case mili = "24"
}
In this simplified Settings class example, you can see that when it’s instantiated I initialise the value of every setting defined. I check if its setting key was found in UserDefaults: if yes, I use the value found, otherwise I set it a default and save it for the first time to UserDefaults.
class Settings {
var timeFormat: TimeFormat!
init() {
self.initTimeFormat()
}
func initTimeFormat() {
guard let format: TimeFormat = UserDefaultsManager.get(forKey: SettingKeys.TimeFormat.rawValue) else {
self.setTimeFormat(to: .ampm)
return
}
self.timeFormat = format
}
func setTimeFormat(to format: TimeFormat) {
UserDefaultsManager.set(format, forKey: SettingKeys.TimeFormat.rawValue)
self.timeFormat = format
}
}
This is working fine, and pretty straightforward. However, thinking ahead, this will be tedious (and therefore error-prone) to replicate for every setting in this app (and every other I look to do in the future). Is there a way for the init<name of setting>() and set<name of setting>() to be generalised, whereby I pass it a key for a setting (and nothing more), and it handles everything else?
I’ve identified every setting to have these shared elements:
settings key (e.g. SettingsKey.TimeFormat in my example)
unique type (could be AnyObject, String, Int, Bool etc. e.g. enum TimeFormat in my example)
unique property (e.g. timeFormat in my example)
default value (e.g. TimeFormat.ampm in my example)
Thanks as always!
UPDATE:
This may or may not make a difference, but considering all settings will have a default value, I realised they don’t need to be a non-optional optional but can have the default set at initialisation.
That is, change:
var timeFormat: TimeFormat!
func initTimeFormat() {
guard let format: TimeFormat = UserDefaultsManager.get(forKey: SettingKeys.TimeFormat.rawValue) else {
self.setTimeFormat(to: .ampm)
return
}
self.timeFormat = format
}
To:
var timeFormat: TimeFormat = .ampm
func initTimeFormat() {
guard let format: TimeFormat = UserDefaultsManager.get(forKey: SettingKeys.TimeFormat.rawValue) else {
UserDefaultsManager.set(self.timeFormat, forKey: SettingKeys.TimeFormat.rawValue)
return
}
self.timeFormat = format
}
The setTimeFormat() function is still needed when its value is changed by the user in-app.
Note that your settings don't have to be stored properties. They can be computed properties:
var timeFormat: TimeFormat {
get {
UserDefaultsManager.get(forKey: SettingKeys.TimeFormat.rawValue) ?? .ampm
}
set {
UserDefaultsManager.set(newValue, forKey: SettingKeys.TimeFormat.rawValue)
}
}
You don't have to write as much code this way, when you want to add a new setting. Since you will just be adding a new setting key enum case, and a computed property.
Further more, this computed property can be wrapped into a property wrapper:
#propertyWrapper
struct FromUserDefaults<T: Codable> {
let key: SettingKeys
let defaultValue: T
init(key: SettingKeys, defaultValue: T) {
self.key = key
self.defaultValue = defaultValue
}
var wrappedValue: T {
get {
UserDefaultsManager.get(forKey: key.rawValue) ?? defaultValue
}
set {
UserDefaultsManager.set(newValue, forKey: key.rawValue)
}
}
}
Usage:
class Settings {
#FromUserDefaults(key: .TimeFormat, defaultValue: .ampm)
var timeFormat: TimeFormat
}
Now to add a new setting, you just need to write a new enum case for the key, and two more lines of code.
I have developed an ios application that allow users to edit a musical score sheet and now i'd like to implement data persistence to prevent the discarding of changes.
Reading on ios documentation i've noticed that exists different ways to improve data persistence and I believe that the best way for my application is Core Data.
Considering that my application use a lot of custom object i met a lot of problems.
I'm trying to use core data to save an entity, referred to a score sheet, composed by two attributes:
name: String
score: Array of another custom object (Measure), composed by other custom object (Score Element)
According to documentation and other q/a I've decided to use a Trasformable type on the model:
So I've declared a generic class used as trasformer for score attribute:
public class NSSecureCodingValueTransformer<T: NSSecureCoding & NSObject>: ValueTransformer {
public override class func transformedValueClass() -> AnyClass { T.self }
public override class func allowsReverseTransformation() -> Bool { true }
public override func transformedValue(_ value: Any?) -> Any? {
guard let value = value as? T else { return nil }
return try? NSKeyedArchiver.archivedData(withRootObject: value, requiringSecureCoding: true)
}
public override func reverseTransformedValue(_ value: Any?) -> Any? {
guard let data = value as? NSData else { return nil }
let result = try? NSKeyedUnarchiver.unarchivedObject(
ofClass: T.self,
from: data as Data
)
return result
}
/// The name of this transformer. This is the name used to register the transformer using `ValueTransformer.setValueTransformer(_:forName:)`
public static var transformerName: NSValueTransformerName {
let className = NSStringFromClass(T.self)
return NSValueTransformerName("DHC\(className)ValueTransformer")
}
/// Registers the transformer by calling `ValueTransformer.setValueTransformer(_:forName:)`.
public static func registerTransformer() {
let transformer = NSSecureCodingValueTransformer<T>()
ValueTransformer.setValueTransformer(transformer, forName: transformerName)
}
}
Using in this way a DHCMeasureValueTransformer as trasformer in DataModel file.
The problem is that when i save, no error occurs but when i fetch data for a new restart of application, i can fetch just the name of score sheet, while the score array it's empty, like if no elements it's been put inside (clearly, before of save, i've try to print array content, that prove that i'm working with a non empty array)
Here is the code of the save:
static func saveContext() {
let context = getContext()
do {
try context.save()
} catch {
print("error during the save.")
}
}
And here is the code of two classes of the entity object:
// DataClass
#objc(ScoreSheet)
public class ScoreSheet: NSManagedObject {
static var supportsSecureCoding: Bool {
return true
}
}
//DataProperties
extension ScoreSheet {
#nonobjc public class func fetchRequest() -> NSFetchRequest<ScoreSheet> {
return NSFetchRequest<ScoreSheet>(entityName: "ScoreSheet")
}
#NSManaged public var name: String
#NSManaged public var score: [Measure]
}
Clearly Measure class implements NSSecureCoding and method for decode and encode the object.
Here is Measure class implementation:
import Foundation
class Measure: NSObject, NSCoding, NSSecureCoding {
var elements : [ScoreElement] = []
var timeSig : TimeSignature
var clef : Clef
static var supportsSecureCoding = true
init(time : TimeSignature, clef : Clef) {
self.timeSig = time
self.clef = clef
}
func encode(with encoder: NSCoder) {
encoder.encode(self.elements, forKey: "elements")
encoder.encode(self.timeSig, forKey: "timeSig")
encoder.encode(self.clef, forKey: "clef")
}
required convenience init? (coder decoder: NSCoder) {
let elements = decoder.decodeObject(forKey: "elements") as! [ScoreElement]
let timeSig = decoder.decodeObject(forKey: "timeSig") as! TimeSignature
let clef = decoder.decodeObject(forKey: "clef") as! Clef
self.init(time: timeSig, clef: clef)
self.elements = elements
}
}
I'm not sure what's going wrong but there are a couple of things that need fixing that might affect your results.
Firstly, the computed transformer name is not the same as the one you're trying to use. When this line executes, and T is Measure,
let className = NSStringFromClass(T.self)
Then className is going to be something like MyProjectName.Measure. The computed transformer name ends up as something like NSValueTransformerName(_rawValue: DHCMyProjectName.MeasureValueTransformer), which doesn't match what you're using in the data model. All of which means that your transformer isn't getting used.
But that probably doesn't matter because if Measure conforms to NSSecureCoding and all of Measure's properties (ScoreElement, TimeSignature, Clef) also conform to NSSecureCoding (which seems to be the case since your code isn't throwing exceptions), then you don't need a custom transformer at all. If a transformable property type conforms to NSSecureCoding then Core Data will automatically use NSSecureCoding. You don't need a custom transformer unless you don't want to or can't conform to NSSecureCoding for some reason. Because of this, it doesn't matter that your transformer isn't being used.
As for why Measure isn't surviving the encode/decode process, I don't know, but you may help clear things up by removing the distraction of the unnecessary encode/decode class. I'd also suggest putting a breakpoint in Measure in the encode(with:) and init(coder:) methods. You should hit those breakpoints when saving and fetching data.
I am working on a school assignment for which I have to develop an iOS application in Swift 5. The application needs to utilize a web service (a web-API) and either file storage or user defaults.
I had chosen to develop a "QR code manager", in which users can create QR codes for a URL by setting a few design parameters, which are then sent to a generator API. This API (upon an OK request) returns an image in a specified format (PNG in my case).
I have a class with the URL and all the design properties of the QR code, which will also contain the image itself. Please see below code snippet for the class.
public class QRCode {
var bsId : Int?
var url : String?
var name: String?
var frame: Frame?
var logo: QrCodeLogo?
var marker: Marker?
var color : String?
var bgColor : String?
var image : Data?
init(data: [String:String]) {
self.url = data["url"]
self.frame = Frame.allCases.first(where: { $0.description == data["frame"] })
self.logo = QrCodeLogo.allCases.first(where: { $0.description == data["logo"] })
self.marker = Marker.allCases.first(where: { $0.description == data["marker"] })
self.bgColor = data["backGroundColor"]
self.color = data["colorLight"]
}
init(json: String) {
// todo
}
}
extension QRCode {
func toDict() -> [String:Any] {
var dict = [String:Any]();
let otherSelf = Mirror(reflecting: self);
for child in otherSelf.children {
if let key = child.label {
dict[key] = child.value;
}
}
return dict;
}
}
All the properties are nullable for ease of development, the class will be further refactored once I have successfully implemented everything.
I have tried various methods I found all over the internet, one of which can be found in the class extension. The function toDict() translates the object properties and their values to a Dictionary object of type [String:Any]. However, I read that when the Any datatype is encoded and then decoded, Swift cannot determine which complex datatype the decoded data is supposed to be, effectively rendering the data either meaningless or unusable.
Another method I found was through extending the Codable-protocol in the class. As far as I am aware however, Codable only accepts primitive datatypes.
Please find below my currently written code for file storage handling. It is not complete yet, but I felt it was a good start and might help in this question.
class StorageManager {
fileprivate let filemanager: FileManager = FileManager.default;
fileprivate func filePath(forKey key: String) -> URL? {
guard let docURL = filemanager.urls(for: .documentDirectory, in: FileManager.SearchPathDomainMask.userDomainMask).first else {
return nil;
}
return docURL.appendingPathComponent(key);
}
func writeToStorage(identifier: String, data: QRCode) -> Void {
guard let path = filePath(forKey: identifier) else {
throw ApplicationErrors.runtimeError("Something went wrong writing the file to storage");
}
let dict = data.toDict();
// TODO:: Implement
}
func readFromStorage(identifier: String) -> Any {
// TODO:: Implement
return 0;
}
func readAllFromStorage() throws -> [URL] {
let docsURL = filemanager.urls(for: .documentDirectory, in: .userDomainMask)[0];
do {
let fileURLs = try filemanager.contentsOfDirectory(at: docsURL, includingPropertiesForKeys: nil);
return fileURLs;
} catch {
throw ApplicationErrors.runtimeError("Something went wrong retrieving the files from \(docsURL.path): \(error.localizedDescription)");
}
}
}
I am very new to Swift and I am running stuck on file storage. Is there any way I could store instances of this class in file storage in such a way that I could reïnstantiate this class when I retrieve the data?
Thanks in advance! Please do not hesitate to ask any questions if there are any.
Edit
Based on matt's comment, please find below the code snippets of the Marker, Frame, and QrCodeLogo enums.
The Frame enum:
public enum Frame: String, CaseIterable {
case noFrame
case bottomFrame
case bottomTooltip
case topHeader
static var count: Int { return 4 }
var description: String {
switch self {
case .noFrame:
return "no-frame"
case .bottomFrame:
return "bottom-frame"
case .bottomTooltip:
return "bottom-tooltip"
case .topHeader:
return "top-header"
}
}
}
The QrCodeLogo enum:
public enum QrCodeLogo: String, CaseIterable {
case noLogo
case scanMe
case scanMeSquare
static var count: Int { return 3 }
var description: String {
switch self {
case .noLogo:
return "no-logo"
case .scanMe:
return "scan-me"
case .scanMeSquare:
return "scan-me-square"
}
}
}
The Marker enum:
public enum Marker: String, CaseIterable {
case version1
case version2
case version3
case version4
case version5
case version6
case version7
case version8
case version9
case version10
case version11
case version12
case version13
case version15
case version16
static var count: Int { return 15 }
var description: String {
switch self {
case .version1:
return "version1"
case .version2:
return "version2"
case .version3:
return "version3"
case .version4:
return "version4"
case .version5:
return "version5"
case .version6:
return "version6"
case .version7:
return "version7"
case .version8:
return "version8"
case .version9:
return "version9"
case .version10:
return "version10"
case .version11:
return "version11"
case .version12:
return "version12"
case .version13:
return "version13"
case .version15:
return "version15"
case .version16:
return "version16"
}
}
}
All the above enums contain the valid design options for the API I use. They serve as an input restriction to prevent "invalid parameter"-errors from occurring.
Hopefully, this clears things up.
Thanks again!
A type can conform to Codable provided all its properties conform to Codable. All of your properties do conform to Codable except for the enums, and they will conform to Codable if you declare that they do. Thus, this simple sketch of your types compiles:
public enum Frame: String, Codable {
case noFrame
case bottomFrame
case bottomTooltip
case topHeader
}
public enum QrCodeLogo: String, Codable {
case noLogo
case scanMe
case scanMeSquare
}
public enum Marker: String, Codable {
case version1
case version2
case version3
case version4
case version5
case version6
case version7
case version8
case version9
case version10
case version11
case version12
case version13
case version15
case version16
}
public class QRCode : Codable {
var bsId : Int?
var url : String?
var name: String?
var frame: Frame?
var logo: QrCodeLogo?
var marker: Marker?
var color : String?
var bgColor : String?
var image : Data?
}
There are many, many other things about your code that could be improved.
You don't need CaseIterable or description for anything. Now that your type is Codable, you can use it to retrieve the values from JSON directly, automatically. If the names of your enum cases do not match the corresponding JSON keys, just make a CodingKey nested enum to act as a bridge.
In other words, being Codable makes your type both populatable directly from the JSON and serializable to disk.
We just switched to swift 4.1 and we are having some difficulties with type conformance for Arrays. Here is the old way:
public typealias XDRCodable = XDREncodable & XDRDecodable
public protocol XDREncodable: Encodable {
func xdrEncode(to encoder: XDREncoder) throws
}
public protocol XDRDecodable: Decodable {
init(fromBinary decoder: XDRDecoder) throws
init(fromBinary decoder: XDRDecoder, count: Int) throws
}
extension Array: XDRCodable {
public func xdrEncode(to encoder: XDREncoder) throws {
try encoder.encode(UInt32(self.count))
for element in self {
try (element as! Encodable).encode(to: encoder)
}
}
public init(fromBinary decoder: XDRDecoder) throws {
guard let binaryElement = Element.self as? Decodable.Type else {
throw XDRDecoder.Error.typeNotConformingToDecodable(Element.self)
}
let count = try decoder.decode(UInt32.self)
self.init()
self.reserveCapacity(Int(count))
for _ in 0 ..< count {
let decoded = try binaryElement.init(from: decoder)
self.append(decoded as! Element)
}
}
}
This gives the following error in swift 4.1:
'XDRDecodable' requires that 'Element' conform to 'Decodable'
So we tried changing the declaration to:
extension Array: XDRCodable where Element : XDRCodable
While this compiles it still fails to encode the array and the following warning is generated:
warning: Swift runtime does not yet support dynamically querying conditional conformance ('Swift.Array': 'stellarsdk.XDREncodable')
I saw that this is a work in progress but does anyone have a workaround for this until type conformance is properly implemented. I'd like it to work as it was in swift 4.0 for now.
I have similar problem with BinaryCoder and created workaround. But you must have access to encoder a decoder implementation.
protocol XDRCodableArray {
func binaryEncode(to encoder: XDREncoder) throws
init(fromBinary decoder: XDRDecoder) throws
}
extension Array: XDRCodableArray {
//copy implementation of XDRCodable from Array: XDRCodable
}
//delete extension Array: XDRCodable
In decode append special implementation for arrays:
...
case let binaryT as BinaryDecodable.Type:
return try binaryT.init(fromBinary: self) as! T
//new case
case let array as BinaryCodableArray.Type:
return try array.init(fromBinary: self) as! T
...
And also in encode:
...
case let binary as BinaryEncodable:
try binary.binaryEncode(to: self)
//new case
case let array as BinaryCodableArray:
try array.binaryEncode(to: self)
...
Generaly there is problem with conditional protocol conformance. You can't cast variable to that protocol (in some cases).
protocol Test: Codable {
func test() -> String
}
extension Array: Test where Element: Codable {
func test() -> String {
return "Success"
}
}
func doSomething(x: Codable) {
let test = x as! Test
test.test()
}
let array = ["value"]
let t = (array as! Test).test //success
doSomething(x: array) //fail on first row inside function
I hope that Swift 4.2 with dynamically querying conditional conformance support will solve this.
I am trying to create an enum of a struct that I would like to initialize:
struct CustomStruct {
var variable1: String
var variable2: AnyClass
var variable3: Int
init (variable1: String, variable2: AnyClass, variable3: Int) {
self.variable1 = variable1
self.variable2 = variable2
self.variable3 = variable3
}
}
enum AllStructs: CustomStruct {
case getData
case addNewData
func getAPI() -> CustomStruct {
switch self {
case getData:
return CustomStruct(variable1:"data1", variable2: SomeObject.class, variable3: POST)
case addNewData:
// Same to same
default:
return nil
}
}
}
I get the following errors:
Type AllStructs does not conform to protocol 'RawRepresentable'
I am assuming that enums cannot be used this way. We must use primitives.
It should be:
struct CustomStruct {
var apiUrl: String
var responseType: AnyObject
var httpType: Int
init (variable1: String, variable2: AnyObject, variable3: Int) {
self.apiUrl = variable1
self.responseType = variable2
self.httpType = variable3
}
}
enum MyEnum {
case getData
case addNewData
func getAPI() -> CustomStruct {
switch self {
case .getData:
return CustomStruct(variable1: "URL_TO_GET_DATA", variable2: 11 as AnyObject, variable3: 101)
case .addNewData:
return CustomStruct(variable1: "URL_TO_ADD_NEW_DATA", variable2: 12 as AnyObject, variable3: 102)
}
}
}
Usage:
let data = MyEnum.getData
let myObject = data.getAPI()
// this should logs: "URL_TO_GET_DATA 11 101"
print(myObject.apiUrl, myObject.responseType, myObject.httpType)
Note that upon Naming Conventions, struct should named as CustomStruct and enum named as MyEnum.
In fact, I'm not pretty sure of the need of letting CustomStruct to be the parent of MyEnum to achieve what are you trying to; As mentioned above in the snippets, you can return an instance of the struct based on what is the value of the referred enum.
I'm not commenting on the choice to use an enum here, but just explaining why you got that error and how to declare an enum that has a custom object as parent.
The error shows you the problem, CustomStruct must implement RawRepresentable to be used as base class of that enum.
Here is a simplified example that shows you what you need to do:
struct CustomStruct : ExpressibleByIntegerLiteral, Equatable {
var rawValue: Int = 0
init(integerLiteral value: Int){
self.rawValue = value
}
static func == (lhs: CustomStruct, rhs: CustomStruct) -> Bool {
return
lhs.rawValue == rhs.rawValue
}
}
enum AllStructs: CustomStruct {
case ONE = 1
case TWO = 2
}
A few important things that we can see in this snippet:
The cases like ONE and TWO must be representable with a Swift literal, check this Swift 2 post for a list of available literals (int,string,array,dictionary,etc...). But please note that in Swift 3, the LiteralConvertible protocols are now called ExpressibleByXLiteral after the Big Swift Rename.
The requirement to implement RawRepresentable is covered implementing one of the Expressible protocols (init?(rawValue:) will leverage the initializer we wrote to support literals).
Enums must also be Equatable , so you'll have to implement the equality operator for your CustomStruct base type.
Did you try conforming to RawRepresentable like the error is asking?
Using JSON representation should work for variable1 and variable3. Some extra work may be required for variable2.
struct CustomStruct: RawRepresentable {
var variable1: String
var variable2: AnyClass
var variable3: Int
init?(rawValue: String) {
guard let data = rawValue.data(using: .utf8) else {
return nil
}
guard let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {
return nil
}
self.variable1 = (json["variable1"] as? String) ?? ""
self.variable2 = (json["variable2"] as? AnyClass) ?? AnyClass()
self.variable3 = (json["variable3"] as? Int) ?? 0
}
var rawValue: String {
let json = ["variable1": self.variable1,
"variable2": self.variable2,
"variable3": self.variable3
]
guard let data = try? JSONSerialization.data(withJSONObject: json, options: []) else {
return ""
}
return String(data: data, encoding: .utf8) ?? ""
}
}
According to the documentation:
If a value (known as a “raw” value) is provided for each enumeration case, the value can be a string, a character, or a value of any integer or floating-point type.
So yes, you cannot set a struct type to be enum's raw value.
In your case I would suggest using string as the enum raw value and some dictionary mapping these strings to CUSTOM_STRUCT type.
A bit late on the party but maybe useful for someone else. I would consider to simply use computed variables instead of a struct.
enum MyEnum {
case getData
case addNewData
var variable1: String {
switch self {
case .getData: return "data1"
case .addNewData: return "data2"
}
}
var variable2: Int {
switch self {
case .getData: return 1
case .addNewData: return 2
}
}
// ....
}
Usage:
let data = MyEnum.getData
print (data.variable1) // "data1"