I have this RxSwift code in swift 3
let bag:DisposeBag = DisposeBag()
var sig:Observable<Int>!
sig = Observable<Int>.interval(1.0, scheduler: MainScheduler.instance)
sig.subscribe(onNext: { (milsec) in
print("Mil: \(milsec)")
}).addDisposableTo(bag)
i run this code when button tapped, but its not print anything on console.
DisposeBag will dispose of your subscription once it goes out of scope. In this instance, it'll be right after the call to subscribe, and it explains why you don't see anything printed to the console.
Move the definition of dispose bag to the class creating the subscription and everything should work fine.
class MyViewController: UIViewController {
let bag:DisposeBag = DisposeBag()
dynamic func onButtonTapped() {
var sig:Observable<Int>!
sig = Observable<Int>.interval(1.0, scheduler: MainScheduler.instance)
sig.subscribe(onNext: { (sec) in
print("Sec: \(sec)")
}).addDisposableTo(bag)
}
}
On a side note, interval expects an interval in seconds, so it will only tick every seconds as oposed to milliseconds.
Related
In Node.js I can set the interval that a certain event should be triggered,
function intervalFunc() {
console.log('whelp, triggered again!');
}
setInterval(intervalFunc, 1500);
However the interface for Tokio's interval is a bit more complex. It seems to be a something to do with a much more literal definition of an interval, and rather than calling a function at an interval, it simply stalls the thread until the time passes (with .await).
Is there a primitive in Tokio that calls a function "every x seconds" or the like? If not, is there an idiom that has emerged to do this?
I only need to run one function on a recurring interval... I don't care about other threads either. It's just one function on Tokio's event loop.
Spawn a Tokio task to do something forever:
use std::time::Duration;
use tokio::{task, time}; // 1.3.0
#[tokio::main]
async fn main() {
let forever = task::spawn(async {
let mut interval = time::interval(Duration::from_millis(10));
loop {
interval.tick().await;
do_something().await;
}
});
forever.await;
}
You can also use tokio::time::interval to create a value that you can tick repeatedly. Perform the tick and call your function inside of the body of stream::unfold to create a stream:
use futures::{stream, StreamExt}; // 0.3.13
use std::time::{Duration, Instant};
use tokio::time; // 1.3.0
#[tokio::main]
async fn main() {
let interval = time::interval(Duration::from_millis(10));
let forever = stream::unfold(interval, |mut interval| async {
interval.tick().await;
do_something().await;
Some(((), interval))
});
let now = Instant::now();
forever.for_each(|_| async {}).await;
}
async fn do_something() {
eprintln!("do_something");
}
See also:
How can I run a set of functions concurrently on a recurring interval without running the same function at the same time using Tokio?
I am still a rust/tokio beginner, but I did find this solution helpful for myself:
use std::time::Duration;
use tokio::time;
use tokio_stream::wrappers::IntervalStream;
#[tokio::main]
async fn main() {
let mut stream = IntervalStream::new(time::interval(Duration::from_secs(1)));
while let Some(_ts) = stream.next().await {
println!("whelp, triggered again!");
}
}
Please note that _ts holds the execution timestamp.
I'm writing some networking code in Swift that prevents initiating a download that is already in progress. I do this by keeping tracking of the identity of the network request along with the associated completion handlers in an (synchronized) array A. When a network call finishes it calls the completion handlers that are associated with that resource and it subsequently removes those handlers from the array A.
I want to make sure there is no way for threads to access the array in certain cases. For example, consider the following scenario:
A request to download resource X is started.
Verify whether the request has already been made.
Add the completion handler to the array A.
If request has not been made, start the download.
What if resource X was already downloading, and the completion handler for this download interrupts the thread between steps 2 and 3? It has been verified that the request has been made so the download will not be started, but the new completion handler will be added to array A which will now never be called.
How would I block this from happening? Can I lock the array for writing while I do steps 2 and 3?
The simple solution is to run everything on the main thread except the actual downloading. All you need to do is make the completion handler a stub that places a block on the main queue to do all the work.
The pseudo code for what you want is something like
assert(Thread.current == Thread.main)
handlerArray.append(myHandler)
if !requestAlreadyRunning)
{
requestAlreadyRunning = true
startDownloadRequest(completionHandelr: {
whatever in
Dispatch.main.async // This is the only line of code that does not run on the main thread
{
for handler in handlerArray
{
handler()
}
handlerArray = []
requestAlreadyRunning = false
}
})
}
This works because all the work that might result in race conditions and synchronisation conflicts runs on one thread - the main thread and so the completion handler can't possibly be running when you are adding new completion handlers to the queue and vice versa.
Note that, for the above solution to work, your application needs to be in a run loop. This will be true for any Cocoa based application on Mac OS or iOS but not necessarily true for a command line tool. If that is the case or if you don't want any of the work to happen on the main thread, set up a serial queue and run the connection initiation and the completion handler on it instead of the main queue.
I'm working on the assumption that you want to be able to add multiple callbacks that will all be run when the latest request completes, whether it was already in-flight or not.
Here's a sketch of a solution. The basic point is to take a lock before touching the array(s) of handlers, whether to add one or to invoke them after the request has completed. You must also synchronize the determination of whether to start a new request, with the exact same lock.
If the lock is already held in the public method where the handlers are added, and the request's own completion runs, then the latter must wait for the former, and you will have deterministic behavior (the new handler will be invoked).
class WhateverRequester
{
typealias SuccessHandler = (Whatever) -> Void
typealias FailureHandler = (Error) -> Void
private var successHandlers: [SuccessHandler] = []
private var failureHandlers: [FailureHandler] = []
private let mutex = // Your favorite locking mechanism here.
/** Flag indicating whether there's something in flight */
private var isIdle: Bool = true
func requestWhatever(succeed: #escaping SuccessHandler,
fail: #escaping FailureHandler)
{
self.mutex.lock()
defer { self.mutex.unlock() }
self.successHandlers.append(succeed)
self.failureHandlers.append(fail)
// Nothing to do, unlock and wait for request to finish
guard self.isIdle else { return }
self.isIdle = false
self.enqueueRequest()
}
private func enqueueRequest()
{
// Make a request however you do, with callbacks to the methods below
}
private func requestDidSucceed(whatever: Whatever)
{
// Synchronize again before touching the list of handlers and the flag
self.mutex.lock()
defer { self.mutex.unlock() }
for handler in self.successHandlers {
handler(whatever)
}
self.successHandlers = []
self.failureHandlers = []
self.isIdle = true
}
private func requestDidFail(error: Error)
{
// As the "did succeed" method, but call failure handlers
// Again, lock before touching the arrays and idle flag.
}
}
This is so broadly applicable that you can actually extract the callback storage, locking, and invocation into its own generic component, which a "Requester" type can create, own, and use.
Based on Josh's answer I created a generic Request & Requester below. It had a few more specific needs than I described in the question above. I want the Request instance to manage only requests with a certain ID (which I made into a String for now, but I guess this could also made more generic). Different ID's require a different Request instance. I created the Requester class for this purpose.
The requester class manages an array of Requests. For example, one could choose T = UIImage, and ID = image URL. This would give us an image downloader. Or one could choose T = User, and ID = user id. This would get a user object only once, even when requested several times.
I also wanted to be able to cancel requests from individual callers. It tags the completion handler with a unique ID that is passed back to the caller. It can use this to cancel the request. If all callers cancel, the request is removed from the Requester.
(The code below has not been tested so I cannot guarantee it to be bug free. Use at your own risk.)
import Foundation
typealias RequestWork<T> = (Request<T>) -> ()
typealias RequestCompletionHandler<T> = (Result<T>) -> ()
typealias RequestCompletedCallback<T> = (Request<T>) -> ()
struct UniqueID {
private static var ID: Int = 0
static func getID() -> Int {
ID = ID + 1
return ID
}
}
enum RequestError: Error {
case canceled
}
enum Result<T> {
case success(T)
case failure(Error)
}
protocol CancelableOperation: class {
func cancel()
}
final class Request<T> {
private lazy var completionHandlers = [(invokerID: Int,
completion: RequestCompletionHandler<T>)]()
private let mutex = NSLock()
// To inform requester the request has finished
private let completedCallback: RequestCompletedCallback<T>!
private var isIdle = true
// After work is executed, operation should be set so the request can be
// canceled if possible
var operation: CancelableOperation?
let ID: String!
init(ID: String,
completedCallback: #escaping RequestCompletedCallback<T>) {
self.ID = ID
self.completedCallback = completedCallback
}
// Cancel the request for a single invoker and it invokes the competion
// handler with a cancel error. If the only remaining invoker cancels, the
// request will attempt to cancel
// the associated operation.
func cancel(invokerID: Int) {
self.mutex.lock()
defer { self.mutex.unlock() }
if let index = self.completionHandlers.index(where: { $0.invokerID == invokerID }) {
self.completionHandlers[index].completion(Result.failure(RequestError.canceled))
self.completionHandlers.remove(at: index)
if self.completionHandlers.isEmpty {
self.isIdle = true
operation?.cancel()
self.completedCallback(self)
}
}
}
// Request work to be done. It will only be done if it hasn't been done yet.
// The work block should set the operation on this request if possible. The
// work block should call requestFinished(result:) if the work has finished.
func request(work: #escaping RequestWork<T>,
completion: #escaping RequestCompletionHandler<T>) -> Int {
self.mutex.lock()
defer { self.mutex.unlock() }
let ID = UniqueID.getID()
self.completionHandlers.append((invokerID: ID, completion: completion))
guard self.isIdle else { return ID }
work(self)
self.isIdle = false
return ID
}
// This method should be called from the work block when the work has
// completed. It will pass the result to all completion handlers and call
// the Requester class to inform that this request has finished.
func requestFinished(result: Result<T>) {
self.mutex.lock()
defer { self.mutex.unlock() }
completionHandlers.forEach { $0.completion(result) }
completionHandlers = []
self.completedCallback(self)
self.isIdle = true
}
}
final class Requester<T> {
private lazy var requests = [Request<T>]()
private let mutex = NSLock()
init() { }
// reuqestFinished(request:) should be called after a single Request has
// finished its work. It removes the requests from the array of requests.
func requestFinished(request: Request<T>) {
self.mutex.lock()
defer { self.mutex.unlock() }
if let index = requests.index(where: { $0.ID == request.ID }) {
requests.remove(at: index)
}
}
// request(ID:, work:) will create a request or add a completion handler to
// an existing request if a request with the supplied ID already exists.
// When a request is created, it passes a closure that removes the request.
// It returns the invoker ID to the invoker for cancelation purposes.
func request(ID: String,
work: #escaping RequestWork<T>,
completion: #escaping RequestCompletionHandler<T>) ->
(Int, Request<T>) {
self.mutex.lock()
defer { self.mutex.unlock() }
if let existingRequest = requests.first(where: { $0.ID == ID }) {
let invokerID = existingRequest.request(work: work, completion: completion)
return (invokerID, existingRequest)
} else {
let request = Request<T>(ID: ID) { [weak self] (request) in
self?.requestFinished(request: request)
}
let invokerID = request.request(work: work, completion: completion)
return (invokerID, request)
}
}
}
I have developed a small Swift code that displays somebody's total likes. So I have one code based on a while loop to add up the amount of likes into one total. Then, I created a function that displays a short phrase including the result of that loop. Now, in my view controller I want to run that display function on a label I have created to finally display the all time likes on the screen. However, I can't seem to understand how I should run the display function on the label and have it display without it being a result of me clicking a button first. Keep in mind, this is my second day of coding, with only prior experience in the basic syntax of Java.
Here is the first code
import UIKit
class likesObject: NSObject {
// This is where I created the loop to solve for the total amount of likes
func complimentsLikeTotal() -> Void {
let monthlyLikes = [20, 15, 13, 32, 14, 38]
var totalLikes = 0
var runTotal = 0
while runTotal < monthlyLikes.count {
totalLikes += monthlyLikes[runTotal]
print(totalLikes)
runTotal += 1
}
}
/*
This is where I created the function to return how many all time likes there are in a phrase
I want to use this function in the viewcontroller code on the label
*/
func display() -> String {
return "\(complimentsLikeTotal) all time likes"
}
}
Here is the view controller code
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var inputLikesLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Here is where I don't know what to do with display and how to link it with the label
self.inputLikesLabel.text = display
}
}
Probably looking for something like this: (please make the class name start with capital as per convention)
var likesObject = LikesObject()
likesObject.complimentsLikeTotal()
let display = likesObject.display()
self.inputLikesLabel.text = display
I assume that the LikesObject class also has some global variables defined to hold the data.
P.s. in swift it is not necessary to explicitly state void return value, if you omit it the compiler implies void.
I am trying to make an array of integers with data I have stored on an online database (Parse). I know for a fact that I am receiving the data and that my variable is storing that data. I also know for a fact that the data value is being added as when I print out the size of the array under my append line, the size constantly increases until it reaches the size of my database (90). However, when I print the size of the array at the end of the method or in my constructor, I get a value of 0. I have a feeling that this problem is happening because of the "self" keyword but I am not sure how to get around it. My end goal is store all the values from the database into an array that I create and can globally access.
Here is the code:
import Foundation
class DataLoader
{
var allData: [Int] = []
init()
{
allData = []
generateAllData()
println(allData.count)
}
private func generateAllData()
{
var tempVal: Int = 0
var tempArray: [Int] = []
Parse.setApplicationId("CENSORED FOR PRIVACY REASONS", clientKey: "CENSORED FOR PRIVACY REASONS")
var query = PFQuery(className: "Snapshot")
query.selectKeys(["objectID"])
query.findObjectsInBackgroundWithBlock
{
(objects: [AnyObject]!, error: NSError!) -> Void in
if error == nil
{
for obj in objects
{
var temp: String = obj.objectId
var newQuery = PFQuery(className: "Data")
newQuery.getObjectInBackgroundWithId(temp)
{
(dataValue: PFObject!, error: NSError!) -> Void in
if error == nil && dataValue != nil
{
tempVal = dataValue["Year"].integerValue
}
else
{
println(error)
}
}
self.allData.append(tempVal)
println(self.allData.count)
}
}
}
println(allData.count)
}
}
The problem is that the println's at the end of the method and the end of the init are happening while the background operation is still getting the data.
When you call "query.findObjectsInBackgroundWithBlock", it will go off into the background and get the data. But that does not block the next line from being called and it does not stop the function returning.
I suggest you add a callback or delegate method so that the rest of your code knows when the data has completely downloaded.
And you use of "self" is correct. Inside a block, you have to provide that context.
Update:
Adding more info about blocks and callbacks.
When you are using a block, the code after the block continues on without waiting for the block to complete. So a good way to know when the block has finished it to send a callback function as one of the parameters to the function containing the block. Once the block has completed, it can use the callback function to report back.
As a shorter example than your code, have a look at this animation function:
doSomeAnimation() // call the function below
func doSomeAnimation() {
println("Starting doSomeAnimation")
UIView.animateWithDuration(2.0,
animations: { () -> Void in
// animate something here
self.alphaButton.alpha = 1.0
}) { (animationDone) -> Void in
// animation over
println("Animation complete")
}
println("Ending doSomeAnimation")
}
You will see it contains 3 println() statements and the order in which they appear may surprise you:
Starting doSomeAnimation
Ending doSomeAnimation
Animation complete
So the doSomeAnimation() function is over long before the animation has completed. In this trivial example, that doesn't matter, but what if you need to know before the rest of your code could proceed?
One solution is to send a callback function to the function with the block. Here is the previous example with a callback in place:
doSomeAnimation() { (complete : Bool) in
println("Animation callback")
}
func doSomeAnimation(callback : ( Bool ) -> Void) {
println("Starting doSomeAnimation")
UIView.animateWithDuration(2.0,
animations: { () -> Void in
// animate something here
self.alphaButton.alpha = 1.0
}) { (animationDone) -> Void in
// animation over
println("Animation complete")
callback(animationDone)
}
println("Ending doSomeAnimation")
}
So the doSomeAnimation() function is called, but it is provided with a function as its parameter. It performs the animation and when the animation has finished, it uses this supplied callback function to report back to the caller.
And now the println() statements show as follows:
Starting doSomeAnimation
Ending doSomeAnimation
Animation complete
Animation callback
Hopefully this makes it clearer, both what the issue is an a possible solution.
Using Swift, I'm having difficulty testing for the expiration / invalidation of an NSTimer.
In particular, why does it appear that a test like while timer1 != nil { } or while timer1?.valid { } does not escape the null loop after it has been seemingly clearly invalidated and set to nil directly.
I have reviewed several of the questions relating to NSTimer already, and unless there is a version in Obj-C that I simply am not recognizing as the same issue due to my poor comprehension of ObjC properly, I don't believe this is covered (directly).
Please note, I'm not looking to directly "fix" the code, I'm looking for comprehension.
// ViewController.swift
// NSTimerTest
import UIKit
class ViewController: UIViewController {
var z:Int = 0
var timer1:NSTimer? = nil
#IBOutlet var lblMessage: UILabel
#IBOutlet var lbl2: UILabel
#IBAction func btnPress(sender: AnyObject) {
doBtnPress()
}
override func viewDidLoad() {
super.viewDidLoad()
z = 0
lblMessage.text = "Timer was called \(z) times."
lbl2.text = "waiting for countdown"
}
func doBtnPress(){
lblMessage.text = "doBtnPress!!"
timer1 = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: "doTimedAction", userInfo: nil, repeats: true)
// All of the below infinitely loop
// while timer1?.valid { } // <=== never escapes, also depreciated in IOS 8.0 ???
// while timer1 { } // <==== never escapes
// while timer1 != nil { } // <==== never escapes
// while z >= 0 { } // <==== test counter instead, but no use
lbl2.text = "timer has been invalidated"
}
func doTimedAction(){
lblMessage.text = "Timer was called \(++z) times."
if z >= 10 {
timer1?.invalidate()
timer1 = nil
z = -1
}
}
}
As you can see, I have two labels -- one that is simply updated with the number of times the NSTimer has been invoked, and the other that hold be updated after I invalidate the timer.
With the testing lines commented out as noted above, things work as I expect. The label indicating the number of timer invocations updates each second (or so) and the immediate but factually incorrect display of the statement that "Timer has been invalidated" )
However, it my expectation that de-commenting any of the three lines that follow should allow the timer to run through 10 iterations, and then be invalidated, allowing the "Timer invalidated" message to display. Instead, however, I end up stuck infinitely in the empty loop, as if the condition(s) never come true. The UI does not update at all.
Instead of testing the timer, I also tried testing the counter z, but this does not work either, leading me to believe there is something more elemental at play that I am not understanding. For example, using while z >= 0 { println("z is \(z)")} in the location results in z is 0 -- the reason the loop is infinite. But again, if I comment out the line, z does in fact clearly increment, as the label DOES change, reflecting the cane in the counter z.
Again, I'm looking more to understand WHY things are failing (i.e., what I am failing to comprehend here). I understand, for example, I could make this "work" by having the doTimedAction() func directly update the label --- but that would not help me understand why my tests fail.
Whilst you are in the loop, you are blocking the timer from firing, as both the timer and loop are on the same thread.
Check out the documentation on Run Loops, and also this post on creating an NSTimer on a background thread.