iOS证券交易App组件化方案

一、前言

1.1 什么是组件化

组件:强调物理拆分,以便复用。例如:图片库网络库等。

模块:强调逻辑拆分,以便解耦。例如:订单模块账户模块等。

介于业界习惯称之为组件化,所以我们继续使用这个术语。【组件化 = 功能组件 + 业务模块

1.2 为什么要组件化

  • 解决代码(工程)臃肿问题,提高编译速度。
  • 核心业务模块化,职责分离,提高代码安全性。
  • 通用功能组件化,提高复用率及完整性。
  • 协议编程具体化,形成独立子系统,可灵活配置。
  • 提高模块自治力,独立升级部署,相互间不形成绝对依赖。
  • 支持异构,提升系统技术多元化能力。

二、业界方案

2.1 组件化演进之路

// TODO

2.2 微服务概念

2.2.1 三个⻆色

服务中介:联系服务提供者服务消费者的桥梁。

服务提供者:将自己提供的服务地址注册到服务中介。

服务消费者:从服务中介那里查找自己想要的服务的地址,然后享受这个服务。

2.2.2 三个⻆色在iOS组件化中的体现

iOS客户端是一个单工程设计,简单说就是APP作为一个独立个体,所有的业务生命周期与APP是一致的。并不能真正实现微服务中的“每个服务完全独立生存、管理、销毁”。为此,组件化更多是解决职责分离高复用独立发布 (非独立运行)问题。

iOS中服务提供者通常指的是业务提供方,比如:行情模块需要调用交易模块交易记录接口,此时交易模块即作为服务提供者。而行情模块作为服务需求方,即为服务消费者。由于模块与模块之间可能处于同一级,也可以不同 级。按照《软件工程设计:分层概述》原则,同一级模块间不能相互依赖,也就是不能相互直接调用。此时就需要 一个服务中介来解决模块间通信问题

2.3 组件间通信常⻅方案

2.3.1 基于路由 URL UI ⻚面统跳管理

概念

统跳路由是⻚面解耦的最常⻅方式,大量应用于前端⻚面
通过把一个 URL 与一个⻚面绑定,需要时通过 URL 可以方便的打开相应⻚面。 比如

bebull://quote/stockDetail?stock_id=00700 /**个股报价⻚**/ 

优点

  • 多端一致,H5、iOS、Android、Weex/RN/Cameron 、PC、Mac等。
  • 浏览器、其他APP可以通过openURL直接唤起APP指定⻚面。
  • 可结合服务端下发配置表,动态绑定/解绑指定URL实现⻚面灰度。

缺点

  • 无法支撑较为复杂操作及数据传输模型,比如跨模块⻓调用链场景。
2.3.2 基于反射的远程接口调用封装

概念

此类型适合动态语言,借助反射技术进行动态化调用,比如OCRuntime,可以采用以下方式调用:

Class quoteManager = NSClassFromString(@"QuoteManager"); 
NSArray *stockList = [quoteManager performSelector:@selector(getStockList)];

优点

  • 调用较为方便
  • 代码自动补全
  • 编译检查有效

缺点

  • 存在hardcode问题
  • Swift静态语言不支持Runtime
2.3.3 基于面向协议思想的服务注册方案

概念

参考后端Dubbo服务框架,通过服务注册的方式来实现远程接口调用的。 即每个模块提供自己对外服务的协议声明(Protocol),然后将此声明注册到服务中介。调用方能从服务中介看到 存在哪些服务接口,然后直接调用即可。

@protocol QuoteModuleService
- (NSArray * _Nullable)getStockList;
@end
@interface QuoteModule : NSObject<QuoteModuleService>
@end
@implementation QuoteModule
+ (void)load {
  [ServiceManager registerService:@protocol(QuoteModuleService)
                  withModule:self.class];
}
- (NSArray * _Nullable)getStockList {
  return nil;
}
@end

id<QuoteModuleService> module = [ServiceManager
objByService:@protocol(QuoteModuleService)];
NSArray *stockList = [module getStockList];

优点

  • 调用方便
  • 代码自动补全
  • 无需hardcode,面向协议,减少对接错误⻛险

缺点

  • 高度依赖协议,协议变更时容易导致所有依赖方编译失败。
  • 存在服务注册过程,有一定性能开销。
2.3.4 基于通知的广播方案

概念

直接基于系统的 NSNotificationCenter

优点

  • 实现简单。
  • 适合一对多场景。

缺点

  • 不适合复杂数据传输场景。
  • 同步调用不方便。

三、架构设计思路

3.1 目标

  • 业务模块分离,业务职责单一,划清边界。
  • 业务模块间通信灵活且统一化。
  • 业务模块间通信协议严格标准化,具备准入原则。
  • 通用逻辑独立化,构建稳定后采用二进制形式链接。
  • ⻚面多端统一性,方便后续PAAS化(可由服务端自主下发配置,灵活管理⻚面调用及组合)。
  • 代码安全,只开放必要的模块代码权限。
  • 服务异构化,后续部分模块可以采用更多语言如c++python等开发后引入。
  • 设计模式规范化,明确各场景采用MVCMVVM等,如何组织类关系等。

3.2 组件间通信场景

3.2.1 场景一:UI独立⻚面

采用 路由 URL UI ⻚面统跳管理方案 ,实现跨端 + 端内任何场景可直接唤起指定⻚面能力。

3.2.2 场景二:跨模块UI嵌套

采用 基于面向协议思想的服务注册方案 ,通过 BNBee组件中心UIService 进行服务调用,获得指定的 View、VC。

3.2.3 场景三:跨模块数据传输

采用 基于面向协议思想的服务注册方案 ,通过 BNBee组件中心OpenService 进行服务调用,提供同步返回

(sync)和 异步返回(async)能力。

四、架构分层

4.1 图示

4.2 各层职责

总体分为 业务层公共层基础框架层 三层。

业务层

  • 承担业务层面相关工作
  • 主要包括VCViewModelViewModelDCManagerAPIClient
  • 目前阶段分为行情交易资讯账户公共主体五类

公共层职责

  • 承担整体性非业务相关的相关工作。
  • 主要包括公共相关事务(如BNURLHelper),组件间通信API、Model,以及皮肤资源整合。
  • 目前阶段分为公共基础平台、业务间通信API、皮肤资源中心。

基础框架层职责

  • 包含一些日常使用,趋于稳定的库。
  • 主要分为自研和第三方依赖两大类。
  • 自研部分采用framework静态库方式,基于pod引入。

4.3 各层间依赖关系

  • 同层之间禁止直接依赖,通信方式通过BNBee组件服务中心。提供方提供具体实现,将协议定义于BNUAPI工程中;消费方于BNUAPI中获得对应API及model。
  • 上层可以直接引用下层,也可以跨层下级引用
  • 下层禁止引用上层。

4.4 模块说明

  • 行情模块:包括行情一级tab下的自选列表、市场列表、个股报价⻚等。
  • 交易模块:包括交易一级tab下的开户、资金账户、持仓、买入卖出、交易记录、入金出金等。
  • 资讯模块:包括资讯一级tab下的资讯列表、资讯详情⻚等。
  • 账户模块:包括我的一级tab下的用户登录、注册、个人资料等。
  • 公共基础平台:主要涵盖与业务无关的部分,包括暂时找不到基础框架层可以下沉的暂留组件,比如:WebView、 tabbar等。
  • 业务间通信API:主要负责上层业务模块的组件间通信API协议定义、传输model定义、业务通知key等。 皮肤资源中心:存放所有必牛项目的图片、lottie动画等资源。

4.5 模块内结构

五、BNBee(待补充)

5.1 基本能力

5.2 URL注册绑定

5.3 面向协议服务注册

六、自研框架库

6.1 目标

  • 将重要且需要稳定的模块独立出来,抽离成framework,实现复用性稳定性安全性目标。
  • 标准化一些常用逻辑,做到多业务一致。
  • 代码安全,只开放必要的代码权限。
  • 服务异构化,后续可以采用更多语言如c++python等开发更多底层框架。

6.2 组件库

  • 网络组件库:负责socket⻓连接管理、HTTPS短链接生命周期、连接、安全、可靠性管理。
  • 组件服务中心:负责管理组件间通信,提供基于URL注册、协议绑定等方案管理。
  • 日志组件库:分级日志系统,对infowarningerrorfile及日志收集存取上报管理。
  • 开发组件库:日常开发常用的工具类系统类分类等。
  • 数据上报组件库:直接接驳三方数据上报平台,提供与上报平台无关的通用接口,包括曝光时⻓曝光量统计接口
  • 分享组件库:主要提供通用分享组件,包括分享到QQ微信Twitter等。
  • UI组件库:主要沉淀通用UI组件,如嵌套Scrollviewtabbar等。
  • 数据管理组件库:主要包装内存级缓存磁盘级缓存的接口,后端对接沙盒、SQLite数据库等。
  • 皮肤组件库:主要提供style定义系统,用于约定常用UI组件如UIButtonUIView的多肤管理。
  • 健康防护系统:防范常⻅类型crash兜底,启动连续crash绕行等方案。
  • k线组件库:k线组件作为核心性能指标,沉淀出来方便后续各业务使用。

七、第三方依赖库

7.1 设定准入标准

券商APP的安全性不容小觑,对于第三方依赖库的引入需持有谨慎的态度;因此后期需要增加第三方依赖库的准入 机制,形成选型评审安全鉴定引入的原则。同时,尽量剔除一些功能重复、失去维护、功能滞后的三方库, 保证APP包大小启动性能等各项指标。

7.2 选型需明确

如:选新不选旧 or 稳定大于能力?Swift实现的优先?轻量化还是重量级?是否跨端配套⻬全?等等问题,需要有 明确的标准,组件化工作落地后,需要再明确这块,保证快捷接入的同时,可以更稳定、更一致。


设计一个聊天系统

前言

聊天应用在我们的日常生活中变得越来越重要,从社交媒体到客户服务,他们都成为我们生活的一部分。因此,如何设计和实现一个高效,可扩展的聊天系统构成了 iOS 开发者面临的一项重要挑战。

系统设计概述

在设计聊天系统时,我们需要考虑的设计因素有:一对一的私人聊天、群聊功能、发送文本和媒体消息、实时消息传输、以及消息的持久化存储。而作为 iOS 开发者,我们会希望这个系统对设备资源的占用最小,且具有出色的性能表现。

客户端框架

在 Swift 中,我们可以利用一些设计模式进行良好的架构设计。其中 MVC(模型-视图-控制器)是最常用的模式。但是根据项目具体需求,也可以考虑使用 MVVM(模型-视图-视图模型)或 VIPER(视图-互动器-呈现器-实体-路由器)来架构你的 iOS 应用。

消息处理

对于消息的处理,分发和同步,使用 Socket.IO 客户端库进行实时通信。Socket.IO 使你能够在客户端和服务器之间进行全双工通讯,实现了信息的推送。利用 Firebase Cloud Messaging,我们可以实现对未在线用户的消息推送。

UI 实现

我们可以借助一些第三方框架来实现漂亮并具有流畅性能的 UI,例如使用 JSQMessagesViewController 或者 MessageKit 来快速构建漂亮的 UI。

网络请求

Alamofire 是 Swift 中处理 HTTP 网络请求的优秀库,功能强大且易用。它能简化许多网络操作的过程,从发出请求到处理响应。

数据解析

在收到服务器的响应之后,我们通常需要处理返回的 JSON 数据。在 Swift 中,我们可以使用 SwiftyJSON 库来让 JSON 解析变得简单并错不可及。

数据存储

在客户端,我们需要有效地管理和存储用户数据和消息。这时,CoreData 和 Realm 数据库成为最佳选择。这两个框架为 iOS 开发者提供了强大的数据管理能力。

消息有序性

在聊天应用中,确保消息的有序性是一项重要任务。我们将每条消息装配一个时间戳或者全局唯一的序列号,根据它们进行排序以保证正确的顺序。

隐私和安全

任何设计中,用户的私密性和数据安全都是第一位的。应用内部应该使用 HTTPS 进行数据传输,同时聊天消息可采取端对端加密的方式以确保通信安全。另外,登陆界面应含有 OAuth 或其他安全认证机制,保障用户账号的安全。

总结

设计和实现聊天应用是代码、设计以及版权等多种深度结合的结果。本文介绍的架构和设计的宝典并不是固定不变的,我们还需要根据自身的应用需求不断去创新和挑战。我希望上述的讨论和建议对你在开发 iOS 聊天应用的路上有所启发和帮助。

参考链接:来源

设计一个证券交易App

前言:

本篇文章从iOS开发者的角度,介绍证券交易App的一些技术选型功能设计等方面的内容。涉及到与server的交互数据格式及定义、接口设计、功能设计等。

一、App编程语言及三方库

iOS端采用原生开发,编程语言是Swift。如果某些功能需要在双端同时实现可使用Flutter。最近也参与了Flutter的跨平台开发,这篇文章简单介绍。

部分第三方库:

  • 网络:AlamofireStarscreamSocketIO
  • 数据解析:HandyJSONSwiftProtobufSwiftyJSON
  • 布局:SnapKit
  • 网络图片:Kingfisher
  • 键盘输入:IQKeyboardManagerSwift
  • 缓存:Track
  • 弹框:Toast-Swift
  • 数据库:WCDB.swift

二、网络部分

1、网络通信协议

常见的应用层通信协议HTTPSMTPWeb Socket等,这里介绍当前项目使用到的两种:

HTTP(HyperText Transfer Protocol,超文本传输协议):

  • 是一种网页(HTML)传输协议,适用于从万维网服务器传输超文本到本地浏览器的传送协议。
  • 是一种请求-响应协议,客户端发送一个请求给服务器,然后服务器回送一个响应回来。这种通讯方式限制了只能有客户端发起通信,服务器不能主动给客户端发送数据。

Web Socket:

  • 是一种在单个 TCP 连接上进行全双工通信的协议。目标就是在网页和服务器之间建立持久连接,然后进行双向数据传输。
  • 比起 HTTP(HTTP 协议中,服务器不能主动向客户端推送信息),WebSocket 更适合于实时性要求较高的场景,例如网页聊天,股票行情,游戏等。

2、数据编码格式

网络通信中传递的是数据,那么如何组织这些数据编称作“编码”,常见的有XMLJSON这两种格式,在app开发中也几乎不会用XML这种格式了。另外一种是Google推出的Protocol Buffers(简称ProtoBuf或PB)。

JSON (JavaScript Object Notation) 是一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析生成。JSON 使用完全独立于语言的文本格式,采用 Key-Value 键值对的方式来存储和表示数据,常用于服务器Web 应用间的数据交换。

ProtoBuf,全称 Protocol Buffers,是 Google 公开的一种数据序列化协议,用于数据结构的序列化,可以用于数据存储通信协议等方面。相较于 JSON、XML 这些数据格式,ProtoBuf 更小更快、也更简单。它使用二进制格式,可以定义复杂的数据结构,还提供了众多语言的 API 接口,可用于网络通讯和数据存储。约定的数据结构能够被跨语言使用,比如 Server 使用 C++,Client 使用 Java,两者之间的通信,可以使用 ProtoBuf 来约定通信协议。

总体上,JSON 更简单易用,并且在WebView、Web Browser 这样的环境中使用更加便利。而 ProtoBuf 则适合于大规模的系统中,需要更高效或者更清晰的协议定义的地方。

我们当前讨论的证券交易App在查看行情的过程中需要接受大量的行情数据,同时对时效性有较高的要求。PB体积小(二进制格式)、效率高(编解码),也支持跨语言,同时Google也提供了完整的工具链(如将.proto格式的文件编译成目标语言如Swift文件),特别适合证券类高频率、大数据量的场景。

3、安全性

// TODO: –

三、主要的业务逻辑

1、与行情服务器建立WebSocket连接

WebSocket 是一个网络通信协议,提供了全双工(Full-duplex)通讯机制。在 WebSocket API 中,客户端(可以是web浏览器或app)和服务器只需要建立一次连接,就可以进行实时双向数据传输。这种协议的主要目标是在客户端和服务器之间实现实时通讯,同时也兼容了现有的 HTTP 协议。

建立WebSocket连接需要知道服务器ip地址端口号port,其URIws://wss://开头,如:"ws://172.16.10.207:11516",其中wss://多了一层security layer实现加密

通常我们会将行情服务器信息ServerInfo保存到本地,并提供更新接口。这样可以加速与行情服务器建立WebSocket连接的过程。如果本地未读取到则需要调用接口获取行情服务器信息,再建立连接、同时保存到本地

2、数据的发送与接收

消息的格式: 容器 消息类型 + 消息体,

数据的发送

数据接收后的解析,传递给业务层

3、登录

检查登录状态

检查refreshtoken是否过期

刷新refreshtoken

单点登录

多点登录

4、在线状态

心跳

踢下线

四、启动后要做的一些事情

1、网络方面

网络状态检测 如果有网络的话,要建立websocket的连接 还要检查登录状态,refreshtoken等,同步用户数据