Pergunta

Eu tenho uma pergunta.Talvez 2 perguntas.

O primeiro está relacionado a este trecho de código:

-(void)isLoggedIn {
NSString *urlString = [NSString stringWithFormat:@"%@%@%@", kBaseURL, kAPI, kUserIsLogged];
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.responseSerializer = [AFJSONResponseSerializer serializerWithReadingOptions:NSJSONReadingAllowFragments];
[manager GET:urlString parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
    NSLog(@"JSON: %@", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"Error: %@", error);
}];
}

Gostaria que esse método não fosse um void, gostaria que retornasse um BOOL ou algo que eu possa usar como:O usuário está logado > Ok, faça algo em algum lugar do aplicativo;O usuário não está logado > Ok leva o usuário a uma tela onde ele pode inserir suas credenciais e fazer login.

A segunda pergunta está relacionada a este trecho de código:

-(void)detail {
NSString *urlString = [NSString stringWithFormat:@"%@%@%@", kBaseURL, kAPI, kUser];
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.responseSerializer = [AFJSONResponseSerializer serializerWithReadingOptions:NSJSONReadingAllowFragments];
[manager GET:urlString parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
    NSLog(@"JSON: %@", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"Error: %@", error);
}];
}

Gostaria que esse método me retornasse um NSArray com o JSON analisado, que eu possa usar onde quiser.

Esses métodos estão contidos em um modelo chamado NetworkModel (subclasse NSObject).Só estou me perguntando por que devo fazer tudo dentro dos blocos.Devo ter peças interagindo com a rede em todos os lugares, e não organizadas em um único local/classe.Existe uma maneira de fazer isso.Não quero ter 5 linhas de solicitações em cada ViewController.Quero definir métodos que posso chamar de qualquer ViewController (obviamente importando os arquivos de cabeçalho quando necessário).

Alguma sugestão?Obrigado em conselhos!

Foi útil?

Solução

Você pode fazer isso com 2 abordagens, uma altera seus métodos para usar blocos ou usa um semáforo para aguardar a solicitação assíncrona:

Com semáforo (A MANEIRA FEIA)

-(BOOL)isLoggedIn {

__block BOOL isLoggedIn;
 dispatch_semaphore_t waiter = dispatch_semaphore_create(0);

NSString *urlString = [NSString stringWithFormat:@"%@%@%@", kBaseURL, kAPI, kUserIsLogged];
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.responseSerializer = [AFJSONResponseSerializer serializerWithReadingOptions:NSJSONReadingAllowFragments];
[manager GET:urlString parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
    NSLog(@"JSON: %@", responseObject);
    isLoggedIn = YES;
    dispatch_semaphore_signal(waiter);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"Error: %@", error);
    isLoggedIn = NO;
    dispatch_semaphore_signal(waiter);

}];

dispatch_group_wait(waiter, DISPATCH_TIME_FOREVER);
return isLoggedIn;
}

A maneira certa de fazer isso

-(void)isLoggedIn:(void(^)(BOOL isLoggedIn))completionBlock {
NSString *urlString = [NSString stringWithFormat:@"%@%@%@", kBaseURL, kAPI, kUserIsLogged];
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.responseSerializer = [AFJSONResponseSerializer serializerWithReadingOptions:NSJSONReadingAllowFragments];
[manager GET:urlString parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
    NSLog(@"JSON: %@", responseObject);
    completionBlock(YES);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"Error: %@", error);
    completionBlock(NO);
}];
}

Para sua segunda pergunta, você pode definir uma variável de bloco em sua classe de rede que será chamada quando a solicitação for concluída ou falhar.

De qualquer forma acho que você não está muito familiarizado com blocos (por causa da sua segunda pergunta) e acho que depois de alguns blocos você começará a gostar deles e verá que o código não é complicado se você tiver pequenos trechos de código que executam solicitação & analisando, você também verá que é muuuito fácil depurá-los e encontrar os problemas, quer dizer, esse também foi o meu caso quando comecei a trabalhar com blocos.

Usando uma variável de bloco

@interface YourNetworkClass:NSObject {
void(^resultHandlerBlock)(NSArray *results); //this block has as param a NSArray, you can change it to whatever you want
}

-(void)setResultHandlerBlock:(void(^)(NSArray *array))handlerBlock;

@implementation YourNetworkClass

-(void)setResultHandlerBlock:(void(^)(NSArray *array))handlerBlock {
resultHandlerBlock = block;
}

-(void)anotherMethodThatDoesNetworkOperationsAndReturnsAnArray {
 ....
 resultHandlerBlock(arrayOfResults);
}


@interface AViewControllerThatUseNetwork: UIViewController {
}

@implementation AViewControllerThatUseNetwork 

-(void)initNetworkClass {
  YourNetworkClass *networkClass = [[YourNetworkClass alloc] init]; //or other init methods
  [networkClass setResultHandlerBlock:^(NSArray *results) {
   //do whatever you want with the array
}];
}

Outras dicas

Eu perguntei algo semelhante há um tempo atrás.Acredito que você encontrará sua solução lá.Passando blocos para um método AFNetworking?

Em geral, você precisará alterar a maneira como acha que deve obter os dados de "se estiver logado, retornar BOOL" para "verificar se consigo fazer login, se isso falhar, faça algo dentro do bloco AFNetworking.

O link acima também responde à sua segunda pergunta.

Seus métodos devem ter um bloco (ou dois) que será executado a partir dos blocos de sucesso (ou falha) do AFNetworking.Essa abordagem abrange a natureza assíncrona do que você está tentando fazer.Você não pode adicionar valores de retorno aos métodos devido a essa assincronicidade.


Então, você mudaria seu método para:

-(void)isLoggedInWithCompletion:(LoginCompletionBlock)completion {
    ...

    [manager GET:urlString parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
        NSLog(@"JSON: %@", responseObject);
        completion(/* whatever parameters your block specifies */);
    } ...
}

Então, pouca atualização sobre a situação (obrigado por todas as sugestões preciosas..obrigado pessoal!).Agora meu código está assim (no meu NetworkModel.m, obviamente adicionei o método ao NetworkModel.h para poder chamá-lo em qualquer lugar):

-(void)isLoggedIn:(void(^)(BOOL isLoggedIn))completionBlock {
NSString *urlString = [NSString stringWithFormat:@"%@%@%@", kBaseURL, kAPI, kUserIsLogged];
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.responseSerializer = [AFJSONResponseSerializer serializerWithReadingOptions:NSJSONReadingAllowFragments];
[manager GET:urlString parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
    NSLog(@"JSON: %@", responseObject);
    if ([[NSString stringWithFormat:@"%@", responseObject] isEqualToString:@"true"]) {
        completionBlock(YES);
    } else {
       completionBlock(NO);
    }
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"Error: %@", error);
    completionBlock(NO);
}];
}

No meu AppDelegate.h incluí meu NetworkModel.h e no meu AppDelegate.m agora tenho o seguinte código:

[[self model] isLoggedIn:^(BOOL isLoggedIn) {
    if (isLoggedIn == YES) {
        UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
        UIViewController *controller = [storyboard instantiateViewControllerWithIdentifier:@"Start01"];
        [self.window setRootViewController:controller];
    } else {
        UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
        UIViewController *controller = [storyboard instantiateViewControllerWithIdentifier:@"Start00"];
        [self.window setRootViewController:controller];
    }    
}];

E funciona muito bem.Mas agora tenho outra pergunta sobre isso.Quando executo o aplicativo, algo estranho acontece.Assim que recupera as informações sobre o status de login do usuário, ele salta do VC (o controlador InitialView designado - Start00 no meu Storyboard) para o VC (loggedInVC - Start01 no meu Storyboard).Existe uma maneira de não mostrar esse tipo de salto para o usuário?Ou este é um comportamento normal?Agora o usuário vê por um segundo a primeira tela e depois a tela que ele realmente deveria ver (porque ele está logado).

Mas acho que a maneira mais fácil de trabalhar com blocos e tudo isso é usá-los exatamente onde preciso deles.Em vez de tentar encapsulá-los em métodos ou classes ou algo assim.Assim que os dados estiverem lá, farei o que tenho, diretamente dentro do bloco de sucesso ou falha.Estou pensando certo?

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top