Best RestKit setup - ios

I would like to use RestKit 0.20 on my app but I am a bit confused on how the pieces fit together. Each one of my view controllers needs to get data from the server each with their own route on the server returning a different object. I would like to keep all of the server requests away from the view controllers so I can manage them centrally (but open to another setup if it makes sense). Right now I have the following setup but I am pretty sure there is a better way to go about it using RKObjectManager and RKRouter :
1) Each view controller triggers a method in a Gateway object that is dedicated to it.
2) The method will create a request and a response map. Then a request and response description and finally a request operation that uses a manually created NSURL.
3) In the success block I pass the response to the view controller with an NSnotification.
Is there a better setup? Can I just use one RKObjectManager for all the request? How does that work? Do I put it in a separate method in my gateway? Is there a better way to get back to my view controller then NSNotification?
Sorry if some of these are very basic.

I would suggest the following structure (using Instagram as an example):
1) first of all split all your requests based on the "Resource" you use e.g. Users/Comments/Likes, etc
2) For each "Resource" create a separate class, subclass of RKObjectManager, for example UsersManager, CommentsManager, LikesManager (all inherited from RKObjectManager)
3) Define extra methods on every manager, that you will use from View Controller.
For example, for loading "likes" resource for user, you'd define this method in LikesManager (or UserManager) -- this is very opinionated decision, and it's really up to you.
- (void)loadLikesForUser:(User *)user
success:(void (^)(NSArray *likes))successCallback
failure:(void (^)(NSError *error))failureCallback;
4) Implement this method and call appropriate methods using self, because you've created a subclass of RKObjectManager class and have access to all basic methods.
[self getObjectsAtPath:#"/resources/" parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
// pass mappingResult.array to the successCallback
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
// pass error object to the failureCallback
}];
5) In your view controller, you'd call this like
[[LikesManager sharedManager] loadLikesForUser:user success:^(NSArray *likes) {
self.likes = likes;
// work with likes
} failure:^(NSError *error) {
// handle error
}];
Notice that you don't do any stuff with NSNotification, as it'll be a bad design decision in this kind of situations.
Generally speaking you can put all of your requests in a subclass of RKObjectManager, if you have very few of them, but if you have more than 5-6 requests, you'll find it tedious and hard to manage to keep all of them in one file. So that's why I suggest to split them based on a Resource.
UPDATE
Based on questions asked in comments, providing answers here
a) Where to set Base URL?
Base URL is a property on instance of RKObjectManager, so you definitely want to set it before making any requests to API. To me ideal place would be at the initialization of RKObjectManager instance.
b) Where to define Object Mapping?
Again it's up to you. Answer to this question is very opinionated. I'd consider 2 options:
create a separate class to hold all your objects related to mappings, like MappingProvider. Then whenever you create RKResponseDescriptor or RKRequestDescriptor, you'd just access properties of MappingProvider to get your mappings.
Define mappings in manager's class, because you'll assign them to the RKResponseDescriptor instances that will be used in this manager.
UPDATE: Check out this blog post on RestKit setup: http://restkit-tutorials.com/code-organization-in-restkit-based-app/

Related

Best architectural approaches for building iOS networking applications (REST clients) [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 1 year ago.
Improve this question
I'm an iOS developer with some experience and this question is really interesting to me. I saw a lot of different resources and materials on this topic, but nevertheless I'm still confused. What is the best architecture for an iOS networked application? I mean basic abstract framework, patterns, which will fit every networking application whether it is a small app which only have a few server requests or a complex REST client. Apple recommends to use MVC as a basic architectural approach for all iOS applications, but neither MVC nor the more modern MVVM patterns explain where to put network logic code and how to organize it in general.
Do I need to develop something like MVCS(S for Service) and in this Service layer put all API requests and other networking logic, which in perspective may be really complex? After doing some research I found two basic approaches for this. Here it was recommended to create a separate class for every network request to web-service API (like LoginRequest class or PostCommentRequest class and so on) which all inherits from the base request abstract class AbstractBaseRequest and in addition to create some global network manager which encapsulates common networking code and other preferences (it may be AFNetworking customisation or RestKit tuning, if the we have complex object mappings and persistence, or even an own network communication implementation with standard API). But this approach seems an overhead for me. Another approach is to have some singleton API dispatcher or manager class as in the first approach, but not to create classes for every request and instead to encapsulate every request as an instance public method of this manager class like: fetchContacts, loginUser methods, etc. So, what is the best and correct way? Are there other interesting approaches I don't know yet?
And should I create another layer for all this networking stuff like Service, or NetworkProvider layer or whatever on top of my MVC architecture, or this layer should be integrated (injected) into existing MVC layers e.g. Model?
I know there exists beautiful approaches, or how then such mobile monsters like Facebook client or LinkedIn client deal with exponentially growing complexity of networking logic?
I know there are no exact and formal answer to the problem. The goal of this question is to collect the most interesting approaches from experienced iOS developers. The best suggested approach will be marked as accepted and awarded with a reputation bounty, others will be upvoted. It is mostly a theoretical and research question. I want to understand basic, abstract and correct architectural approach for networking applications in iOS. I hope for detailed explanation from experienced developers.
I want to understand basic, abstract and correct architectural approach for networking applications in iOS
There is no "the best", or "the most correct" approach for building an application architecture. It is a very creative job. You should always choose the most straightforward and extensible architecture, which will be clear for any developer, who begin to work on your project or for other developers in your team, but I agree, that there can be a "good" and a "bad" architecture.
You said:
collect the most interesting approaches from experienced iOS developers
I don't think that my approach is the most interesting or correct, but I've used it in several projects and satisfied with it. It is a hybrid approach of the ones you have mentioned above, and also with improvements from my own research efforts. I'm interesting in the problems of building approaches, which combine several well-known patterns and idioms. I think a lot of Fowler's enterprise patterns can be successfully applied to the mobile applications. Here is a list of the most interesting ones, which we can apply for creating an iOS application architecture (in my opinion): Service Layer, Unit Of Work, Remote Facade, Data Transfer Object, Gateway, Layer Supertype, Special Case, Domain Model. You should always correctly design a model layer and always don't forget about the persistence (it can significantly increase your app's performance). You can use Core Data for this. But you should not forget, that Core Data is not an ORM or a database, but an object graph manager with persistence as a good option of it. So, very often Core Data can be too heavy for your needs and you can look at new solutions such as Realm and Couchbase Lite, or build your own lightweight object mapping/persistence layer, based on raw SQLite or LevelDB. Also I advice you to familiarize yourself with the Domain Driven Design and CQRS.
At first, I think, we should create another layer for networking, because we don't want fat controllers or heavy, overwhelmed models. I don't believe in those fat model, skinny controller things. But I do believe in skinny everything approach, because no class should be fat, ever. All networking can be generally abstracted as business logic, consequently we should have another layer, where we can put it. Service Layer is what we need:
It encapsulates the application's business logic, controlling transactions and coordinating responses in the implementation of its operations.
In our MVC realm Service Layer is something like a mediator between domain model and controllers. There is a rather similar variation of this approach called MVCS where a Store is actually our Service layer. Store vends model instances and handles the networking, caching etc. I want to mention that you should not write all your networking and business logic in your service layer. This also can be considered as a bad design. For more info look at the Anemic and Rich domain models. Some service methods and business logic can be handled in the model, so it will be a "rich" (with behaviour) model.
I always extensively use two libraries: AFNetworking 2.0 and ReactiveCocoa. I think it is a must have for any modern application that interacts with the network and web-services or contains complex UI logic.
ARCHITECTURE
At first I create a general APIClient class, which is a subclass of AFHTTPSessionManager. This is a workhorse of all networking in the application: all service classes delegate actual REST requests to it. It contains all the customizations of HTTP client, which I need in the particular application: SSL pinning, error processing and creating straightforward NSError objects with detailed failure reasons and descriptions of all API and connection errors (in such case controller will be able to show correct messages for the user), setting request and response serializers, http headers and other network-related stuff. Then I logically divide all the API requests into subservices or, more correctly, microservices: UserSerivces, CommonServices, SecurityServices, FriendsServices and so on, accordingly to business logic they implement. Each of these microservices is a separate class. They, together, form a Service Layer. These classes contain methods for each API request, process domain models and always returns a RACSignal with the parsed response model or NSError to the caller.
I want to mention that if you have complex model serialisation logic - then create another layer for it: something like Data Mapper but more general e.g. JSON/XML -> Model mapper. If you have cache: then create it as a separate layer/service too (you shouldn't mix business logic with caching). Why? Because correct caching layer can be quite complex with its own gotchas. People implement complex logic to get valid, predictable caching like e.g. monoidal caching with projections based on profunctors. You can read about this beautiful library called Carlos to understand more. And don't forget that Core Data can really help you with all caching issues and will allow you to write less logic. Also, if you have some logic between NSManagedObjectContext and server requests models, you can use Repository pattern, which separates the logic that retrieves the data and maps it to the entity model from the business logic that acts on the model. So, I advice to use Repository pattern even when you have a Core Data based architecture. Repository can abstract things, like NSFetchRequest,NSEntityDescription, NSPredicate and so on to plain methods like get or put.
After all these actions in the Service layer, caller (view controller) can do some complex asynchronous stuff with the response: signal manipulations, chaining, mapping, etc. with the help of ReactiveCocoa primitives , or just subscribe to it and show results in the view. I inject with the Dependency Injection in all these service classes my APIClient, which will translate a particular service call into corresponding GET, POST, PUT, DELETE, etc. request to the REST endpoint. In this case APIClient is passed implicitly to all controllers, you can make this explicit with a parametrised over APIClient service classes. This can make sense if you want to use different customisations of the APIClient for particular service classes, but if you ,for some reasons, don't want extra copies or you are sure that you always will use one particular instance (without customisations) of the APIClient - make it a singleton, but DON'T, please DON'T make service classes as singletons.
Then each view controller again with the DI injects the service class it needs, calls appropriate service methods and composes their results with the UI logic. For dependency injection I like to use BloodMagic or a more powerful framework Typhoon. I never use singletons, God APIManagerWhatever class or other wrong stuff. Because if you call your class WhateverManager, this indicates than you don't know its purpose and it is a bad design choice. Singletons is also an anti-pattern, and in most cases (except rare ones) is a wrong solution. Singleton should be considered only if all three of the following criteria are satisfied:
Ownership of the single instance cannot be reasonably assigned;
Lazy initialization is desirable;
Global access is not otherwise provided for.
In our case ownership of the single instance is not an issue and also we don't need global access after we divided our god manager into services, because now only one or several dedicated controllers need a particular service (e.g. UserProfile controller needs UserServices and so on).
We should always respect S principle in SOLID and use separation of concerns, so don't put all your service methods and networks calls in one class, because it's crazy, especially if you develop a large enterprise application. That's why we should consider dependency injection and services approach. I consider this approach as modern and post-OO. In this case we split our application into two parts: control logic (controllers and events) and parameters.
One kind of parameters would be ordinary “data” parameters. That’s what we pass around functions, manipulate, modify, persist, etc. These are entities, aggregates, collections, case classes. The other kind would be “service” parameters. These are classes which encapsulate business logic, allow communicating with external systems, provide data access.
Here is a general workflow of my architecture by example. Let's suppose we have a FriendsViewController, which displays list of user's friends and we have an option to remove from friends. I create a method in my FriendsServices class called:
- (RACSignal *)removeFriend:(Friend * const)friend
where Friend is a model/domain object (or it can be just a User object if they have similar attributes). Underhood this method parses Friend to NSDictionary of JSON parameters friend_id, name, surname, friend_request_id and so on. I always use Mantle library for this kind of boilerplate and for my model layer (parsing back and forward, managing nested object hierarchies in JSON and so on). After parsing it calls APIClient DELETE method to make an actual REST request and returns Response in RACSignal to the caller (FriendsViewController in our case) to display appropriate message for the user or whatever.
If our application is a very big one, we have to separate our logic even clearer. E.g. it is not *always* good to mix `Repository` or model logic with `Service` one. When I described my approach I had said that `removeFriend` method should be in the `Service` layer, but if we will be more pedantic we can notice that it better belongs to `Repository`. Let's remember what Repository is. Eric Evans gave it a precise description in his book [DDD]:
A Repository represents all objects of a certain type as a conceptual set. It acts like a collection, except with more elaborate querying capability.
So, a Repository is essentially a facade that uses Collection style semantics (Add, Update, Remove) to supply access to data/objects. That's why when you have something like: getFriendsList, getUserGroups, removeFriend you can place it in the Repository, because collection-like semantics is pretty clear here. And code like:
- (RACSignal *)approveFriendRequest:(FriendRequest * const)request;
is definitely a business logic, because it is beyond basic CRUD operations and connect two domain objects (Friend and Request), that's why it should be placed in the Service layer. Also I want to notice: don't create unnecessary abstractions. Use all these approaches wisely. Because if you will overwhelm your application with abstractions, this will increase its accidental complexity, and complexity causes more problems in software systems than anything else
I describe you an "old" Objective-C example but this approach can be very easy adapted for Swift language with a lot more improvements, because it has more useful features and functional sugar. I highly recommend to use this library: Moya. It allows you to create a more elegant APIClient layer (our workhorse as you remember). Now our APIClient provider will be a value type (enum) with extensions conforming to protocols and leveraging destructuring pattern matching. Swift enums + pattern matching allows us to create algebraic data types as in classic functional programming. Our microservices will use this improved APIClient provider as in usual Objective-C approach. For model layer instead of Mantle you can use ObjectMapper library or I like to use more elegant and functional Argo library.
So, I described my general architectural approach, which can be adapted for any application, I think. There can be a lot more improvements, of course. I advice you to learn functional programming, because you can benefit from it a lot, but don't go too far with it too. Eliminating excessive, shared, global mutable state, creating an immutable domain model or creating pure functions without external side-effects is, generally, a good practice, and new Swift language encourages this. But always remember, that overloading your code with heavy pure functional patterns, category-theoretical approaches is a bad idea, because other developers will read and support your code, and they can be frustrated or scary of the prismatic profunctors and such kind of stuff in your immutable model. The same thing with the ReactiveCocoa: don't RACify your code too much, because it can become unreadable really fast, especially for newbies. Use it when it can really simplify your goals and logic.
So, read a lot, mix, experiment, and try to pick up the best from different architectural approaches. It is the best advice I can give you.
According to the goal of this question, I'd like to describe our architecture approach.
Architecture approach
Our general iOS application’s architecture stands on following patterns: Service layers, MVVM, UI Data Binding, Dependency Injection; and Functional Reactive Programming paradigm.
We can slice a typical consumer facing application into following logical layers:
Assembly
Model
Services
Storage
Managers
Coordinators
UI
Infrastructure
Assembly layer is a bootstrap point of our application. It contains a Dependency Injection container and declarations of application’s objects and their dependencies. This layer also might contain application’s configuration (urls, 3rd party services keys and so on). For this purpose we use Typhoon library.
Model layer contains domain models classes, validations, mappings. We use Mantle library for mapping our models: it supports serialization/deserialization into JSON format and NSManagedObject models. For validation and form representation of our models we use FXForms and FXModelValidation libraries.
Services layer declares services which we use for interacting with external systems in order to send or receive data which is represented in our domain model. So usually we have services for communication with server APIs (per entity), messaging services (like PubNub), storage services (like Amazon S3), etc. Basically services wrap objects provided by SDKs (for example PubNub SDK) or implement their own communication logic. For general networking we use AFNetworking library.
Storage layer’s purpose is to organize local data storage on the device. We use Core Data or Realm for this (both have pros and cons, decision of what to use is based on concrete specs). For Core Data setup we use MDMCoreData library and bunch of classes - storages - (similar to services) which provide access to local storage for every entity. For Realm we just use similar storages to have access to local storage.
Managers layer is a place where our abstractions/wrappers live.
In a manager role could be:
Credentials Manager with its different implementations (keychain, NSDefaults, ...)
Current Session Manager which knows how to keep and provide current user session
Capture Pipeline which provides access to media devices (video recording, audio, taking pictures)
BLE Manager which provides access to bluetooth services and peripherals
Geo Location Manager
...
So, in role of manager could be any object which implements logic of a particular aspect or concern needed for application working.
We try to avoid Singletons, but this layer is a place where they live if they are needed.
Coordinators layer provides objects which depends on objects from other layers (Service, Storage, Model) in order to combine their logic into one sequence of work needed for certain module (feature, screen, user story or user experience). It usually chains asynchronous operations and knows how to react on their success and failure cases. As an example you can imagine a messaging feature and corresponding MessagingCoordinator object. Handling sending message operation might look like this:
Validate message (model layer)
Save message locally (messages storage)
Upload message attachment (amazon s3 service)
Update message status and attachments urls and save message locally (messages storage)
Serialize message to JSON format (model layer)
Publish message to PubNub (PubNub service)
Update message status and attributes and save it locally (messages storage)
On each of above steps an error is handled correspondingly.
UI layer consists of following sublayers:
ViewModels
ViewControllers
Views
In order to avoid Massive View Controllers we use MVVM pattern and implement logic needed for UI presentation in ViewModels. A ViewModel usually has coordinators and managers as dependencies. ViewModels used by ViewControllers and some kinds of Views (e.g. table view cells). The glue between ViewControllers and ViewModels is Data Binding and Command pattern. In order to make it possible to have that glue we use ReactiveCocoa library.
We also use ReactiveCocoa and its RACSignal concept as an interface and returning value type of all coordinators, services, storages methods. This allows us to chain operations, run them parallelly or serially, and many other useful things provided by ReactiveCocoa.
We try to implement our UI behavior in declarative way. Data Binding and Auto Layout helps a lot to achieve this goal.
Infrastructure layer contains all the helpers, extensions, utilities needed for application work.
This approach works well for us and those types of apps we usually build. But you should understand, that this is just a subjective approach that should be adapted/changed for concrete team's purpose.
Hope this will help you!
Also you can find more information about iOS development process in this blog post iOS Development as a Service
Because all iOS apps are different, I think there are different approaches here to consider, but I usually go this way:
Create a central manager (singleton) class to handle all API requests (usually named APICommunicator) and every instance method is an API call. And there is one central (non-public) method:
-(RACSignal *)sendGetToServerToSubPath:(NSString *)path withParameters:(NSDictionary *)params;
For the record, I use 2 major libraries/frameworks, ReactiveCocoa and AFNetworking. ReactiveCocoa handles async networking responses perfectly, you can do (sendNext:, sendError:, etc.).
This method calls the API, gets the results and sends them through RAC in 'raw' format (like NSArray what AFNetworking returns).
Then a method like getStuffList: which called the above method subscribes to it's signal, parses the raw data into objects (with something like Motis) and sends the objects one by one to the caller (getStuffList: and similar methods also return a signal that the controller can subscribe to).
The subscribed controller receives the objects by subscribeNext:'s block and handles them.
I tried many ways in different apps but this one worked the best out of all so I've been using this in a few apps recently, it fits both small and big projects and it's easy to extend and maintain if something needs to be modified.
Hope this helps, I'd like to hear others' opinions about my approach and maybe how others think this could be maybe improved.
In my situation I'm usually using ResKit library to set up the network layer. It provides easy-to-use parsing. It reduces my effort on setting up the mapping for different responses and stuff.
I only add some code to setup the mapping automatically.
I define base class for my models (not protocol because of lot of code to check if some method is implemented or not, and less code in models itself):
MappableEntry.h
#interface MappableEntity : NSObject
+ (NSArray*)pathPatterns;
+ (NSArray*)keyPathes;
+ (NSArray*)fieldsArrayForMapping;
+ (NSDictionary*)fieldsDictionaryForMapping;
+ (NSArray*)relationships;
#end
MappableEntry.m
#implementation MappableEntity
+(NSArray*)pathPatterns {
return #[];
}
+(NSArray*)keyPathes {
return nil;
}
+(NSArray*)fieldsArrayForMapping {
return #[];
}
+(NSDictionary*)fieldsDictionaryForMapping {
return #{};
}
+(NSArray*)relationships {
return #[];
}
#end
Relationships are objects which represent nested objects in response:
RelationshipObject.h
#interface RelationshipObject : NSObject
#property (nonatomic,copy) NSString* source;
#property (nonatomic,copy) NSString* destination;
#property (nonatomic) Class mappingClass;
+(RelationshipObject*)relationshipWithKey:(NSString*)key andMappingClass:(Class)mappingClass;
+(RelationshipObject*)relationshipWithSource:(NSString*)source destination:(NSString*)destination andMappingClass:(Class)mappingClass;
#end
RelationshipObject.m
#implementation RelationshipObject
+(RelationshipObject*)relationshipWithKey:(NSString*)key andMappingClass:(Class)mappingClass {
RelationshipObject* object = [[RelationshipObject alloc] init];
object.source = key;
object.destination = key;
object.mappingClass = mappingClass;
return object;
}
+(RelationshipObject*)relationshipWithSource:(NSString*)source destination:(NSString*)destination andMappingClass:(Class)mappingClass {
RelationshipObject* object = [[RelationshipObject alloc] init];
object.source = source;
object.destination = destination;
object.mappingClass = mappingClass;
return object;
}
#end
Then I'm setting up the mapping for RestKit like this:
ObjectMappingInitializer.h
#interface ObjectMappingInitializer : NSObject
+(void)initializeRKObjectManagerMapping:(RKObjectManager*)objectManager;
#end
ObjectMappingInitializer.m
#interface ObjectMappingInitializer (Private)
+ (NSArray*)mappableClasses;
#end
#implementation ObjectMappingInitializer
+(void)initializeRKObjectManagerMapping:(RKObjectManager*)objectManager {
NSMutableDictionary *mappingObjects = [NSMutableDictionary dictionary];
// Creating mappings for classes
for (Class mappableClass in [self mappableClasses]) {
RKObjectMapping *newMapping = [RKObjectMapping mappingForClass:mappableClass];
[newMapping addAttributeMappingsFromArray:[mappableClass fieldsArrayForMapping]];
[newMapping addAttributeMappingsFromDictionary:[mappableClass fieldsDictionaryForMapping]];
[mappingObjects setObject:newMapping forKey:[mappableClass description]];
}
// Creating relations for mappings
for (Class mappableClass in [self mappableClasses]) {
RKObjectMapping *mapping = [mappingObjects objectForKey:[mappableClass description]];
for (RelationshipObject *relation in [mappableClass relationships]) {
[mapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:relation.source toKeyPath:relation.destination withMapping:[mappingObjects objectForKey:[relation.mappingClass description]]]];
}
}
// Creating response descriptors with mappings
for (Class mappableClass in [self mappableClasses]) {
for (NSString* pathPattern in [mappableClass pathPatterns]) {
if ([mappableClass keyPathes]) {
for (NSString* keyPath in [mappableClass keyPathes]) {
[objectManager addResponseDescriptor:[RKResponseDescriptor responseDescriptorWithMapping:[mappingObjects objectForKey:[mappableClass description]] method:RKRequestMethodAny pathPattern:pathPattern keyPath:keyPath statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]];
}
} else {
[objectManager addResponseDescriptor:[RKResponseDescriptor responseDescriptorWithMapping:[mappingObjects objectForKey:[mappableClass description]] method:RKRequestMethodAny pathPattern:pathPattern keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]];
}
}
}
// Error Mapping
RKObjectMapping *errorMapping = [RKObjectMapping mappingForClass:[Error class]];
[errorMapping addAttributeMappingsFromArray:[Error fieldsArrayForMapping]];
for (NSString *pathPattern in Error.pathPatterns) {
[[RKObjectManager sharedManager] addResponseDescriptor:[RKResponseDescriptor responseDescriptorWithMapping:errorMapping method:RKRequestMethodAny pathPattern:pathPattern keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassClientError)]];
}
}
#end
#implementation ObjectMappingInitializer (Private)
+ (NSArray*)mappableClasses {
return #[
[FruiosPaginationResults class],
[FruioItem class],
[Pagination class],
[ContactInfo class],
[Credentials class],
[User class]
];
}
#end
Some example of MappableEntry implementation:
User.h
#interface User : MappableEntity
#property (nonatomic) long userId;
#property (nonatomic, copy) NSString *username;
#property (nonatomic, copy) NSString *email;
#property (nonatomic, copy) NSString *password;
#property (nonatomic, copy) NSString *token;
- (instancetype)initWithUsername:(NSString*)username email:(NSString*)email password:(NSString*)password;
- (NSDictionary*)registrationData;
#end
User.m
#implementation User
- (instancetype)initWithUsername:(NSString*)username email:(NSString*)email password:(NSString*)password {
if (self = [super init]) {
self.username = username;
self.email = email;
self.password = password;
}
return self;
}
- (NSDictionary*)registrationData {
return #{
#"username": self.username,
#"email": self.email,
#"password": self.password
};
}
+ (NSArray*)pathPatterns {
return #[
[NSString stringWithFormat:#"/api/%#/users/register", APIVersionString],
[NSString stringWithFormat:#"/api/%#/users/login", APIVersionString]
];
}
+ (NSArray*)fieldsArrayForMapping {
return #[ #"username", #"email", #"password", #"token" ];
}
+ (NSDictionary*)fieldsDictionaryForMapping {
return #{ #"id": #"userId" };
}
#end
Now about the Requests wrapping:
I have header file with blocks definition, to reduce line length in all APIRequest classes:
APICallbacks.h
typedef void(^SuccessCallback)();
typedef void(^SuccessCallbackWithObjects)(NSArray *objects);
typedef void(^ErrorCallback)(NSError *error);
typedef void(^ProgressBlock)(float progress);
And Example of my APIRequest class that I'm using:
LoginAPI.h
#interface LoginAPI : NSObject
- (void)loginWithCredentials:(Credentials*)credentials onSuccess:(SuccessCallbackWithObjects)onSuccess onError:(ErrorCallback)onError;
#end
LoginAPI.m
#implementation LoginAPI
- (void)loginWithCredentials:(Credentials*)credentials onSuccess:(SuccessCallbackWithObjects)onSuccess onError:(ErrorCallback)onError {
[[RKObjectManager sharedManager] postObject:nil path:[NSString stringWithFormat:#"/api/%#/users/login", APIVersionString] parameters:[credentials credentialsData] success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
onSuccess(mappingResult.array);
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
onError(error);
}];
}
#end
And all you need to do in code, simply initialize API object and call it whenever you need it:
SomeViewController.m
#implementation SomeViewController {
LoginAPI *_loginAPI;
// ...
}
- (void)viewDidLoad {
[super viewDidLoad];
_loginAPI = [[LoginAPI alloc] init];
// ...
}
// ...
- (IBAction)signIn:(id)sender {
[_loginAPI loginWithCredentials:_credentials onSuccess:^(NSArray *objects) {
// Success Block
} onError:^(NSError *error) {
// Error Block
}];
}
// ...
#end
My code isn't perfect, but it's easy to set once and use for different projects. If it's interesting to anyone, mb I could spend some time and make a universal solution for it somewhere on GitHub and CocoaPods.
To my mind all software architecture is driven by need. If this is for learning or personal purposes, then decide the primary goal and have that drive the architecture. If this is a work for hire, then the business need is paramount. The trick is to not let shiny things distract you from the real needs. I find this hard to do. There are always new shiny things appearing in this business and lots of them are not useful, but you can't always tell that up front. Focus on the need and be willing to abandon bad choices if you can.
For example, I recently did a quick prototype of a photo sharing app for a local business. Since the business need was to do something quick and dirty, the architecture ended up being some iOS code to pop up a camera and some network code attached to a Send Button that uploaded the image to a S3 store and wrote to a SimpleDB domain. The code was trivial and the cost minimal and the client has an scalable photo collection accessible over the web with REST calls. Cheap and dumb, the app had lots of flaws and would lock the UI on occasion, but it would be a waste to do more for a prototype and it allows them to deploy to their staff and generate thousands of test images easily without performance or scalability concerns. Crappy architecture, but it fit the need and cost perfectly.
Another project involved implementing a local secure database which synchronizes with the company system in the background when the network is available. I created a background synchronizer that used RestKit as it seemed to have everything I needed. But I had to write so much custom code for RestKit to deal with idiosyncratic JSON that I could have done it all quicker by writing my own JSON to CoreData transformations. However, the customer wanted to bring this app in house and I felt that RestKit would be similar to the frameworks that they used on other platforms. I waiting to see if that was a good decision.
Again, the issue to me is to focus on the need and let that determine the architecture. I try like hell to avoid using third party packages as they bring costs that only appears after the app has been in the field for a while. I try to avoid making class hierarchies as they rarely pay off. If I can write something in a reasonable period of time instead of adopting a package that doesn't fit perfectly, then I do it. My code is well structured for debugging and appropriately commented, but third party packages rarely are. With that said, I find AF Networking too useful to ignore and well structured, well commented, and maintained and I use it a lot! RestKit covers a lot of common cases, but I feel like I've been in a fight when I use it, and most of the data sources I encounter are full of quirks and issues that are best handled with custom code. In my last few apps I just use the built in JSON converters and write a few utility methods.
One pattern I always use is to get the network calls off the main thread. The last 4-5 apps I've done set up a background timer task using dispatch_source_create that wakes up every so often and does network tasks as needed. You need to do some thread safety work and make sure that UI modifying code gets sent to the main thread. It also helps to do your onboarding/initialization in such a way that the user doesn't feel burdened or delayed. So far this has been working rather well. I suggest looking into these things.
Finally, I think that as we work more and as the OS evolves, we tend to develop better solutions. It has taken me years to get over my belief that I have to follow patterns and designs that other people claim are mandatory. If I am working in a context where that is part of the local religion, ahem, I mean the departmental best engineering practices, then I follow the customs to the letter, that's what they are paying me for. But I rarely find that following older designs and patterns is the optimal solution. I always try to look at the solution through the prism of the business needs and build the architecture to match it and keep things as simple as they can be. When I feel like there isn't enough there, but everything works correctly, then I'm on the right track.
I use the approach that I've gotten from here: https://github.com/Constantine-Fry/Foursquare-API-v2. I've rewritten that library in Swift and you can see the architectural approach from these parts of the code:
typealias OpertaionCallback = (success: Bool, result: AnyObject?) -> ()
class Foursquare{
var authorizationCallback: OperationCallback?
var operationQueue: NSOperationQueue
var callbackQueue: dispatch_queue_t?
init(){
operationQueue = NSOperationQueue()
operationQueue.maxConcurrentOperationCount = 7;
callbackQueue = dispatch_get_main_queue();
}
func checkIn(venueID: String, shout: String, callback: OperationCallback) -> NSOperation {
let parameters: Dictionary <String, String> = [
"venueId":venueID,
"shout":shout,
"broadcast":"public"]
return self.sendRequest("checkins/add", parameters: parameters, httpMethod: "POST", callback: callback)
}
func sendRequest(path: String, parameters: Dictionary <String, String>, httpMethod: String, callback:OperationCallback) -> NSOperation{
let url = self.constructURL(path, parameters: parameters)
var request = NSMutableURLRequest(URL: url)
request.HTTPMethod = httpMethod
let operation = Operation(request: request, callbackBlock: callback, callbackQueue: self.callbackQueue!)
self.operationQueue.addOperation(operation)
return operation
}
func constructURL(path: String, parameters: Dictionary <String, String>) -> NSURL {
var parametersString = kFSBaseURL+path
var firstItem = true
for key in parameters.keys {
let string = parameters[key]
let mark = (firstItem ? "?" : "&")
parametersString += "\(mark)\(key)=\(string)"
firstItem = false
}
return NSURL(string: parametersString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding))
}
}
class Operation: NSOperation {
var callbackBlock: OpertaionCallback
var request: NSURLRequest
var callbackQueue: dispatch_queue_t
init(request: NSURLRequest, callbackBlock: OpertaionCallback, callbackQueue: dispatch_queue_t) {
self.request = request
self.callbackBlock = callbackBlock
self.callbackQueue = callbackQueue
}
override func main() {
var error: NSError?
var result: AnyObject?
var response: NSURLResponse?
var recievedData: NSData? = NSURLConnection.sendSynchronousRequest(self.request, returningResponse: &response, error: &error)
if self.cancelled {return}
if recievedData{
result = NSJSONSerialization.JSONObjectWithData(recievedData, options: nil, error: &error)
if result != nil {
if result!.isKindOfClass(NSClassFromString("NSError")){
error = result as? NSError
}
}
if self.cancelled {return}
dispatch_async(self.callbackQueue, {
if (error) {
self.callbackBlock(success: false, result: error!);
} else {
self.callbackBlock(success: true, result: result!);
}
})
}
override var concurrent:Bool {get {return true}}
}
Basically, there is NSOperation subclass that makes the NSURLRequest, parses JSON response and adds the callback block with the result to the queue. The main API class constructs NSURLRequest, initialises that NSOperation subclass and adds it to the queue.
We use a few approaches depending on the situation. For most things AFNetworking is the simplest and most robust approach in that you can set headers, upload multipart data, use GET, POST, PUT & DELETE and there are a bunch of additional categories for UIKit which allow you to for example set an image from a url. In a complex app with a lot of calls we sometimes abstract this down to a convenience method of our own which would be something like:
-(void)makeRequestToUrl:(NSURL *)url withParameters:(NSDictionary *)parameters success:(void (^)(id responseObject))success failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure;
There are a few situations where AFNetworking isn't appropriate however such as where you are creating a framework or other library component as AFNetworking may already be in another code base. In this situation you would use an NSMutableURLRequest either inline if you are making a single call or abstracted into a request / response class.
I avoid singletons when designing my applications. They are a typical go to for a lot of people but I think you can find more elegant solutions elsewhere. Typically what I do is a build out my entities in CoreData and then put my REST code in an NSManagedObject category. If for instance I wanted to create and POST a new User, I'd do this:
User* newUser = [User createInManagedObjectContext:managedObjectContext];
[newUser postOnSuccess:^(...) { ... } onFailure:^(...) { ... }];
I use RESTKit for the object mapping and initialize it at start up. I find routing all of your calls through a singleton to be a waste of time and adds a lot of boilerplate that isn't needed.
In NSManagedObject+Extensions.m:
+ (instancetype)createInContext:(NSManagedObjectContext*)context
{
NSAssert(context.persistentStoreCoordinator.managedObjectModel.entitiesByName[[self entityName]] != nil, #"Entity with name %# not found in model. Is your class name the same as your entity name?", [self entityName]);
return [NSEntityDescription insertNewObjectForEntityForName:[self entityName] inManagedObjectContext:context];
}
In NSManagedObject+Networking.m:
- (void)getOnSuccess:(RESTSuccess)onSuccess onFailure:(RESTFailure)onFailure blockInput:(BOOL)blockInput
{
[[RKObjectManager sharedManager] getObject:self path:nil parameters:nil success:onSuccess failure:onFailure];
[self handleInputBlocking:blockInput];
}
Why add extra helper classes when you can extend the functionality of a common base class through categories?
If you're interested in more detailed info on my solution let me know. I'm happy to share.
In mobile software engineering, the most widely used are Clean Architecture + MVVM and Redux patterns.
Clean Architecture + MVVM consist of 3 layers:
Domain, Presentation, Data layers.
Where the Presentation Layer and Data Repositories Layer depend on Domain Layer:
Presentation Layer -> Domain Layer <- Data Repositories Layer
And Presentation Layer consist of ViewModels and Views (MVVM):
Presentation Layer (MVVM) = ViewModels + Views
Domain Layer = Entities + Use Cases + Repositories Interfaces
Data Repositories Layer = Repositories Implementations + API (Network) + Persistence DB
In this article, there is a more detailed description of Clean Architecture + MVVM
https://tech.olx.com/clean-architecture-and-mvvm-on-ios-c9d167d9f5b3
Try https://github.com/kevin0571/STNetTaskQueue
Create API requests in separated classes.
STNetTaskQueue will deal with threading and delegate/callback.
Extendable for different protocols.
From a purely class design perspective, you will usually have something like this:
Your view controllers controlling one or more views
Data model class - It really depends upon how many real distinct entities you are dealing with, and how they are related.
For example, if you have an array of items to be displayed in four different representations (list, chart, graph etc), you will have one data model class for list of items, one more for an item. The list of item class will be shared by four view controllers - all children of a tab bar controller or a nav controller.
Data model classes will come handy in not only displaying data, but also serializing them wherein each of them can expose their own serialization format through JSON / XML / CSV (or anything else) export methods.
It is important to understand that you also need API request builder classes that map directly with your REST API endpoints. Let's say you have an API that logs the user in - so your Login API builder class will create POST JSON payload for login api. In another example, an API request builder class for list of catalog items API will create GET query string for corresponding api and fire the REST GET query.
These API request builder classes will usually receive data from view controllers and also pass the same data back to view controllers for UI update / other operations. View controllers will then decide how to update Data Model objects with that data.
Finally, the heart of the REST client - API data fetcher class which is oblivious to all sorts of API requests your app makes. This class will more likely be a singleton, but as others pointed out, it doesn't have to be a singleton.
Note that the link is just a typical implementation and does not take into consideration scenarios like session, cookies etc, but it is enough to get you going without using any 3rd party frameworks.
This question has a lot of excellent and extensive answers already, but I feel I have to mention it since no one else has.
Alamofire for Swift. https://github.com/Alamofire/Alamofire
It's created by the same people as AFNetworking, but is more directly designed with Swift in mind.
I think for now medium project use MVVM architecture and Big project use VIPER architecture
and try to achieved
Protocol oriented programming
Software design patterns
S.O.L.D principle
Generic programming
Don't repeat yourself (DRY)
And Architectural approaches for building iOS networking applications (REST clients)
Separation concern for clean and readable code avoid duplication:
import Foundation
enum DataResponseError: Error {
case network
case decoding
var reason: String {
switch self {
case .network:
return "An error occurred while fetching data"
case .decoding:
return "An error occurred while decoding data"
}
}
}
extension HTTPURLResponse {
var hasSuccessStatusCode: Bool {
return 200...299 ~= statusCode
}
}
enum Result<T, U: Error> {
case success(T)
case failure(U)
}
dependancy inversion
protocol NHDataProvider {
func fetchRemote<Model: Codable>(_ val: Model.Type, url: URL, completion: #escaping (Result<Codable, DataResponseError>) -> Void)
}
Main responsible:
final class NHClientHTTPNetworking : NHDataProvider {
let session: URLSession
init(session: URLSession = URLSession.shared) {
self.session = session
}
func fetchRemote<Model: Codable>(_ val: Model.Type, url: URL,
completion: #escaping (Result<Codable, DataResponseError>) -> Void) {
let urlRequest = URLRequest(url: url)
session.dataTask(with: urlRequest, completionHandler: { data, response, error in
guard
let httpResponse = response as? HTTPURLResponse,
httpResponse.hasSuccessStatusCode,
let data = data
else {
completion(Result.failure(DataResponseError.network))
return
}
guard let decodedResponse = try? JSONDecoder().decode(Model.self, from: data) else {
completion(Result.failure(DataResponseError.decoding))
return
}
completion(Result.success(decodedResponse))
}).resume()
}
}
You will find here is the GitHub MVVM architecture with rest API Swift Project

What is the recommended way to intercept and prevent redirects with AFNetworking 2.0?

It seems to me that the proper place to do this is in AFURLSessionManager, in setting the taskWillPerformHTTPRedirection block, but I am unsure of the best way to handle it.
Currently, in my AFHTTPSessionManager subclass, I am setting the redirect block globally for all requests, and I know I can prevent redirects by returning nil here:
- (void)setupRedirectBlock {
[self setTaskWillPerformHTTPRedirectionBlock:^NSURLRequest *(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request) {
return nil;
}];
}
...but I need to only do this on specific tasks, and there doesn't appear to be a way to get this information from the task itself.
I guess I am looking for some sort of user info dictionary or something I can use to set a flag telling this method to either return the request or return nil. Currently, it looks like I would have to do a string comparison on the response/request URL in the client where it is far away from where the task and path is actually created.
So this begs the question, am I fighting convention, or is there really no better way to intercept an AFNetworking 2.0 redirect on a task-by-task basis?
setTaskWillPerformHTTPRedirectionBlock is the best way to intercept redirects. The session manager is responsible for determining when or when not to prevent redirects based on the request associated with the task. In most cases, the path of the request should be a sufficient determination, but the user could additionally tag information in a custom request header field.
I have the same question, but unfortunately I don't yet have a good enough answer. One workaround could be using taskDescription https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSURLSessionTask_class/Reference/Reference.html#//apple_ref/occ/instp/NSURLSessionTask/taskDescription
Just put there some constant like DO_NOT_FOLLOW_REDIRECT and check for it in your setTaskWillPerformHTTPRedirectionBlock block.

Iphone connecting to a web server, design issues/questions

Currently I am working on building an iOS app that asynchronously connects to a web server. I make about 5 different calls to the web service, depending on which view controller I am in. On a lot of the views, I end up making the same calls, so I have a lot of identical/similar code. Do you guys have any suggestions on how I can limit the amount of code reuse I am making. I was thinking about creating a special class that has the method/code for all these asynchronous calls, then on each View controller, I can just instantiate the special class and invoke whatever methods I want. The problem with this is that since I am making asynchronous calls, how will I know which view controller to send the data back to (via a callback). I know there should be a simple, elegant way to achieve this but I am just new to this way of thinking so help would be greatly appreciated.
Usually what I do is a class that is responsible for the requests (or part of them depending on the amount of requests you have and how they are logically related).
Imagine that you have a NetworkManager. In this manager you can have a singleton if you need to persist state or just use class methods. All methods should have at least a completion block that will be called when the async method that you use for your server has the response.
The block can be defined with a typedef:
typedef void (^NetworkCompletionBlock)(NSError *error, NSArray* data);
Then you call methods like this:
+(void) requestInfoFromServerOnCompletion:(NetworkCompletionBlock)onComplete;
Then, on your method, you call your async functions and when they are complete you call the onComplete block with the parameters he requests, like this:
if(onComplete)
onComplete(yourError, yourDataArray);
To call this class method just do:
[NetworkManagerrequestInfoFromServerOnCompletion:^(NSError *error, NSArray* data)){
if(error)
NSLog(#"Ops, an error ocurred");
else
NSLog(#"Received data: %#", data);
}];
You can also have two blocks, one for error and one for completion.
Depending on how you are doing your requests you might need to save the block in the NetworkManager. Imagine that you use a NSURLConnection with delegates. You had to save your block in a networkManager singleton and then call it when the request is complete.
To do this you need to declare a property on your singleton:
#property (nonatomic, copy) NetworkCompletionBlock onRequestCompletedBlock;
And then on your method you do:
self.onRequestCompletedBlock = onComplete;
When the request is completed you can call with:
self.onRequestCompletedBlock(requestError, requestDataArray);
Note that if you do this you need to be careful with retain cycles.

How keep things DRY with afnetworking

I'm writing a ios app which has to send and receive data from the API at various screens in the app. Currently each view controller is calling this code
// AFAppDotNetAPIClient is a subclass of AFHTTPClient, which defines the base URL and default HTTP headers for NSURLRequests it creates
[[AFAppDotNetAPIClient sharedClient] getPath:#"stream/0/posts/stream/global" parameters:nil success:^(AFHTTPRequestOperation *operation, id JSON) {
NSLog(#"App.net Global Stream: %#", JSON);
} failure:nil];
I want to keep things DRY and so I created a requested builder and response handler to create request and parse responses. I also want to move all the API calls to one class but since it uses blocks I don't know how do this.
Can someone explain how this is done so I call one method with a enum and some params for request and I probably just get a NSDictionary back without having API calls and blocks in all view controllers. Thanks
This is a concern for the Model part of your MVC architecture. The example project has a good implementation of this:
Post.h
+ (void)globalTimelinePostsWithBlock:(void (^)(NSArray *posts, NSError *error))block;
Define class methods on the model that take care of making requests (translating any method parameters into request params) and serializing objects from the response.
Define your new interface to include your enum, parameters and a block. Define the block to have a BOOL (status) and an NSDictionary (result). This is basically just simplifying and abstracting the interface away from the client, path and operation. Then in the success and failure blocks you can call the outer block with appropriate parameters.
The block is required to maintain the asynchronous nature of the interface. You could use a target and selector but that will be more code and less flexible. You can't have the method just return the resulting dictionary.
There are obviously many other options for what parameters to pass to the method and in the block.

How to read the response headers every time using AFNetworking?

While using a 3rd party API, I have the requirement to cancel all traffic when a custom response header is set to a certain value. I am trying to find a nice place to do this check only once in my code (and not in every success/failure block, where it works fine). From what I understand, this could be done by overriding -(void)enqueueHTTPRequestOperation:(AFHTTPRequestOperation *)operation in my custom AFHTTPClient subclass, but when I implement it like that:
-(void)enqueueHTTPRequestOperation:(AFHTTPRequestOperation *)operation
{
NSLog(#"[REQUEST URL]\n%#\n", [operation.request.URL description]);
NSLog(#"[RESPONSE HEADERS]\n%#\n", [[operation.response allHeaderFields] descriptionInStringsFileFormat]);
[super enqueueHTTPRequestOperation:operation];
}
the response headers are nil. Can anybody help me with that?
At the moment when operations are being created and enqueued in AFHTTPClient, they will not have the response from the server--that will be assigned when the request operation is actually executed.
Although the requirement to cancel all traffic seems unorthodox (at least if outside of the conventions of HTTP), this is easy to accomplish:
In your AFHTTPClient subclass, add a BOOL property that stores if requests should be prevented, and then used in enqueueHTTPRequestOperation. Then, override HTTPRequestOperationWithRequest:success:failure: to execute the specified success block along with some logic to set the aforementioned property if the salient response is present.

Resources