ios - Auto-repeating count down timer in ReactiveCocoa -
i'm new reactivecocoa , there problem couldn't yet find way solve. have network request in app returns data encoded in qr code valid 30 seconds. network request returns racsignal
, send data encoded in signal view model. in view model map data qr image , expose property in view model interface. after create qr image, want update timeleftstring
property says "this code valid 30 seconds" seconds change time progresses, , after 30 seconds complete, want make request fetch qr code data valid 30 seconds more , after completes request fetch data valid 30 seconds...up until screen dismissed. how go implementing this?
currently have data:
- (racsignal *)newpaymentsignal { @weakify(self); return [[[[apimanager sharedmanager] newpayment] map:^id(nsstring *paymenttoken) { zxmultiformatwriter *writer = [zxmultiformatwriter writer]; zxbitmatrix *result = [writer encode:paymenttoken format:kbarcodeformatqrcode width:250 height:250 error:nil]; if (!result) { return nil; } cgimageref cgimage = [[zximage imagewithmatrix:result] cgimage]; uiimage *image = [uiimage imagewithcgimage:cgimage]; return uiimagepngrepresentation(image); }] donext:^(nsdata *data) { @strongify(self); self.qrimagedata = data; }]; }
and timer
- (racsignal *)timeremainingsignal { @weakify(self); return [[[racsignal interval:0.5 onscheduler:[racscheduler scheduler]] // startwith:[nsdate date]] // initially:^{ @strongify(self); self.expirydate = [[nsdate date] datebyaddingtimeinterval:30]; }]; }
the flow is: data api, start timer, , when time make new request new data , start timer again..and repeat forever.
1- how start timer after data api?
2- how make flow repeat forever?
3- how stop timer before 30 seconds complete , start flow beginning if user taps button on user interface?
4- have expirydate
property 30 seconds added current date because thought take difference of expirydate
, [nsdate date]
decide whether time - there better way implement this?
5- how break flow when it's repeating forever , unsubscribe when screen dismissed (or, say, when user taps button)?
thanks in advance answers.
i think missing piece of puzzle useful flattenmap
operator. replaces nexts incoming signal nexts signal returned it.
here's 1 approach solving problem (i replaced newpaymentsignal method simple signal sending string):
- (racsignal *)newpaymentsignal { return [[racsignal return:@"token"] delay:2]; } - (void)start { nsinteger refreshinterval = 30; racsignal *refreshtokentimersignal = [[racsignal interval:refreshinterval onscheduler:[racscheduler mainthreadscheduler]] startwith:[nsdate date]]; [[[[refreshtokentimersignal flattenmap:^racstream *(id _) { return [self newpaymentsignal]; }] map:^nsdate *(nsstring *paymenttoken) { // display paymenttoken here nslog(@"%@", paymenttoken); return [[nsdate date] datebyaddingtimeinterval:refreshinterval]; }] flattenmap:^racstream *(nsdate *expirydate) { return [[[[racsignal interval:1 onscheduler:[racscheduler mainthreadscheduler]] startwith:[nsdate date]] takeuntil:[refreshtokentimersignal skip:1]] map:^nsnumber *(nsdate *now) { return @([expirydate timeintervalsincedate:now]); }]; }] subscribenext:^(nsnumber *remaining) { // update timer readout here nslog(@"%@", remaining); }]; }
every time outer refreshtokentimersignal
fires, gets mapped new newpaymentsignal
, in turn when returns value gets mapped expiry date, used create new "inner" timer signal fires every second.
the takeuntil
operator on inner timer completes signal outer refresh timer sends next.
(one peculiar thing here had add skip:1
refreshtokentimersignal
, otherwise inner timer never got started. have expected work without skip:1
, maybe better versed in internals of rac explain why is.)
to break flow of outer signal in response various events, can experiment using takeuntil
, takeuntilblock
on too.
Comments
Post a Comment