Swift- access enumeration by type - ios

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

Related

How to check enum in same value using switch in swift

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"

Objective-C External constant property to Swift enum conversion

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.

Printing imported enum case gives enum name instead of case name

As per documentation and this thread, among others, for an enum of Ints I can print the case name as a string by simply doing this:
enum TestEnum: Int {
case one
case two
case three
}
let testEnum = TestEnum.two
print(testEnum)
// prints "two"
Which works of course. But if I try to do the same thing with CKAccountStatus, it prints the name of the enum:
import CloudKit
let testStatus = CKAccountStatus.noAccount
print(testStatus)
// prints "CKAccountStatus"
CKAccountStatus is an enum of Ints, just like the test enum above:
public enum CKAccountStatus : Int {
case couldNotDetermine
case available
case restricted
case noAccount
}
What am I doing wrong and/or why is this happening?
Your TestEnum is a swift enum. CKAccountStatus could be Objective C enum.
You can achieve it by confirming the CustomStringConvertible protocol by adding:
extension CKAccountStatus: CustomStringConvertible {
public var description: String {
switch self {
case .noAccount:
return "noAccount"
case .available:
return "available"
case .restricted:
return "restricted"
case .couldNotDetermine:
return "couldNotDetermine"
}
}
}
let testStatus = CKAccountStatus.available
print(testStatus) // available

Swift get associated value in enums without switch

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
}
}

How do I implement a Swift protocol with a generic constrained type property?

I would like to have a protocol that looks something like this:
protocol ReturnType {
var returnType: ImmutableMappable.Type { get }
}
The part of the enum implementing the protocol:
extension ShimEndPoint: ReturnType {
var returnType: ImmutableMappable.Type {
switch self {
case .deAuthorize(_, _):
return EmptyResponse.self
case .authorize(_, _):
return AuthorizeResponse.self
case .step(_, _, _, _):
return StepResponse.self
}
}
}
EmptyResponse, AuthorizeResponse and StepResponse all implement ImmutableMappable.
Now I would like to use the "returnType" property in a function call:
return Shim.provider
.request(endPoint)
.timeout(7,
scheduler: MainScheduler.asyncInstance)
.retry(3)
.mapObject(endPoint.returnType)
The line mapObject gives me the following compiler error:
"Cannot convert value of type 'ImmutableMappable.Type' to expected argument type 'T.Type'
The function signature of "mapObject" is:
public func mapObject<T : ImmutableMappable>(_ type: T.Type) -> RxSwift.Observable<T>
How do I define and implement the protocol so I can pass in my returnType to the mapObject function?
I found a similar question but unfortunately I could not solve my problem with the help of the answer given:
Returning constrained generics from functions and methods
Personally, I feel like some of your code just doesn't really make sense. Like :
extension ShimEndPoint: ReturnType {
var returnType: ImmutableMappable.Type {
switch self {
case .deAuthorize(_, _):
return EmptyResponse.self
case .authorize(_, _):
return AuthorizeResponse.self
case .step(_, _, _, _):
return StepResponse.self
}
}
}
How can an object determined the type of itself based on itself ? And then return a value type different than itself ?
So what I'm trying to help here is just trying to find a solution of what you are trying to achieve, rather than solve the existing code.
I assume that what you trying to do is determined a type of object to map to depends on the result case at runtime. The enum itself is ImmutableMappable and it has 3 cases : .authorize, .deauthorize and .step.
The function .request will return this type and determined the type to map to, accordingly to the case. However, since the type of object need to be determined at compile time, all the result type EmptyResponse, AuthorizeResponse and StepResponse here must be following a same protocol ( I assume, Response here) and the result will also only return the protocol rather a specific class type.
return Shim.provider
.request(endPoint)
.timeout(7,
scheduler: MainScheduler.asyncInstance)
.retry(3)
.flatMap { result: ImmutableMappable -> Observable<Response> in
return Observable.create{
switch result {
case .authorize:
observer.onNext(AuthorizeResponse())
case .deAuthorize:
//etc
case step:
//etc.
}
}
}
Hope this answer your question !

Resources