How can i cancel the subscriber? - ios

#weakify(self)
[[self.listModel.firstLoadCommand execute:nil] subscribeNext:^(NSNumber *nType) {
#strongify(self)
[HUDManager removeHUDFromView:self.view];
self.isFirstRequest = NO;
NSInteger pageIndex = nType.integerValue;
//Select some page
[self.listTabBar selectTabBarAtIndex:pageIndex ];
} error:^(NSError *error)
{
#strongify(self)
self.isFirstRequest = NO;
[HUDManager removeHUDFromView:self.view];
JKBaseSearchViewController *viewController = [_pageArray objectOrNilAtIndex:0];
[HUDManager showNonNetWorkHUDInView:viewController.view];
}];
now,
I use the signal send a request, but the network service is not active,
i find the controller wait a minute free, i want when i pop this controller,it's free! how solve this problem ? thanks!!

Related

How i can set the auto print symenteneously 3-4 prints on epson tm-m30 printer

How I can set the auto print symenteneously 3-4 prints on Epson TM-m30 printer.
Here is my code please check and provide the solution.
if I remove the time delay this code will break and if I set the time delay then it's working...!!
It's continuously connect & disconnect the printer.
this is the common process for the do connection and print the receipt.
1) initializing
2) connecting
3) printing
4) disconnecting & clear buffer
I have noticed that one printer can't connect with multiple Ipads or multiple devices, And our requirement is that we have to connect with multiple devices.
So, is there any way to set a queue to manage multiple requests?
NSArray *arr=[[NSArray alloc]initWithObjects:#"1",#"2", nil];
[arr enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop)
{
if (idx==0)
{
[self runPrintReceiptSequence];
}
else
{
[self performSelector:#selector(runPrintReceiptSequence) withObject:nil afterDelay:1.5];
}
}];
- (BOOL)runPrintReceiptSequence
{
_textWarnings.text = #"";
if (![self initializeObject]) {
return NO;
}
if (![self createReceiptData]) {
[self finalizeObject];
return NO;
}
if (![self printData]) {
[self finalizeObject];
return NO;
}
return YES;
}
- (void) onPtrReceive:(Epos2Printer *)printerObj code:(int)code status:(Epos2PrinterStatusInfo *)status printJobId:(NSString *)printJobId
{
[ShowMsg showResult:code errMsg:[self makeErrorMessage:status]];
[self dispPrinterWarnings:status];
[self updateButtonState:YES];
[self performSelectorInBackground:#selector(disconnectPrinter) withObject:nil];
}
I fix this issue by a queue that regulates the execution of operations using the NSOperationQueue with maxConcurrentOperationCount = 1.
but this solution make a delay between printing operations. so now, I search solution to print (multiple) without any delay.
Any suggestions ? Regards.

Using Reactivecocoa login button only triggered once

Here is my basic scenario:
I am making a simple login framework, so I got two text field, one for username and the other for password, and of course a login button.
Now I bind them with RACSignal like this:
RACSignal *validPasswordSignal = [passwordTextField.rac_textSignal map:^id(NSString *text)
{
return #([self isPasswordValid:text]);
}];
RACSignal *validUsernameSignal = [usernameTextField.rac_textSignal map:^id(NSString *text)
{
return #([self isUsernameValid:text]);
}];
And combine two signals (username and password) into one like this:
RACSignal *submitActiveSignal = [RACSignal combineLatest:#[validpasswordSignal, validUsernameSignal] reduce:^id(NSNumber *validPW, NSNumber *validUN)
{
return #(validPW.boolValue && validUN.boolValue);
}];
Of course I need a method to submit signals for username and password with request to server, so I have it like this:
-(RACSignal *)submitSignal
{
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber)
{
[[[RequestClass alloc] init] requestWithUsername:_usernameTextField.text.trim password:_passwordTextField.text.trim completionHandler:^(MyResult *results, NSURLResponse *response, NSError *error)
{
if (!error)
{
if (results.code == 0)
{
[subscriber sendNext:#(YES)];
[subscriber sendCompleted];
}
else
{
NSError *invalidError = [NSError errorWithDomain:MyErrorDomain code:MyErrorInvalidSigniture userInfo:#{#"NSLocalizedDescription": results.message}];
[subscriber sendError:invalidError];
}
}
else
{
[subscriber sendError:error];
}
}];
return nil;
}];
}
Then I need to let my login button catch the signal from subscriber's sendNext and sendError, so I did it like this:
//subscribeNext
[[[[_submitButton rac_signalForControlEvents:UIControlEventTouchUpInside]
doNext:^(id x)
{
_submitButton.enabled = NO;
}]
flattenMap:^RACStream *(id value)
{
return [self submitSignal];
}]
subscribeNext:^(NSNumber *signal)
{
_submitButton.enabled = YES;
BOOL success = signal.boolValue;
if (success)
{
[self successCallback];
}
else
{
//do sth
}
}];
//subscribeError
[[[[_submitButton rac_signalForControlEvents:UIControlEventTouchUpInside]
doNext:^(id x)
{
_submitButton.enabled = NO;
}]
flattenMap:^RACStream *(id value)
{
return [self submitSignal];
}]
subscribeError:^(NSError *error)
{
[self failureCallbackWithError:error];
}];
Above is the reactivecocoa code I wrote for my login procedure. When the pass and username was correct, the signal would go to sendNext's response block: subscribeNext,
but the issue is, when the credentials were incorrect, and server returned error, neither the sendError nor subscribeError would be triggered.
It's like the error signal was lost or something.
I'm not sure if I am using the right reactivecocoa methods to handle success signal and error signal.
So please help, thanks.
Also please let me know if I'm not clear on my question.
You can use a RACCommand and it will take care of a lot of this for you:
#weakify(self)
RACCommand *submitCommand =
[[RACCommand alloc] initWithEnabled:submitActiveSignal signalBlock:^RACSignal *(id input) {
#strongify(self)
return [[self submitSignal]
doCompleted:^{
#strongify(self)
[self successCallback];
}];
}];
_submitButton.rac_command = submitCommand;
By providing the submitActiveSignal as the initWithEnabled: argument the button will automatically be enabled/disabled based on the value emitted from that signal.
For error handling, RACCommand has a special errors signal that sends every error that occurs in the command. You could subscribe to the errors signal to handle all errors that result from your signal returned from inside the command:
[[submitCommand.errors
takeUntil:self.rac_willDeallocSignal]
subscribeNext:^(NSError *error) {
#strongify(self)
[self failureCallbackWithError:error];
}];
And don't forget to #weakify/#strongify references to self inside of signals to avoid memory leaks.

Is this implementation of a timeout in Reactive Cocoa correct?

I did a login which is connected to a button in ReactiveCocoa. Even I tested this piece of code and it seems to work correctly, I am not sure if I do it right.
The login signal returns "next" on success and "error" in any other case. Since I don't want the Button to be unsubscribed on an error I use the catch function.
What I want: I want a timeout to fire after 2 seconds, if the loginSignal is not fired. Is this correctly done? Is it also done right the "reactive way"?
[[[[self.loginButton rac_signalForControlEvents:UIControlEventTouchUpInside]
doNext:^(id x) {
[self disableUI];
}]
flattenMap:^(id value) {
return [[[[[self.login loginSignalWithUsername:self.usernameTextField.text
andPassword:self.passwordTextField.text]
catch:^RACSignal *(NSError *error) {
[self enableUI];
[self showAlertWithTitle:NSLocalizedString(#"ERROR_TITLE", #"Error")
message:NSLocalizedString(#"LOGIN_FAILURE", #"Login not successful.")];
return [RACSignal empty];
}]
deliverOn:[RACScheduler mainThreadScheduler]]
timeout:2.0 onScheduler:[RACScheduler mainThreadScheduler]]
catch:^RACSignal *(NSError *error) {
[self enableUI];
[self showAlertWithTitle:NSLocalizedString(#"TIMEOUT_TITLE", #"Timeout occured")
message:NSLocalizedString(#"REQUEST_NOT_POSSIBLE", #"Server request failed")];
return [RACSignal empty];
}];
}]
subscribeNext:^(id x) {
[self enableUI];
// Go to next page after login
}];
You should in my opinion use RACCommand which is a nice hub for binding signals to UI :
RACCommand* command = [[RACCommand alloc] initWithSignalBlock:^(id _) {
return [[[self.login loginSignalWithUsername:self.username password:self.password]
doCompleted:^{
// move to next screen
}]
timeout:2.0 onScheduler:[RACScheduler mainThreadScheduler]];
}];
self.button.rac_command = command;
You can then handle any errors (login or timeout) using the command "errors" signal :
[[command errors] subscribeNext:^(NSError* err) {
// display error "err" to the user
}];
The signal will automatically disable the button while it is executing. If you need to disable other parts of your UI you can use the "executing" signal of the command.
[[command executing] subscribeNext:^(NSNumber* executing) {
if([executing boolValue]) {
[self disableUI];
} else {
[self enableUI];
}
}];
// bonus note: if your enableUI method took a BOOL you could lift it in one line :
[self rac_liftSelector:#selector(enableUI:) withSignals:[command executing], nil];
here is a blog article talking about RACCommands

Sleep thread until another thread completes for iOS

I'm very new to backend programming and iOS programming. I'm currently integrating DynamoDB into my iOS application. I'm doing a signup function that queries the database to check whether the username is taken.
Here's the main idea of my code:
__block BOOL isPlayerTaken = NO;
//Block of code that queries the database
//Mutates value of isPlayerTaken if player exists
[[dynamoDBObjectMapper query:[DDBRunnerTableRow class]
expression:queryExpression]
continueWithExecutor:[BFExecutor mainThreadExecutor] withSuccessBlock:^id(BFTask *task) {
AWSDynamoDBPaginatedOutput *paginatedOutput = task.result;
DDBRunnerTableRow *queriedRunner = paginatedOutput.items[0];
if (queriedRunner) {
isPlayerTaken = YES;
NSLog(#"%#", queriedRunner.runnerName);
}
return nil;
}];
return isPlayerTaken;
However, the function returns before the query result is completed and hence the output is always NO.
How can I ensure that the function doesn't return until the query block completes?
Thank you so much for your help. I believe I would need this in many functions of my code that requires querying and inserting of data and I didn't want to come up with a hackish solution for it.
Try something like this:
[[dynamoDBObjectMapper query:[DDBRunnerTableRow class]
expression:queryExpression]
continueWithExecutor:[BFExecutor mainThreadExecutor] withSuccessBlock:^id(BFTask *task) {
AWSDynamoDBPaginatedOutput *paginatedOutput = task.result;
DDBRunnerTableRow *queriedRunner = paginatedOutput.items[0];
if (queriedRunner)
{
NSLog(#"%#", queriedRunner.runnerName);
[self loginSuccessfull:queriedRunner];
}
else
{
[self loginFailed];
}
}];
then
- (void)loginSuccessfull:(DDBRunnerTableRow *)queriedRunner
{
//whatever
}
- (void)loginFailed
{
//whatever
}

ReactiveCocoa, is there a better way to RAC first and refresh later

Here's the situation
I've a ViewModel (ok, follow the MVVM architecture), which has an array property, called "specialtySegments", and a flag property called "generic", I wanna when the generic value is changed, update the specialtySegments value again
Code snippet
- (instancetype)initWithTerritory:(SAPTerritory *)territory {
self = [super init];
if (self) {
_territory = territory;
_generic = NO;
_selectedProduct = [_products firstObject];
RAC(self, specialtySegments) = [self querySpecialtySegments];
}
return self;
}
- (RACSignal *)querySpecialtySegments {
return [[[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[self asynQuerySpecialtySegments:^(NSArray *model) {
[subscriber sendNext:model];
[subscriber sendCompleted];
}];
return [RACDisposable disposableWithBlock:^{
}];
}] deliverOn:[RACScheduler mainThreadScheduler]] publish] autoconnect];
}
- (void)refreshData {
[self asynQuerySpecialtySegments:^(NSArray *model) {
dispatch_async(dispatch_get_main_queue(), ^{
self.specialtySegments = model;
});
}];
}
And in the ViewController
#weakify(self);
[[RACObserve(self.viewModel, specialtySegments) filter:^BOOL(id value) {
return (value != nil);
}] subscribeNext:^(id x) {
#strongify(self) {
[self.indicatorView stopAnimating];
self.indicatorView.hidden = YES;
[self.tableView reloadData];
}
}];
And some place to change the generic value, like
if (specialtyButton.selected == NO) {
self.viewModel.generic = NO;
genericButton.selected = NO;
specialtyButton.selected = YES;
[self.viewModel refreshData];
}
That's what I know to do now, it's not kind of fancy, I think.
Could I observe the generic property in the ViewModel, and subscripNext and first the signal again.
I've try like this, but code didn't enter the querySpecialtySegments, I put a breakpoint in the method.
[RACObserve(self, generic) subscribeNext:^(id x) {
[self querySpecialtySegments];
}];
and if I do like this, the asynQuerySpecialtySegments method would enter twice, as the RAC before, would run it once, I think
[RACObserve(self, generic) subscribeNext:^(id x) {
[self asynQuerySpecialtySegments:^(NSArray *model) {
dispatch_async(dispatch_get_main_queue(), ^{
self.specialtySegments = model;
});
}];
}];
I'm a newbie to ReactiveCocoa, trying using in the project, so is there a better way to the thing I described above, thanks guy
All correct, it's not works on start, because you are setting instance variable with underscore, if you in init method will call self.generic = NO; , RACObserve will works just as planned.
This happens, because when you setting instance variable, setter will not be called, and methods for KVO like - willChangeValue: and -didChangeValue: also will not be called.
OK, after one day reading I think I've found the answer by myself
The key point is
Most signals start out "cold," which means that they will not do any work until subscription.
So this part code should change like this
[RACObserve(self, generic) subscribeNext:^(id x) {
[[self querySpecialtySegments] subscribeNext:^(id x) { // subscription is the key to trigger
self.specialtySegments = x; // would trigger the observer in view controller to do something
}];
}];
Updated:
Personal idea is to observe generic property in ViewModel and then trigger View to update something is not a best option, some kind of black magic, without calling refresh or something, but the view refresh automatically. With a public method -reloadData is a good choice
- (void)refreshData {
#weakify(self);
[[self querySpecialtySegments] subscribeNext:^(id x) {
#strongify(self) {
self.specialtySegments = x;
}
}];
}

Resources