I am not so convinced with RxSwift yet, and it's really hard to cleat understanding. After reviewing different materials, I cant' still work and manipulate sequences.
On the whole I have problem with type converting:
Cannot convert return expression of type 'Observable<Bool>' to return type 'Observable<Void>' (aka 'Observable<()>')
I have CocoaAction processing, and should return Observable<Void>
func stopProject() -> CocoaAction {
return CocoaAction { _ in
let stopProject = stop(project: self.projectId)
return stopProject.asObservable() //wrong converting
}
}
The stop function return Observable<Bool>
Finish view:
return stop(project: self.projectId).flatMap { _ in
Observable<Void>.empty()
}
let voidObservable = boolObservable.map { Void() }
let voidObservable = boolObservable.map { _ in Void() }
And in the case that you only want to emit a value if the boolean value is true:
let voidObservable = boolObservable.filter { $0 }.map { _ in Void() }
Adding this extension:
public extension ObservableType {
func mapToVoid() -> Observable<Void> {
return map { _ in }
}
}
so you could do
myObservable.mapToVoid()
Related
My sample is create call rest api two observable of result from load rx.request api from moya
func dataOneObservable() -> Observable<ObJectOneClass> {
return myprovider.rx.request(API.loadDataDetail())
.asObservable()
.retry()
.observeOn(MainScheduler.instance)
.filterSuccessfulStatusAndRedirectCodes()
.catchObjectError()
.mapObject(ObJectOneClassResult.self)
.map({ (response) -> ObJectOneClass in
if ObJectOneClass.data != nil {
if let item = response.data {
return item
}
return ObJectOneClass()
}
return ObJectOneClass()
})
}
func dataTwoObservable() -> Observable<ObJectTwoClass> {
return myprovider.rx.request(API.loadDataProfile())
.asObservable()
.retry()
.observeOn(MainScheduler.instance)
.filterSuccessfulStatusAndRedirectCodes()
.catchObjectError()
.mapObject(ObJectTwoClassResult.self)
.map({ (response) -> ObJectTwoClass in
if ObJectTwoClass.data != nil {
if let item = response.data {
return item
}
return ObJectTwoClass()
}
return ObJectTwoClass()
})
}
Then I want to combine result by use combineLastest of RxSwift but when I use .subscribe my response event can't passing result
my function call is same this:
func testCombine(completion:#escaping(_ result:Result<(ObJectOneClass,ObJectTwoClass),Error>) -> ()){
_ = Observable.combineLatest(dataOneObservable(), dataTwoObservable())
.asObservable()
.subscribe({ event in
//Event<(ObJectOneClass,ObJectTwoClass)>
//case .next((a, b)):
switch event{
case .next(response):
completion(.success(response))
case let .error(error):
completion(.failure(error as NSError))
default:
break
}
})
}
Then
please help me guide to syntax completion this.
Here's an interesting solution:
func testCombine(completion: #escaping(_ result: Result<(ObJectOneClass, ObJectTwoClass), Error>) -> ()) {
_ = Observable.combineLatest(dataOneObservable(), dataTwoObservable())
.map(Result.success)
.catch(combine(Result.failure, Observable.just))
.subscribe(onNext: completion)
}
func combine<A, B, C>(_ f: #escaping (A) -> B, _ g: #escaping (B) -> C) -> (A) -> C {
{ g(f($0)) }
}
That map to Result.success, catch just Result.failure is really common when you are dealing with Result types. It's so common that you might want to make a single operator to capture the notion.
extension Observable {
func toResult() -> Infallible<Result<Element, Error>> {
map(Result.success)
.asInfallible(onErrorRecover: combine(Result.failure, Infallible.just))
}
}
In response to your comment, here's a somewhat less advanced version:
extension Observable {
func toResult() -> Infallible<Result<Element, Error>> {
map(Result.success)
.asInfallible(onErrorRecover: { Infallible.just(Result.failure($0)) })
}
}
OK This my topic can fix problem by declare let in .next
func testCombine(completion:#escaping(_ result:Result<(ObJectOneClass,ObJectTwoClass),Error>) -> ()){
_ = Observable.combineLatest(dataOneObservable(), dataTwoObservable())
.asObservable()
.subscribe({ event in
switch event{
case .next((let one,let two)):
completion(.success((one,two)))
case let .error(error):
completion(.failure(error as NSError))
default:
break
}
})
}
I'm trying to create isHighlighted Observable for my UIButton, to send sequence every time isHiglighted for UIButton has changed. And I've write something like this
extension Reactive where Base: UIButton {
var isHighlighted: Observable<Bool> {
let property = self.base.rx.controlProperty(editingEvents: .allTouchEvents,
getter: { _ in self.base.isHighlighted },
setter: { (_, _) in })
return property
.distinctUntilChanged()
.asObservable()
}
}
The problem is, that it doesn't work for .touchUpInside. If I drag finger outside UIButton and then come back, it works fine, but not for tap action. I think immediately after .touchUpInside it's still highlighted for very short time.
Thanks #iWheelBuy I've created code that works, without RxOptional, I based it partially on your answer so Thank you! Here is working code:
extension Reactive where Base: UIButton {
var isHighlighted: Observable<Bool> {
let anyObservable = self.base.rx.methodInvoked(#selector(setter: self.base.isHighlighted))
let boolObservable = anyObservable
.flatMap { Observable.from(optional: $0.first as? Bool) }
.startWith(self.base.isHighlighted)
.distinctUntilChanged()
.share()
return boolObservable
}
}
I think I have a solution. It can be simplified, but I just copy paste the full solution I have.
public extension Reactive where Base: UIButton {
public func isHighlighted() -> Observable<Bool> {
let selector = #selector(setter: UIButton.isHighlighted)
let value: ([Any]) -> Bool? = { $0.first(where: { $0 is Bool }) as? Bool }
return base
.observable(selector: selector, value: value)
.filterNil()
.startWith(base.isHighlighted)
.distinctUntilChanged()
.share(replay: 1, scope: .whileConnected)
}
}
Also, to make it work. You need RxOptional and some extra code:
public enum InvocationTime: Int {
case afterMessageIsInvoked
case beforeMessageIsInvoked
}
and
public extension NSObject {
public func observable<T>(selector: Selector, value: #escaping ([Any]) -> T, when: InvocationTime = .afterMessageIsInvoked) -> Observable<T> {
let observable: Observable<[Any]> = {
switch when {
case .afterMessageIsInvoked:
return rx.methodInvoked(selector)
case .beforeMessageIsInvoked:
return rx.sentMessage(selector)
}
}()
return observable
.map({ value($0) })
.share(replay: 1, scope: .whileConnected)
}
}
Hope it helps (^
I'm fairly new to RxSwift, so I have the following problem, lets suppose I have 3 Observable example functions which return different observable types:
func observableFunc1(item1: DummyObject) -> Observable<AVURLAsset> {
return Observable.create { observer in
let something_with_AVURLAsset = AVURLAsset(url: URL(fileURLWithPath: "file"))
observer.onNext(something_with_AVURLAsset)
observer.onCompleted()
return Disposables.create()
}
}
func observableFunc2(item: AVURLAsset) -> Observable<Data> {
return Observable.create { observer in
let something_with_data = Data()
observer.onNext(something_with_data)
observer.onCompleted()
return Disposables.create()
}
}
func observableFunc3(_ params: [String:Any]) -> Observable<Any> {
let disposeBag = DisposeBag()
return Observable.create { observer in
RxAlamofire.request(api.sendData(params)).debug().subscribe(
onNext: { reponse in
observer.onCompleted()
},
onError: { error in
observer.onError(customError.theError("error"))
}
)
.addDisposableTo(disposeBag)
return Disposables.create()
}
}
How can I execute these 3 functions sequentially with the return value of func1 to be used on func2 and then when func2 is completed finally run func3.
I hope I made the question clear enough, but then again I'm really new to RxSwift and I don't know if these operations are possible or not.
Here's an example...
Assuming you have the three functions:
func func1() -> Observable<Data1>
func func2(_ data: Data1) -> Observable<Data2>
func func3(_ data: Data2) -> Observable<Data3>
Then you can:
let a = func1()
let b = a.flatMap { func2($0) }
let c = b.flatMap { func3($0) }
or:
let c = func1()
.flatMap { func2($0) }
.flatMap { func3($0) }
That said, your observableFunc3 is quite broken. You need to remove the dispose bag from it. As it stands, the network call will cancel before it starts.
If you really don't want it to emit any values then:
func observableFunc3(_ params: [String:Any]) -> Observable<Void> {
return RxAlamofire.request(api.sendData(params))
.filter { false }
}
The above will emit either a completed or an error but no next values.
Better would be to write it like:
func observableFunc3(_ params: [String:Any]) -> Observable<Void> {
RxAlamofire.request(api.sendData(params))
.map { _ in }
}
The above will emit one next and then a completed or an error. This is better because you can map or flatMap after it to have things happen once it's done.
I have the following code:
class Function<T> {
var ptr: () throws -> T
init<Func>(block: Func, args: AnyObject...) {
self.ptr = {() throws -> T in
let result: AnyObject? = nil
if T.self == Void.self {
return Void() as! T
}
return result //Error Here.. Cannot as! cast it either.. Cannot unsafeBitCast it either..
}
}
}
postfix operator ^ { }
postfix func ^ <T>(left: Function<T>) throws -> T {
return try left.ptr()
}
func call() {
let block: (String) -> String? = {(arg) -> String? in
return nil
}
let fun = Function<String?>(block: block, args: "Hello")
fun^
}
The function Block.execute returns AnyObject?. My Generic class Function<T> expects a return type of T.
If T is already String? why can't I return nil?
Is there any way to return nil as type T which is already Optional?
If I make T Optional, then the return type becomes Optional<Optional<String>> which is not what I want.. then the compiler complains that OptionalOptional is not unwrapped with ?? That is how I know that T is already optional.
After a long hunt on google I have finally found an elegant way to do this. It's possible to write a class extension with type constraints.
class Foo<T> {
func bar() -> T {
return something
// Even if T was optional, swift does not allow us to return nil here:
// 'nil' is incompatible with return type 'T'
}
}
extension Foo where T: ExpressibleByNilLiteral {
func bar() -> T {
return nil
// returning nil is now allowed
}
}
The appropriate method will be invoked depending on whether T is optional or not.
I solved it with a nasty work around.. I throw an exception for nil return type. Then in the generic function operator I catch that specific exception and return nil if T is NilLiteralConvertible.. Otherwise I just execute normally..
class Function<T> {
var ptr: () throws -> T
init<Func>(block: Func, args: AnyObject...) {
self.ptr = {() throws -> T in
let result: AnyObject? = execute(block, args...)
if T.self == Void.self {
return Void() as! T
}
throw BlockError.BlockReturnsNil
}
}
}
postfix func ^ <T>(left: Function<T>) throws -> T {
return try left.ptr()
}
postfix func ^ <T : NilLiteralConvertible>(left: Function<T>) throws -> T {
do {
return try left.ptr()
}
catch BlockError.BlockReturnsNil {
return nil
}
}
The following casts Any? to T returning nil only if T is optional and from is nil. It crashes, if from cannot be cast to T.
func cast<T>(from v: Any?)->T {
return v as! T
}
func cast<T>(from v: Any?)->T where T: ExpressibleByNilLiteral {
guard let v = v else { return nil }
return v as! T
}
This does the trick as of Swift 3.1. Modification of user3763801's answer.
func cast<T>(_ v: Any) -> T {
return v as! T
}
I'm trying to add an extension method in Array like so:
extension Array {
func contains(obj: T) -> Bool {
let filtered = self.filter {$0 == obj}
return filtered.count > 0
}
}
But self.filter {$0 == obj} don't work. Compiler error:
could not find an overload for '==' that accepts the supplied arguments
you don't actually need to write an extension, you can use the global func contains from the Swift library:
contains([1,2,3], 1)
Swift 1.x
As I mentioned in the comments, there is a contains function. But to answer the question of how to write an extension and what the compiler error means:
The elements in the array can't necessarily be compared with ==. You need to make sure the parameter is Equatable and you need to make sure the array element is of the same type.
extension Array {
func contains<T : Equatable>(obj: T) -> Bool {
let filtered = self.filter {$0 as? T == obj}
return filtered.count > 0
}
}
Swift 2/Xcode 7 (Beta)
Swift 2 includes SequenceType.contains, which is exactly what you were trying to create.
This is made possible by a Swift syntax that allows restricting methods to certain (e.g. Equatable) type arguments. It looks like this:
extension SequenceType where Generator.Element: Equatable {
func contains(element: Self.Generator.Element) -> Bool {
...
}
}
I found that the built-in contains doesn't work with reference types. I needed this and solved it with the code below. I'm pasting it here because somebody else might be confused about contains() like I was.
extension Array {
func containsReference(obj: AnyObject) -> Bool {
for ownedItem in self {
if let ownedObject: AnyObject = ownedItem as? AnyObject {
if (ownedObject === obj) {
return true
}
}
}
return false
}
}
This works with Swift 2.1 for reference types pretty good.
extension SequenceType where Generator.Element: AnyObject {
func contains(obj: Self.Generator.Element?) -> Bool {
if obj != nil {
for item in self {
if item === obj {
return true
}
}
}
return false
}
}
For value types you can add this:
extension SequenceType where Generator.Element: Equatable {
func contains(val: Self.Generator.Element?) -> Bool {
if val != nil {
for item in self {
if item == val {
return true
}
}
}
return false
}
}
Not perfect, but this version built on nschum's answer supports optional arguments (though not arrays with optional types) as well:
extension Array {
private func typeIsOptional() -> Bool {
return reflect(self[0]).disposition == .Optional
}
func contains<U : Equatable>(obj: U) -> Bool {
if isEmpty {
return false
}
if (typeIsOptional()) {
NSException(name:"Not supported", reason: "Optional Array types not supported", userInfo: nil).raise()
}
// cast type of array to type of argument to make it equatable
for item in self.map({ $0 as? U }) {
if item == obj {
return true
}
}
return false
}
// without this version, contains("foo" as String?) won't compile
func contains<U : Equatable>(obj: U?) -> Bool {
if isEmpty {
return false
}
if (typeIsOptional()) {
NSException(name:"Not supported", reason: "Optional Array types not supported", userInfo: nil).raise()
}
return obj != nil && contains(obj!)
}
}
If you have an array of optionals, you can get a copy of it with non-optionals (nil arguments removed) with this global function thanks to jtbandes:
func unwrapOptionals<T>(a: [T?]) -> [T] {
return a.filter { $0 != nil }.map { $0! }
}
Usage:
1> func unwrapOptionals<T>(a: [T?]) -> [T] {
2. return a.filter { $0 != nil }.map { $0! }
3. }
4>
5> let foo = ["foo" as String?]
foo: [String?] = 1 value {
[0] = "foo"
}
6> let bar = unwrapOptionals(foo)
bar: [String] = 1 value {
[0] = "foo"
}
For good measure, add one that just returns the array if its type is not optional. This way you avoid runtime errors if you call unwrapOptionals() on a non-optional array:
func unwrapOptionals<T>(a: [T]) -> [T] {
return a
}
Note you might think you could just call unwrapOptionals inside func contains<U : Equatable>(obj: U?). However, that doesn't work, because the Element type in the Array extension is just a type--it doesn't "know" it's an optional type. So if you call unwrapOptionals, the second version will be invoked, and you'll just get the array full of optionals back.