Swift mark enum elements with special keyword - ios

I need to know if Swift language has some way to achieve this feature of marking some elements of an enum. For instance, I have a set of properties in Swift enum and some of the properties are animatable while others are not. I want something like:
public enum Property: String {
case prop1,
case prop2 #animatable,
case prop3 #animatable,
case prop4,
case prop5 #animatable,
...
...
...
case prop100
}
And then in a function I can pass an argument such as:
func animate(_ prop: Property #animatable, startTime:CFTimeInterval, duration:CFTimeInterval) {
}
So I pass a property which are elements of enum that are only animatable, not others. Is there a clean way to achieve something like this in Swift language (Swift 5)?

Related

Swift enum with custom object raw value

I try to implement a state machine for my ViewController, so i create a enum to express the possible state of my ViewController :
enum SMState:RawRepresentable{
case empty,data(UIView),failed(UIView),noData(UIView)
}
The 4 state of enum is suitable for my ViewController, and some state associate with a custom view to show when ViewController enter the specify state.
Then i make SMState impl RawRepresentable Protocol
enum SMState:RawRepresentable{
case empty,data(UIView),failed(UIView),noData(UIView)
typealias RawValue = UIView
init?(rawValue: SMState.RawValue) {
// rawValue is a view but i can't judge what to return
return what???
}
var rawValue: UIView{
switch self {
case .data(let v):
return v
case .failed(let v):
return v
case .noData(let v):
return v
case .empty:
return UIView()
}
}
}
How should i implement init?(rawValue:SMState.RawValue) function above, i can't image how to do this.
Update
Why i implement RawRepresentable :
I think enum is more suitable for representable different state for ViewController instead of Class or Struct, but enum cannot contains stored property, it can only carry a UIView Object through RawRepresentable, any better idea or magic is welcome :D
#Hamish You are right, i shouldn't insist using RawRepresentable, after a nice sleep, i finally archive what i want :
//: Playground - noun: a place where people can play
import UIKit
enum SMState{
case empty,data(UIView),failed(UIView),noData(UIView)
}
extension SMState{
init() {
self = .empty
}
init(failed view:UIView) {
self = .failed(view)
}
init(data view:UIView) {
self = .data(view)
}
init(noData view:UIView) {
self = .noData(view)
}
}
let state = SMState(failed: UIView())
switch state {
case .failed(let v):
print(v)
default:break
}
This is what i want, each enum state own a separate View, i do different operation in different state, using the View that the state carry ~
This approach is much like using Class, in my situation, i am not sure that there may be more states, then it is impossible to extend or subclass the SMState because its a enum, i am think about using Class
What i am trying to do is much like this library StatefulViewController, and i want to make it more flexible.
Any suggestion is welcome ~
:D
There is already available a specific state machine you could use instead of inventing the wheels: GKStateMachine. It has all the required method to transition from state to state. We also use it for different view controller states. Just take you time to read a bit and find some examples, to have your code nice and clean. You will also be able to combine the implementation with a corresponding enum of states, which will just define the states without a need of having associated values there.

How to pass enum as a parameter in a optional method within a protocol

enum SearchBarAction {
case Cancel
case Bookmark
case Location
case Category
case Filter
}
#objc protocol SearchBarNavigatorDelegate: class {
optional func searchBarNavigator(clicked: SearchBarAction)
}
This gave me an error
Method cannot be a member of an #objc protocol because the type of the parameter cannot be represented in Objective-C
I need this method to be optional therefore i cannot remove #objc to solve this.
Is there any way to use optional protocol method while passing an enum as a parameter to the method?
It will work if you declare the enumeration as #objc with raw type:
#objc enum SearchBarAction: Int {
case cancel
case bookmark
case location
case category
case filter
}

Swift Variable Constraints

Is it possible in the current iteration of Swift (2.1) to add constraints on a variable type?
If I have a class Element
class Element: NSObject {
var type: ElementType
init(type: ElementType) {
self.type = type
}
}
with an enum ElementType
enum ElementType {
case Cell
case Header
case Footer
case Decoration(kind: String)
}
in some other class if I have a variable of type Element, is it at all possible to put constraints like:
var element: Element? where Self == .Header
or instead would I have to override didSet
var element: Element? {
didSet {
if let value = element where value.type == Decoration {
element = Optional.None
}
}
}
I'm sure it's not possible, the generic system isn't as powerful as I would like (being able to only constrain extensions by protocol and class inheritance for example and no variadic generic parameters).
Type constraints and types checks are a compilation feature but you want to check the runtime value of an object.
I am pretty sure that's impossible. You will have to use a runtime check (e.g. willSet or didSet).
Or, instead of enforcing the type by the value of a type atrribute, enforce it with a real type, e.g.
subclassing
class Header: Element
or make Element a protocol implemented by four separate classes:
protocol Element {
}
class Header: Element
(you can use protocol extension for shared functions).

How to declare variable and enum declaration in one line?

Is there a way to simplify this into one line in Swift 2.0?
enum Direction {
case Up
case Down
}
var panDirection: Direction?
Something like this, which doesn't work:
var panDirection = enum Direction {
case Up
case Down
}
Even if you could do it, those are not the same at all. enum is an object type, like class. In your first example, panDirection is an instance of the Direction enum. In your second example, if it could compile and run, panDirection would end up as the enum itself (the type, not an instance of the type) — which is not at all what you want.
Thus, what you are trying to do is to declare a type in the middle of a line. You can't do that. The rules for where you can declare a type are very clear and very strict.
Note, however, that you can declare a type within another type, or even purely locally, e.g. within a function's code. Thus, for example, you can declare the type temporarily as a way of passing data around inside a function. Nutty but legal:
func myCoolFunction(up:Bool) {
enum Direction : String {
case Up
case Down
}
let dir : Direction = (up ? .Up : .Down)
print("user wants \(dir)")
}
No, an enum type must be declared separately before it can be used as a variable's type.

Swift dynamic variable can't be of type Printable

I have a Swift project that contains two UITableViewControllers. The second UITableViewController is linked to a MVC model called Model. According to the UITableViewCell I select in the first UITableViewController, I want to initialize some properties of Model with Ints or Strings. Therefore, I've decided to define those properties with Printable protocol type. In the same time, I want to perform Key Value Observing on one of these properties.
Right now, Model looks like this:
class Model: NSObject {
let title: String
let array: [Printable]
dynamic var selectedValue: Printable //error message
init(title: String, array: [Printable], selectedValue: Printable) {
self.title = title
self.array = array
self.selectedValue = selectedValue
}
}
The problem here is that the following error message appears on the selectedValue declaration line:
Property cannot be marked dynamic because its type cannot be
represented in Objective-C
If I go to the Xcode Issue Navigator, I can also read the following line:
Protocol 'Printable' is not '#objc'
Is there any workaround?
There is no way to do what you want. Non-#objc protocols cannot be represented in Objective-C. One reason is that Non-#objc protocols can represent non-class types (and indeed, you said that you wanted to use it for Int and String, both non-class types), and protocols in Objective-C are only for objects.
KVO is a feature designed for Objective-C, so you must think about what you expect it to see from the perspective of Objective-C. If you were doing this in Objective-C, you would not want to have a property that could either be an object like id or a non-object like int -- you can't even declare that. Instead, as you said in your comment, you probably want it to be just objects. And you want to be able to use Foundation's bridging to turn Int into NSNumber * and String into NSString *. These are regular Cocoa classes that inherit from NSObject, which implements Printable.
So it seems to me you should just use NSObject or NSObjectProtocol.
Unfortunately ObjC does not treat protocols as types, they are just a convenient way of grouping members. Under the covers they are of type Any, so regretfully you will have to make the property Any and cast to Printable.
The best I can thing of is:
dynamic var selectedValue: Any
var printableValue : Printable {
get {
return (Printable)selectedValue
}
set {
selectedValue = newValue
}
}

Resources