{"id":19281502,"url":"https://github.com/coding/coding-ipad","last_synced_at":"2025-04-09T14:14:50.350Z","repository":{"id":71844132,"uuid":"43038892","full_name":"coding/Coding-iPad","owner":"coding","description":"CODING iPad 客户端源代码  ","archived":false,"fork":false,"pushed_at":"2015-12-11T12:29:50.000Z","size":0,"stargazers_count":300,"open_issues_count":0,"forks_count":88,"subscribers_count":34,"default_branch":"master","last_synced_at":"2025-04-02T08:33:59.800Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://coding.net/u/coding/p/Coding-iPad","language":"Objective-C","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/coding.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"License","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-09-24T02:06:07.000Z","updated_at":"2025-03-01T15:10:45.000Z","dependencies_parsed_at":"2023-03-04T23:00:36.325Z","dependency_job_id":null,"html_url":"https://github.com/coding/Coding-iPad","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coding%2FCoding-iPad","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coding%2FCoding-iPad/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coding%2FCoding-iPad/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coding%2FCoding-iPad/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/coding","download_url":"https://codeload.github.com/coding/Coding-iPad/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248054193,"owners_count":21039952,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-09T21:22:58.060Z","updated_at":"2025-04-09T14:14:50.297Z","avatar_url":"https://github.com/coding.png","language":"Objective-C","funding_links":[],"categories":[],"sub_categories":[],"readme":"#Coding-iPad客户端说明\n## Just run it！\n想要看看 iPad 版本什么样，没问题！ clone 或者下载代码后，初次执行时，双击根目录下的 **bootstrap** 脚本，该脚本会准备初始数据，完成后会打开工程，点击 Xcode 运行！So easy，妈妈再也不用担心我的代码编译出错了！（之后只需打开 CodingForiPad.xcworkspace 即可）\n\n## 嗯……，你的代码好像很棒，请告诉我xx是怎么做的\n先告诉大家代码大概在哪里。\n\n\t.\n\t├── CodingForiPad\n\t│   ├── Vendor：因为各种原因没有用Pods管理的第三方库\n\t│   ├── Resources：资源文件\n\t│   ├── Util：一些工具类，Category等\n\t│   ├── Request：网络请求\n\t│   ├── Models：数据模型，一般一个网络请求会对应一个model\n\t│   ├── RequestExt：请求的业务扩展，用于分离基本请求以便于复用代码\n\t│   ├── ModelsExt：数据模型的业务扩展，用于分离基本模型以便于代码复用\n\t│   ├── Manager：一些单例\n\t│   │   ├── AddressManager：iPhone版本代码\n\t│   │   ├── Coding_FileManager：文件上传（iPhone版本代码）\n\t│   │   ├── COSession：登录用户管理\n\t│   │   ├── COUnReadCountManager：读信息、私信管理\n\t│   │   ├── ImageSizeManager：iPhone版本代码\n\t│   │   ├── JobManager：iPhone版本代码\n\t│   │   ├── StartImagesManager：iPhone版本代码\n\t│   │   ├── TagsManager：iPhone版本代码\n\t│   │   └── WebContentManager：格式化为网页使用，iPhone版本代码 \n\t│   └── ViewController\n\t│       ├── Style：基本样式，颜色等\n\t│       ├── Custom：一些自定义的View\n\t│       ├── Base：基本Controller\n\t│       ├── User：用户资料相关的UI\n\t│       ├── Project：项目相关的UI\n\t│       ├── Task：任务相关的UI\n\t│       ├── Tweet：冒泡相关的UI\n\t│       ├── Message：消息和私信相关的UI\n\t│       └── Setting：设置相关的UI\n\t└── Pods：项目使用了[CocoaPods](http://code4app.com/article/cocoapods-install-usage)这个类库管理工具\n    \n有两个比较隐蔽的问题，需要说明一下。首先，Coding 上部分资源是WebP格式，使用 SDWebImage 时，需要加入 WebP.framework；另外，在 Coding 的一些图片需要验证 Cookie 的，所以，在使用 SDWebImage 时，注意启用 Cookie，增加 SDWebImageHandleCookies 选项，代码如下所示：\n\n\t[self.regCodeImageView sd_setImageWithURL:[NSURL URLWithString:url] placeholderImage:nil options:SDWebImageRetryFailed | SDWebImageHandleCookies | SDWebImageRefreshCached completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {\n        if (error) {\n            dispatch_async(dispatch_get_main_queue(), ^{\n                weakself.regCodeImageView.image = [UIImage imageNamed:@\"captcha_loadfail\"];\n            });\n        }\n    }];\n    \niPad 客户端使用了 Storyboard，所以在代码阅读上，建议先从 Storyboard 开始，了解整个项目的脉络（由于项目比较大，打开 Storyboard 项目的速度取决于机器的配置，Orz）。另外从 UI 来入手也比较直观，在 Storyboard 中也可以直接看到 UI 对应的 Controllor。\n\n关于 ViewControllerr目录的组织基本是按照 UI 上展现来组织的，比如 Tweet 目录，主要是存放冒泡相关实现的代码，而其所对应的网络请求和 Models 也都是 COTweet 开头，这样大家很容易提纲挈领从一点贯通全面。\n\nRequestExt 和 ModelsExt 目录下是与业务比较相关的请求和模型，这样做是为了使数据请求和模型与业务逻辑无关，保证这部分代码能够复用。\n\n## 很好，我也要写一个这么棒的Coding客户端\n没问题，现在来告诉大家，怎么复用代码。\n\n不过，略微有点遗憾，由于项目比较紧张，当前只有网络请求和 Model 可以轻松复用。\n\n首先，再讲一个隐蔽的问题，关于用户登录！Coding 的用户验证是通过 Cookie 来验证的，所以问题来了，如果你直接调用登录接口，会发现登录成功了，但是访问其他接口会提示用户未登录，为什么呢，因为 Cookie 没有存储，为什么没有存储，因为你在登录之前没有请求验证码接口。所以，再你调用登录接口之前，**必须先请求验证码接口！！！必须先请求验证码接口！！！必须先请求验证码接口！！！**（重要的事情说三遍）\n\n### 网络请求\n网络请求时基于 AFNetworking 的，稍微封装了一下，所有接口请求都是继承于 CODataRequest。 Coding 是 RestFul 接口，HTTP 请求除了GET、POST，之外还有 PUT 和 DELETE 方法，所以在 CODataRequest 实现了上述四种基本请求方法。一个接口可能既支持 GET，也支持 DELETE，所需要的参数不尽相同。因此，我定义了几个指示说明性的宏来标识请求和参数，如下所示：\n\n\t#define COQueryParameters\t\t// Get 参数\n\t#define COUriParameters        // URI 参数\n\t#define COFormParameters       // Post 参数\n\n\t#define COGetRequest    /** Get请求 */\n\t#define COPostRequest   /** Post请求 */\n\t#define COPutRequest    /** Pust请求 */\n\t#define CODeleteRequest /** Delete请求 */\n\n以用户登录接口为例：\n\n\tCOPostRequest\n\t@interface COAccountLoginRequest : CODataRequest\n\t\n\t@property (nonatomic, copy) COFormParameters NSString *email;\n\t@property (nonatomic, copy) COFormParameters NSString *password;\n\t@property (nonatomic, copy) COFormParameters NSString *jCaptcha;\n\t@property (nonatomic, copy) COFormParameters NSString *rememberMe;\n\t\n\t@end\n\t\nCOPostRequest 表示这个接口是 POST 接口，调用的时候需要执行 postWithSuccess 方法；\n\nCOFormParameters 表示参数是 Post 使用的参数；\n\n####请求实现\nCODataRequest 将一个请求分为了四个阶段，请求准备，参数填充，完成准备，发起请求，数据解析。如果需要实现一个新请求，正常来说，需要完成请求准备和参数填充两个阶段，一般来说在准备阶段我们主要设置请求的 path，代码如下所示：\n\n\t- (void)prepareForRequest\n\t{\n\t    self.path = @\"/login\";\n\t}\n\n参数准备主要是建立参数映射字典，将请求的参数映射为对应的接口参数，代码如下：\n\n\t- (NSDictionary *)parametersMap\n\t{\n\t    return @{\n\t             @\"email\" : @\"email\",\n\t             @\"password\" : @\"password\",\n\t             @\"rememberMe\" : @\"remember_me\",\n\t             @\"jCaptcha\" : @\"j_captcha\",\n\t             };\n\t}\n\t\n其中 key 是请求的属性，对应的 value 是接口的参数名。\n\n\n数据解析阶段是将接口返回的 JSON 封装为 CODataResponse 对象，并将数据映射成对应的 Model，代码如下：\n\n\t- (CODataResponse *)postResponseParser:(id)response\n\t{\n\t    return [[CODataResponse alloc] initWithResponse:response dataModleClass:[COUser class] responseType:CODataResponseTypeDefault];\n\t}\n\t\n还有一个完成准备的阶段，这是表示，参数准备就绪，可以开始了，这时，我们可能还需要做一些事情，比如我封装了一个 COPageRequest，所有分页请求的数据都继承于它，让我们看看，它是怎么做的：\n\n\t- (void)readyForRequest\n\t{\n\t    NSMutableDictionary *params = [NSMutableDictionary dictionaryWithDictionary:self.params];\n\t    [params setObject:@(self.page) forKey:@\"page\"];\n\t    [params setObject:@(self.pageSize) forKey:@\"pageSize\"];\n\t    self.params = [NSDictionary dictionaryWithDictionary:params];\n\t}\n\t\nCOPageRequest 使用了 readyForRequest，将 page 和 pageSize 两个参数追加到参数映射中，这样其他的分页请求就不需要在参数映射中写这两个参数了。\n\n### 数据模型\n\n数据模型采用了[Mantle](https://github.com/Mantle/Mantle)将 JSON 映射为对象。来看一个简单的列子：\n\n\t@interface COFileCount : MTLModel\u003cMTLJSONSerializing\u003e\n\n\t@property (nonatomic, assign) NSInteger folderId;\n\t@property (nonatomic, assign) NSInteger count;\n\t\n\t@end\n\n\t@implementation COFileCount\n\n\t+ (NSDictionary *)JSONKeyPathsByPropertyKey\n\t{\n\t    return @{@\"folderId\" : @\"folder\",\n\t             @\"count\" : @\"count\",\n\t             };\n\t}\n\t\n\t@end\n\t\n是不是有些熟悉？没错，网络请求也参考了 Mantle 的实现。同样的 JSONKeyPathsByPropertyKey 中，key 对应的是 Model 的属性，value 对应的是 JSON 中的数据名。\n\n复杂模型的建立，请详细参考 Mantle 的文档。\n\n### 请求与模型的桥梁\n\nCODataResponse 是请求于模型之间的桥梁。CODataResponse 封装了整个网络请求返回数据，包含了code、msg、error以及数据等。所以，一个网络请求返回后，我们需要先检查 CODataResponse 中的 code，如果 code 为0，则表示请求 OK，然后还需要判断 error，这个 error主要是我们解析数据时产生的错误，多数是模型建立的不对，或者接口模型调整导致的。\n\n因为 Coding 的接口返回比较规范，主要是三种形式，字典，列表和 Pageable，因此 CODataResponse 封装了这三种形式的数据解析，解析数据变的非常简单，代码如下所示：\n\n\t- (CODataResponse *)postResponseParser:(id)response\n\t{\n\t    return [[CODataResponse alloc] initWithResponse:response dataModleClass:[COUser class] responseType:CODataResponseTypeDefault];\n\t}\n\t\n这是用户登录接口的数据解析，是不是很容易？\n\n### 代码使用\n拷贝 Request 和 Model 目录到你的项目中，在 Podfile 中添加如下两行：\n\n\tpod 'AFNetworking'\n\tpod 'Mantle'\n\t\n代码复用从未如此轻松……\n\n# 好了，扬帆起航\n你可以专注于 UI 和交互了，去写一个牛闪闪的 Coding 客户端吧！\n\n#License\n[MIT](License)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcoding%2Fcoding-ipad","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcoding%2Fcoding-ipad","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcoding%2Fcoding-ipad/lists"}