I have enum:
enum RetailDemandReturnOperation {
case salesReturn(value: MSRetailSalesReturnRealm)
case demand(value: MSRetailDemandRealm)
}
MSRetailDemandRealm and MSRetailDemandRealm are both implement same protocol, that have variables title and stats. I want to extract this values, but i don't care of which object actually stored in. Consider following:
switch data! {
case .salesReturn(let object):
titleString = object.title
statistics = object.stats
case .demand(let object):
titleString = object.title
statistics = object.stats
break
}
I have to go in each enum value to get property of protocol. Is any way i can do it shorter and cleaner? Get associated value, no matter what it is, as long as it conforms to my protocol, and get protocol values?
Thanks.
You could add a property to your enum that returns the protocol. For example:
enum RetailDemandReturnOperation {
case salesReturn(value: MSRetailSalesReturnRealm)
case demand(value: MSRetailDemandRealm)
var realm: MSRetailRealm {
switch self {
case .salesReturn(let realm):
return realm
case .demand(let realm):
return realm
}
}
}
Then, when you want to access those properties on a specific value of the enum, just use:
let operation = RetailDemandReturnOperation.salesReturn(value: MSRetailSalesReturnRealm())
let title = operation.realm.title
Since RetailDemandReturnOperation always has the associated value for MSRetailRealm, you can give it a new property of type RetailDemandReturnOperation. Then, you can get rid of the the associated value in your enum.
enum RetailDemandReturnOperation {
case salesReturn
case demand
}
protocol MSRetailRealm {
var stats: Int { get set }
var title: String { get set }
var operation: RetailDemandReturnOperation { get }
}
struct MSRetailDemandRealm: MSRetailRealm {
//.. your old code
var operation: RetailDemandReturnOperation { return .demand }
}
struct MSRetailSalesReturnRealm: MSRetailRealm {
//.. your old code
var operation: RetailDemandReturnOperation { return .salesReturn }
}
Now you can access stats and title no matter what operation it is. And if you care about the operation, simply access the operation property.
func example(object: MSRetailRealm) {
let titleString = object.title
switch object.operation {
case .salesReturn:
break
case .demand:
break
}
}
Related
I'm kinda block for this scenario , I have a enum which have the same value now the question here is that they have different usecases how can I put a condition for this to case in switch:
supposed I have this:
enum ApiType: String, CaseIterable {
case dashboard = "dashboardRootIdApi"
case profile = "profileRootIdApi"
case usemeInLogin = "authenticationAccessIdApi"
case usemeInLogout = "authenticationAccessIdApi"
}
and from my usecases classes:
func authenticationDtoScreen(for: ApiType) -> UIControllerSharedScreen {
switch myType {
case .usemeInLogin: {
return UIControllerScreenConfiguration(
for: .usemeInLogin,
title: "Login"
)
}
case .usemeInLogout: {
return UIControllerScreenConfiguration(
for: .usemeInLogout,
title: "Logout"
)
}
}
}
I know .usemeInLogout will never be happend cause of this .usemeInLogin.
Those string values don't have to be the raw value of your enum. It can be a calulated property:
enum ApiType: CaseIterable {
case dashboard
case profile
case usemeInLogin
case usemeInLogout
var apiType: String {
switch self {
case .dashboard: return "dashboardRootIdApi"
case .profile: return "profileRootIdApi"
case .usemeInLogin, .usemeInLogout: return "authenticationAccessIdApi"
}
}
}
Its a bit hard without context, but I'd suggest using simple enum for use case differentiation:
enum MyType: String {
case usemeInLogin
case usemeInLogout
}
And if needed, have a map from this enum to String, like:
var map: [MyType:String] = [:]
map[.usemeInLogin] = "something"
map[.usemeInLogout] = "something"
I have the following code to implement animation models based on this answer:
public enum AnimationType {
public enum Kind<Value> {
case scalar(Value)
case keyframes([Keyframe<Value>])
}
case position(Kind<Float>)
case scale(Kind<Float>)
case rect(Kind<CGRect>)
case transform(Kind<CGAffineTransform>)
....
....
}
public class Keyframe<T> : Codable, Comparable where T:Codable, T:Interpolatable {
public var time:CMTime
public var property:String
public var value:T
...
...
}
This data model was chosen as I found it type safe for each animation property as it couples data type with the property (for instance, value for property transform can only be CGAffineTransform, the code wouldn't accept anything else). But now I face two troubles:
Directly fetching property value or modifying keyframes is not so easy, one needs to write a big switch statement everytime or atleast if case let statement to fetch the property, which makes code look messy if done at hundreds of places,
Most important, Swift enums are pass by value but I realize I need pass by reference or class based implementation. This is because it would be much easier to modify the underlying object parameters in case of pass by reference. In case of pass by value such as enum, I need to again create new values and pass them to the animation code (which has it's own data model for rendering animation). Reconstructing or updating data structures for animation rendering is a pain and can be avoided with class.
However, I am not sure if there is such a type safe approach to convert such an enum to class, or make enum pass by reference for that matter. Any inputs are welcome.
If you want to keep using enums I think you can make issue 1 a bit easier to deal with if you add an optional property for each associated value in the enum cases.
Something like:
public enum AnimationType {
public enum Kind<Value> {
case scalar(Value)
case keyframes([Keyframe<Value>])
var scalar: Value? {
guard case let .scalar(value) = self else { return nil }
return value
}
var keyframes: [Keyframe<Value>]? {
guard case let .keyframes(keyframes) = self else { return nil }
return keyframes
}
}
case position(Kind<Float>)
case scale(Kind<Float>)
case rect(Kind<CGRect>)
case transform(Kind<CGAffineTransform>)
var position: Kind<Float>? {
guard case let .position(kind) = self else { return nil }
return kind
}
var transform: Kind<CGAffineTransform>? {
guard case let .transform(kind) = self else { return nil }
return kind
}
}
With this you won't need to have the big switch you are mentioning because you directly try to get the associated value you need.
You can also have a look at swift-case-paths which basically adds Keypath support to enum with associated values and removes the need to add this vars boilerplate.
I wanted to make an enum with the Contacts framework CNLabeledValue's CNPhoneNumber type which are defined as:
// Generic labels
CONTACTS_EXTERN NSString * const CNLabelHome NS_AVAILABLE(10_11, 9_0);
CONTACTS_EXTERN NSString * const CNLabelWork NS_AVAILABLE(10_11, 9_0);
CONTACTS_EXTERN NSString * const CNLabelSchool NS_AVAILABLE(10_15, 13_0);
CONTACTS_EXTERN NSString * const CNLabelOther NS_AVAILABLE(10_11, 9_0);
I did something like this:
enum CNLabeledValueType: String {
case home
case work
case school
case other
var rawValue: String {
switch self {
case .home:
return CNLabelHome
case .work:
return CNLabelHome
case .school:
return CNLabelSchool
default:
return CNLabelOther
}
}
}
But I think I still need to do some map the enum to the correct runtime strings, do I need to also override the init of the enum somehow?
What I would like to achieve would be the same result as if this would be possible:
enum CNLabeledValueType: String {
case home = CNLabelHome
case work = CNLabelWork
case school = CNLabelSchool
case other = CNLabelOther
}
But it is not possible because Swift enums require that "Raw value for enum must be a String literal" as the compiler complains. So is there any way to make something similar using computed properties to be possible to switch by case string and also get the correct string computed value at runtime for each case?
You can implement init(rawValue:) like this:
init?(rawValue: String) {
switch rawValue {
case CNLabelHome:
self = .home
case CNLabelWork:
self = .work
case CNLabelSchool:
self = .school
case CNLabelOther:
self = .other
default:
return nil
}
}
Also note that you have a typo in the rawValue implementation. The second case should return CNLabelWork. I also suggest not to use default: in the raw value switch statement:
var rawValue: String {
switch self {
case .home:
return CNLabelHome
case .work:
return CNLabelWork
case .school:
return CNLabelSchool
case .other:
return CNLabelOther
}
}
This way, when you want to add a new label, an error will appear at the switch statement, so you won't forget to add another case.
I have had to use type erasure in Swift a few times however it always involved a generic protocol. In this case, it involves both a generic enum and and generic protocol and I'm stumped.
Here is my generic enum and generic protocol with the necessary extension:
enum UIState<T> {
case Loading
case Success([T])
case Failure(ErrorType)
}
protocol ModelsDelegate: class {
associatedtype Model
var state: UIState<[Model]> { get set }
}
extension ModelsDelegate {
func getNewState(state: UIState<[Model]>) -> UIState<[Model]> {
return state
}
func setNewState(models: UIState<[Model]>) {
state = models
}
}
And here is my type erased generic class:
class AnyModelsDelegate<T>: ModelsDelegate {
var state: UIState<[T]> {
get { return _getNewState(UIState<[T]>) } // Error #1
set { _setNewState(newValue) }
}
private let _getNewState: ((UIState<[T]>) -> UIState<[T]>)
private let _setNewState: (UIState<[T]> -> Void)
required init<U: ModelsDelegate where U.Model == T>(_ models: U) {
_getNewState = models.getNewState
_setNewState = models.setNewState
}
}
I'm getting the following errors (they are marked in the code sample):
Error #1:
Cannot convert value of type '(UIState<[T]>).Type' (aka 'UIState<Array<T>>.Type') to expected argument type 'UIState<[_]>' (aka 'UIState<Array<_>>')
I have been working on this for awhile and there have been quite a few variations on this code that "almost worked". The error always has to do with the getter.
The problem that causes this error, as #dan has pointed out, is that on this line you're trying to pass a type as an argument, instead of an instance of that type:
get { return _getNewState(UIState<[T]>) }
However, I would question your use of an argument to this function in the first place, surely a getting function should have no argument at all? In this case you'll simply want your _getNewState function to have the signature () -> UIState<[T]>, and call it like so:
get { return _getNewState() }
Also, if your getNewState and setNewState(_:) functions in your protocol extension only exist in order to forward the getting and setting of your state property to the type-erasure – you can simplify your code by getting rid of them entirely and use closure expressions in the type-erasure's init instead:
_getNewState = { models.state }
_setNewState = { models.state = $0 }
(These work by capturing a reference to the models argument, for more info see Closures: Capturing Values)
Finally, I suspect that you mean to refer to UIState<T> rather than UIState<[T]> throughout your code, as T in this case refers to an element in the array that your .Success case has as an associated value (unless you want a 2D array here).
All in all, with the above proposed changes, you'll want your code to look something like this:
enum UIState<T> {
case Loading
case Success([T])
case Failure(ErrorType)
}
protocol ModelsDelegate: class {
associatedtype Model
var state: UIState<Model> { get set }
}
class AnyModelsDelegate<T>: ModelsDelegate {
var state: UIState<T> {
get { return _getNewState() }
set { _setNewState(newValue) }
}
private let _getNewState: () -> UIState<T>
private let _setNewState: (UIState<T>) -> Void
required init<U: ModelsDelegate where U.Model == T>(_ models: U) {
_getNewState = { models.state }
_setNewState = { models.state = $0 }
}
}
I've got an enum in Swift. It's kind of like
enum LegalArgs {
case AsString(String)
case AsBool(Bool)
... etc
}
I want to access this enum conditionally by type. So if I have an instance of LegalArgs, I can pass T and get back a T? if the instance was of that type. Otherwise I will have to duplicate a bunch of code for different cases.
My current code looks a bit like this:
String? maybeAsString(arg: LegalArgs) {
switch arg {
case .AsString(let str):
return str;
default:
return nil;
}
}
The problem is that I've got to duplicate this function for every case in the enum.
You can use a generic asType function:
enum LegalArgs {
case AsString(String)
case AsBool(Bool)
case AsNumber(Int)
func asType<T>(type: T.Type) -> T? {
switch self {
case AsString(let str): return str as? T
case AsBool(let bol): return bol as? T
case AsNumber(let num): return num as? T
}
}
}
// usage
LegalArgs.AsBool(true).asType(Bool.self) // true
LegalArgs.AsBool(true).asType(String.self) // nil