In Swift3, combine responds#to and calling in one fell swoop? - swift

For example,
superview?.subviews.filter{
$0 != self &&
$0.responds(to: #selector(setter: Blueable.blue))
}.map{
($0 as! Blueable).blue = false
}
Is there a concept like ..
x.blue??? = false
'???' meaning 'if it responds to blue, call blue'...
Note - I fully appreciate I could write an extension, callIfResponds:to, or a specific extension, blueIfBlueable.
I'm wondering if there's some native swiftyness here, which I don't know about. It seems to be a pretty basic concept.
Footnote:
in the ensuing heated discussion, there is mention of using a protocol. Just for the benefit of anyone reading, here's one approach to using a protocol:
protocol Blueable:class {
var blue:Bool { get set }
}
extension Blueable where Self:UIView {
func unblueAllSiblings() { // make this the only blued item
superview?.subviews.filter{$0 != self}
.flatMap{$0 as? Blueable}
.forEach{$0.blue = false}
}
}
// explanation: anything "blueable" must have a blue on/off concept.
// you get 'unblueAllSiblings' for free, which you can call from
// any blueable item to unblue all siblings (likely, if that one just became blue)
To use it, for example...
#IBDesignable
class UILabelStarred: UILabel, Blueable {
var blueStar: UIView? = nil
let height:CGFloat = 40
let shinyness:CGFloat = 0.72
let shader:Shader = Shaders.Glossy
let s:TimeInterval = 0.35
#IBInspectable var blue:Bool = false {
didSet {
if (blue == true) { unblueAllSiblings() }
blueize()
}
}
func blueize() {
if (blueStar == nil) {
blueStar = UIView()
self.addSubview(blueStar!)
... draw, say, a blue star here
}
if (blue) {
UIView.animate(withDuration: s) {
self. blueStar!.backgroundColor = corporateBlue03
self.textColor = corporateBlue03
}
}
else {
UIView.animate(withDuration: s) {
self. blueStar!.backgroundColor = UIColor.white
self.textColor = sfBlack5
}
}
}
}
Just going back to the original question, that's all fine. But you can't "pick up on" an existing property (a simple example is isHidden) in existing classes.
Furthermore as long as we're discussing it, note that in that example protocol extension, you unfortunately can NOT have the protocol or extension automatically, as it were, call unblueAllSiblings from "inside" the protocol or extension, for exactly this reason: why you can't do it

As others have said, a better way of going about this is by conditionally type-casting rather than using responds(to:). However, don't overlook just using a for in loop – they're pretty powerful, allowing you to use pattern matching and where clauses, allowing you to iterate over a given subset of elements.
for case let blueable as Blueable in superview?.subviews ?? [] where blueable !== self {
blueable.blue = false
}
case let blueable as Blueable uses the type-casting pattern in order to only match elements that are Blueable, and bind them to blueable if successful.
where blueable !== self excludes self from being matched.

Somehow I think what you want is:
superview?.subviews.filter {
$0 != self // filter out self
}.flatMap {
$0 as? Blueable
}.forEach {
$0.blue = false
}
Why should you be checking whether a class conforms to a setter when you can check the type?
Checking selectors should not be used in pure Swift. You will need it mostly for interacting with Obj-C APIs - dynamic methods, optional methods in protocols or informal protocols.

Why not check conformance to the type directly? Something like:
superview?.subviews.forEach {
guard $0 !== self else { return }
($0 as? Blueable)?.blue = false
}

You can add this extension
extension UIView {
var siblings: [UIView] { return self.superview?.subviews.filter { $0 != self } ?? [] }
}
Now pick the solution that you prefer among the followings
Solution 1
siblings.flatMap { $0 as? Blueable }.forEach { $0.blue = false }
Solution 2
siblings.forEach { ($0 as? Blueable)?.blue = false }

Related

A way to compare generics in Swift only if they conform to Equatable

Wondering if there's a good way to do this or not:
I have a #propertyWrapper named "Enhanced" that I use. I use the wrappedValue.set to do some actions, and I would also like to do some further actions if the property is Equatable.
Currently, the code looks like this:
#propertyWrapper
class Enhanced<T: Equatable>: Codable
{
private var value: T
var projectedValue: Enhanced<T> { self }
var wrappedValue: T
{
get { value }
set { set(newValue, notify: nil) }
}
func set(_ proposedValue: T, notify: Bool?)
{
let oldValue = value
let newValue = proposedValue
let changed = newValue != oldValue
if changed { /* more magic here */ }
value = newValue
}
}
Now I would like to remove the Equatable conformance over the generic T, but still be able to compare the old and new values IF the generic T conforms to Equatable.
I've tried a handful of techniques, all of which dead end somewhere. My latest was this:
let changed: Bool
switch T.self
{
case let equatableType as any Equatable.Type:
if
let oldEquatableValue = oldValue as? any Equatable,
let newEquatableValue = newValue as? any Equatable
{
changed = newEquatableValue != oldEquatableValue
}
default:
changed = true
}
...but the error is an understandable Binary operator '!=' cannot be applied to two 'any Equatable' operands.
I tried different patterns to cast the generic type T into an Equatable and silently fail if the generic does not conform, but even if they do, the resulting "cast" types I get back aren't equatable themselves.
Any revelations to the proper pattern would be great!
After some deep web-sleuthing, I came across a snippet of code that does the magic I need:
private extension Equatable
{
func isEqualTo(_ rhs: Any) -> Bool
{
if let castRHS = rhs as? Self
{
return self == castRHS
}
else
{
return false
}
}
}
(HT to neonichu/equalizer.swift on GitHub)
With this bit of pseudo type-erasure, I can make this work:
let changed: Bool
if let oldEquatableValue = oldValue as? any Equatable,
let newEquatableValue = newValue as? any Equatable
{
changed = oldEquatableValue.isEqualTo(newEquatableValue) == false
}
else
{
changed = true
}
By using an extension on Equatable that does further casting of the values, this allows for these two values to be compared (and fail if they are not the same).
Hope this helps someone else!

Check if class is equal swift

I have a class where I have an extension to check equality but on test, the equality crashes.
here's my code
extension LPItemAction {
public override func isEqual(_ other: Any?) -> Bool {
if (other as? LPItemAction) == self {
return true
} else if !(other is LPItemAction) {
return false
} else {
let otherAction = other as? LPItemAction
return hash == otherAction?.hash
}
}
}
and my test case is like this
func testIsEqualSelf() {
// Given
let action = LPItemAction()
action.type = .account
// When
let equal = action.isEqual(action)
// Then
XCTAssertTrue(equal)
}
I got a crash with error Thread 1: EXC_BAD_ACCESS (code=2, address=0x16e747fc0)
Since this is obviously a NSObject, you are probably right to override isEqual. There are some rules though.
You cannot use ==. This operator invokes the Equality protocol, which, on NSObject is implemented using isEqual, therefore you end up with infinite recursion.
Another thing is that using hash to compare equality is just wrong.
// test type first
guard let otherAction = other as? LPItemAction else {
return false
}
// test reference equality
if self === otherAction {
return true
}
// test equality of all properties
return type === otherAction.type

UIView extension weird behavior when using generics [duplicate]

Is there any way to get the following working in Swift 3?
let button = UIButton().apply {
$0.setImage(UIImage(named: "UserLocation"), for: .normal)
$0.addTarget(self, action: #selector(focusUserLocation),
for: .touchUpInside)
$0.translatesAutoresizingMaskIntoConstraints = false
$0.backgroundColor = UIColor.black.withAlphaComponent(0.5)
$0.layer.cornerRadius = 5
}
The apply<T> function should take a closure of type (T)->Void, run it passing self into it, and then simply return self.
Another option would be to use an operator for this like "=>"
(borrowed the idea from Kotlin and Xtend languages).
Tried to do extension of NSObject like this:
extension NSObject {
func apply<T>(_ block: (T)->Void) -> T
{
block(self as! T)
return self as! T
}
}
But it requires explicit declaration of the parameter type in closure:
let button = UIButton().apply { (it: UIButton) in
it.setImage(UIImage(named: "UserLocation"), for: .normal)
it.addTarget(self, action: #selector(focusUserLocation),
for: .touchUpInside)
...
This is not convenient and makes the whole idea not worth the effort. The type is already specified at object creation and it should be possible not to repeat it explicitly.
Thanks!
The HasApply protocol
First of all lets define the HasApply protocol
protocol HasApply { }
and related extension
extension HasApply {
func apply(closure:(Self) -> ()) -> Self {
closure(self)
return self
}
}
Next let make NSObject conform to HasApply.
extension NSObject: HasApply { }
That's it
Let's test it
let button = UIButton().apply {
$0.titleLabel?.text = "Tap me"
}
print(button.titleLabel?.text) // Optional("Tap me")
Considerations
I wouldn't use NSObject (it's part of the Objective-C way of doing things and I assume it will be removed at some point in the future). I would prefer something like UIView instead.
extension UIView: HasApply { }
I had the same issue and ended up solving it with an operator:
infix operator <-< : AssignmentPrecedence
func <-<<T:AnyObject>(left:T, right:(T)->()) -> T
{
right(left)
return left
}
let myObject = UIButton() <-< { $0.isHidden = false }
There's a very good and simple Cocoapods library available called Then that does exactly that. Only that it uses then instead of apply. Simply import Then and then you can do as the OP asked for:
import Then
myObject.then {
$0.objectMethod()
}
let label = UILabel().then {
$0.color = ...
}
Here's how the protocol is implemented: https://github.com/devxoul/Then/blob/master/Sources/Then/Then.swift
extension Then where Self: Any {
public func then(_ block: (Self) throws -> Void) rethrows -> Self {
try block(self)
return self
}
Alain has a good answer if you're not allergic to custom operators. If you'd rather not use those, the best alternative I could come up with was:
#discardableResult func apply<T>(_ it:T, f:(T)->()) -> T {
f(it)
return it
}
which then allows you to use:
let button = apply(UIButton()) { $0.setTitleText("Button") }
It's not quite the same, but works out pretty well in general and has the advantage that T is completely unrestrained. It's an obviously contrived example, but:
func apply<T,R>(_ it:T, f:(T)->R) -> R {
return f(it)
}
even allows:
print("\(apply(32) { $0 + 4 })")
In case of object which must be created with non optional init:
let button = UIButton()
Optional(button).map {
$0.isEnabled = true
}
In case of object which must be created with optional init:
let button = UIButton(coder: coder)
button.map {
$0.isEnabled = true
}
In case of existing optional object we can use map function:
optionalObject.map {
$0.property1 = true
$0.property2 = true
}
If object must be cast before being use:
(optionalObject as? NewType).map {
$0.property1 = true
$0.property2 = true
}

Compare / Equatable weak generics in Swift

I want to create an array of weak referenced delegates like so...
fileprivate class WeakDelegate<T:AnyObject> {
weak var value:T?
init (value:T) {
self.value = value
}
}
class Radio {
private var delegates = [WeakDelegate<AnyObject>]()
}
So far so good...? what I'd like to do also is tidy up my array in these two ways...
1.
func removeDeadDelegates() {
let liveDelegates = delegates.filter { $0.value != nil }
delegates = liveDelegates
}
and 2.
func remove<T>(specificDelegate:T) {
let filteredDelegates = delegates.filter { $0.value != specificDelegate }
listeners = filteredDelegates
}
Says Cannot convert value of type 'T' to expected argument type '_OptionalNilComparisonType'
Now I can just add this to make the warning go away like this...
let liveDelegates = delegates.filter {
if let d = specificDelegate as? _OptionalNilComparisonType {
return $0.value != d
}
return true
}
but this cast doesn't work...
I'm concerned because I'm not sure what this means... can anyone explain why I can't compare generics with == and why this cast is failing?
Thanks for your time
EDIT
Like this?
func remove<T:AnyObject>(delegate:T) {
let filteredDelegates = delegates.filter { $0.value != delegate }
delegates = filteredDelegates
}
No joy sadly...
Instances of a class type can be compared with the “identical to”
=== and “not identical to” !== operators:
func remove(specificDelegate: AnyObject) {
let filteredDelegates = delegates.filter { $0.value !== specificDelegate }
delegates = filteredDelegates
}
The same works for a generic method
func remove<T:AnyObject>(specificDelegate: T) {
let filteredDelegates = delegates.filter { $0.value !== specificDelegate }
delegates = filteredDelegates
}
(but I do not yet see the advantage of doing so).

RxSwift with MVVM Best Practice Declaring Output Variable/Function

I'm using RxSwift with MVVM and I'm found myself a bit confused. Here's why:
My Code Right Now
ViewModel
internal protocol DetailViewModelInput {
func viewDidLoad(with name: String)
}
internal protocol DetailViewModelOutput {
var gnomeObject: Observable<Gnome?> { get }
}
struct DetailViewModel: DetailViewModelType, DetailViewModelInput, DetailViewModelOutput {
let disposeBag = DisposeBag()
let gnomeObject: Observable<Gnome?>
init() {
gnomeObject = viewDidLoadProperty
.asObservable()
.filter { !$0.isEmpty }
.map { guard let gnome = Gnome
.fetch(uniqueValue: $0, forKey: "name")! as? Gnome else { return nil }
return gnome
}
}
let viewDidLoadProperty = Variable<String>("")
func viewDidLoad(with name: String) {
viewDidLoadProperty.value = name
}
}
ViewController
I make the binding as follows:
func bindViewModel() {
viewModel.outputs.gnomeObject
.subscribe { observable in self.populate(with: observable.element != nil ? observable.element! : nil) }
.addDisposableTo(viewModel.disposeBag)
}
And this is "fine". It works perfectly (at least as expected).
But, I while reading the following book: https://victorqi.gitbooks.io/rxswift/content/tips.html
In the tips section it says:
Always strive to model your systems or their parts as pure functions. Those pure functions can be tested easily and can be used to modify operator behaviors.
And after reading it I'm changed my ViewModel as follows:
ViewModel (Edited)
internal protocol DetailViewModelInput {
func viewDidLoad(with name: String)
}
internal protocol DetailViewModelOutput {
func gnomeObject() -> Observable<Gnome?>
}
protocol DetailViewModelType {
var disposeBag: DisposeBag { get }
var inputs: DetailViewModelInput { get }
var outputs: DetailViewModelOutput { get }
}
struct DetailViewModel: DetailViewModelType, DetailViewModelInput {
let disposeBag = DisposeBag()
let viewDidLoadProperty = Variable<String>("")
func viewDidLoad(with name: String) {
viewDidLoadProperty.value = name
}
}
// MARK: DetailViewModelOutput
extension DetailViewModel: DetailViewModelOutput {
func gnomeObject() -> Observable<Gnome?> {
return viewDidLoadProperty
.asObservable()
.filter { !$0.isEmpty }
.map { guard let gnome = Gnome
.fetch(uniqueValue: $0, forKey: "name")! as? Gnome else { return nil }
return gnome
}
}
}
The difference in the ViewModels is the GnomeObject declaration, in one it is a var and in the "edited" is a func.
My concern is, that every time gnomeObject() gets called from the ViewController, it will create a new instance of the observable.
What should be the best practice in this case?
Hmm, in the first version, gnomeObject is a let, not a var. Once it is set, it is never changed to a different object.
In the second version gnomeObject() returns a different object every time it's called. So this actually breaks the "pure function" paradigm. (Note: if the Observable was a struct instead of a class then this wouldn't be the case because structs don't have identity.)
Your first example follows the pure function concept while your second version breaks it.
If you're looking to eliminate the need to instantiate gnomeObject in the initializer, you could modify the first example to use a lazy var like so:
lazy var gnomeObject: Observable<Gnome?> = self.viewDidLoadProperty
.asObservable()
.filter { !$0.isEmpty }
.map { guard let gnome = Gnome
.fetch(uniqueValue: $0, forKey: "name")! as? Gnome else { return nil }
return gnome
}
When they say you should use pure functions they mean that functions (when possible) should have the same output for the same set of inputs, meaning, if a function is called twice with the same set of inputs it should return the same thing twice.
That means you don't have any hidden mutable state that the caller of the functions is not aware of (a property in the class that owns the method, for example). Everything should be as explicit as possible.
So, it's something you should be aware of when it comes to functions. But it's completely ok to use properties, as you were doing in the first code, they don't apply to this.