在iOS开发过程中,我们在编码Server接口的时候,不可避免的要硬编码一些接口名、参数key等文档中定义的内容。而怎么防止由于人的原因,编码意识模糊一时大意导致这些硬编码出错的问题?
暂时想到了两种方式,一种静态,一种动态。
1.静态处理的方式,需要开发人员在编码过程中,在接口代码编写时需遵循某种指定的格式,或添加某些指定的注释或标记。然后通过shell脚本,在Xcode的run script中,或CI系统中执行,检查相关文件携带指定标记的代码,看是否和Server文档标准中一致。这样可以一次性检查全部的接口。
2.动态处理的方式,需要修改网络层代码,在请求前或请求成功后,得到Request或Response,再根据本地导入的xml、Plist格式的Server文档标准文件,通过比对文档中的数据与请求数据是否一致,来得出结论。这个只能项目运行起来,用了哪个接口才能检查哪个。
依赖:
这两种方案,实际上都非常依赖一份Server提供的指定格式的标准文档,但Server本身就在维护一份接口文档,现在又要出一份xml或者Plist格式的文档,增加了他们的工作量。
解决方案:
初步想到的是,自行研发一套接口文档编写系统,通过规范编写方法,在某些位置的接口文档需要遵循某些编写格式,提交到数据库后,再通过专门的接口或功能将这些内容导出为json或xml或Plist文件即可。
动态处理的方案,写了一个简单的demo,可以实现在接口书写错误的情况下,报错提示程序员哪里错误。如下:
这个是接口规范文件:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>/mall/login</key>
<dict>
<key>POST</key>
<dict>
<key>header</key>
<false/>
<key>parameters</key>
<dict>
<key>loginId</key>
<true/>
<key>loginMethod</key>
<true/>
<key>password</key>
<true/>
</dict>
</dict>
<key>GET</key>
<dict>
<key>header</key>
<false/>
<key>parameters</key>
<dict>
<key>refreshToken</key>
<true/>
</dict>
</dict>
</dict>
</dict>
</plist>
这个是校验逻辑,我是写在了接口返回的时候,代码没有好好处理,只是提供个思路,请勿直接使用:
private func validate(response: DataResponse<Any>) {
guard let request = response.request else {
return
}
let path = Bundle.main.path(forResource: "NetworkDocument", ofType: "xml") ?? ""
if let dict = NSDictionary(contentsOfFile: path) as? [String: [String: [String: Any]]] {
let apiKeys = dict.keys
var matchedKey = ""
let apiMatch = apiKeys.contains(where: { (key) -> Bool in
if request.url?.absoluteString.contains(key) ?? false {
matchedKey = key
return true
} else {
return false
}
})
if apiMatch {
if let subDict = dict[matchedKey] {
if subDict.keys.contains(request.httpMethod ?? "") {
let method = request.httpMethod ?? ""
if let finalDict = subDict[request.httpMethod ?? ""] {
let header = finalDict["header"] as? Bool ?? true
let auth = request.value(forHTTPHeaderField: "Authorization") ?? ""
if header == true {
if auth.isEmpty {
fatalError("header validation failed in <\(matchedKey)> API")
}
}
if let parameters = finalDict["parameters"] as? [String: Bool] {
for (key, value) in parameters {
if value == true {
if method == "GET" {
if !(request.url?.absoluteString.contains(key) ?? false) {
fatalError("parameters validation failed in <\(matchedKey)> API, missing key <\(key)>")
}
} else {
if let body = request.httpBody {
let bodyDict = try? JSONSerialization.jsonObject(with: body, options: .allowFragments) as? Dictionary<String, Any> ?? [:]
if !(bodyDict?.keys.contains(key) ?? false) {
fatalError("parameters validation failed in <\(matchedKey)> API, missing key <\(key)>")
}
}
}
}
}
}
}
} else {
fatalError("method validation failed in <\(matchedKey)> API")
}
}
}
}
}