为iOS应用程序处理多个数据源

在移动应用程序上管理多个数据源的一种优雅方法是使用存储库模式。

构建基于iOS客户端-服务器的应用程序很容易。 您可以在3步中完成。 (1)只需将Alamofire pod倾斜度插入到您的项目中,然后(2)从UIViewController调用API,然后(3)在View上显示其结果。 例如,您有一个api端点来请求Todos,比如说www.localhost/api/todos ,它将返回一个JSON对象。

  { 
“待办事项”:[
{
“ is_completed”:“ 0”,
“ tempId”:“ 710BBEC4-8A7B-486F-9329-412DFD5FB738”,
“ title”:“编码”,
“ id”:“ SJcbh6mtb”
},
{
“ is_completed”:“ 0”,
“ tempId”:“ FAF6D1D3-A8B6-4E46-B25F-484B783EDD99”,
“ title”:“ goto futsal”,
“ id”:“ SyXOu_utb”
}
]
}

现在从UIViewController一侧,导入Alamofire倾斜度,然后在View完全加载后使用Alamofire.request()进行调用。

 进口Alamofire 
 类TodosViewController:UIViewController { 
func viewDidLoad(){
Alamofire.request(URL,method:.get).responseJSON {
如果response.result.isSuccess {
var todos:[Todo] = []
让数据= JSON(rawValue:response.value!)
data?.forEach({(index,obj)in
让id = obj [“ id”]。stringValue
让tempId = obj [“ tempId”]。stringValue
让title = obj [“ title”]。stringValue
let isCompleted = obj [“ is_completed”]。stringValue ==“ 1”
todos.append(Todo(id:id,title:标题,isCompleted:isCompleted,tempId:tempId))
})
displayToTableView(待办事项)
}其他{
displayTheErrorPopup(message:“ failed”)
}
}
}
}

完成了 现在您可以使用该应用程序了。

当然,要求并非如此简单。 也许您拥有登录页面或显示待办事项详细信息的页面以及更多功能,因此您必须从一个页面移至其他页面。 您是否要让用户在打开特定页面时始终向服务器发出请求? 某些用户可能因为等待而又不喜欢它。

解决方案是在内存中缓存。 您可以将todos项目保留在变量中,并且在您的应用程序运行期间将可用。 即使用户频繁切换许多页面,它也可以通过首先显示缓存数据中的待办事项来显着提高性能。 不要忘记放置刷新按钮或“拉到刷新”功能来发出新的服务器请求。

低效的请求。

让我们更详细地讨论它。 由于您的数据很少更改,因此实际上在用户打开应用程序时无需向服务器发出请求。 数据集可以临时存储在本地存储中,您可以通过建立同步机制对其进行验证。

低效率的请求问题可以通过结合使用缓存的数据, 本地存储远程存储来解决。 但是下一个挑战是如何在不违反SOLID原则的情况下管理三个数据源。 使用扎实的原则来衡量我们的代码基础(无论是否模块化)是有益的。 在Qiscus办公室,工程师经常讨论和分享与体系结构和代码优化有关的内容。 我们正在努力快速交付高质量的产品。

您的UIViewController(视图)不需要知道详细信息。

视图部分的主要职责是管理视图自身的状态。 例如,当视图已经完成加载时将发生什么,或者当视图出现/消失时将发生什么。 它不需要知道向服务器发出请求的过程。 它不应该依赖Alamofire。 因此,我们需要更改ViewController并将其过程包装到新类中。

 类TodosViewController:UIViewController { 
class viewDidLoad {
让dataManager = DataManager()
让项目:[Todo] = dataManager.getTodos()
displayToTableView(items)
}
}

数据管理器是一个封装类,它决定何时使用缓存的数据或从服务器获取数据。 在下一个讨论中,我将使用CacheRepository术语表示缓存的数据,并使用RemoteRepository术语表示API请求。 两者都应具有相同的方法,例如使用getTodos()获取所有集合项,使用getTodo()获取单个项,使用updateTodo()更改特定数据以及使用addTodo()插入新项。 不幸的是,两者都有完全不同的逻辑实现,因此我们必须分别进行设置(使其与单一职责原则匹配)。

入门:使用协议进行抽象

“软件实体(类,模块,功能等)应为扩展而开放,但为修改而应封闭。” — 开放封闭原则

如果要创建易于维护的类,则必须具有两个重要特征:

  • 可以扩展 :您应该能够轻松地扩展或更改类的行为。
  • 封闭修改 :您必须扩展一个类而不更改实现。

通过抽象,您可以实现这些特性。

让我们开始为存储库域创建合同。 它具有四个主要功能: getTodos()用于获取所有待办事项), getTodo()用于获取单个待办事项), addTodo()用于放置新的Todo项)和updateTodo()用于使用新值替换待办事项updateTodo() 。 我们还需要创建一个CachedData抽象来将待办事项的集合保存在内存中。

 协议存储库{ 
func getTodos()-> [Todo]
func getTodo(id:Int)->待办事项
func addTodo(todo:Todo)-> Todo
func updateTodo(todo:Todo)-> Todo
}
协议CachedData {
var todosInCache:[Todo] {获取设置}
}
  // ...这是Todo类 
Todo类{
var tempId:字符串
var id:Int
var标题:字符串
var isCompleted:布尔

init(id:Int,title:String,isCompleted:Bool){
self.id = id
self.title =标题
self.isCompleted = isCompleted
self.tempId = id == -1吗? UUID():无
}
}

当我们创建实现类时,抽象将非常有用。

 类RemoteRepository:存储库{ 
私人var todos:[Todo] = []
func getTodos()-> [Todo] {
//将逻辑发送到远程服务器
}
func getTodo(id:Int)-> Todo {}
func addTodo(todo:Todo)-> Todo {}
func updateTodo(todo:Todo)-> Todo {}
}
  CacheRepository类:存储库,CachedData { 
var todosInCache:[Todo] = []
func getTodos()-> [Todo] {}
func getTodo(id:Int)-> Todo {}
func addTodo(todo:Todo)-> Todo {}
func updateTodo(todo:Todo)-> Todo {}
}

上面的存储库与“单一责任原则”相匹配,它们之间的实现方式有所不同。 这意味着无论何时更改RemoteRepository内部的实现,都不会对CacheRepository产生CacheRepositoryRemoteRepository不知道CacheRepository ,反之亦然。

让我们再创建一个类来封装逻辑,无论待办事项是从缓存内存还是从远程服务器获取。 为了可用,每个存储库都必须在init()内部进行初始化。

  DataManager类{ 
var cacheRepository:Repository&CachedData
var remoteRepository:储存库

在里面(){
cacheRepository = CacheRepository()
remoteRepository = RemoteRepository()
}
}

放置getTodos()函数来触发两个存储库执行其任务。 请记住,仅在此处提供用于确定应使用哪个存储库的逻辑。

  DataManager类{ 
var cacheRepository:Repository&CachedData
var remoteRepository:储存库

在里面(){
cacheRepository = CacheRepository()
remoteRepository = RemoteRepository()
}

func getTodos()-> [Todos] {
如果cacheRepository.todosInCache.isEmpty {
cacheRepository.todosInCache = remoteRepository.getTodos()
}
返回cacheRepository.todos.value
}
}

您可以通过从github下载完整的源代码,在Playground上尝试本教程。 对于真正的实现,您可以从这里检查它并从单元测试中运行它。