/Coding-iPad

Coding iPad 客户端源代码

Primary LanguageObjective-CMIT LicenseMIT

#Coding-iPad客户端说明

Just run it!

想要看看 iPad 版本什么样,没问题! clone 或者下载代码后,初次执行时,双击根目录下的 bootstrap 脚本,该脚本会准备初始数据,完成后会打开工程,点击 Xcode 运行!So easy,妈妈再也不用担心我的代码编译出错了!(之后只需打开 CodingForiPad.xcworkspace 即可)

嗯……,你的代码好像很棒,请告诉我xx是怎么做的

先告诉大家代码大概在哪里。

.
├── CodingForiPad
│   ├── Vendor:因为各种原因没有用Pods管理的第三方库
│   ├── Resources:资源文件
│   ├── Util:一些工具类,Category等
│   ├── Request:网络请求
│   ├── Models:数据模型,一般一个网络请求会对应一个model
│   ├── RequestExt:请求的业务扩展,用于分离基本请求以便于复用代码
│   ├── ModelsExt:数据模型的业务扩展,用于分离基本模型以便于代码复用
│   ├── Manager:一些单例
│   │   ├── AddressManager:iPhone版本代码
│   │   ├── Coding_FileManager:文件上传(iPhone版本代码)
│   │   ├── COSession:登录用户管理
│   │   ├── COUnReadCountManager:读信息、私信管理
│   │   ├── ImageSizeManager:iPhone版本代码
│   │   ├── JobManager:iPhone版本代码
│   │   ├── StartImagesManager:iPhone版本代码
│   │   ├── TagsManager:iPhone版本代码
│   │   └── WebContentManager:格式化为网页使用,iPhone版本代码 
│   └── ViewController
│       ├── Style:基本样式,颜色等
│       ├── Custom:一些自定义的View
│       ├── Base:基本Controller
│       ├── User:用户资料相关的UI
│       ├── Project:项目相关的UI
│       ├── Task:任务相关的UI
│       ├── Tweet:冒泡相关的UI
│       ├── Message:消息和私信相关的UI
│       └── Setting:设置相关的UI
└── Pods:项目使用了[CocoaPods](http://code4app.com/article/cocoapods-install-usage)这个类库管理工具

有两个比较隐蔽的问题,需要说明一下。首先,Coding 上部分资源是WebP格式,使用 SDWebImage 时,需要加入 WebP.framework;另外,在 Coding 的一些图片需要验证 Cookie 的,所以,在使用 SDWebImage 时,注意启用 Cookie,增加 SDWebImageHandleCookies 选项,代码如下所示:

[self.regCodeImageView sd_setImageWithURL:[NSURL URLWithString:url] placeholderImage:nil options:SDWebImageRetryFailed | SDWebImageHandleCookies | SDWebImageRefreshCached completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
    if (error) {
        dispatch_async(dispatch_get_main_queue(), ^{
            weakself.regCodeImageView.image = [UIImage imageNamed:@"captcha_loadfail"];
        });
    }
}];

iPad 客户端使用了 Storyboard,所以在代码阅读上,建议先从 Storyboard 开始,了解整个项目的脉络(由于项目比较大,打开 Storyboard 项目的速度取决于机器的配置,Orz)。另外从 UI 来入手也比较直观,在 Storyboard 中也可以直接看到 UI 对应的 Controllor。

关于 ViewControllerr目录的组织基本是按照 UI 上展现来组织的,比如 Tweet 目录,主要是存放冒泡相关实现的代码,而其所对应的网络请求和 Models 也都是 COTweet 开头,这样大家很容易提纲挈领从一点贯通全面。

RequestExt 和 ModelsExt 目录下是与业务比较相关的请求和模型,这样做是为了使数据请求和模型与业务逻辑无关,保证这部分代码能够复用。

很好,我也要写一个这么棒的Coding客户端

没问题,现在来告诉大家,怎么复用代码。

不过,略微有点遗憾,由于项目比较紧张,当前只有网络请求和 Model 可以轻松复用。

首先,再讲一个隐蔽的问题,关于用户登录!Coding 的用户验证是通过 Cookie 来验证的,所以问题来了,如果你直接调用登录接口,会发现登录成功了,但是访问其他接口会提示用户未登录,为什么呢,因为 Cookie 没有存储,为什么没有存储,因为你在登录之前没有请求验证码接口。所以,再你调用登录接口之前,必须先请求验证码接口!!!必须先请求验证码接口!!!必须先请求验证码接口!!!(重要的事情说三遍)

网络请求

网络请求时基于 AFNetworking 的,稍微封装了一下,所有接口请求都是继承于 CODataRequest。 Coding 是 RestFul 接口,HTTP 请求除了GET、POST,之外还有 PUT 和 DELETE 方法,所以在 CODataRequest 实现了上述四种基本请求方法。一个接口可能既支持 GET,也支持 DELETE,所需要的参数不尽相同。因此,我定义了几个指示说明性的宏来标识请求和参数,如下所示:

#define COQueryParameters		// Get 参数
#define COUriParameters        // URI 参数
#define COFormParameters       // Post 参数

#define COGetRequest    /** Get请求 */
#define COPostRequest   /** Post请求 */
#define COPutRequest    /** Pust请求 */
#define CODeleteRequest /** Delete请求 */

以用户登录接口为例:

COPostRequest
@interface COAccountLoginRequest : CODataRequest

@property (nonatomic, copy) COFormParameters NSString *email;
@property (nonatomic, copy) COFormParameters NSString *password;
@property (nonatomic, copy) COFormParameters NSString *jCaptcha;
@property (nonatomic, copy) COFormParameters NSString *rememberMe;

@end

COPostRequest 表示这个接口是 POST 接口,调用的时候需要执行 postWithSuccess 方法;

COFormParameters 表示参数是 Post 使用的参数;

####请求实现 CODataRequest 将一个请求分为了四个阶段,请求准备,参数填充,完成准备,发起请求,数据解析。如果需要实现一个新请求,正常来说,需要完成请求准备和参数填充两个阶段,一般来说在准备阶段我们主要设置请求的 path,代码如下所示:

- (void)prepareForRequest
{
    self.path = @"/login";
}

参数准备主要是建立参数映射字典,将请求的参数映射为对应的接口参数,代码如下:

- (NSDictionary *)parametersMap
{
    return @{
             @"email" : @"email",
             @"password" : @"password",
             @"rememberMe" : @"remember_me",
             @"jCaptcha" : @"j_captcha",
             };
}

其中 key 是请求的属性,对应的 value 是接口的参数名。

数据解析阶段是将接口返回的 JSON 封装为 CODataResponse 对象,并将数据映射成对应的 Model,代码如下:

- (CODataResponse *)postResponseParser:(id)response
{
    return [[CODataResponse alloc] initWithResponse:response dataModleClass:[COUser class] responseType:CODataResponseTypeDefault];
}

还有一个完成准备的阶段,这是表示,参数准备就绪,可以开始了,这时,我们可能还需要做一些事情,比如我封装了一个 COPageRequest,所有分页请求的数据都继承于它,让我们看看,它是怎么做的:

- (void)readyForRequest
{
    NSMutableDictionary *params = [NSMutableDictionary dictionaryWithDictionary:self.params];
    [params setObject:@(self.page) forKey:@"page"];
    [params setObject:@(self.pageSize) forKey:@"pageSize"];
    self.params = [NSDictionary dictionaryWithDictionary:params];
}

COPageRequest 使用了 readyForRequest,将 page 和 pageSize 两个参数追加到参数映射中,这样其他的分页请求就不需要在参数映射中写这两个参数了。

数据模型

数据模型采用了Mantle将 JSON 映射为对象。来看一个简单的列子:

@interface COFileCount : MTLModel<MTLJSONSerializing>

@property (nonatomic, assign) NSInteger folderId;
@property (nonatomic, assign) NSInteger count;

@end

@implementation COFileCount

+ (NSDictionary *)JSONKeyPathsByPropertyKey
{
    return @{@"folderId" : @"folder",
             @"count" : @"count",
             };
}

@end

是不是有些熟悉?没错,网络请求也参考了 Mantle 的实现。同样的 JSONKeyPathsByPropertyKey 中,key 对应的是 Model 的属性,value 对应的是 JSON 中的数据名。

复杂模型的建立,请详细参考 Mantle 的文档。

请求与模型的桥梁

CODataResponse 是请求于模型之间的桥梁。CODataResponse 封装了整个网络请求返回数据,包含了code、msg、error以及数据等。所以,一个网络请求返回后,我们需要先检查 CODataResponse 中的 code,如果 code 为0,则表示请求 OK,然后还需要判断 error,这个 error主要是我们解析数据时产生的错误,多数是模型建立的不对,或者接口模型调整导致的。

因为 Coding 的接口返回比较规范,主要是三种形式,字典,列表和 Pageable,因此 CODataResponse 封装了这三种形式的数据解析,解析数据变的非常简单,代码如下所示:

- (CODataResponse *)postResponseParser:(id)response
{
    return [[CODataResponse alloc] initWithResponse:response dataModleClass:[COUser class] responseType:CODataResponseTypeDefault];
}

这是用户登录接口的数据解析,是不是很容易?

代码使用

拷贝 Request 和 Model 目录到你的项目中,在 Podfile 中添加如下两行:

pod 'AFNetworking'
pod 'Mantle'

代码复用从未如此轻松……

好了,扬帆起航

你可以专注于 UI 和交互了,去写一个牛闪闪的 Coding 客户端吧!

#License MIT