Is this the best way to convert Swift protocol to RxDelegateProxy? - ios

Sorry I could not come up with better title than that, Ill modify it if anybody suggests a better one after.
I have a protocol
#objc public protocol MyCollectionViewProtocol {
func scrollViewShouldScrollToTop()
}
I have declared it to be #objc because unfortunately DelegateProxy does not work with non NSObject protocols (I assume, if somebody can clarify that, will be a great help)
My collectionView
public class MyCollectionView: UICollectionView {
weak var cvDelegate : MyCollectionViewProtocol?
... //rest of the code isnt related to this question in particular
Now I declare delegate proxy as
open class RxMyCollectionViewDelegateProxy : DelegateProxy<MyCollectionView, MyCollectionViewProtocol>
, DelegateProxyType
, MyCollectionViewProtocol {
public static func currentDelegate(for object: MyCollectionView) -> MyCollectionViewProtocol? {
return object.cvDelegate
}
public static func setCurrentDelegate(_ delegate: MyCollectionViewProtocol?, to object: MyCollectionView) {
object.cvDelegate = delegate
}
public weak private(set) var collectionView: MyCollectionView?
internal lazy var shouldScrollPublishSubject: PublishSubject<Void> = {
let localSubject = PublishSubject<Void>()
return localSubject
}()
public init(collectionView: ParentObject) {
self.collectionView = collectionView
super.init(parentObject: collectionView, delegateProxy: RxMyCollectionViewDelegateProxy.self)
}
// Register known implementations
public static func registerKnownImplementations() {
self.register { RxMyCollectionViewDelegateProxy(collectionView: $0) }
}
//implementation of MyCollectionViewProtocol
public func scrollViewShouldScrollToTop() {
shouldScrollPublishSubject.onNext(())
self._forwardToDelegate?.scrollViewShouldScrollToTop()
}
deinit {
shouldScrollPublishSubject.onCompleted()
}
}
Finally I declare my Reactive extension for MyCollectionView as
extension Reactive where Base: MyCollectionView {
public var delegate: DelegateProxy<MyCollectionView, MyCollectionViewProtocol> {
return RxMyCollectionViewDelegateProxy.proxy(for: base)
}
public var shouldScrollToTop: ControlEvent<Void> {
let source = RxMyCollectionViewDelegateProxy.proxy(for: base).shouldScrollPublishSubject
return ControlEvent(events: source)
}
}
Finally, I use it as
collectionView.rx.shouldScrollToTop.debug().subscribe(onNext: { (state) in
print("I should scroll to top")
}, onError: { (error) in
print("errored out")
}, onCompleted: {
print("completed")
}, onDisposed: {
print("Disposed")
}).disposed(by: disposeBag)
Question
Because none of the online tutorials(Raywenderlich)/courses (Udemy)/Books(Raywenderlich) explains how to convert the swift protocol to Rx style am confused as what I am doing is correct or wrong. The code works but even the worst designed code might work, hence I wanna be sure what am doing is correct or am messing something. I wrote the above code following the approach used in UIScrollView+Rx.swift and RxScrollViewDelegateProxy.swift
Though the code above works only for protocols without any return type example method I used above func scrollViewShouldScrollToTop() has no return type associated with it. I could not imagine how could I use the DelegateProxy above to convert the protocol methods with return types, like numberOfRowsInSection which has Int as return type.
I happened to look at the RxDataSource implementation and realized in order to convert cellForRowAtIndexPath RxDataSource constructor expects you to pass the block as a init parameter and executes it whenever tableView calls cellForRowAtIndexPath in its proxyDelegate.
Now I could do the same thing if thats the only way out. Need to know is that how am supposed to code it or can I modify ProxyDelegate implementation above to convert the protocol method with return types.

Related

Swift - How can I test with generic?

I wanna unittest with generics.
But I can't think of a way.
My code is as below, and I want to test the part prepare().
class A_ViewModel {
var useCase: UseCaseProtocol = UseCase()
var item: A_CellModel = .init()
// I want to unittest prepare()
func prepare() {
// logic....
// and call calcuate
useCase.calculate(item: item)
}
}
protocol TestableProtocol {
var testProperty: Bool { get set }
}
class A_CellModel: TestableProtocol {
var testProperty: Bool = false
}
protocol UseCaseProtocol {
func calculate<T: TestableProtocol>(item: T)
}
class UseCase: UseCaseProtocol {
func calculate<T: TestableProtocol>(item: T) {
// logic......
}
}
However, since calcaulte(item:) in usecase uses generic, it is necessary to hand over the clear type (A_CellModel).
In that case, there is a dependence between A_ViewModel and A_CellModel, which makes it difficult to test the unit.
In order to test prepare(), should calcaulte(item:) give up generic?
Should I use existential type?

Subscribing to a property

I have a singleton service class which maintains a value of heading it gets from the compass. I have a UIView which draws some custom graphics based on this. I'm trying to do something like an Observable in javascript, where my code gets executed when there's a change in the value.
final class LocationService: NSObject {
static let shared = LocationService()
public var heading:Int
public func getHeading() -> Int {
return self.heading
}
Then in my UIView subclass:
var ls:LocationService = LocationService.shared
var heading: Int = ls.getHeading() {
didSet {
setNeedsDisplay()
}
}
I tried also just directly accessing the property via ls.heading but this doesn't get accepted either. It's telling me I cannot use the instance member within the property initialiser. What's a proper swift method of doing this?
Edit:
I've been working with Christian's answer below and some other documentation and now got to here where it all compiles nicely, but doesn't actually work. Here's my delegator and protocol:
final class LocationService: NSObject {
static let shared = LocationService()
weak var delegate: CompassView?
var heading:Int
func headingUpdate(request:HeadingRequest, updateHeading:CLHeading) {
print ("New heading found: \(updateHeading)")
self.heading = Int(updateHeading.magneticHeading)
self.delegate?.setHeading(newHeading: Int(updateHeading.magneticHeading))
}
public func getHeading() -> Int {
return self.heading
}
}
protocol LSDelegate: class {
func setHeading(newHeading:Int)
}
Then in the delegate:
class CompassView: UIView, LSDelegate {
func setHeading(newHeading:Int) {
self.heading = newHeading
print("heading updated in compass view to \(self.heading)")
setNeedsDisplay()
}
}
So I get the print message that the heading has been updated in the headingUpdate function. The print message in the setHeading function in the delegate CompassView never gets displayed.
You can use the delegation pattern and have that class that wants to consume your events implement the functions in your protocol.
protocol MyDelegate {
func setNeedsDisplay()
}
class LocationService: NSObject {
var myDelegate : MyDelegate?
var heading: Int = ls.getHeading() {
didSet {
myDelegate?.setNeedsDisplay()
}
}
...
func assignDelegate() {
self.myDelegate = MyConsumer()
}
}
class MyConsumer : MyDelegate {
func setNeedsDisplay()
{
}
}

Override function error in swift

I got a struct :
struct ErrorResultType: ErrorType {
var description: String
var code: Int
}
and a protocol:
protocol XProtocol {
func dealError(error: ErrorResultType)
}
Now I want to make an extention of UIViewController:
extension UIViewController: XProtocol {
func dealError(error: ErrorResultType) {
// do something
}
}
So I can subclass from this and override the function like:
class ABCViewController: UIViewController {
--->override func dealError(error: ErrorResultType) {
super.dealError(error)
// do something custom
}
}
But it goes wrong with: Declarations from extensions cannot be overridden yet
It doesn't make any sense to me. When I replace all ErrorResultType with AnyObject, the error won't appear any more.
Anything I missed?
For now the method in the extension must be marked with #objc to allow overriding it in subclasses.
extension UIViewController: XProtocol {
#objc
func dealError(error: ErrorResultType) {
// do something
}
}
But that requires all types in the method signature to be Objective-C compatible which your ErrorResultType is not.
Making your ErrorResultType a class instead of a struct should work though.
If i am not making mistake this is connected with Swift official extension mechanism for adding methods to classes.
Conclusion :
At the moment, it's not possible to override entities declared in
extension by subclassing, like so:
class Base { }
extension Base {
var foo: String { return "foo" }
}
class Sub: Base {
override var foo: String { return "FOO" } // This is an error
}
Please check this resource for more information : https://github.com/ksm/SwiftInFlux/blob/master/README.md#overriding-declarations-from-extensions

Delegate a structure in swift?

I am developing an app to increase a little more my knowledge about swift. One of my questions if is it possible to delegate a optional function with a structure as an argument.
What yes Im able to do:
#objc protocol someProtocol {
optional func optionalFunc(someClass: someClass)
}
class someClass: NSObject {
}
But, what I want to do (problems representing a structure in objc):
#objc protocol someProtocol {
optional func optionalFunc(someStructure: someStructure)
}
struct someStructure {
}
And Im not able to find the way to solve this problem.
And the other thing I want, is similar to this but with enums instead of structs:
#objc protocol someProtocol {
optional func optionalFunc(someEnum: someEnum)
}
enum someEnum {
case example
}
If somebody can help me, I will be very grateful!
Lot of thanks! Luciano!
Swift 2.0 lets you do default implementations of protocols.
protocol someProtocol {
func optionalFunc(someStructure: SomeStructure)
}
extension someProtocol {
func optionalFunc(someStructure: SomeStructure){
// optional, leave empty
}
}
struct SomeStructure {
}
This way you can get around using the optional-decoration and do what you wanted.
You cannot pass the parameters as struct or enum, because it's only valid on Swift language, so it cannot be represented in Objective-C.
Another approach, you can declare a function as variable instead of func:
protocol someProtocol {
var optionalFunc: (someStructure) -> ()? { get set}
}
Implementation:
class someClass : someProtocol {
var optionalFunc: (someStructure) -> ()? = { yourStruct in
// Do anything with yourStruct
return
}
}
Using:
var someVar:someClass = someClass()
var result = someVar.optionalFunc(someStructure())
The result is a ()?. If you do not implement the variable, result will nil

Swift - How can I override an extension method in a concrete subclass

I have an extension on UIView implementing a protocol
protocol SomeProtocol {
var property : Int
}
extension UIView : SomeProtocol {
var property : Int {
get {
return 0
}
set {
// do nothing
}
}
}
in a concrete subclass I want to override this extension method:
class Subclass : UIView, SomeProtocol {
var _property : Int = 1
var property : Int {
get { return _property}
set(val) {_property = val}
}
}
I set breakpoints and see that the extension method is called and not the concrete subclass method:
var subclassObject = Subclass()
someObject.doSomethingWithConcreteSubclassObject(subclassObject)
// other code;
fun doSomethingWithConcreteSuclassObject(object : UIView) {
var value = object.property // always goes to extension class get/set
}
As others have noted, Swift does not (yet) allow you to override a method declared in a class extension. However, I'm not sure whether you'll ever get the behavior you want even if/when Swift someday allows you to override these methods.
Consider how Swift deals with protocols and protocol extensions. Given a protocol to print some metasyntactic variable names:
protocol Metasyntactic {
func foo() -> String
func bar() -> String
}
An extension to provide default implementations:
extension Metasyntactic {
func foo() -> String {
return "foo"
}
func bar() -> String {
return "bar"
}
}
And a class that conforms to the protocol:
class FooBar : Metasyntactic {
func foo() -> String {
return "FOO"
}
func bar() -> String {
return "BAR"
}
}
Swift will use dynamic dispatch to call the appropriate implementations of foo() and bar() based on each variable's runtime type rather than on the type inferred by the compiler:
let a = FooBar()
a.foo() // Prints "FOO"
a.bar() // Prints "BAR"
let b: Metasyntactic = FooBar()
b.foo() // Prints "FOO"
b.bar() // Prints "BAR"
If, however, we extend the protocol further to add a new method:
extension Metasyntactic {
func baz() -> String {
return "baz"
}
}
And if we override our new method in a class that conforms to the protocol:
class FooBarBaz : Metasyntactic {
func foo() -> String {
return "FOO"
}
func bar() -> String {
return "BAR"
}
func baz() -> String {
return "BAZ"
}
}
Swift will now use static dispatch to call the appropriate implementation of baz() based on the type inferred by the compiler:
let a = FooBarBaz()
a.baz() // Prints "BAZ"
let b: Metasyntactic = FooBarBaz()
b.baz() // Prints "baz"
Alexandros Salazar has a fantastic blog post explaining this behavior in depth, but suffice it to say that Swift only uses dynamic dispatch for methods declared in the original protocol, not for methods declared in protocol extensions. I imagine the same would be true of class extensions, as well.
I know this question has been asked a while ago. But this will be handy for someone who looking for an easier way. There is a way of overriding an extension methods. I know its bit hacky but it does the job beautifully.
If you declare your protocol with #objc
#objc protocol MethodOverridable {
func overrideMe()
}
In Extension
extension MainClass: MethodOverridable {
func overrideMe() {
print("Something useful")
}
}
Subclass - You can able to override it in your subclass. It works like a magic. Well, not really when adding #objc it exposes your protocol to Objective-C and its Runtime. That allows your subclass to override.
class SubClass: MainClass {
override func overrideMe() {
print("Something more useful")
}
}
Swift 5
class Class
{
#objc dynamic func make() { print("make from class") }
}
class SubClass: Class {}
extension SubClass {
override func make() {
print("override")
}
}
It looks like you can override property for 2nd super class property. For example, you can access UIView property by making extension to the UILabel wanting to override frame property of UIView. This sample works for me in Xcode 6.3.2
extension UILabel {
override public var frame: CGRect {
didSet {
println("\(frame)")
}
}
}
You can't do this through normal means.
It's in Apple's docs that you can't override a method in an extension in a subclass.
Also, extensions can add new functionality to a type, but they cannot override existing functionality.
https://docs.swift.org/swift-book/LanguageGuide/Extensions.html
I think you forgot to override the superclass property in your subclass:
class Subclass : UIView {
var _property : Int = 1
override var property : Int {
get { return _property}
set(val) {_property = val}
}
}

Resources