# iOS手册
# 简介
本文档主要介绍加加移动服务平台iOS SDK的接入和使用。
- SDK文件结构功能
├── AppSpSDK.h // SDK头文件
├── AppSpService.swift // SDK功能对外API接口文件
├── AppSpNotice.swift // 公告API文件
├── AppSpVersion.swift // 版本更新API文件
├── AppSpRequest.swift // 基础请求文件
├── AppSpDeviceInfo.swift // 获取设备基础信息文件
├── AJAppSpReachability.swift // 获取网络状态文件
├── AppSpUtils.swift // 基本通用方法文件
└── AppSpRSACrypt.swift // RSA加密文件
- 支持版本说明
- iOS 9.0+
- Xcode 11+
- Swift 5.0+
# 版本更新记录
上线日期 | 版本号 | 更新内容 |
---|---|---|
2020.12.16 | 0.0.3 | 添加 log debug开关、自定义设置host服务 |
2020.11.17 | 0.0.2 | 添加网络状态、数据相关优化 |
2020.10.16 | 0.0.1 | iOS SDK第一版,包含版本更新、公告服务功能 |
# iOS SDK集成
- 使用CocoaPods引入 AJSDKAppSp 先进入项目Podfile文件进行配置
platform :ios, '9.0'
use_frameworks!
target '<Your Target Name>' do
# 示例:0.0.1,可以选择指定发布版本
pod 'AJSDKAppSp', '~> 0.0.1'
end
- 然后打开终端,进入Podfile所在目录路径
$ pod install
# 快速接入使用
# 初始化服务
AppKey的获取见 操作手册
import UIKit
//导入头文件
import AJSDKAppSp
//初始化SDK数据 (建议在项目启动以后 AppDelegate 中初始化)
func initAJSDKConfigure() {
let appKey = "移动管理平台获取自己的AppKey"
/**
* AppSpService.shareService.initConfig 该接口SDK 0.0.3版本支持 增加如下功能
* appkey: 移动服务平台创建应用获取
* debug: 是否显示调试log 默认开启
* host: 配置自己的host 服务地址 默认nil
* AppSpService.shareService.initConfig(appkey: <String>, debug: <Bool>, <host: String?>)
*/
AppSpService.shareService.initConfig(appkey: "your App Key", debug: true, "your server host")
}
# 版本更新服务
/*
获取 版本更新数据
1、因为是异步,操作UI建议判断是否是主线程
2、repInfo:服务器响应数据包(具体格式见下文)
3、errorInfo:请求异常error信息(具体格式见下文)
*/
func requestUpdateVersion() {
AppSpService.shareService.checkVersionUpdate { (repInfo) in
print(repInfo)
} failure: { (errorInfo) in
print(errorInfo)
}
}
请求返回数据结构如下
repInfo 数据详情
{
"repCode": "0000", //业务返回码,0000表示成功
"repMsg": "成功", //业务日志
"repData": {
"downloadUrl": "app下载地址",
"mustUpdate": false, //是否强制更新,true为强制更新
"showUpdate": true, //是否允许弹出更新
"updateLog": "更新日志"
}
}
字段 | 类型 | 说明 |
---|---|---|
repCode | string | 业务返回码,0000表示成功 |
repMsg | string | 业务日志、异常信息 |
repData | Object | 请求业务数据包、详情见下 |
repData 数据详情
字段 | 类型 | 说明 |
---|---|---|
downloadUrl | string | app下载地址 |
mustUpdate | boolean | 是否强制更新,true为强制更新; false为非强制更新 |
showUpdate | boolean | 是否提示更新:允许弹出更新 |
updateLog | string | 更新日志 |
errorInfo 数据详情
repCode: 抛出异常Code
repMsg: 异常信息
字段 | 类型 | 说明 |
---|---|---|
repCode | string | 抛出异常Code ; 1001: 请求异常;1202: appKey为空;1203: appKey校验失败;1207: 系统版本号不能为空;1208: 应用版本号不能为空 |
repMsg | string | 异常信息 |
# 公告服务
/*
获取 公告信息数据
1、因为是异步,操作UI建议判断是否是主线程
2、repInfo:服务器响应数据包(具体格式见下文)
3、errorInfo:请求异常error信息(具体格式见下文)
*/
func requestNoticeInfo() {
AppSpService.shareService.getNoticeInfo { (repInfo) in
print(repInfo)
} failure: { (errorInfo) in
print(errorInfo)
}
}
请求返回数据结构如下
repInfo 数据详情
{
"repCode": "0000", //业务返回码,0000表示成功
"repMsg": "成功", //业务日志
"repData": [ // 返回数据为 Array
{
"title": "公告标题",
"details": "公告内容",
"templateType": "dialog", //公告类型( 弹窗:dialog; 水平滚动:horizontal_scroll)
"startTime": 1601186454000, //公告有效开始时间(毫秒级)
"endTime": 1601359255000 //公告有效截止时间(毫秒级)
}
]
}
字段 | 类型 | 说明 |
---|---|---|
repCode | string | 业务返回码,0000表示成功 |
repMsg | string | 业务日志、异常信息 |
repData | Object | 请求业务数据包、详情见下 |
repData 数据详情
字段 | 类型 | 说明 |
---|---|---|
title | string | 公告标题 |
details | string | 公告内容 |
templateType | string | 公告类型( 弹窗:dialog; 水平滚动:horizontal_scroll) |
startTime | timestamp | 公告有效开始时间(毫秒级) |
endTime | timestamp | 公告有效截止时间(毫秒级) |
errorInfo数据详情
repCode: 抛出异常Code
repMsg: 异常信息
字段 | 类型 | 说明 |
---|---|---|
repCode | string | 抛出异常Code ; 1001: 请求异常;1202: appKey为空;1203: appKey校验失败;1207: 系统版本号不能为空;1208: 应用版本号不能为空 |
repMsg | string | 异常信息 |
# iOS SDK解析
- 对外提供初始化接口服务
/**
* initConfig 该接口SDK 0.0.3版本支持 增加如下功能;
* appkey: 移动服务平台创建应用获取
* debug: 是否显示调试log 默认开启
* host: 配置自己的host 服务地址 默认nil
*/
@objc public func initConfig(appkey: String,
debug: Bool = true,
_ host: String? = nil) {
isDebug = debug
if host != nil {
appSpBaseURL = host!
}
self.setAppkey(appKey: appkey)
}
//初始化使用服务 后续会关闭该方法,建议使用如下 func initConfig() 该方法进行初始化
@objc public func setAppkey(appKey: String) {
_appKey = appKey
deviceInit()
}
//初始化设备信息
private func deviceInit() {
AppSpDeviceInfo.deviceInit()
}
- 对外提供功能
//获取版本更新接口
@objc public func checkVersionUpdate(success: @escaping (_ response: [String : Any]) -> (),
failure: @escaping ((_ errorInfo: [String: Any]) -> ())) {
AppSpVersion.checkVersionUpdate(success: success, failure: failure)
}
//获取公告信息接口
@objc public func getNoticeInfo(success: @escaping (_ response: [String : Any]) -> (),
failure: @escaping ((_ errorInfo: [String: Any]) -> ())) {
AppSpNotice.getNotice(success: success, failure: failure)
}
# 数据RSA加密
- 见AppSpRSACrypt.swift
// RSA加密
/// - Parameters:
/// - text: 加密字符串
/// - publicKey: 秘钥
/// - Returns: 返回加密后的结果
open class func encrypt(_ text: String, _ publicKey: String) -> String? {
guard let textData = text.data(using: String.Encoding.utf8) else { return nil }
let encryptedData = encryptWithRSAPublicKey(textData, pubkeyBase64: publicKey, keychainTag: publicKey)
if ( encryptedData == nil ) {
appSpLog("Error while encrypting")
return nil
} else {
let encryptedDataText = encryptedData!.base64EncodedString(options: NSData.Base64EncodingOptions())
return encryptedDataText
}
}
# 基础信息获取
- 见AppSpDeviceInfo.swift 文件
- 初始化设备信息接口
//初始化设备信息接口
class func deviceInit() {
AppSpRequest.share.request(path: AppSpDeviceInitPath, success: { (repData) in
}) { (errorData) in
}
}
//获取接口请求的基础参数字典
class func getDeviceInfo() -> [String: String] {
return [
"brand": getBrandInfo(), //获取手机品牌信息
"deviceId": getDeviceId(), //获取UUID
"sdkVersion": AppSpSDKVersion, //SDK发布版本号
"netWorkStatus": AppSpService.shareService.connectionStatus//网络状态,
"osVersion": getOSVerison(),//获取系统版本
"platform": getPlatform(), //获取系统平台
"screenInfo": getScreenInfo(),//获取屏幕宽高
"versionCode": getAppBuildCode(),//获取版本号
"versionName": getAppVersion()//获取版本名
]
}
- 实现
//获取版本名
class func getAppVersion() -> String {
let infoDictionary = Bundle.main.infoDictionary
let majorVersion = infoDictionary?["CFBundleShortVersionString"] as! String//主程序版本号
return majorVersion
}
//获取版本号
class func getAppBuildCode() -> String {
let infoDictionary = Bundle.main.infoDictionary
let appBuild = infoDictionary?["CFBundleVersion"] as! String//主程序版本号
return appBuild
}
//获取屏幕宽高
class func getScreenInfo() -> String {
let width = UIScreen.main.bounds.size.width
let height = UIScreen.main.bounds.size.height
return "\(height)*\(width)"
}
//获取系统版本
class func getOSVerison() -> String {
return UIDevice.current.systemVersion
}
//获取系统平台
class func getPlatform() -> String {
return UIDevice.current.systemName
}
//获取UUID
class func getDeviceId() -> String {
let identifierNumber = UIDevice.current.identifierForVendor
return identifierNumber?.uuidString ?? ""
}
//获取手机品牌信息
class func getBrandInfo() -> String {
return UIDevice().iphoneTypeName
}
# 版本更新信息接口
- 见AppSpVersion.swift文件
class func checkVersionUpdate(success: @escaping (_ response: [String : Any]) -> (),
failure: @escaping ((_ errorInfo: [String: Any]) -> ())) {
AppSpRequest.share.request(path: AppSpAppVersionPath,
success: success,
failure: failure)
}
# 公告信息获取接口
- 见AppSpNotice.swift文件
static func getNotice(success: @escaping (_ response: [String : Any]) -> (),
failure: @escaping ((_ errorInfo: [String: Any]) -> ())) {
AppSpRequest.share.request(path: AppSpNoticePath,
success: success,
failure: failure)
}
# 基础网络实现
- 见AppSpRequest.swift文件
- 由于网络组建的容易与项目工程冲突使用,该SDK对系统URLSession进行封装使用,详情如下
//初始化请求session
var _apiSession: URLSession!
override init() {
super.init()
let configuration = URLSessionConfiguration.default
configuration.httpAdditionalHeaders = ["Accept": "application/json"]
configuration.timeoutIntervalForRequest = requestTimeOut
configuration.timeoutIntervalForResource = responseTimeOut
_apiSession = URLSession(configuration: configuration)
}
//具体实现细节
func request(path: String,
options: [String: Any]? = nil,
success: @escaping (_ response: [String : Any]) -> (),
failure: @escaping ((_ errorInfo: [String: Any]) -> ())) {
let reqUrlStr = AppSpService.shareService.appSpBaseURL + path
guard let reqUrl = URL(string: reqUrlStr) else {
failure(self.apiErrorInfo(code: CustomErrorCode, messge: "URL解析异常"))
return
}
var request = URLRequest(url: reqUrl)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Accept")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
//获取appKey
let appKey = AppSpService.shareService.getAppKey()
//获取设备信息
let deviceParams = AppSpDeviceInfo.getDeviceInfo()
//拼装请求使用数据
var paramData: [String: Any] = ["appKey": appKey]
for (key, value) in deviceParams {
paramData[key] = value
}
var params: [String: Any] = [
"data": paramData,
"sign": ""
]
let oriEnStr = self.toEncryptString(data: paramData)
if oriEnStr != nil {
params["sign"]! = AppSpRSACrypt.encrypt(oriEnStr!, AJ_RSA_PUBLIC_KEY_TAG) ?? ""
}
appSpLog("========================================")
appSpLog("reqUrl: \(reqUrl)")
appSpLog("params: \(params)")
request.httpBody = formateRequestBody(parmas: params)
_apiSession.dataTask(with: request) { (data, response, error) in
//请求返回
}.resume()
}
- 网络请求数据解析
_apiSession.dataTask(with: request) { (data, response, error) in
if (error != nil) {
if let err = error as NSError? {
failure(self.apiErrorInfo(code: "\(err.code)", messge: err.localizedDescription))
} else {
failure(self.apiErrorInfo(code: CustomErrorCode, messge: "请求异常"))
}
return
}
guard let repData = data else {
failure(self.apiErrorInfo(code: CustomErrorCode, messge: "服务器数据异常"))
return
}
var dict:[String: Any]?
do {
dict = try JSONSerialization.jsonObject(with: repData,
options: JSONSerialization.ReadingOptions.init(rawValue: 0)) as? Dictionary
} catch {
failure(self.apiErrorInfo(code: CustomErrorCode, messge: "数据解析异常"))
return
}
if let repDict = dict {
let repMsg = repDict["repMsg"] as? String
if let repCode = repDict["repCode"] as? String {
if repCode == "0000" {
success(repDict)
} else {
failure(self.apiErrorInfo(code: repCode, messge: repMsg ?? "数据异常"))
}
}
appSpLog("response: \(repDict)")
} else {
failure(self.apiErrorInfo(code: CustomErrorCode, messge: "数据解析异常"))
}
}.resume()
# SDK Demo下载
若要参考具体集成流程,可下载我们提供的Demo,
iOS SDK Demo下载地址: https://gitee.com/anji-plus/aj-appsp/demo/ios (opens new window)
iOS SDK地址: https://gitee.com/anji-plus/aj-appsp/sdk/ios (opens new window)