Sou desenvolvedor iOS com alguma experiência e essa pergunta é realmente interessante para mim. Vi muitos recursos e materiais diferentes sobre esse tópico, mas ainda assim estou confuso. Qual é a melhor arquitetura para um aplicativo em rede iOS? Quero dizer, estrutura abstrata básica, padrões, que se encaixam em todos os aplicativos de rede, seja um aplicativo pequeno que tenha apenas algumas solicitações de servidor ou um cliente REST complexo. A Apple recomenda usar MVC
como uma abordagem arquitetural básica para todos os aplicativos iOS, mas MVC
nem os MVVM
padrões mais modernos explicam onde colocar o código lógico da rede e como organizá-lo em geral.
Preciso desenvolver algo como MVCS
( S
for Service
) e, nessa Service
camada, colocar todos os ajustes, se tivermos mapeamentos de objetos complexos e persistência, ou mesmo uma implementação de comunicação de rede própria com a API padrão). Mas essa abordagem parece uma sobrecarga para mim. Outra abordagem é ter um singletonAPI
solicitações e outra lógica de rede, que em perspectiva pode ser realmente complexa? Depois de fazer algumas pesquisas, encontrei duas abordagens básicas para isso. Aqui , foi recomendado criar uma classe separada para cada solicitação de rede para serviço da Web API
(como LoginRequest
classe ou PostCommentRequest
classe e assim por diante), que herda toda a classe abstrata de solicitação básica AbstractBaseRequest
e, além disso, criar algum gerenciador de rede global que encapsule o código de rede comum e outras preferências (pode ser AFNetworking
personalização ouRestKit
API
despachante ou classe gerente como na primeira abordagem, mas não para criar classes para cada pedido e, em vez de encapsular cada pedido como um método de instância público desta classe de gestão, como: fetchContacts
, loginUser
métodos, etc. Então, o que é a melhor e a maneira correta? Existem outras abordagens interessantes que ainda não conheço?
E devo criar outra camada para todo esse material de rede como Service
, ouNetworkProvider
camada ou qualquer outra coisa em cima da minha MVC
arquitetura, ou essa camada deve ser integrada (injetada) nas MVC
camadas existentes, por exemplo Model
?
Sei que existem abordagens bonitas ou como esses monstros móveis, como o cliente do Facebook ou o LinkedIn, lidam com a crescente complexidade da lógica da rede?
Eu sei que não há resposta exata e formal para o problema. O objetivo desta pergunta é coletar as abordagens mais interessantes de desenvolvedores iOS experientes . A melhor abordagem sugerida será marcada como aceita e premiada com uma recompensa de reputação; outras serão votadas. É principalmente uma questão teórica e de pesquisa. Quero entender a abordagem arquitetônica básica, abstrata e correta para aplicativos de rede no iOS. Espero uma explicação detalhada de desenvolvedores experientes.
fonte
Respostas:
I want to understand basic, abstract and correct architectural approach for networking applications in iOS
: não existe uma abordagem "melhor" ou "mais correta" para criar uma arquitetura de aplicativo. É muito trabalho criativo. Você deve sempre escolher a arquitetura mais direta e extensível, que ficará clara para qualquer desenvolvedor que começar a trabalhar no seu projeto ou para outros desenvolvedores da sua equipe, mas eu concordo que pode haver um "bom" e um "ruim" "arquitetura.Você disse:
collect the most interesting approaches from experienced iOS developers
não acho que minha abordagem seja a mais interessante ou correta, mas a usei em vários projetos e estou satisfeita com ela. É uma abordagem híbrida das que você mencionou acima e também com melhorias de meus próprios esforços de pesquisa. Sou interessante nos problemas de construção de abordagens, que combinam vários padrões e expressões conhecidas. Penso que muitos padrões empresariais da Fowler podem ser aplicados com sucesso aos aplicativos móveis. Aqui está uma lista dos mais interessantes, que podemos aplicar para criar uma arquitetura de aplicativo iOS ( na minha opinião ): Camada de serviço , Unidade de trabalho , Fachada remota , Objeto de transferência de dados ,Gateway , Supertipo de camada , ou criar sua própria camada leve de mapeamento / persistência de objetos leve, com base em SQLite bruto ou LevelDB, Caso Especial , Modelo de Domínio . Você sempre deve projetar corretamente uma camada de modelo e sempre não esquecer a persistência (isso pode aumentar significativamente o desempenho do seu aplicativo). Você pode usarCore Data
para isso. Mas você não deve esquecer que issoCore Data
não é um ORM ou um banco de dados, mas um gerenciador de gráficos de objetos com persistência como uma boa opção. Portanto, muitas vezesCore Data
pode ser muito pesado para suas necessidades e você pode procurar novas soluções, como Realm e Couchbase Lite. Também aconselho você a se familiarizar com o Domain Driven Design e o CQRS .A princípio, acho que devemos criar outra camada para a rede, porque não queremos controladores de gordura ou modelos pesados e sobrecarregados. Eu não acredito nessas
fat model, skinny controller
coisas. Mas acredito naskinny everything
abordagem, porque nenhuma classe deve ser gorda, nunca. Todas as redes geralmente podem ser abstraídas como lógica de negócios; consequentemente, devemos ter outra camada, onde podemos colocá-la. Camada de serviço é o que precisamos:Em nosso
MVC
reino,Service Layer
há algo como um mediador entre o modelo de domínio e os controladores. Há uma variação bastante semelhante dessa abordagem chamada MVCS, onde aStore
é realmente a nossaService
camada.Store
vends modela instâncias e lida com a rede, o cache etc. Quero mencionar que você não deve escrever toda a lógica de rede e de negócios em sua camada de serviço. Isso também pode ser considerado como um design ruim. Para mais informações, consulte os modelos de domínio Anêmico e Rico . Alguns métodos de serviço e lógica de negócios podem ser manipulados no modelo, por isso será um modelo "rico" (com comportamento).Eu sempre uso extensivamente duas bibliotecas: AFNetworking 2.0 e ReactiveCocoa . Eu acho que é um item obrigatório para qualquer aplicativo moderno que interaja com a rede e os serviços da web ou contenha lógica complexa da interface do usuário.
ARQUITETURA
Inicialmente, crio uma
APIClient
classe geral , que é uma subclasse de AFHTTPSessionManager . Esta é uma força de trabalho de todas as redes no aplicativo: todas as classes de serviço delegam solicitações REST reais a ele. Ele contém todas as personalizações do cliente HTTP, que eu preciso no aplicativo em particular: fixação de SSL, processamento de erros e criação deNSError
objetos diretos com motivos de falha detalhados e descrições de todosAPI
e erros de conexão (nesse caso, o controlador poderá mostrar mensagens corretas para usuário), configurando serializadores de solicitação e resposta, cabeçalhos http e outros itens relacionados à rede. Então eu logicamente dividir todas as solicitações de API em subserviços ou, mais corretamente, microservices :UserSerivces
,CommonServices
,SecurityServices
,FriendsServices
e assim por diante, de acordo com a lógica de negócios que eles implementam. Cada um desses microsserviços é uma classe separada. Eles juntos formam umService Layer
. Essas classes contêm métodos para cada solicitação de API, modelos de domínio de processo e sempre retornam umRACSignal
com o modelo de resposta analisado ouNSError
para o responsável pela chamada.Quero mencionar que se você tiver uma lógica de serialização de modelo complexa - crie outra camada para ela: algo como o Data Mapper, mas mais geral, por exemplo, JSON / XML -> Model mapper. Se você tiver cache: crie-o também como uma camada / serviço separado (você não deve misturar lógica de negócios com cache). Por quê? Porque a camada de armazenamento em cache correta pode ser bastante complexa com suas próprias dicas. As pessoas implementam lógica complexa para obter cache válido e previsível, como, por exemplo, cache monoidal com projeções baseadas em profunctors. Você pode ler sobre esta bela biblioteca chamada Carlos para entender mais. E não esqueça que o Core Data pode realmente ajudá-lo com todos os problemas de cache e permitirá que você escreva menos lógica. Além disso, se você tiver alguma lógica entre os
NSManagedObjectContext
modelos de solicitação e servidor, poderá usar RepositórioPadrão de , que separa a lógica que recupera os dados e os mapeia para o modelo de entidade da lógica de negócios que atua no modelo. Portanto, aconselho usar o padrão de repositório mesmo quando você tiver uma arquitetura baseada em dados principais. Repositório pode coisas abstratas, comoNSFetchRequest
,NSEntityDescription
,NSPredicate
e assim por diante para os métodos simples, comoget
ouput
.Depois de todas essas ações na camada Serviço, o responsável pela chamada (controlador de exibição) pode executar algumas tarefas assíncronas complexas com a resposta: manipulações de sinal, encadeamento, mapeamento etc. com a ajuda de
ReactiveCocoa
primitivos, ou apenas se inscreva e mostre os resultados na exibição . Eu injetar com a injeção de dependência em todas estas classes de serviço meusAPIClient
, que se traduzirá uma chamada de serviço especial em correspondentesGET
,POST
,PUT
,DELETE
, etc. pedido para o terminal REST. Nesse caso,APIClient
é passado implicitamente para todos os controladores, você pode explicitar isso explicandoAPIClient
as classes de serviço. Isso pode fazer sentido se você quiser usar personalizações diferentes doAPIClient
para classes de serviço específicas, mas se você, por algum motivo, não quiser cópias extras ou tiver certeza de que sempre usará uma instância específica (sem personalizações) doAPIClient
- faça dele um singleton, mas NÃO, por favor, NÃO Faça aulas de serviço como singletons.Em seguida, cada controlador de exibição novamente com o DI injeta a classe de serviço necessária, chama métodos de serviço apropriados e compõe seus resultados com a lógica da interface do usuário. Para injeção de dependência, eu gosto de usar o BloodMagic ou um framework Typhoon mais poderoso . Eu nunca uso singletons,
APIManagerWhatever
aulas de Deus ou outras coisas erradas. Porque se você ligar para a sua turmaWhateverManager
, isso indica que você não conhece seu objetivo e é uma má escolha de design . Singletons também é um anti-padrão e, na maioria dos casos (exceto os raros), é uma solução errada . Singleton deve ser considerado apenas se todos os três dos seguintes critérios forem atendidos:No nosso caso, a propriedade da instância única não é um problema e também não precisamos de acesso global depois de dividirmos o nosso god manager em serviços, porque agora apenas um ou vários controladores dedicados precisam de um serviço específico (por exemplo,
UserProfile
necessidades do controladorUserServices
e assim por diante) .Devemos sempre respeitar o
S
princípio no SOLID e usar a separação de preocupações . Portanto, não coloque todos os métodos de serviço e chamadas de rede em uma classe, porque é uma loucura, especialmente se você desenvolver um aplicativo corporativo de grande porte. É por isso que devemos considerar a injeção de dependência e a abordagem de serviços. Considero essa abordagem moderna e pós-OO . Nesse caso, dividimos nosso aplicativo em duas partes: lógica de controle (controladores e eventos) e parâmetros.Aqui está um fluxo de trabalho geral da minha arquitetura, por exemplo. Vamos supor que temos um
FriendsViewController
, que exibe a lista de amigos do usuário e temos a opção de remover dos amigos. Eu crio um método na minhaFriendsServices
classe chamado:onde
Friend
é um objeto de modelo / domínio (ou pode ser apenas umUser
objeto se eles tiverem atributos semelhantes). Underhood este parses métodoFriend
paraNSDictionary
de parâmetros JSONfriend_id
,name
,surname
,friend_request_id
e assim por diante. Eu sempre uso a biblioteca Mantle para esse tipo de clichê e para a minha camada de modelo (analisando para frente e para trás, gerenciando hierarquias de objetos aninhadas em JSON e assim por diante). Depois de analisá-lo chamaAPIClient
DELETE
método para fazer um pedido de descanso efectivo e voltaResponse
emRACSignal
para o chamador (FriendsViewController
no nosso caso) para exibir mensagem apropriada para o usuário ou qualquer outra coisa.Se nossa aplicação é muito grande, temos que separar nossa lógica ainda mais claramente. Por exemplo, nem sempre é bom misturar
Repository
ou modelar a lógica comService
uma. Quando descrevi minha abordagem, eu disse que esseremoveFriend
método deveria estar naService
camada, mas se formos mais pedantes, podemos notar que ele pertence melhorRepository
. Vamos lembrar o que é Repositório. Eric Evans deu uma descrição precisa em seu livro [DDD]:Portanto, a
Repository
é essencialmente uma fachada que usa a semântica no estilo de coleção (Adicionar, Atualizar, Remover) para fornecer acesso aos dados / objetos. É por isso que quando você tem algo como:getFriendsList
,getUserGroups
,removeFriend
você pode colocá-lo noRepository
, porque coleção-like semântica é bastante clara aqui. E código como:é definitivamente uma lógica de negócios, porque está além das
CRUD
operações básicas e conecta dois objetos de domínio (Friend
eRequest
), é por isso que deve ser colocada naService
camada. Também quero observar: não crie abstrações desnecessárias . Use todas essas abordagens com sabedoria. Porque se você sobrecarregar seu aplicativo com abstrações, isso aumentará sua complexidade acidental, e a complexidade causará mais problemas em sistemas de software do que qualquer outra coisaDescrevo um exemplo "antigo" do Objective-C, mas essa abordagem pode ser muito fácil de adaptar à linguagem Swift com muito mais melhorias, porque possui recursos mais úteis e açúcar funcional. Eu recomendo usar esta biblioteca: Moya . Permite criar uma
APIClient
camada mais elegante (nosso cavalo de batalha, como você se lembra). Agora, nossoAPIClient
provedor será um tipo de valor (enum) com extensões em conformidade com protocolos e alavancando a correspondência de padrões de desestruturação. Enumerações rápidas + correspondência de padrões nos permitem criar tipos de dados algébricos como na programação funcional clássica. Nossos microsserviços usarão esseAPIClient
provedor aprimorado como na abordagem comum de Objective-C. Para a camada do modelo, em vez deMantle
você pode usar biblioteca ObjectMapperou gosto de usar a biblioteca Argo mais elegante e funcional .Então, descrevi minha abordagem arquitetônica geral, que pode ser adaptada para qualquer aplicativo, eu acho. Pode haver muito mais melhorias, é claro. Eu aconselho você a aprender programação funcional, porque você pode se beneficiar muito disso, mas também não vá muito longe. Eliminar um estado mutável global excessivo, compartilhado, criar um modelo de domínio imutável ou criar funções puras sem efeitos colaterais externos é, geralmente, uma boa prática, e uma nova
Swift
linguagem incentiva isso. Mas lembre-se sempre de que sobrecarregar seu código com padrões funcionais puros pesados, abordagens teóricas de categoria é uma péssima idéia, porque outros desenvolvedores lerão e darão suporte ao seu código e poderão ser frustrados ou assustadores.prismatic profunctors
e esse tipo de coisa no seu modelo imutável. A mesma coisa comReactiveCocoa
: nãoRACify
exagere muito no seu código , pois ele pode se tornar ilegível muito rápido, especialmente para iniciantes. Use-o quando realmente puder simplificar seus objetivos e lógica.Então
read a lot, mix, experiment, and try to pick up the best from different architectural approaches
,. É o melhor conselho que posso lhe dar.fonte
". I don't like singletons. I have an opinion that if you decided to use singletons in your project you should have at least three criteria why you do this (I edited my answer). So I inject them (lazy of course and not each time, but
uma vez `) em todos os controladores.De acordo com o objetivo desta pergunta, gostaria de descrever nossa abordagem de arquitetura.
Abordagem de arquitetura
A arquitetura de nosso aplicativo iOS geral se baseia nos seguintes padrões: Camadas de serviço , MVVM , Vinculação de dados da interface do usuário , Injeção de dependência ; e paradigma de Programação Reativa Funcional .
Podemos dividir um aplicativo voltado para o consumidor típico nas seguintes camadas lógicas:
A camada de montagem é um ponto de inicialização do nosso aplicativo. Ele contém um contêiner de injeção de dependência e declarações dos objetos do aplicativo e suas dependências. Essa camada também pode conter a configuração do aplicativo (URLs, chaves de serviços de terceiros e assim por diante). Para esse fim, usamos a biblioteca Typhoon .
A camada de modelo contém classes, validações, mapeamentos de modelos de domínio. Usamos a biblioteca Mantle para mapear nossos modelos: suporta serialização / desserialização em
JSON
formato eNSManagedObject
modelos. Para validação e representação forma de nossos modelos usamos FXForms e FXModelValidation bibliotecas.A camada de serviços declara os serviços que usamos para interagir com sistemas externos, a fim de enviar ou receber dados representados em nosso modelo de domínio. Geralmente, temos serviços de comunicação com APIs do servidor (por entidade), serviços de mensagens (como o PubNub ), serviços de armazenamento (como o Amazon S3), etc. Basicamente, os serviços envolvem objetos fornecidos pelos SDKs (por exemplo, o PubNub SDK) ou implementam sua própria comunicação lógica. Para redes em geral, usamos a biblioteca AFNetworking .
O objetivo da camada de armazenamento é organizar o armazenamento de dados local no dispositivo. Usamos Core Data ou Realm para isso (ambos têm prós e contras, a decisão sobre o que usar é baseada em especificações concretas). Para a configuração do Core Data, usamos a biblioteca MDMCoreData e várias classes - armazenamentos - (semelhantes aos serviços) que fornecem acesso ao armazenamento local para cada entidade. Para o Realm, usamos apenas armazenamentos semelhantes para ter acesso ao armazenamento local.
A camada de gerentes é um local onde nossas abstrações / invólucros vivem.
Em uma função de gerente, pode ser:
Portanto, no papel de gerente, pode haver qualquer objeto que implemente a lógica de um aspecto ou preocupação específica necessária para o funcionamento do aplicativo.
Tentamos evitar Singletons, mas essa camada é um lugar onde eles moram, se necessário.
A camada Coordenadores fornece objetos que dependem de objetos de outras camadas (Serviço, Armazenamento, Modelo) para combinar sua lógica em uma sequência de trabalho necessária para determinado módulo (recurso, tela, história do usuário ou experiência do usuário). Geralmente, encadeia operações assíncronas e sabe como reagir aos casos de sucesso e falha. Como exemplo, você pode imaginar um recurso de mensagens e o
MessagingCoordinator
objeto correspondente . A manipulação da operação de envio de mensagens pode ser assim:Em cada uma das etapas acima, um erro é tratado de forma correspondente.
A camada da interface do usuário consiste nas seguintes subcamadas:
Para evitar os Massive View Controllers, usamos o padrão MVVM e implementamos a lógica necessária para a apresentação da interface do usuário no ViewModels. Um ViewModel geralmente possui coordenadores e gerentes como dependências. ViewModels usados pelo ViewControllers e alguns tipos de Views (por exemplo, células de exibição de tabela). A cola entre os ViewControllers e os ViewModels é o padrão Data Binding and Command. Para possibilitar essa cola, usamos a biblioteca ReactiveCocoa .
Também usamos ReactiveCocoa e seu
RACSignal
conceito como uma interface e retornando o tipo de valor de todos os métodos de coordenadores, serviços e armazenamentos. Isso nos permite encadear operações, executá-las paralelamente ou em série, e muitas outras coisas úteis fornecidas pelo ReactiveCocoa.Tentamos implementar nosso comportamento de interface do usuário de maneira declarativa. Ligação de dados e layout automático ajudam muito a atingir esse objetivo.
A camada de infraestrutura contém todos os auxiliares, extensões, utilitários necessários para o trabalho do aplicativo.
Essa abordagem funciona bem para nós e para os tipos de aplicativos que costumamos criar. Mas você deve entender que esta é apenas uma abordagem subjetiva que deve ser adaptada / alterada para o objetivo da equipe concreta.
Espero que isso ajude você!
Além disso, você pode encontrar mais informações sobre o processo de desenvolvimento do iOS nesta postagem do blog Desenvolvimento como serviço do iOS
fonte
Como todos os aplicativos iOS são diferentes, acho que existem abordagens diferentes a serem consideradas aqui, mas geralmente eu faço o seguinte:
Crie uma classe de gerente central (singleton) para lidar com todas as solicitações de API (geralmente denominadas APICommunicator) e todo método de instância é uma chamada de API . E existe um método central (não público):
Para o registro, eu uso duas bibliotecas / estruturas principais, ReactiveCocoa e AFNetworking. O ReactiveCocoa lida perfeitamente com as respostas assíncronas da rede, você pode fazer (sendNext :, sendError :, etc.).
Esse método chama a API, obtém os resultados e os envia pelo RAC no formato 'bruto' (como NSArray, o que a AFNetworking retorna).
Em seguida, um método como o
getStuffList:
chamado método acima assina seu sinal, analisa os dados brutos em objetos (com algo como Motis) e envia os objetos um a um para o chamador (getStuffList:
e métodos semelhantes também retornam um sinal que o controlador pode assinar) )O controlador inscrito recebe os objetos pelo
subscribeNext:
bloco e os manipula.Tentei de várias maneiras em aplicativos diferentes, mas este funcionou da melhor maneira possível, então eu tenho usado isso em alguns aplicativos recentemente, ele se encaixa em projetos pequenos e grandes e é fácil estender e manter, se algo precisar ser modificado.
Espero que isso ajude, gostaria de ouvir as opiniões dos outros sobre minha abordagem e talvez como os outros pensam que isso poderia ser melhorado.
fonte
+ (void)getAllUsersWithSuccess:(void(^)(NSArray*))success failure:(void(^)(NSError*))failure;
e- (void)postWithSuccess:(void(^)(instancetype))success failure:(void(^)(NSError*))failure;
que fazem os preparativos necessários e depois chamarão o gerente de API.Na minha situação, geralmente estou usando a biblioteca ResKit para configurar a camada de rede. Ele fornece análise fácil de usar. Isso reduz meu esforço em configurar o mapeamento para diferentes respostas e outras coisas.
Eu adiciono apenas algum código para configurar o mapeamento automaticamente. Eu defino a classe base para meus modelos (não protocolo por causa de muito código para verificar se algum método está implementado ou não e menos código nos modelos):
MappableEntry.h
MappableEntry.m
Relacionamentos são objetos que representam objetos aninhados em resposta:
RelationshipObject.h
RelationshipObject.m
Então, estou configurando o mapeamento para o RestKit assim:
ObjectMappingInitializer.h
ObjectMappingInitializer.m
Alguns exemplos de implementação de MappableEntry:
User.h
Usuário.m
Agora, sobre o agrupamento de solicitações:
Eu tenho um arquivo de cabeçalho com definição de blocos, para reduzir o comprimento da linha em todas as classes APIRequest:
APICallbacks.h
E exemplo da minha classe APIRequest que estou usando:
LoginAPI.h
LoginAPI.m
E tudo o que você precisa fazer no código, basta inicializar o objeto API e chamá-lo sempre que precisar:
SomeViewController.m
Meu código não é perfeito, mas é fácil definir uma vez e usar em diferentes projetos. Se é interessante para alguém, mb eu poderia gastar algum tempo e criar uma solução universal para isso em algum lugar no GitHub e CocoaPods.
fonte
Na minha opinião, toda a arquitetura de software é orientada pela necessidade. Se isso é para fins pessoais ou de aprendizado, decida o objetivo principal e faça com que ele direcione a arquitetura. Se este é um trabalho contratado, a necessidade do negócio é fundamental. O truque é não deixar que coisas brilhantes o distraiam das necessidades reais. Acho isso difícil de fazer. Sempre existem novas coisas brilhantes aparecendo neste negócio e muitas delas não são úteis, mas você nem sempre pode dizer isso com antecedência. Concentre-se na necessidade e esteja disposto a abandonar as más escolhas, se puder.
Por exemplo, recentemente fiz um protótipo rápido de um aplicativo de compartilhamento de fotos para uma empresa local. Como a necessidade comercial era fazer algo rápido e sujo, a arquitetura acabou sendo um código iOS para abrir uma câmera e um código de rede anexado a um botão Enviar que carregava a imagem em uma loja S3 e gravava em um domínio SimpleDB. O código era trivial e o custo mínimo, e o cliente possui uma coleção de fotos escalável acessível pela web com chamadas REST. Barato e burro, o aplicativo tinha muitas falhas e bloqueava a interface do usuário de vez em quando, mas seria um desperdício fazer mais por um protótipo e permitir que eles implantem em sua equipe e gerem milhares de imagens de teste facilmente, sem desempenho ou escalabilidade preocupações. Arquitetura de baixa qualidade, mas se encaixa perfeitamente na necessidade e no custo.
Outro projeto envolveu a implementação de um banco de dados local seguro que sincroniza com o sistema da empresa em segundo plano quando a rede está disponível. Criei um sincronizador em segundo plano que usava o RestKit, pois parecia ter tudo o que eu precisava. Mas eu tive que escrever tanto código personalizado para o RestKit lidar com JSON idiossincrático que eu poderia ter feito tudo mais rápido escrevendo minhas próprias transformações JSON em CoreData. No entanto, o cliente queria trazer esse aplicativo internamente e achei que o RestKit seria semelhante às estruturas usadas em outras plataformas. Estou esperando para ver se foi uma boa decisão.
Novamente, o problema para mim é focar na necessidade e deixar isso determinar a arquitetura. Eu tento como o inferno evitar o uso de pacotes de terceiros, pois eles trazem custos que só aparecem após o aplicativo estar em campo por um tempo. Eu tento evitar criar hierarquias de classe, pois elas raramente valem a pena. Se eu posso escrever algo em um período de tempo razoável, em vez de adotar um pacote que não se encaixa perfeitamente, eu faço. Meu código está bem estruturado para depuração e comentado adequadamente, mas raramente são pacotes de terceiros. Com isso dito, acho o AF Networking muito útil para ignorar e bem estruturado, bem comentado e mantido, e eu o uso muito! O RestKit cobre muitos casos comuns, mas sinto que estive em uma briga quando o uso, e a maioria das fontes de dados que encontro são cheias de peculiaridades e problemas que são melhor tratados com código personalizado. Nos meus últimos aplicativos, apenas uso os conversores JSON internos e escrevo alguns métodos utilitários.
Um padrão que eu sempre uso é tirar as chamadas de rede do segmento principal. Os últimos 4-5 aplicativos que fiz configuraram uma tarefa de timer em segundo plano usando o dispatch_source_create, que acorda de vez em quando e executa tarefas de rede conforme necessário. Você precisa fazer algum trabalho de segurança de encadeamento e garantir que o código de modificação da interface do usuário seja enviado ao encadeamento principal. Também ajuda a fazer a sua integração / inicialização de forma que o usuário não se sinta sobrecarregado ou atrasado. Até agora, isso tem funcionado bastante bem. Eu sugiro olhar para essas coisas.
Por fim, acho que, à medida que trabalhamos mais e à medida que o sistema operacional evolui, tendemos a desenvolver melhores soluções. Levei anos para superar minha crença de que tenho de seguir padrões e projetos que outras pessoas afirmam serem obrigatórios. Se estou trabalhando em um contexto em que isso faz parte da religião local, quero dizer as melhores práticas departamentais de engenharia, então sigo os costumes à risca, é para isso que eles estão me pagando. Mas raramente acho que seguir padrões e desenhos mais antigos é a solução ideal. Eu sempre tento olhar para a solução através do prisma das necessidades dos negócios e construir a arquitetura para corresponder a ela e manter as coisas o mais simples possível. Quando sinto que não há o suficiente, mas tudo funciona corretamente, estou no caminho certo.
fonte
Eu uso a abordagem que recebi daqui: https://github.com/Constantine-Fry/Foursquare-API-v2 . Reescrevi essa biblioteca no Swift e você pode ver a abordagem arquitetural dessas partes do código:
Basicamente, há a subclasse NSOperation que faz o NSURLRequest, analisa a resposta JSON e adiciona o bloco de retorno de chamada com o resultado à fila. A classe principal da API constrói NSURLRequest, inicializa a subclasse NSOperation e a adiciona à fila.
fonte
Usamos algumas abordagens, dependendo da situação. Para a maioria das coisas, o AFNetworking é a abordagem mais simples e robusta, na qual você pode definir cabeçalhos, fazer upload de dados com várias partes, usar GET, POST, PUT & DELETE e existem várias categorias adicionais para o UIKit que permitem, por exemplo, definir uma imagem de um URL. Em um aplicativo complexo com muitas chamadas, às vezes resumimos isso em um método de conveniência próprio, que seria algo como:
Existem algumas situações em que o AFNetworking não é apropriado, por exemplo, onde você está criando uma estrutura ou outro componente de biblioteca, pois o AFNetworking já pode estar em outra base de código. Nessa situação, você usaria um NSMutableURLRequest em linha se estiver fazendo uma única chamada ou abstraído para uma classe de solicitação / resposta.
fonte
Evito singletons ao projetar meus aplicativos. Eles são típicos para muitas pessoas, mas acho que você pode encontrar soluções mais elegantes em outros lugares. Normalmente, o que faço é criar minhas entidades no CoreData e, em seguida, colocar meu código REST em uma categoria NSManagedObject. Se, por exemplo, eu desejasse criar e postar um novo usuário, eu faria o seguinte:
Eu uso o RESTKit para o mapeamento de objetos e o inicializo na inicialização. Acho que o roteamento de todas as suas chamadas através de um singleton é uma perda de tempo e adiciona um monte de clichê desnecessário.
No NSManagedObject + Extensions.m:
No NSManagedObject + Networking.m:
Por que adicionar classes auxiliares extras quando você pode estender a funcionalidade de uma classe base comum por meio de categorias?
Se você estiver interessado em informações mais detalhadas sobre minha solução, entre em contato. Estou feliz em compartilhar.
fonte
Tente https://github.com/kevin0571/STNetTaskQueue
Crie solicitações de API em classes separadas.
O STNetTaskQueue lidará com encadeamento e delegação / retorno de chamada.
Extensível para diferentes protocolos.
fonte
De uma perspectiva de design puramente de classe, você geralmente terá algo parecido com isto:
Classe de modelo de dados - Depende realmente de quantas entidades distintas reais você está lidando e como elas estão relacionadas.
Por exemplo, se você tiver uma matriz de itens a serem exibidos em quatro representações diferentes (lista, gráfico, gráfico etc.), você terá uma classe de modelo de dados para a lista de itens, e mais uma para um item. A lista da classe do item será compartilhada por quatro controladores de exibição - todos filhos de um controlador de barra de guias ou de um controlador de navegação.
As classes de modelo de dados serão úteis não apenas na exibição de dados, mas também na serialização delas, na qual cada uma delas pode expor seu próprio formato de serialização através dos métodos de exportação JSON / XML / CSV (ou qualquer outra coisa).
É importante entender que você também precisa de classes do construtor de solicitações de API que mapeiam diretamente com os terminais da API REST. Digamos que você tenha uma API que efetue login no usuário - portanto, sua classe de construtor de API de login criará uma carga útil POST JSON para a API de login. Em outro exemplo, uma classe de construtor de solicitação de API para a lista de itens de catálogo API criará a sequência de consultas GET para a API correspondente e acionará a consulta REST GET.
Essas classes do construtor de solicitações de API geralmente recebem dados dos controladores de exibição e também transmitem os mesmos dados para exibir os controladores para atualização da interface do usuário / outras operações. Os controladores de exibição decidirão como atualizar os objetos do Modelo de Dados com esses dados.
Finalmente, o coração do cliente REST - dados API classe fetcher que é alheio a todos os tipos de API solicita seus aplicativos marcas. É provável que essa classe seja um singleton, mas, como outros apontaram, não precisa ser um singleton.
Observe que o link é apenas uma implementação típica e não leva em consideração cenários como sessão, cookies etc., mas é o suficiente para você continuar sem usar estruturas de terceiros.
fonte
Essa pergunta já tem muitas respostas excelentes e abrangentes, mas acho que preciso mencionar isso, já que ninguém mais tem.
Alamofire para Swift. https://github.com/Alamofire/Alamofire
Ele foi criado pelas mesmas pessoas que a AFNetworking, mas foi projetado mais diretamente com o Swift em mente.
fonte
Eu acho que agora o projeto médio usa a arquitetura MVVM e o Big project usa a arquitetura VIPER e tenta alcançar
Abordagens arquitetônicas para a criação de aplicativos de rede iOS (clientes REST)
A preocupação com a separação de códigos limpos e legíveis evita a duplicação:
inversão de dependência
Responsável principal:
Você encontrará aqui a arquitetura GitHub MVVM com o restante API Swift Project
fonte
Na engenharia de software móvel, os mais utilizados são os padrões Clean Architecture + MVVM e Redux.
Arquitetura Limpa + MVVM consistem em 3 camadas: Domínio, Apresentação, Camadas de Dados. Onde a camada de apresentação e a camada de repositórios de dados dependem da camada de domínio:
E a camada de apresentação consiste em ViewModels e Views (MVVM):
Neste artigo, há uma descrição mais detalhada de Clean Architecture + MVVM https://tech.olx.com/clean-architecture-and-mvvm-on-ios-c9d167d9f5b3
fonte