Swift enum with custom object raw value - ios

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.

Related

Allow Swift function parameter to be of multiple types

A function
func stepperValueChanged(_ myStepper: UIStepper) {
// do stuff with myStepper
}
A second function
func switchValueChanged(_ mySwitch: UISwitch) {
// do stuff with mySwitch
}
How do I create a third (alternative) function that can take either type?
func valueChanged(_ myComponent: /* ??? UIStepper or UISwitch, but nothing else ??? */) {
// do stuff with myComponent
}
I've explored using enums, typealiases and protocols; which resulted in lots of interesting Stackoverflow reads but no solution.
Examples that don't work
// ** DON'T COPY AND PASTE, DONT WORK!! ** //
typealias validUIComponent = UIStepper, UISwitch
// or
typealias validUIComponent = UIStepper & UISwitch
// or
enum UIComponent { case stepper(UIStepper); case _switch(UISwitch) }
// or
protocol UIComponent { }
extension UIStepper: UIComponent { }
extension UISwitch: UIComponent { }
// ** DON'T COPY AND PASTE, DONT WORK!! ** //
Why would I want to do this? Type checking. I don't want any other UI element to be passed to the function.
I realise I could if let/guard let or some other form of checking once in the function body and bail as required, but this would only catch run-time not compile-time type errors.
Also I realise I could use Any? or (better) UIControl and downcast as needed.
func valueChanged(_ myComponent: UIControl) {
// do stuff with
myComponent as! UIStepper
// do stuff with
myComponent as! UISwitch
}
But is there a syntactic/more expressive solution?
You mentioned enums, which sound like an excellent fit for this use case.
You can explicitly only require the types you expect, and nothing else.
By adding a property to the enum you can then expose a UIControl property to interact as needed, without the need for down-casting (which is generally considered to be an anti-pattern).
enum Component {
case `switch`(UISwitch)
case stepper(UIStepper)
var control: UIControl {
switch self {
case .switch(let comp):
return comp
case .stepper(let comp):
return comp
}
}
}
Then ask for a Component as a parameter to the function.
func controlValueChanged(_ myComponent: Component) {
// Now you can use them as a generic UIControl
let control = myComponent.control
// ...or different behaviours for each element
switch myComponent {
case .switch(let swit):
// use the `swit`
case .stepper(let step):
// use the `step`
}
}
Having said that, if the implementations for these types are totally different anyway, it may be more clear to define two separate functions.
I think you are over complicating things here and that there is no need to create any overhead by introducing a protocol or a new type (enum).
Instead I would handle this by using polymorphism and declaring multiple functions with the same signature.
func controlValueChanged(_ switch: UISwitch) {
// code…
}
func controlValueChanged(_ stepper: UIStepper) {
// code…
}
This will keep the calling code nice and tidy and the responsibility of each function clear.
If there are some common code between the two functions then extract that into a third common function

Recursive Swift Either Enum with Generic Associated Values

I want to develop a recursive Either type as a Swift enum (at least a partially working version (;), and so have been playing around with generics (trying to work with the type system).
Here is what I have so far:
enum NumericEitherEnum<Left, Right> {
case left(Left)
case right(Right)
var left: Left? {
switch self {
case .left(let leftie) where leftie is NumericEitherEnum<Int, Float>:
return (leftie as? NumericEitherEnum<Int, Float>)?.left // This won't work (obviously)
case .left(let left):
return left
case .right(_):
return nil
}
}
var right: Right? {
switch self {
case .left(_):
return nil
case .right(let right) where right is NumericEitherEnum:
return (right as? NumericEitherEnum)?.right // This assumes the generic parameters of Left, Right in the current enum so won't work.
case .right(let right):
return right
}
}
}
I get cryptic diagnostic error messages in Xcode, which I haven't been able to get around:
'Replace (leftie as? NumericEitherEnum<Int, Float>)? with NumericEitherEnum<Int, Float>'
'Enum case 'left' cannot be used as an instance member'
What I am trying to achieve:
print(NumericEitherEnum<NumericEitherEnum<Int, Float>, NumericEitherEnum<Int, Float>>.left(.left(3)).left ?? "failed :(") // Parses the innermost value 3
The fix-it doesn't fix the error or advise how to actually address the underlying cause. I think this is probably an edge case for the compiler to parse (maybe even bug) ;). I also realise that in fact it doesn't make sense logically to return an Int for a Left? type but is there any way I can express this within the type system (I tried associated types but I still don't know how to make this dynamic). There's also a problem to handle nested enums that have a different generic type signature (not sure how to express this).
How can I resolve this issue in a better way? Ideally, I want to have a clean call site without too much indirection and extra data structures but would be open to trying out a different data structure if this isn't practically achievable.
So I discovered that Swift 2 introduced support for recursive enums ages ago - source! While this is great, I didn't manage to find any SO questions for this specific problem i.e. Generic Recursive Enums that had also been answered (here). I will summarise the final (partial solution) code now.
It turns out that I needed to describe what the problem is in terms of data structures. Essentially we want .left to contain either a value of type Left or a RecursiveEither<Left, Right>. With this simple idea, we can create two enums - one for the wrapper enum, and one for the nested enum which can take a Value or another wrapper.
enum RecursiveEither<Left, Right> {
case left(ValueOrLeftOrRight<Left, Left, Right>)
case right(ValueOrLeftOrRight<Right, Left, Right>)
var left: Left? {
guard case .left(let leftie) = self else { return nil }
return leftie.left
}
var right: Right? {
guard case .right(let rightie) = self else { return nil }
return rightie.right
}
}
enum ValueOrLeftOrRight<Value, Left, Right> {
case value(Value)
indirect case left(ValueOrLeftOrRight<Left, Left, Right>)
indirect case right(ValueOrLeftOrRight<Right, Left, Right>)
var left: Left? {
switch self {
case .value(let left): return left as? Left
case .left(let content): return content.left
default: return nil
}
}
var right: Right? {
switch self {
case .value(let right): return right as? Right
case .right(let content): return content.right
default: return nil
}
}
}
The call site is nice with this design:
let e = RecursiveEither<Int, Int>.left(.left(.left(.left(.value(3)))))
That being said, there are still limitations to this answer - assuming Left and Right won't change even in nested Either. Also, it's possible to design this differently with only one indirect case to wrap the Either type in another associated value - that might be more memory efficient since fewer frames get pushed onto the call stack at runtime.
I have posted a gist with attempts on this problem.
If anyone has suggestions to improve this implementation, feel free to add to this.

How do I change value of a property of type Driver<Bool> using a property of type Driver<Void>?

I am new to RxSwift and I apologise how badly the question is framed but I couldn't find the proper terminology.
So basically I have a let infoIconTapped: Driver<Void> in one struct, and I have let shouldShowInfoPopup: Driver<Bool> in another struct.
What I want to do is I want to change the value of (or drive it) shouldShowInfoPopup to true or false using infoIconTapped. This should be done using Driver only which is my requirement.
Any idea on how to do this?
It sounds like what you want to do is toggle the visibility of the popup based on the tapping of the button. If so, then you need to maintain state and that means using the .scan operator.
func shouldShowInfoPopup(infoIconTapped: Driver<Void>) -> Driver<Bool> {
return infoIconTapped
.scan(false) { current, _ in !current }
.startWith(false)
}
The above would be a free function (not in any class or struct) and could be used like this:
let showInfo = shouldShowInfoPopup(infoIconTapped: infoIconTapped)
If you must put the function in a class or struct, then put it in an extension on Driver itself like this:
extension SharedSequence where SharingStrategy == DriverSharingStrategy {
var shouldShowInfoPopup: Driver<Bool> {
scan(false) { current, _ in !current }
.startWith(false)
}
}
Which could be used like this:
let shouldShowInfoPopup = infoIconTapped.shouldShowInfoPopup

Factory pattern with different protocols in swift

I am trying to use factory pattern in swift, and give is my code
I have two protocols
protocol MyProtocol1{
func callLoginAPI()
}
protocol MyProtocol2{
func callPaymentAPI()
}
I have two structures which conform to these protocols
struct MyManager1: MyProtocol1{
func callLoginAPI()
{
debugPrint("Inisde--MyManager1 and ready to call an Login API")
}
}
struct MyManager2: MyProtocol2{
func callPaymentAPI()
{
debugPrint("Inisde--MyManager2 and ready to call Payment API")
}
}
I want to use the factory pattern to create the instance of Manager by passing the Protocol and returning a concrete object of the struct that conforms to that protocol
Example: ManagerFactory.create(MyProtocol1) --> should give me instance of MyManager1 and doing ManagerFactory.create(MyProtocol2) --> should give me instance of MyManager2
I assume that this may not work as I am asking for a concrete type at runtime, so I ended up doing something like this
protocol IManagerFactory{
func getMyManager1() -> MyProtocol1
func getMyManager2() -> MyProtocol2
}
struct ManagerFactory: IManagerFactory
{
func getMyManager1() -> MyProtocol1 {
return MyManager1()
}
func getMyManager2() -> MyProtocol2 {
return MyManager2()
}
}
But I am curious if what I am trying to do in the example is achievable, I am using swift 4.2.
I have seen other examples but all of them have the same protocol which they conform to like rectangle, square and circle conform to same protocol shape.
In my case, I have two separate protocols which do completely different things so is that even possible what I am trying to do in the example? OR the way I ended up with is the only way to go about it.
Please suggest what is the best possible approach.
First, I'm very suspicious of a "manager" that is a value (a struct) rather than an instance (a class). Do you really mean to be using structs and protocols here at all? Structs have no identity; two structs with the same properties must be completely interchangeable for each other, and things like that don't usually get named "manager."
What you're describing is certainly writeable. It's just kind of useless. Here's how you write it:
struct ManagerFactory {
static func create(_ type: MyProtocol1.Protocol) -> MyProtocol1 {
return MyManager1()
}
static func create(_ type: MyProtocol2.Protocol) -> MyProtocol2 {
return MyManager2()
}
}
let mgr1 = ManagerFactory.create(MyProtocol1.self)
let mgr2 = ManagerFactory.create(MyProtocol2.self)
But this is just an elaborate way to use method overloading to replace names.
What you seem to want is a single method, but what would its return type be? Even in C#, I don't think this is writable without adding nasty downcasts. (This is the point you and paulw11 discuss in the comments.) This isn't a limitation of Swift; it's a fundamental characteristic of types. Even in JavaScript, you'd need to know what you expect to get back or else you won't know what methods you can call on it (it's just you track that expectation in your head and docs rather than in the compiler and code).
You seem to have created a lot of protocols here, and I'm betting they mostly have exactly one implementation. That's bad Swift. Don't create protocols until you have a specific need for one. Don't create attractions for fun. They will burn you very quickly.
If your protocols really look like this, and you really have different implementations of these methods (i.e. don't do this if there is only one implementation of MyProtocol1 "just in case"), what you really want are functions:
struct API {
let login: () -> Void
let payment: () -> Void
}
let myAPI = API(login: myLoginFunction, payment: myPaymentFunction)
This will let you create different APIs if you need them. But again, only if you need this flexibility. If not, just use classes for instances, and structs for values, and avoid protocols until you have at least 2, and better 3, implementations in your program.
Suggest a swifty option:
protocol Manager {
// a generic protocol for all managers
}
protocol BadManager: Manager {
// properties and behaviour of BadManager
mutating func noBonus()
}
protocol GoodManager: Manager {
// properties and behaviour of GoodManager
mutating func bigBonus()
}
protocol ManagerFactory {
associatedtype Manager
mutating func createManager() -> Manager
}
struct gm: GoodManager {
mutating func bigBonus() {
print("tons of cookies & coffee")
}
}
struct bm: BadManager {
mutating func noBonus() {
print("all fired")
}
}
enum ManagerCreator<Manager>: ManagerFactory {
func createManager() -> Manager {
switch self {
case .goodManager:
return gm() as! Manager
case .badManager:
return bm() as! Manager
}
}
case goodManager
case badManager
}

Is it possible to add a case to an existing enumeration where there is no source?

I am implementing my own TableView because I would like to have special animation when reloading a row using reloadRowsAtIndexPaths:withRowAnimation.
The problem is that I need to give a type of animation when I'm calling this method. So I would like to know if it's possible to add an extra case to UITableViewRowAnimation enumeration?
No, it's not possible, for at least one reason: switch statements not using a default case wouldn't compile, because they would be missing the newly added case.
Consider this enum:
enum TestEnum {
case ONE
case TWO
}
and some code using it:
let testOne = TestEnum.ONE
switch testOne {
case .ONE:
println("one")
case .TWO:
println("two")
}
If you were able to add one or more cases in an extension:
extension TestEnum {
case THREE
}
then the switch statement written above wouldn't compile because the new case is not handled.

Resources