Sort enum array with associate values - ios

I have enum like below
enum HomeScreenDataType {
case topBanners([TopBanner])
case bottomBanner([BottomBanner])
case productCategory([ProductCategory])
case favouriteStores([FavouriteStores])
case subscriber([Subscriber])
}
I have an array of that enum
var homeScreenData = [HomeScreenDataType]()
5 async API calls will be there for 5 types(TopBanner, BottomBanner, ProductCategory, FavouriteStores, Subscriber) of data and I will append them in above array as I receive data. Once I get data from all 5 APIs, I want to sort them it in specific order(TopBanner, BottomBanner, ProductCategory, FavouriteStores, Subscriber), how to do that?

This would work if the enum could have a raw value that can be used for sorting but that isn't possible in this case but we could fake one by creating a computed property
var sortValue: Int {
switch self {
case .topBanners(_):
return 1
case .bottomBanner(_):
return 2
case .productCategory(_):
return 3
case .favouriteStores(_):
return 4
case .subscriber(_):
return 5
}
}
and then use it when sorting
homeScreenData.sort(by: { $0.sortValue < $1.sortValue })
or make the enum conform to Comparable
extension HomeScreenDataType: Comparable {
static func < (lhs: HomeScreenDataType, rhs: HomeScreenDataType) -> Bool {
lhs.sortValue < rhs.sortValue
}
}
to simplify the sort call
homeScreenData.sort()

Related

Converting Swift enum to Class

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.

How to return closure in the generic

Now I using Realm to create a singleton, but I faced a problem, if I want to return a closure in the generic, and there are different types (Ex:.Case type return [Case], .Product type return [Product]), but I have no idea to implement this fuction.
Could anyone help me with this problem?Thanks!
DataType:
enum DataType{
case Case
case Product
case Category
case Composition
}
fetchItem fuction:
func fetchItem<T>(type: DataType,complete:#escaping ([T]) -> ()) {
var list:[T]? = []
switch type {
case .Case:
let realm = try! Realm()
let caseResult = realm.objects(Case.self)
caseResult.forEach { (item) in
...
}
complete([])
return
case .Product:
return
case .Category:
return
case .Composition:
return
}
}
Templating a function is the class that you want to get.
Let's take for exemple this function
func showTemplate<T>(myVariable: T) {
print(myVariable)
}
Here, if I want to call the showTemplate function I must do it with the type I want :
showTemplate<String>() # print a string
showTemplate<Int>() # print a int
showTemplate<myClass> # print a myClass
So you are having a problem, but in the wrong way because with templating, you HAVE to know the class before you call the function.
You can for example try to use inheritance and templating your motherClass with your wanted class.
class wantedClass {
var myCaseVariable: Case
var myProductVariable: Product
var myThingsVariable: Things
init() {
}
fillClass<T: Case>() {
// set case
}
// Etc etc
}
Moreover, I don't think that templating is the good solution here, I suggest to look at this : Using a Type Variable in a Generic
I believe you cannot do this for completely different types in one place using only generic type (not any class-holders or so). Maybe you should create separate funcs for each item (fetchCaseItems(), fetchProductItems, etc.). It's much clear to read and every func is responsible only for its own data type.

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

Swift- access enumeration by type

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

How to simplify almost equal enum extensions in Swift

I have extensions for about 20 enums which look like this:
extension CurrencyValue : JSONDecodable {
static func create(rawValue: String) -> CurrencyValue {
if let value = CurrencyValue(rawValue: rawValue) {
return value
}
return .unknown
}
static func decode(j: JSONValue) -> CurrencyValue? {
return CurrencyValue.create <^> j.value()
}
}
extension StatusValue : JSONDecodable {
static func create(rawValue: String) -> StatusValue {
if let value = StatusValue(rawValue: rawValue) {
return value
}
return .unknown
}
static func decode(j: JSONValue) -> StatusValue? {
return StatusValue.create <^> j.value()
}
}
They are almost the same except the enum type name and I have 20 of them - thats obviously very stupid. Does anybody have an idea how to reduce them to one, maybe by using generics? I have no idea at the moment.
UPDATE
The enums are as simple as this:
enum CurrencyValue : String {
case EUR = "EUR"
case unknown = "unknown"
}
enum StatusValue : String {
case ok = "ok"
case pending = "pending"
case error = "error"
case unknown = "unknown"
}
And lets assume the following:
Every ENUM has the .unknown case
I need to exchange the enum type in the extension with something generic.
There must be some trick to not implement the same extension multiple times and just alter the type.
UPDATE
As it s stated by Gregory Higley below I use the JSON lib Argo. You can read about the operators there.
The essence of your question is that you want to avoid writing boilerplate code for all of these enumerations when implementing Argo's JSONDecodable. It looks like you've also added a create method, which is not part of the type signature of JSONDecodable:
public protocol JSONDecodable {
typealias DecodedType = Self
class func decode(JSONValue) -> DecodedType?
}
Unfortunately this cannot be done. Swift protocols are not mixins. With the exception of operators, they cannot contain any code. (I really hope this gets "fixed" in a future update of Swift. Overridable default implementations for protocols would be amazing.)
You can of course simplify your implementation in a couple of ways:
As Tony DiPasquale suggested, get rid of .unknown and use optionals. (Also, you should have called this .Unknown. The convention for Swift enumeration values is to start them with a capital letter. Proof? Look at every enumeration Apple has done. I can't find a single example where they start with a lower case letter.)
By using optionals, your create is now just a functional alias for init? and can be implemented very simply.
As Tony suggested, create a global generic function to handle decode. What he did not suggest, though he may have assumed it was implied, was to use this to implement JSONDecodable.decode.
As a meta-suggestion, use Xcode's Code Snippets functionality to create a snippet to do this. Should be very quick.
At the asker's request, here's a quick implementation from a playground. I've never used Argo. In fact, I'd never heard of it until I saw this question. I answered this question simply by applying what I know about Swift to an examination of Argo's source and reasoning it out. This code is copied directly from a playground. It does not use Argo, but it uses a reasonable facsimile of the relevant parts. Ultimately, this question is not about Argo. It is about Swift's type system, and everything in the code below validly answers the question and proves that it is workable:
enum JSONValue {
case JSONString(String)
}
protocol JSONDecodable {
typealias DecodedType = Self
class func decode(JSONValue) -> DecodedType?
}
protocol RawStringInitializable {
init?(rawValue: String)
}
enum StatusValue: String, RawStringInitializable, JSONDecodable {
case Ok = "ok"
case Pending = "pending"
case Error = "error"
static func decode(j: JSONValue) -> StatusValue? {
return decodeJSON(j)
}
}
func decodeJSON<E: RawStringInitializable>(j: JSONValue) -> E? {
// You can replace this with some fancy Argo operators,
// but the effect is the same.
switch j {
case .JSONString(let string): return E(rawValue: string)
default: return nil
}
}
let j = JSONValue.JSONString("ok")
let statusValue = StatusValue.decode(j)
This is not pseudocode. It's copied directly from a working Xcode playground.
If you create the protocol RawStringInitializable and have all your enumerations implement it, you will be golden. Since your enumerations all have associated String raw values, they implicitly implement this interface anyway. You just have to make the declaration. The decodeJSON global function uses this protocol to treat all of your enumerations polymorphically.
If you can provide an example of an enum as well it might be easier to see what could be improved.
Just looking at this I would say you might want to consider removing .unknown from all the enums and just have the optional type represent an .unknown value with .None. If you did this you could write:
extension StatusValue: JSONDecodable {
static func decode(j: JSONValue) -> StatusValue? {
return j.value() >>- { StatusValue(rawValue: $0) }
}
}
No need for all the create functions now. That will cut down on a lot of the duplication.
Edit:
You could possibly use Generics. If you create a protocol DecodableStringEnum like so:
protocol DecodableStringEnum {
init?(rawValue: String)
}
Then make all your enums conform to it. You don't have to write any more code because that init comes with raw value enums.
enum CreatureType: String, DecodableStringEnum {
case Fish = "fish"
case Cat = "cat"
}
Now write a global function to handle all those cases:
func decodeStringEnum<A: DecodableStringEnum>(key: String, j: JSONValue) -> A? {
return j[key]?.value() >>- { A(rawValue: $0) }
}
Finally, in Argo you can have your creature decode function look like this:
static func decode(j: JSONValue) -> Creature? {
return Creature.create
<^> j <| "name"
<*> decodeStringEnum("type", j)
<*> j <| "description"
}
For anyone else coming along, the most up to date answer is to follow: https://github.com/thoughtbot/Argo/blob/td-decode-enums/Documentation/Decode-Enums.md
Essentially, for the enums
enum CurrencyValue : String {
case EUR = "EUR"
case unknown = "unknown"
}
enum StatusValue : String {
case ok = "ok"
case pending = "pending"
case error = "error"
case unknown = "unknown"
}
You only need to do
extension CurrencyValue: Decodable {}
extension StatusValue: Decodable {}
Also as it seems to have been pointed out repeatedly, if you just get rid of the .Unknown field and instead use optionals you can use the builtin support for enums as a RawRepresentable type.
See also: https://github.com/thoughtbot/Argo/blob/master/Argo/Extensions/RawRepresentable.swift for the implementation that makes this possible.

Resources