Converting Swift enum to Class - ios

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.

Related

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: can properties be required based on another property being set?

I want to re-use a single viewcontroller but re-purposed slightly and I'm wondering if there is a structured way to require some properties to be set based on another property.
For example assume that the viewcontroller has the following properties
- var displayMode: DisplayMode // see below
- var id: Int
- var description: String
- var name: String
If we are in quickView mode then I expect a ID and Description values to be set.
Else if we are in defaultView mode I expect the Name property to be set.
enum DisplayMode {
case quickView
case defaultView
}
Obviously I could just set those and expect them to be set but I'm wondering if there is a structured Swift-like way of forcing this, like having the properties nested in the DisplayMode type?
Consider using associated values for your enum cases, like so:
enum DisplayMode {
case quickview(id: Int, description: String)
case defaultview(name: String)
}
This forces the user to provide valid associated values whenever a DisplayMode variable is declared:
var mode = DisplayMode.quickView(id: 11, description: "Prosecco")
To get the associated values back out, you bind them to variables in your switch:
switch mode {
case let .quickView(id, description):
// do something with id and description
case let .defaultview(name):
// do something with name
}
By using associated values, you wouldn't have to declare stand-alone properties (i.e. object variables) for id, description, or name.
I think something like this is what you want:
var displayMode: DisplayMode {
didSet {
if displayMode == quickview {
// self.id = whatever
// do whatever else you want
}
else if displayMode == default {
// self.id = whatever
// do whatever else you want
}
}
}
var id: Int
var description: String
var name: String
(Edit: Of course, it'd be a little different because you're using an enum, but you get the gist of it.)
Another option might be to use KVO. https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/KeyValueObserving/KeyValueObserving.html

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.

Swift dynamic property setting based on array keys

I know a few questions already exist that seem to kinda cover what I'm asking (such as Dynamically set properties from Dictionary<String, Any?> in Swift), but this doesn't seem to be quite what I'm looking for.
Basically, I have a base object where I want to set the properties based on an object, like so:
init(data: Dictionary<String,String>)
{
for (key, value) in data
{
//TODO set property as value here?
}
}
I want to be able to pass a Dictionary in of keys/values and have them be dynamically added as properties of the object. I know that this behavior is possible in PHP, for example by doing $this->{$key} = $value, but I am somewhat unfamiliar with Swift so I haven't been able to figure out how to do this yet.
Also, if anybody could tell me the name of the functionality I'm trying to achieve here, that'd be really helpful. Not knowing what this concept is called is making searching for answers difficult :c
I want to expand on the example given by #Okapi. If your class is a subclass of NSObject, then the setValue:forKey: and valueForKey: method are present by default. So, you could simply set your properties using the following code,
class Foo: NSObject {
var x:String=""
var y:String=""
var z:String=""
init(data: Dictionary<String,String>) {
super.init()
for (key,value) in data {
self.setValue(value, forKey: key)
}
}
}
In your object define (or override) setValue:forKey:, then call this from you init method like so:
class Foo {
var x:String=""
var y:String=""
var z:String=""
init(data: Dictionary<String,String>) {
for (key,value) in data {
self.setValue(value, forKey: key)
}
}
func setValue(value: AnyObject?, forKey key: String) {
switch key {
case "x" where value is String:
self.x=value as String
case "y" where value is String:
self.y=value as String
case "z" where value is String:
self.z=value as String
default:
//super.setValue(value, forKey: key)
return
}
}
}

Resources