How to convert Signal to SignalProducer - ios

Unfortunately some RAC pieces does not offer SignalProducers, but Signals — like Action has a values field which is Signal. But for my logic I need the SignalProducer.
How can I convert Signal to SignalProducer?
toSignalProducer(toRACSignal(x)) does not seem to be a good solution

Currently I stopped on this extension:
extension Signal {
func toSignalProducer() -> SignalProducer<T, E> {
return SignalProducer { (sink, compositeDisposable) in
compositeDisposable.addDisposable(self.observe(sink))
}
}
}

Related

How do I avoid callback hell on swift functions that I didn't write?

I don't want this to be misconstrued as a duplicate. I want to deal with callback hell arising from API calls from Firestore (database by Firebase, a Google platform). I can't modify their function declarations, so I assume I will have to wrap their functions with some of my code.
For example, in the code below, the function eventCreatedSuccessfully() can only be called after the asynchronous function completes. eventCreatedSuccessfully() also contains a function call to firebase that has a closure, which another function relies on and etc...Though this isn't causing me problems right now, it probably will as my App grows larger and larger. I researched online and found solutions like Futures and Streams from third-party frameworks, but I didn't know how to integrate them into code I have no control over (the API calls).
batch.commit { (error) in
self.dismiss(animated: true) {
if error == nil {
self.eventCreatedSuccessfully()
print("Event created successfully")
} else {
print(error!.localizedDescription)
}
}
}
Wrap the calls in promises. Any of the popular libraries will do the trick. The one that comes to mind is PromiseKit, available (at the time of this writing) at https://github.com/mxcl/PromiseKit.
Here is code I wrote for a work project (it's open source) which wraps a function that takes a completion, and returns a Promise which will signal with the result when the completion is called. It's using an internal Promise implementation, but the process can be adapted to other implementations.
public func promise<Return>(_ task: (#escaping (Return?, Error?) -> ()) -> ()) -> Promise<Return> {
let p = Promise<Return>()
task { (value: Return?, error: Error?) -> Void in
if let error = error {
p.signal(error)
}
if let value = value {
p.signal(value)
}
}
return p
}
The completion is expected to be called with a result of some kind, or an error. Adapt as required for your use-case.
An example usage follows.
public typealias BalanceCompletion = (Balance?, Error?) -> Void
func balance(completion: #escaping BalanceCompletion) {
guard deleted == false else {
completion(nil, KinError.accountDeleted)
return
}
Stellar.balance(account: stellarAccount.publicKey!, asset: asset, node: node)
.then { balance -> Void in
completion(balance, nil)
}
.error { error in
completion(nil, KinError.balanceQueryFailed(error))
}
}
func balance() -> Promise<Balance> {
return promise(balance)
}
I researched online and found solutions like Futures and Streams (...)
In most cases futures and streams is all about PromiseKit and RxSwift frameworks.
If you have only big amount of closures try to use PMK. It's very simple and easy to use. PMK also has nice documentation section on github.
RxSwift is more advanced level, because it requires you to write code fully to paradigm of itself - starts from server/firebase request and ends with ui. Also, there is a good note at PMK github about difference of these two.
Also, should be note, that google also has nice library called promises. According to their benchmarks google's library is leader in almost all nominations.

Swift: DispatchQueue.global

I am writing an app that always use dispatchQueue to help me handle packet received from another devices.
Hence, in my code, depends on the packet received, it will trigger codes like:
if (// condition fulfilled) {
let queue = DispatchQueue.global(qos: DispatchQoS.QoSClass.default)
queue.async { () -> Void in
self.handlePacket(packet: packet) {
DispatchQueue.main.async {
// do something.
}
}
}
}
Then I found that when one part of the code with the same variable "queue" is executed, another part code can be triggered without calling. And the app crash at the line queue.async { () -> Void in
Is that I cannot use the same name for the queue? Or my code actually mess up those queues?
Please advise.
First of all, you should ask yourself, what exactly are you trying to achieve? Is handlePacket(_) too heavy to process it on current queue?
Concurrency is hard, and you should try to avoid it, until you realize how does concurrency and queues work in iOS.

Creating a "reactive" API with RxSwift

I'm dipping toes into RxSwift and would like to create a "streaming API" for one of my regular API calls.
My idea is to take the regular call (which already uses observables without any problems) and have a timer fire such calls and send the results on the same observable, so the view controller can update automatically, so instead of doing this (pseudocode follows):
func getLocations() -> Observable<[Location]> {
return Observable<[Location]>.create {
sink in
NSURLSession.sharedSession.rx_JSON(API.locationsRequest).map {
json in
return json.flatMap { Location($0) }
}
}
}
I'd like for this to happen (pseudocode follows):
func getLocations(interval: NSTimeInterval) -> Observable<[Location]> {
return Observable<[Location]>.create {
sink in
NSTimer(interval) {
NSURLSession.sharedSession.rx_JSON(API.locationsRequest).map {
json in
sink.onNext(json.flatMap { Location($0) })
}
}
}
}
Last thing I tried was adding an NSTimer to the mix, but I can't figure out how to take the reference to the sink and pass it around to the method called by the timer to actually send the events down the pipe, given that the handler for the timer must be on a standalone method. I tried throwing in the block timer extensions from BlocksKit but the timer was fired every second instead of being fired at the specified interval, which defeated the purpose.
I've also read about the Interval operator but I'm not sure it's the right way to go.
Any pointers on how to get this right?
The end goal would be to have the timer re-fire only after the previous call has finished (either success or fail).
You should do something like the code below:
func getLocations(interval: NSTimeInterval) -> Observable<[CLLocation]> {
return Observable<[CLLocation]>.create { observer in
let interval = 20.0
let getLocationDisposable = Observable<Int64>.interval(interval, scheduler: MainScheduler.instance)
.subscribe { (e: Event<Int64>) in
NSURLSession.sharedSession.rx_JSON(API.locationsRequest).map {
json in
observer.onNext(json.flatMap { Location($0) })
}
}
return AnonymousDisposable {
getLocationDisposable.dispose()
}
}
}
The code above fire every 20 seconds the API.locationsRequest and send the result on the same observable, Please note that you have to dispose the Interval when the maim observable dispose.

Sequencing sounds with a delay in Swift

Apologies if this has been posted before but I haven't had much luck searching around on this topic. I'm trying to build a morse code converter using Swift. As part of this I've made a function that accepts a string of dots and dashes, and will hopefully play the corresponding audio. I've already successfully loaded up 2 audio players for the short and long beeps.
I started by looping through the string and playing the corresponding sound for each character. However, that just played all the sounds in parallel. Now I'm trying to use dispatch_after, but still running into the same issue. My code is below.
func audioMorseMessage(message: String) {
var time = dispatch_time(DISPATCH_TIME_NOW, Int64(NSEC_PER_SEC))
for character in message.characters {
if String(character) == "-" {
dispatch_after(time,dispatch_get_main_queue()){
self.longBeep.play()
}
}
if String(character) == "." {
dispatch_after(time,dispatch_get_main_queue()){
self.shortBeep.play()
}
}
}
}
Is this the right way to approach this? Is there another way where I can concatenate the audio files during the loop (with small gaps placed between beeps) and then play back the entire file once the loop has completed? Thanks for any help in advance.
This seems like a great opportunity to use NSOperation and NSOperationQueue. I would recommend creating a serial queue and then loading your individual sound operations in sequence. The following code is not fully formed but is pretty close. Hopefully your dot and dash sound files already include the dot-lengthed space after each tone. If they don't then you will have to insert the additional spaces (pauses) yourself.
class LongBeep: NSOperation {
override func main() {
if self.cancelled { return }
print("L", terminator: "") // play long sound
}
}
class ShortBeep: NSOperation {
override func main() {
if self.cancelled { return }
print("S", terminator: "") // play short sound
}
}
class Pause: NSOperation {
override func main() {
if self.cancelled { return }
print(" pause ", terminator: "") // play empty sound or use actual delay
}
}
func audioMorseMessage(message: String) {
let queue = NSOperationQueue()
queue.name = "morse-player"
queue.maxConcurrentOperationCount = 1
message.characters.map{code in
switch code {
case "-": queue.addOperation(LongBeep())
case ".": queue.addOperation(ShortBeep())
case " ": queue.addOperation(Pause())
default: break
}
}
}

To wait in a loop (that backgrounds anyway) in Swift?

In Swift (that's Swift) there are a number of ways to handle asynchronous,
Say you have a loop like this - it's calling a parse cloud code call which goes to background anyway.
public func runImages()
{
print("test begins...")
for i in 1...3
{
print("Tick tock tick tock ..\(i)")
PFCloud.callFunctionInBackground("blah", withParameters:["bla":i,"bla":"bla] )
{
(response: AnyObject?, error: NSError?) -> Void in
print(".. done! Now go again...")
if let rr = response as? String { print(rr) }
if let err = error { print(err.domain) }
}
}
}
How to make that wait for the end of each PFCloud call?
Really is just an ordinary flag best, or? (Note that (a) I can't get a flag to work in Swift and (b) as Paul points out you're blocking the UI!!)
What is the "Swift way" in the context you see here? I feel it would be very inelegant to use a recursive pattern here, for example.
If you want the three calls to execute serially then you can use a serial dispatch queue and a dispatch_barrier_async to do something when all three are finished -
let dispatchQueue=dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL)
for i in 1...3 {
dispatch_async(dispatchQueue, { () -> Void in
print("i=\(i)")
let result = PFCloud.callFunction("blah", withParameters:["bla":i,"bla":"bla] )
})
}
dispatch_barrier_async(dispatchQueue, { () -> Void in
print("really done")
})
print(" done")
In order for this to work with your Parse function you need to use the synchronous cloud code call, not an asynchronous. And if you update UI in the dispatch_barrier_async closure you would need to dispatch that on the main queue.

Resources