HBKit-Swift 框架说明

1. HBKit 来源

HBKit最早由HuobanKit演变而来,我将HuobanKit中跟业务无关的代码抽离出来,精简成基本的网络框架和数据库(CoreData)框架,对MVVM架构设计方案清晰分层,并结合VIPER、Riblets架构设计思路引入Router路由,组成:网络、数据库、路由三个体系。可以以极简的方式来使用,并让ViewController间解耦。并由Swift进行代码重写,一个全新Swift框架。
而HuobanKit则以后由业务model组成,负责业务的数据与服务器交互。

目前本框架还没有公布出来,尚需进一步完善

1.1 网络部分

网络请求部分针对Alamofire进行二次封装,
提取出可以方便快捷使用的网络请求GET/ POST/ PUT/ DELETE的四种方法

1.2 数据库部分

采用apple原生CoreData作为基础数据库,提供数据增、删、查(改)的动作,
并支持Api属性对应数据entity模型的驼峰命名法的匹配:如user_id => userId

1.3 路由部分

采用url网络链接写法,基于url进行拆分并提取出class/function?params进行ViewController窗口的初始化

2. 用法

2.1 发起网络请求

2.1.1 GET

  • 按任意请求举例
// 接口域在HBKitConfig.swift HBApiURL中设置
let request = HBRequest.GETRequestWithPath(path: "/uri")
request.completion { [unowned self] (error:NSError?, response:HBResponse) in
    if error != nil {
        print("faild to request, error :", error)
    } else {
        print("response:", response)
    }
}
request.send()

2.1.2 POST

  • 按用户登录举例
var parameters: Dictionary = Dictionary<String, Any>()
parameters["username"] = "username"
parameters["password"] = "password"

let request:HBRequest = HBRequest.POSTRequestWithPath(path: "/auth", parameters: parameters)
request.completion { (error:NSError?, response:HBResponse) in
    if error != nil {
        print("faild to request, error :", error)
    } else {
        print("response:", response.body as Any)
    }
}
request.send()

2.1.3 MultiRequest

  • 多请求需要服务器支持batch接口、并对POST请求进行拆分和返回数据
// 基于GET请求将send方法添加参数packer: HBMultiRequestPacker.shared
request.send(packer: HBMultiRequestPacker.shared)

// 并由HBMultiRequestPacker类send
HBMultiRequestPacker.shared.send()

2.2 数据库

2.2.1 网络请求入库

2.2.1.1 如何根据接口返回数据构建数据表?

  • 如果已经存在 {YourProjectName}.xcdatamodeld 文件则建立Entity, 比如ItemModel
  • 在ItemModel当中新建两个字段:

ItemModel

itemId (Integer 64)

name (String)

  • 并打开XCode -> Editor -> Create NSManagedObject Class 新建一个针对于ItemModel的类
  • 打开ItemModel的类,在class后面加入HBManagedObject声明
  • 并为ItemModel类添加一个类方法identityPropertyNames指明主键(Primary key):
public class ItemModel:NSManagedObject, HBManagedObject {
    static func identityPropertyNames() -> [String] {
        return ["itemId"]
    }
}

2.2.1.2 如何将接口数据存入CoreData?

  • 比如:接收到Api数据如下
{"item_id": 10000, "name": "测试Item"}
  • 在线请求HBRequest接收到的数据在response.body当中(response:HBResponse),尝试入库
// 网络请求返回值片段...
request.completion { [unowned self] (error:NSError?, response:HBResponse) in
    if error == nil {
        // response.body = {"item_id": 10000, "name": "测试Item"}
        let body = response.body as! Dictionary
        let itemModel = ItemModel.hb_createObjectOrUpdate(fromDictionary:  body, isSave: true)
    }
}
  • 如果请求的response.body是一个Array,则可以使用HBManagedObject针对Array的扩展方法:
/* 
response.body = [
                        {"item_id": 10000, "name": "测试Item0"},
                        {"item_id": 10001, "name": "测试Item1"},
                        {"item_id": 10002, "name": "测试Item2"}
                    ]
*/
// 网络请求返回值,入库片段
request.completion { [unowned self] (error:NSError?, response:HBResponse) in
    let body = response.body as! [Dictionary]
    let itemArray:Array = ItemModel.hb_createObjectsOrUpdate(fromArray:  body,
                     isSave: true, 
                     step: { (itemModel:ItemModel, index:Int) in
        // 单步执行,可以改造数据等
    }, completion: { 
        // 注意completion还可以做其他事情,比如Array循环结尾的回调      
    })!
}

2.2.2 数据库数据查询

  • 数据库查询方法,可以写在ItemModel中,比如按itemId查询一个指定数据
public class ItemModel: NSManagedObject, HBManagedObject {
    static func identityPropertyNames() -> [String] {
        return ["itemId"]
    }
    
    static func queryModel(itemId:Int64) -> ItemModel? {
        let result:Array = self.hb_getObjects(predicate: NSPredicate.init(format: "itemId=%ld", itemId))
        if result.count > 0 {
            return result.first
        }
        return nil
    }
}

2.2.3 数据库数据删除

  • 通过ItemModel查询到的数据,通过实例方法.delete()即可删除,比如:
let itemModel = ItemModel.queryModel(itemId:10000)
itemModel.delete()

2.2.4 数据库数据修改和保存

let itemModel = ItemModel.queryModel(itemId:10000)
itemModel.name = "change the name"
itemModel.save()

2.3 Router 路由解耦

  • HBKit提供一个HBRouter类来进行业务层(UIViewController)开发解耦,并提供HBBaseViewController的协议来约定解耦规则
  • HBRouter基于网络链接来进行url的拆解,识别huoban://host(class)/action(function)?params...
  • HBRouter会根据host,自动寻找host + "ViewController"的类来初始化

2.3.1 声明一个可用于HBRouter路由的HBBaseViewController

  • HBBaseViewController 存在一个协议,需要实现该方法,才能完成Router的调用
protocol HBBaseViewController {
    static func createViewController() -> UIViewController
}
  • 开始声明一个基于HBBaseViewController协议的ViewController,叫ItemViewController
  • ItemViewController可以有HBRouter传参,比如:itemId:NSNumber
class ItemViewController: UIViewController, HBBaseViewController {
    var itemId:NSNumber? = nil
    
    // 基于HBBaseViewController协议,声明createViewController,方便HBRouter
    static func createViewController() -> UIViewController {
        //这里采用从StoryBoard来获取一个UIViewController
        return self.instantiateViewControllerFromStoryboard(name: "Item")
    }
    
    override func viewDidLoad() {
    }
}
  • 需要打开ItemViewController的地方,可以采用HBRouter的url形式来打开它:
HBRouter.shared.push(path: "huoban://item", params: ["item_id": NSNumber.init(value: 10000)])

当然我们也可以通过?item_id=10000传参

HBRouter.shared.push(path: "huoban://item?item_id=10000")
  • HBRouter会自动将item_id转换成itemId的方式进行赋值(?itemId=10000则不会转换)
  • 这代码的结果将会是:
var itemViewController = ItemViewController.createViewController()
itemViewController.itemId = NSNumber.init(value:10000)
self.navigationController.pushViewController(itemViewController)
  • HBRouter有两个方法打开ViewController: 1. push, 2. present

2.4 OBServable(订阅被观察者Block)

2.4.1 HBKit/Extension/Observable.swift 是被观察者

  • 由于MVVM的架构变更,ViewModel层和ViewController层需要一种方式来解耦,将ViewModel 状态变更自动回调给 ViewController。做到减少状态设置,对ViewController减肥。
  • 具体用法
//ViewModel
class ItemViewModel {
    var itemName = Observable<String>("")
    init (_ itemId:Int) {
        // itemId......do something
    }
    
    func requestNetworkData() {
        itemName <- "refresh new name!"
    }
    func requestNetworkData2() {
        itemName <- "refresh new name2!"
    }
}

// ViewController
class ItemViewController {
    var itemViewModel = ItemViewModel(10000)
    @IBOutlet weak var itemNameLabel: UILabel!
    
    itemViewModel.itemName.subscribe{ [unowned self] (oldValue, newValue:Bool) in
        self.itemNameLabel.text = newValue
    }

    itemViewModel.requestNetworkData()
    itemViewModel.requestNetworkData2()
}

2.5 工程目录结构推荐

  • 基于尽可能的解耦业务逻辑间的联系,我们在使用HBRouter的时候,会将ViewController进行分类,我推荐的目录结构为:

Project

Core

Common

HBKit

Data

ItemModel.swift

Model

ItemAgent.swift

Module

Item

Item.storyboard

ItemList

ItemListViewController.swift


3. HBKit 目录结构

  • HBKitConfig (Config设置类)

  • Network (网络请求组件目录)

    • HBClient
    网络请求发送,认证信息拼接,用于将HBRequest包装成真实的URLRequest类的请求
    拼接Header
    
    • HBRequest
    请求体包装用户接口类
    用户可以设定HttpMethod: GET/ POST/ PUT/ DELETE
    
    • HBResponse
    网络请求返回值的包装用户接口类
    
    • HBAuthorization
    登录用户验证类
    
    • HBMultiRequest
    多请求类,基于Batch接口进行Post打包提交
    
  • CoreData (基于CoreData实现数据组件目录)

    • HBManagedObject (NSManagedObject+extension)
    对NSManagedObject的扩展,封装了方便增、删、改、查的动作,使用反射的原理映射给CoreData的Model类
    
  • Extension

    • Class

      • HBNotificationCenter
      广播的block轻巧封装
      
      • Observable
      极简观察者模式
      
      • HBImageURLSessionDataTask
      轻巧的图片下载类
      
    • NSObject扩展

    1.获取NSObject中的属性名称列表
    
    • String扩展
    1.字符串切割"_"现转换为驼峰命名法
    
    • UIImageView扩展
    1. 针对ImageView提供快捷网络图片设置
    
    • UIButton扩展
    1. 针对UIButton提供快捷网络图片设置
    
    • UIViewController扩展
    定义常规HBBaseViewController针对HBRouter和Storyboard初始化的动作
    
    • UIActivityIndicatorView
    loading的快捷显隐的扩展
    
    • NSError
    网络请求错误的NSError扩展
    
  • Router

    • HBRouter
    路由,与HBBaseViewController一起联合进行路由的反射操作,用于解耦业务间的类 
    
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,616评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,020评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,078评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,040评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,154评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,265评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,298评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,072评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,491评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,795评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,970评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,654评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,272评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,985评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,815评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,852评论 2 351

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,071评论 4 62
  • iOS网络架构讨论梳理整理中。。。 其实如果没有APIManager这一层是没法使用delegate的,毕竟多个单...
    yhtang阅读 5,180评论 1 23
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,644评论 18 139
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,913评论 25 707
  • 我小心翼翼地,像是揭开一道伤口上的还粘着强有力的胶带,粘着汗毛,牵动着伤口上的皮肤,疼在我心里。下面一张有着她家房...
    一个贱男人阅读 414评论 0 1