ReactiveCocoa ignore nil in Swift - swift

I'm using ReactiveCocoa in many places around my app. I've build a check to skip nil values as followed:
func subscribeNextAs<T>(nextClosure:(T) -> ()) -> RACDisposable {
return self.subscribeNext {
(next: AnyObject!) -> () in
self.errorLogCastNext(next, withClosure: nextClosure)
}
}
private func errorLogCastNext<T>(next:AnyObject!, withClosure nextClosure:(T) -> ()){
if let nextAsT = next as? T {
nextClosure(nextAsT)
} else {
DDLogError("ERROR: Could not cast! \(next)", level: logLevel, asynchronous: false)
}
}
This helps to log failed castings, but will also fail for nil values.
In Objective-C you would simply call ignore as followed:
[[RACObserve(self, maybeNilProperty) ignore:nil] subscribeNext:^(id x) {
// x can't be nil
}];
But in Swift the ignore property can't be nil. Any idea to use ignore in Swift?

Finally, with help from powerj1984 I created this method for now:
extension RACSignal {
func ignoreNil() -> RACSignal {
return self.filter({ (innerValue) -> Bool in
return innerValue != nil
})
}
}

Related

Return Type is not Optional, but asking to unwrap it

I have a method in ViewModel which is called registerSchool() and return is Bool. When I call this method in ViewController, then it says I need to unwrap it, because it is optional type. I did not mark the return type is optional! Why am I getting this error message?
ViewController -> RegisterViewController
var registerVM = RegisterVM()
#IBAction func registerBtnClicked(_ sender: Any) {
RegisterServiceManager.allUsers { [weak self] (result) in
guard let result = result else { return }
DispatchQueue.main.async {
// the following line saying the return is Bool?
if self?.registerVM.registerSchool() {
}
}
}
}
ViewModel -> RegisterVM
func registerSchool() -> Bool {
return true
}
Optional chaining. If self is Optional, self?.registerVM.registerSchool() is Optional.

How can I update this class to support binding a keypath value to a function?

I have a class that allows me to declare a property as Bindable
let user: Bindable<User> = Bindable(someUser)
user.update(with: someNewUser)
......
user.bind(\.name, to: label, \.text)
Doing so allows changes to be bound to a UI element directly.
This is based on an article found here
import Foundation
final class Bindable<Value> {
private var observations = [(Value) -> Bool]()
private var lastValue: Value?
init(_ value: Value? = nil) {
lastValue = value
}
}
extension Bindable {
func update(with value: Value) {
lastValue = value
observations = observations.filter { $0(value) }
}
}
extension Bindable {
// Non Optionals
func bind<O: AnyObject, T>(_ sourceKeyPath: KeyPath<Value, T>, to object: O, _ objectKeyPath: ReferenceWritableKeyPath<O, T>) {
addObservation(for: object) { object, observed in
let value = observed[keyPath: sourceKeyPath]
object[keyPath: objectKeyPath] = value
}
}
// Optionals
func bind<O: AnyObject, T>(_ sourceKeyPath: KeyPath<Value, T>, to object: O, _ objectKeyPath: ReferenceWritableKeyPath<O, T?>) {
addObservation(for: object) { object, observed in
let value = observed[keyPath: sourceKeyPath]
object[keyPath: objectKeyPath] = value
}
}
}
private extension Bindable {
func addObservation<O: AnyObject>(for object: O, handler: #escaping (O, Value) -> Void) {
// If we already have a value available, give the handler access to it directly.
lastValue.map { handler(object, $0) }
// Each observation closure returns a Bool that indicates
// whether the observation should still be kept alive,
// based on whether the observing object is still retained.
observations.append { [weak object] value in
guard let object = object else { return false }
handler(object, value)
return true
}
}
}
What I would like to do is also be able to bind a property to a function.
The current syntax for binding values is something like -
user.bind(\.name, to: label, \.text)
But I'd like to extend this so perhaps that property at that keypath can invoke a method.
someting like -
func doSomethingWithProp(_ prop: String) {
// do something
}
user.bind(\.name, to: doSomething)
In this case doSomething may invoke a helper for NSAttributedString and accepts the name prop as an argument to be used in that helper.
I have seen something similar in RxSwift using bind(onNext: ....).
I tried to do this using -
func bind<O: AnyObject, T, P>(_ sourceKeyPatch: KeyPath<Value, T>, to onNext: #escaping (P) -> Void) {
addObservation(for: onNext) { onNext, observed in
let value = observed[keyPath: sourceKeyPath]
onNext(value)
}
}
Bit I get the follow errors -
Generic parameter 'O' is not used in function signature
Generic parameter 'O' could not be inferred
This Bindable approach expects there to be some observing object, but you don't have one. That said, it doesn't actually care what that object is. It's just something passed back to the handler. So you could handle this in an extension this way, by using self as a placeholder object:
func bind<T>(_ sourceKeyPath: KeyPath<Value, T>, onNext: #escaping (T) -> Void) {
addObservation(for: self) { object, observed in
let value = observed[keyPath: sourceKeyPath]
onNext(value)
}
}
That said, this feels a little messy, so I might redesign Bindable to support this natively, and build object binding on top of it. Make the private addObservation do a bit less, by just calling a handler:
private extension Bindable {
func addObservation(handler: #escaping (Value) -> Bool) { // <== Require a bool here
lastValue.map { handler($0) }
observations.append { handler($0) } // <== Just call the handler
}
}
And make all the public methods do a bit more checking about object, so the private extension doesn't have to know about it.:
extension Bindable {
// Non Optionals
func bind<O: AnyObject, T>(_ sourceKeyPath: KeyPath<Value, T>, to object: O, _ objectKeyPath: ReferenceWritableKeyPath<O, T>) {
addObservation { [weak object] observed in
guard let object = object else { return false } // <== Have to check object here instead
let value = observed[keyPath: sourceKeyPath]
object[keyPath: objectKeyPath] = value
return true
}
}
// Optionals
func bind<O: AnyObject, T>(_ sourceKeyPath: KeyPath<Value, T>, to object: O, _ objectKeyPath: ReferenceWritableKeyPath<O, T?>) {
addObservation { [weak object] observed in
guard let object = object else { return false }
let value = observed[keyPath: sourceKeyPath]
object[keyPath: objectKeyPath] = value
return true
}
}
// Function
func bind<T>(_ sourceKeyPath: KeyPath<Value, T>, onNext: #escaping (T) -> Void) {
addObservation { observed in
let value = observed[keyPath: sourceKeyPath]
onNext(value)
return true
}
}
}
There's probably some more refactoring you could do here to reduce some of the code duplication, but the key point would be to make the primitive handler do less.
Note that in iOS 13+, this should be done with Combine instead. It does this all in a more powerful way.

protocol type constraint by two protocols

I've setup two protocols below in swift 2
Generative (for the lack of a better name) which holds an array of items and provides Datasource like functionality like count and subscript
public protocol Generative {
typealias GeneratedType
var elements: [GeneratedType] { get }
}
public extension Generative {
func count() -> Int {
return elements.count
}
subscript(index:Int) -> GeneratedType? {
if index >= count() {
return nil
}
return elements[index]
}
}
public protocol Selectable {
typealias SelectableType: Hashable
var selectedElements: Set<SelectableType> { get set }
}
extension Selectable {
public func isSelected(elem: SelectableType) -> Bool {
return selectedElements.contains(elem)
}
public mutating func addSelection(elem: SelectableType) {
selectedElements.insert(elem)
}
public mutating func removeSelection(elem: SelectableType) {
selectedElements.remove(elem)
}
public mutating func clearSelections() {
selectedElements.removeAll()
}
}
So in case an object implements both Generative and Selectable then I want it to be able to return the selected indices so I wrote this function:
func selectedIndices<S: Selectable where S: Generative, S.GeneratedType == S.SelectableType>(ds: S) -> [NSIndexPath] {
var selections: [NSIndexPath] {
return ds.selectedElements
.map{ (p: S.GeneratedType) -> NSIndexPath? in
if let idx = ds.elements.indexOf(p) { idx
return NSIndexPath(forRow: idx, inSection: 0)
}
return nil
}
.filter{ $0 != nil }
.map{ $0! }
}
return selections
}
for some reason the linker prints out:
Command failed due to signal: Segmentation fault: 11
Not sure why is that, I can't figure out another way to specify this function to work on objects which implement both protocols and their associated types match...
Any ideas ?
P.S. the code in a Gist: https://gist.github.com/edwardIshaq/715b0e134fb47d2e28cc
------- UPDATE
removing the computed property seemed to do the trick :
func selectedIndices<S: Selectable where S: Generative, S.GeneratedType == S.SelectableType>(ds: S) -> [NSIndexPath] {
return ds.selectedElements
.flatMap { (p: S.GeneratedType) -> NSIndexPath? in
if let idx = ds.elements.indexOf(p) { idx
return NSIndexPath(forRow: idx, inSection: 0)
}
return nil
}
}
The compiler should never crash, but it looks like the cause is simpler, seems to be to do with generic functions that use computed properties internally, this is as simple as I can get it:
// swiftc crashes when compiling this
func f<T>(t: T) -> Int {
var v: Int {
return 0
}
return v
}
so as a workaround, try refactoring the code to not use the computed property.
You could do that, and write this as an extension of the two protocols, like this:
extension Selectable
where Self: Generative,
Self.GeneratedType == NSIndexPath,
Self.SelectableType == NSIndexPath
{
func selectedIndices() -> [NSIndexPath] {
return self.selectedElements.flatMap {
self.elements.indexOf($0).map { NSIndexPath(index: $0) }
}
}
}

Unexpected defer behaviours

I have a function that does processing asynchronously:
func something(completion: [Something] -> Void) {
dispatch_async(queue) {
...
dispatch_async(dispatch_get_main_queue()) {
completion(something)
}
}
}
I thought it would be wise to use defer to guarantee that completion gets called every time, so I tried this:
func something(completion: [Something] -> Void) {
dispatch_async(queue) {
...
defer {
dispatch_async(dispatch_get_main_queue()) {
completion(something)
}
}
}
}
Working well. Then I tried to use a guard statement within the asynchronous dispatch that always failed, to see if defer will activate. It didn't:
func something(completion: [Something] -> Void) {
dispatch_async(queue) {
...
guard let shouldFail = ... else { return }
defer {
dispatch_async(dispatch_get_main_queue()) {
completion(something)
}
}
}
}
defer would not be called. Why?
Because you are using defer after returning. The compiler doesn't know that you specified defer instructions (because it returned already and didn't see any defer instructions in that point, so the next lines are not fired up). If you'd move defer {} before the guard, then it will be called.
guard will return before even getting to the defer. Try doing it the other way around:
func something(completion: [Something] -> Void) {
dispatch_async(queue) {
...
defer {
dispatch_async(dispatch_get_main_queue()) {
completion(something)
}
}
guard let shouldFail = ... else { return }
}
}

How to use #autoclosure parameter in async block in Swift?

I would like to call an #autoclosure parameter inside dispatch_async block.
func myFunc(#autoclosure condition: () -> Bool) {
dispatch_async(dispatch_get_main_queue()) {
if condition() {
println("Condition is true")
}
}
}
I get the following error.
Closure use of #noescape parameter may allow it to escape.
Is it possible to call #autoclosure parameter asynchronously?
Tested in Xcode 6.4 (6E23).
Yes, so long as you declare them #autoclosure(escaping):
Declarations with the autoclosure attribute imply noescape as well, except when passed the optional attribute escaping.
So this should do it:
func myFunc(#autoclosure(escaping) condition: () -> Bool) {
dispatch_async(dispatch_get_main_queue()) {
if condition() {
println("Condition is true")
}
}
}
Updating the answer from Airspeed Velocity, you can pass weak self directly into escaping autoclosure.
var boolValue: Bool = true
func myFunc(condition: #autoclosure #escaping () -> Bool?) {
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
if let condition = condition() {
print("Condition is \(condition ? "true" : "false")")
}
}
}
func main() {
weak var weakSelf = self
myFunc(condition: weakSelf?.boolValue)
}