BasicServicesLib 2.0.2

BasicServicesLib 2.0.2

qiancaox 维护。



  • 作者:
  • qiancaox

BasicServicesLib使用说明

BasicServicesLib是一个iOS项目的基础组件库。包含了很多的便捷方法和函数;对部分 UI 组件进行扩展,很方便地实现界面开发;基于AFN实现了链式网络请求,利用请求池管理请求的生命周期,使用代理作为回调,避免代码块造成的内存泄漏问题;简单的数据库设计方案等...

更新日志

[TOC]

项目依赖库

+ Using AFNetworking(3.2.1)
+ Using FMDB(2.7.5)
+ Using MBProgressHUD(1.1.0)

演示项目

将仓库克隆下来后,使用Pod安装即可使用。

功能说明及示例

在说明之前先感谢下代码中Refer标注的作者,感谢他们贡献的干货

2.0 常用的宏定义及函数

“AppBase”中有两个文件:MacrosFunctions,前者主要包括日常开发中使用较多的宏定义,弱/强引用是根据ReactiveCocoa 之 @weakify/@strongify 实现的,实际测试有效。如果出现其他问题请直接issue,在宏定义中还定义了kiPhoneXScreenBottomSafeAreaHeight用于适配iPhoneX的安全区域,并且适配了横屏和竖屏两种情况。后者是常用的函数,使用_CGRect替换了CGRectMake等;还包括了诸如:Array/Dictionary/String判断空、UIView添加圆角和阴影、通知中心添加观察者和发送通知、方法交换和一些简单的数组操作等。

2.1 网络请求封装

Networking将网络请求分成三个部分:请求连接、请求参数、请求响应,并同时引入请求池来管理请求的生命周期。如果相同请求在上一个请求未响应时到达,下一个请求会自动取消上一个请求。

请求支持关联性请求:URLSessionChainTask实现了关联请求。用法如下:

[URLSessionTaskResponse setResponseErrorMessageKeyAddition:@"msg"];
URLSessionTaskURL *url0 = [[URLSessionTaskURL alloc] initWithBaseURL:@"***" relativeURL:@"rest/login/loginByName"];
URLSessionTaskURL *url1 = [[URLSessionTaskURL alloc] initWithBaseURL:@"***" relativeURL:@"rest/auth/getTokenByTicket"];

URLSessionChainTask *chainTask = [[URLSessionChainTask alloc] initWithDelegate:self];
[chainTask addTaskURL:url0];
[chainTask addTaskURL:url1];
[chainTask send];

[MBProgressHUDUtil LoadingWithText:@"正在加载..." inView:self.view contentBackgroundColor:[UIColor colorWithWhite:0 alpha:0.8] maskBackground:NO userInteraction:YES];

#pragma mark - <URLSessionChainTaskDelegate>

- (void)sessionChainTask:(URLSessionChainTask *)task requestDidSuccessThrowResponse:(URLSessionTaskResponse *)response atIndex:(NSInteger)index
{
    if (index == 0) {
        self.response0 = response;
    }
    else
    {
        self.response1 = response;
        if (self.response1.correct) {
            [MBProgressHUDUtil SuccessWithText:@"登录成功" inView:self.view];
        }
    }
}

- (void)sessionChainTask:(URLSessionChainTask *)chainTask prepareParamsFor:(URLSessionTask *)task atIndex:(NSInteger)index
{
    if (index == 0)
    {
        URLSessionTaskParams *params = [[URLSessionTaskParams alloc] initWithParams:@{@"name":@"100021", @"password":@"123456", @"systemCode":@"ISCC_MOBILE"}];
        task.params = params;
    }
    else
    {
        URLSessionTaskParams *params = [[URLSessionTaskParams alloc] initWithParams:@{@"ticketId":[self.response0.response safetyValueForKeyAddition:@"data.ticket"]}];
        task.method = URLSessionTaskMethodGET;
        task.params = params;
    }
}

- (void)sessionChainTask:(URLSessionChainTask *)task requestDidFailedThrowError:(NSError *)error atIndex:(NSInteger)index
{
    [MBProgressHUDUtil ErrorWithText:@"登录失败" inView:self.view];
    [self.view setMessage:@"网络错误,请稍后尝试" type:NoDatasourceTypeError];
}

需要注意的是,由于代码块的原因,AFN在网络请求发送后,如果请求没有在block中结束,那么在该block中使用的对象(主要是控制器等)可能会在某些情况下等到网络请求结束后才能被正常释放。因此,我已将代码块修改为代理回调,确保网络请求的生命周期与内部变量的生命周期互不影响。

另外,方法- (void)sessionChainTask:(URLSessionChainTask *)chainTask prepareParamsFor:(URLSessionTask *)task atIndex:(NSInteger)index用于组装数据,这是在异步线程中进行回调的,因此如果需要可以在主线程中进行回调。

2.2 简单的数据库设计

“简单”二字用得恰到好处,因为这个设计真的非常简单。表格设计就是key-value模型,其中值是将原始数据(NSDictionary)转换为字符串后,进行一次AES加密后的字符串。与sqlite的交互是通过FMDB实现的,对于order类型的表格查询使用了事务来提升查询速度。

2.3 常用的分类

“Categories”包含了许多常用分类,我在命名分类时尽量准确地描述其功能,都是将目的作为名字来命名分类。以下我将简要说明几个我认为比较典型的分类,其余的可以直接查看源码。

2.3.0 UIKit+Foundation+Blocked

这里包含了3个文件,实现了KVO、通知中心、UIControl的block回调。虽然使用起来比较方便,但请在block中使用弱引用,以避免内存泄露。至于是否使用,见仁见智。

2.3.1 UIAlertController

UIAlertController是开发中常用的一种弹出框。传统的使用方法是通过初始化控制器、初始化操作,将操作加入到控制器中使用,这种方式比较麻烦。因此,我将其进行了封装,只需使用一个方法就可以完成上述步骤,开发者只需关注回调处理即可:

UIAlertController *controller = [UIAlertController alertWithTitle:@"Alert" message:@"This is a test alert message!" actionHandler:^(UIAlertActionStyle style, NSInteger index) {
                
            } destructiveStyleButtonTitle:@"Destructive" cancelStyleButtonTitle:@"Cancel" defaultStyleButtonTitles:@"Default", nil];
[self.navigationController presentViewController:controller animated:YES completion:nil];

在代码块回调时请注意使用弱引用。

2.3.2 NSDictionary

这个分类起源于公司项目的变动。起初我们使用MJExtension将字典序列化为模型对象,但由于结构体不稳定,一旦模型搭建完成,结构体可能会改变,给我们带来巨大的工作量。因此,我们不再使用模型,而是去模型化。虽然直接使用字典避免了模型改变带来的困扰,但在取值时又显得十分被动。因此需要这样一个东西来实现对字典的取值和设值操作,所以写了一个分类来完成:

NSDictionary *dict = @{ @"data" : @{
                                         @"list" : @[ @8.627, @2, @3 ]
                                         }
                          };
id value = [dict safetyValueForKeyAddition:@"data.list[0]"];
DEV_LOG(@"%@", value);
dict = [dict safetySetValue:@"nidayede" forKeyAddition:@"data.object.name"];
DEV_LOG(@"%@", dict);
dict = [dict safetySetValue:@{@"name":@"woshinibaba"} forKeyAddition:@"data.list[1]"];
DEV_LOG(@"%@", dict);

2.3.3 UIView+CornerRadius

大多数情况下,设置圆角确实让人头疼,单是使用layer.cornerRadius会因离屏渲染而影响性能。此外,“部分圆角”的需求也较为常见。现在您只需一行代码就能实现:

[_redView setCorners:UICornerRadiusMake(10, 5, 6, 1)];

请注意,这句代码应在View的Rect确认的情况下设置,支持四个不同的圆角值,并且不会因离屏渲染而影响性能。

2.4 常用的组件

“Classes”是我根据日常开发工作中总结的UI组件和功能性的库。在开发过程中力求方便业务方使用,可能存在bug,如有发现请直接提交issue,在此再次表示感谢。

2.4.0 UIPlaceholderTextView

常用的UITextField不支持多行文字输入,而UITextView虽然存在placeholder的属性(使用runtime抓取到的),但没有实现。因此,UIPlaceholderTextView实现了带占位符的UITextView,占位符的位置是根据光标的起始位置计算的,目前看起来没有问题,使用如下:

_textView.placeholder = @"我是占位符";

占位符的默认颜色是lightGrayColor,支持自定义修改。为了达到和UITextField一样的效果,我在初始化时为其添加了通知中心的观察者,只有在文字内容大于0时才会隐藏占位符。

2.4.1 UIRelayoutButton

常用的UIButton都是水平布局,但在实际开发中,经常需要文字和图片垂直布局,UIRelayoutButton就是这样的一种解决方案,实现了文字和图片布局的调整,目前支持四种样式:

typedef NS_ENUM(NSUInteger, UIRelayoutButtonStyle) {
    // image在上,label在下
    UIRelayoutButtonStyleTop,
    // image在左,label在右
    UIRelayoutButtonStyleLeft,
    // image在下,label在上
    UIRelayoutButtonStyleBottom,
    // image在右,label在左
    UIRelayoutButtonStyleRight
};

并且还支持设置文字和图片的间距。

2.4.2 PresentedHUDBasicViewController

弹出视图的使用形式多样,但它们都有一个共同点,那就是半透明黑底。通过继承自PresentedHUDBasicViewController的控制器,可以方便地实现自定义的弹出控制器(而不是使用UIView了)。开发者只需重写两个方法就能实现弹入和弹出的动画:

/// Rewrite the following methods to achieve the animation of the pop-up box.
- (void)willPresentInView:(UIView *)toView duration:(NSTimeInterval)duration;
/// Rewrite the following methods to achieve the animation of the pop up box.
- (void)willDismissFromView:(UIView *)fromView duration:(NSTimeInterval)duration;

同时你只需考虑自定义弹出视图的样式和业务,其余的转场动画都由基类实现。

2.4.3 无视图的占位图 - NoDatasourceView

基于UIView的分类,只需要一行代码即可实现无数据或网络错误的占位图:

[self.tableView setMessage:@"网络错误,请稍后尝试" type:NoDatasourceTypeError];

并且在1.1.6版本的更新中修复了由于UIScrollView导致的占位图不在视图中间的问题。尽管底层实现是使用frame进行布局的,但通过监听屏幕旋转实现了类似自动布局的效果。

3.0 工具类的使用

在项目开发过程中,经常会遇到一些小型的功能性工具,方便直接调用。我目前使用较多的包括:MBProgressHUD的工具、数字金额转换的工具以及时间戳转换的工具,未来还将继续添加更多工具到文件夹中。我会尽可能使工具类功能单一化,以便于后期维护。

感谢

我的基础库参考了很多博客,同时在代码中也对参考的博客来源进行了注释,特别感谢这些作者分享的“干货”。另外,如果您在使用中遇到任何疑问,都可以通过邮箱联系我。在此感谢大家对我的支持,同时也感谢大家为我点star。