365bet地区构建由365bet地区后端驱动的Swift iOS客户端

365bet地区本指南演示了如何将365bet地区用作使用Swift构建的iOS应用的后端

s塞尔吉奥·德尔阿莫

365bet地区版本 3.3.0

365bet地区培训

365bet地区培训由创建并积极维护365bet地区框架的人们开发和交付

入门

在本指南中,您将构建一个365bet地区365bet地区应用它将用作公司Intranet后端。它公开了公司公告的JSON API

另外,您将建立一个iOS应用使用后端提供的JSON API的Intranet客户端

该指南探讨了不同API版本的支持

您将需要什么

要完成本指南,您将需要以下内容

  • 花些时间在你手上

  • 体面的文本编辑器或IDE

  • 安装了JDK或更高版本JAVA首页适当配置

  • 最新的稳定版本Xcode本指南是使用Xcode编写的

如何完成指南

要完成本指南,您需要从Github中签出源代码并按照指南中介绍的步骤进行操作

要开始,请执行以下操作

365bet地区遵循365bet地区部分

  • 光盘进入grails指导构建由grails后端初始驱动的ios swift客户端

365bet地区或者,如果您已经安装了365bet地区,则可以在“终端”窗口中使用以下命令创建新的应用程序

grails创建应用程序intranet后端grails应用程序配置文件rest api cd grails应用程序

365bet地区当create app命令完成时,365bet地区将使用配置为创建REST应用程序的应用程序创建grails应用程序目录,这是由于使用了参数配置文件rest api并配置为将休眠功能与H数据库一起使用

365bet地区您可以直接转到完整的365bet地区示例,如果您光盘进入grails指导构建由grails后端支持的ios swift客户端

跟随iOS部分

  • 光盘进入grails指导构建由grails后端初始swift ios提供支持的ios swift客户端

  • 前往下一部分

或者,您可以使用Xcode Studio的“新建项目向导”来创建iOS应用程序,如以下屏幕截图所示。

创建新项目
选择主要细节应用
选择迅捷
您可以直接转到iOS示例的完整版本光盘进入grails指导构建由grails后端提供动力的ios swift客户端完整的swift ios v
您可以直接转到iOS示例的完整版本光盘进入grails指导构建由grails后端提供动力的ios swift客户端完整的swift ios v

总览

下图说明了iOS应用版本的行为iOS应用由两个View Controller组成

  • 当iOS应用程序初始屏幕加载时,它会请求一个公告列表

  • 365bet地区365bet地区应用程序发送一个JSON负载,其中包括公告列表。对于每个公告,都包含唯一的标识符,标题和HTML正文

  • iOS应用程序将JSON有效负载呈现在UITableView

  • 用户点击公告的标题,应用程序会转到详细信息屏幕。初始屏幕向详细信息屏幕发送公告标识标题和HTML正文,后者将以UIWebView

概观

365bet地区编写365bet地区应用程序

365bet地区现在您可以开始编写365bet地区应用程序了

创建域类持久性实体

365bet地区我们需要创建持久性实体来存储公司公告,365bet地区通过使用来处理持久性365bet地区365bet地区域类:

365bet地区域类满足Model View Controller MVC模式中的M,并表示映射到基础数据库表上的持久实体。在365bet地区中,域是位于grails应用程序域目录中的类。

365bet地区365bet地区使用以下命令简化了域类的创建创建域类命令.

grailsw创建域类公告解决依赖关系请等待成功配置总时间secs创建grails应用grails公司intranet公告groovy创建src test groovy grails公司intranet AnnouncementSpec groovy

为了简单起见,我们假设公司公告中仅包含一个标题还有一个HTML主体我们将修改在上一步中生成的域类以存储该信息

365bet地区应用程序域Intranet后端公告groovy
内部网后端 公告 {

    标题身体静态的约束标题尺寸: 0..255身体可为空: 
    }

    静态的映射体类型: '文本'  (1)
    }
}
1 它使我们能够在字符串中存储多个字符身体.

域类单元测试

365bet地区365bet地区使测试更加容易从低级单元测试到高级功能测试

我们将测试在Announcement域中定义的约束类的constraints属性,尤其是标题属性和正文属性的可空性和长度

src测试groovy内部网后端公告Spec groovy
内部网后端 免费测试gorm DomainUnitTest
 spock lang规格

 公告规格 延伸规格实施DomainUnitTest {

    虚空 "测试体可以为空"() {
        期望:
        公告身体: 空值验证'身体'])
    }

    虚空 "测试标题不能为空"() {
        期望:
        !公告标题: 空值验证'标题'])
    }

    虚空 "测试体可以有多个字符"() {

        什么时候: '用于一串字符'
        海峡''
        256Times Str'一种' }

        然后: '身体验证合格'
        公告身体str验证'身体'])
    }

    虚空 "测试标题最多可包含字符"() {

        什么时候: '用于一串字符'
        海峡''
        256Times Str'一种' }

        然后: '标题验证失败'
        !公告标题str验证'标题'])

        什么时候: '用于一串字符'海峡''
        255Times Str'一种' }

        然后: '标题验证通行证'
        公告标题str验证'标题'])
    }
}

我们可以运行所有测试,包括我们刚刚使用命令test应用创建的测试

grailsw test app解析依赖项,请等待成功配置总时间secs完成compileJava UP截止日期完成compileGroovy完成buildProperties完成processResources完整类完成compileTestJava UP截止日期完整compileTestGroovy完成processTestResources截止日期Date testGroy完成测试完成compileIntegrationTest DATE完成processIntegrationTestResources UP截止日期DATE完全IntegrationTestClasses UP截止日期DATE完全IntegrationTest UP截止日期DATE完全mergeTestReports建立成功的测试通过

版本控制

从一开始就考虑API版本控制非常重要,尤其是当您创建手机应用程序使用的API时用户将运行该应用程序的不同版本,并且需要对API进行版本控制以创建高级功能,但仍要支持旧版本

365bet地区365bet地区允许多种方式版本REST资源.

  • 使用URI

  • 使用接受版本标题

  • 使用超媒体Mime类型

在本指南中,我们将使用“接受版本”标头对API进行版本控制

运行版本的设备将调用公告要求通过接受版本HTTP头

curl i H接受版本X GET http localhost公告

运行版本的设备将调用公告指定在接受版本标头中传递

curl i H接受版本X GET http localhost公告

创建一个控制器

我们创建一个控制者为了域类我们之前创建的Controller扩展了RestfulController这将为我们提供RESTful功能,以列出创建更新和删除的内容公告使用不同HTTP方法的资源

365bet地区应用程序控制器Intranet后端v AnnouncementController groovy
内网后端v 365bet地区 Rest RestfulController
 Intranet后端公告

 公告控制器 延伸RestfulController {
    静态的命名空间'' (1)
    静态的responseFormats'json'] (2)公告控制器公告
1 该控制器将处理我们的api的v
2 我们只想回应JSON有效负载

网址映射

我们希望端点监听公告,而不是公告。此外,我们希望为先前的控制器声明一个v的命名空间,以将Accept Version Http Header设置为来处理请求。

365bet地区365bet地区使功能强大URL映射进行配置,将下一行添加到映射闭包中

365bet地区应用程序控制器Intranet后端UrlMappings groovy
得到"公告"(:'1.0', 控制者: '公告', 命名空间:'')

加载测试数据

当应用程序启动时,我们将用几条公告填充数据库

为了做到这一点,我们编辑grails应用程序初始化grails公司Intranet BootStrap常规.

365bet地区公司内部网 引导带 {

    定义初始化servletContext公告每个保存定义破坏静态的 清单公告公告标题: '365bet地区365bet地区 Quickcast 365bet地区拦截器'),
                公告标题: '365bet地区365bet地区 Quickcast JSON视图')
        ]
    }
}
上一个代码片段中的公告不包含身体保持代码样本较小的内容365bet地区应用程序初始化Intranet后端BootStrap常规查看完整的代码

功能测试

功能测试涉及对正在运行的应用程序发出HTTP请求并验证结果行为

我们使用365bet地区Rest Client Builder 365bet地区插件当我们使用休息api轮廓

家庭travis构建grails指南构建由grails后端驱动的ios swift客户端完成

src集成测试groovy内部网后端AnnouncementControllerSpec groovy
内部网后端 grails插件rest client RestBuilder
 grails测试mixin集成集成
 org springframework bean工厂注释值
 spock lang规格
 javax servlet http HttpServletResponse

积分
 公告控制器规格 延伸规格定义 "测试体存在于Api的json公告有效载荷中"() {
        给定RestBuilder的休息RestBuilder的什么时候: '要求发布版本'
        定义休息休息"HTTP本地主机${服务器端口}公告") { (1)标头"接受版本", "1.0") (2)
        }

        然后: '请求成功'响应状态HttpServletResponse SC OK(3)

        : '响应是JSON有效负载'resp标头获取'内容类型') == ['应用程序json字符集UTF']

        : 'json有效内容包含ID为title和body的通告数组'分别回复json断言 ID断言 标题断言 身体(4)
        }
    }

    定义 "测试体不存在于Api的json公告有效载荷中"() {
        给定RestBuilder的休息RestBuilder的
1 365bet地区serverPort属性是自动注入的,它包含功能测试期间运行365bet地区应用程序的随机端口
2 将api版本作为Http头传递
3 验证响应代码是否正确
4 正文存在于JSON有效负载中

365bet地区365bet地区命令测试应用运行单元集成和功能测试

运行应用程序

要运行该应用程序,请使用gradlew bootRun命令将在端口上启动应用程序

编写iOS应用程序

获取公告

365bet地区下图说明了365bet地区应用程序公开的通知的获取和呈现中涉及的类

ios公告概述

模型

服务器发送的公告将被渲染为一个对象

完整的Swift iOS v IntranetClient公告h
类公告let primaryKey Int let标题字符串var body字符串init primaryKey Int标题字符串self primaryKey primaryKey self标题title

杰森到模型

要从数据(例如网络请求)构建模型对象,我们使用构建器类

完整的Swift v IntranetClient AnnouncementBuilder h
导入Foundation类AnnouncementBuilder func NoticesFromJSON数据Data Announcement让json尝试使用带有数据选项的JSONSerialization jsonObject var公告如果让title dict标题为String,让primaryKey dict id为Int let公告Announcement primaryKey primaryKey标题标题,如果让body dict body为String公告,body公告附加公告返回公告

联网代码

我们用NSURLSession365bet地区连接到365bet地区 API中设置了几个常量365bet地区365bet地区Fetcher

365bet地区完整的Swift ios v IntranetClient 365bet地区Fetcher swift
365bet地区struct 365bet地区Api静态让serverUrl http(1)struct Announcements静态let Path公告(2)静态让版本(3)
}
1 365bet地区365bet地区应用服务器网址
2 365bet地区我们在365bet地区应用中配置的路径UrlMappings常规
3 API版本
您可能需要更改IP地址以匹配本地计算机
完整的Swift ios v IntranetClient公告Fetcher swift
365bet地区导入Foundation类AnnouncementsFetcher NSObject URLSessionDelegate弱var委托AnnouncementsFetcherDelegate让构建器AnnouncementBuilder func fetchAnnouncements允许url URL字符串365bet地区Api serverUrl 365bet地区Api Announcements路径let req NSMutableURLRequest url URL req setValue 365bet地区Api版本用于HTTPHeaderField接受版本(1)let config URLSessionConfiguration默认为let会话URLSession配置config委托自己的self委托队列OperationQueue main let任务会话dataTask带有req的URL请求数据响应错误,如果错误为nil公告任务简历
1 我们设置接受版本每个请求的Http标头

一旦获得公告列表,我们就将响应传达给实现委托的类

完整的Swift ios v IntranetClient AnnouncementsFetcherDelegate swift
协议公告FetcherDelegate类功能​​公告获取失败的功能公告获取公告公告

MasterViewController实现fetcher委托协议,从而接收通知

完整的Swift ios v IntranetClient MasterViewController swift
导入UIKit类MasterViewController UITableViewController AnnouncementsFetcherDelegate var detailViewController DetailViewController nil var个对象任何var tableViewDataSource AnnouncementsTableViewDataSource var tableViewDelegate AnnouncementsTableViewDelegate允许fetcher AnnouncementsFetcher MARK LifeCycle重写func viewDidLoad SuperviewDidLoad self self splitViewController isCollapsed super viewWillAppear动画的self registerNotifications self fetchAnnouncements(1)覆盖func viewWillDisappear动画Bool super viewWillDisappear动画self unregisterNotifications MARK通知func registerNotifications NotificationCenter默认addObserver自我选择器选择器self notice通知对象为Announcement self performSegue withIdentifier showDetail发送者公告func fetchAnnouncements self setNetworkActivityIndi​​cator true self self fetcher fetchAnnounc ements func setNetworkActivityIndi​​cator可见的Bool UIApplication共享的isNetworkActivityIndi​​catorVisible可见的MARK Segues覆盖func为segue做准备UIStoryboardSegue发送者如果segue标识符showDetail让控制器segue目的地作为UINavigationController topViewController作为DetailViewController,如果让公告发送者作为Announcement Controller公告控制器导航项目leftBarButtonItems split真正的MARK公告Fetcher代理功能公告Fetching失败的自设置NetworkActivityIndi​​cator错误的功能公告获取的公告公告(2)self setNetworkActivityIndi​​cator false self tableViewDataSource公告公告self tableViewDelegate公告公告self tableView reloadData
1 触发公告获取
2 收到公告列表后刷新UI

MasterViewController设置其UITableView的数据源并委托给下一个类

完整的Swift iOS和IntranetClient AnnouncementsTableViewDataSource swift
导入UIKit类AnnouncementsTableViewDataSource NSObject UITableViewDataSource var公告
完整的Swift ios v IntranetClient AnnouncementsTableViewDelegate swift
导入UIKit类AnnouncementsTableViewDelegate NSObject UITableViewDelegate var公告公告func tableView tableView UITableView didSelectRowAt indexPath IndexPath让公告公告indexPath行NotificationCenter默认帖子名称通知名称AnnouncementTappedNotification对象公告(1)
    }

}
1 当用户点击公告时,通知被举起MasterViewController并启动segue到DetailViewController

详细视图控制器

当用户点击公告时,通知已发布,其中包含已窃听的公告prepareForSegue发送MasterViewController我们设置的公告属性DetailViewController

完整的Swift ios v IntranetClient MasterViewController swift
重写func为segue UIStoryboardSegue发送者做准备如果segue标识符showDetail让控制器segue的目的地作为UINavigationController topViewController作为DetailViewController,如果让公告发送者作为Announcement控制器公告公告Controller NavigationItem leftBarButtonItem self splitViewController displayModeButtonItem控制器navigationItem leftItemsSupplementBackButton true
跟随

为了呈现公告,我们使用UILabel和UIWebView,它们将连接到StoryBoard中的IBOutlet,如下所示

连接细节iboutlet

这是完整的DetailViewController代码不涉及网络代码

完整的Swift ios v IntranetClient DetailViewController swift
导入UIKit类DetailViewController UIViewController UIWebViewDelegate IBOutlet弱var titleLabel UILabel IBOutlet弱var webView UIWebView IBOutlet弱activity activityIndi​​catorView UIActivityIndi​​catorView func configureView如果让公告自我公告,则使详细信息的用户界面更新;如果让标签自我title,则使标签更新文本界面let主体公告主体self self hideActivityIndi​​cator webView loadHTMLString body baseURL nil否则self hideActivityIndi​​cator否则self hideActivityIndi​​cator函数func hideActivityIndi​​cator如果让activityIndi​​catorView自身activityIndi​​catorView activityIndi​​catorView stopAnimating覆盖f unc viewDidLoad super viewDidLoad通常在从笔尖加载视图后再执行任何其他设置self configureView varannouncement didSet更新视图self configureView MARK UIWebViewDelegate func webViewDidFinishLoad webView UIWebView self hideActivityIndi​​cator func webView webView UIWebView didFailLoadWithError错误错误self selfActivityIndi​​cator

API版本

API的第一个版本的问题是,我们在用于显示列表的有效载荷中包含了每个公告主体。公告主体可以是很大的HTML块,用户可能只想检查几个公告即可。如果不在初始请求中发送公告正文,则可以增加带宽并提高应用程序的速度。相反,我们将要求API提供完整的公告,包括身体一旦用户点击公告

版本概述

365bet地区365bet地区 V的变化

我们将使用创建一个新的Controller来处理API的版本。我们将使用带有投影的Criteria查询来仅获取公告的ID和标题

365bet地区应用程序控制器Intranet后端v AnnouncementController groovy
内网后端v 365bet地区 Rest RestfulController
 Intranet后端公告

 公告控制器 延伸RestfulController {
    静态的命名空间''
    静态的responseFormats'json']

    定义NoticeService公告控制器公告定义 指数(整数最大参数最大数学最小最大10, 100)
        定义公告公告announceService findAllIdAndTitleProjections参数响应公告模型: [("${resourceName}计数"toString countResources

我们将查询封装在服务中

365bet地区应用程序服务Intranet后端AnnouncementService groovy
内部网后端 交易事务

交易性只读)
 公告服务 {

    清单<findAllIdAndTitleProjections参数定义c公告createCriteria定义公告c列表参数投影属性'ID'属性'标题'收藏ID: [0], 标题: [1]] }  清单<>
    }
}

我们测试一下

src测试groovy内部网后端AnnouncementServiceSpec groovy
内部网后端 grails测试休眠HibernateSpec
 免费测试服务ServiceUnitTest

 公告服务规格 延伸休眠规范实施ServiceUnitTest {

    定义 "带有投影的测试标准查询返回地图列表"() {

        什么时候: '保存一些公告'
        [公告标题: '365bet地区 Quickcast 365bet地区拦截器'),
        公告标题: '365bet地区 Quickcast JSON视图'),
        公告标题: '365bet地区365bet地区 Quickcast多项目构建'),
        公告标题: '365bet地区365bet地区 Quickcast角形脚手架'),
        公告标题: '365bet地区在365bet地区中检索运行时配置值'),
        公告标题: '365bet地区使用IntelliJ IDEA开发365bet地区应用程序'保存然后: '公告已保存'公告数6

        什么时候: '获取投影'
        定义响应服务findAllIdAndTitleProjections然后: '响应中有六张地图'分别大小6

        : '地图仅包含ID和标题'分别键集'标题', 'ID']  <>
         }

        : '非空值'分别断言 标题断言 ID

网址映射

我们需要映射版本2.0接受标头到名称空间

365bet地区应用程序控制器Intranet后端UrlMappings groovy
得到"公告"(:'2.0', 控制者: '公告', 命名空间:''得到"公告$ID(.$格式)?"(:'2.0', 控制者: '公告', 行动: '', 命名空间:'')

api功能测试

我们要测试Api版本在收到GET请求时不包含body属性公告端点下一个功能测试将验证该行为

家庭travis构建grails指南构建由grails后端驱动的ios swift客户端完成

src集成测试groovy内部网后端AnnouncementControllerSpec groovy
内部网后端 grails插件rest client RestBuilder
 grails测试mixin集成集成
 org springframework bean工厂注释值
 spock lang规格
 javax servlet http HttpServletResponse

积分
 公告控制器规格 延伸规格定义 "测试体存在于Api的json公告有效载荷中"() {
        给定RestBuilder的休息RestBuilder的什么时候: '要求发布版本'
        定义休息休息"HTTP本地主机${服务器端口}公告"标头"接受版本", "2.0")
        }

        然后: '请求成功'响应状态HttpServletResponse SC OK: '响应是JSON有效负载'resp标头获取'内容类型') == ['应用程序json字符集UTF']

        : 'json有效内容包含ID为title的通告数组'分别回复json断言 ID断言 标题断言 !身体(2)
        }
    }

    定义 "公告的测试详细信息包含版本和正文"() {
        给定:
1 serverPort属性是自动注入的,它包含功能测试期间运行365bet地区应用程序的随机端口
2 正文不在JSON有效负载中

365bet地区命令测试应用运行单元集成和功能测试

iOS V变更

首先,我们需要更改在中定义的Api版本常量365bet地区365bet地区Fetcher h

365bet地区完整Swift ios v IntranetClient 365bet地区Api swift
365bet地区struct 365bet地区Api静态let serverUrl http静态let版本(1)struct Announcements静态let Path公告
1 使用Api版本

在版本中,Api不会返回公告的主体,而不是设置公告属性,我们将只在资源标识符主键中设置DetailViewController我们已经改变了prepareForSegue发送方法中MasterViewController如下图所示

完整的Swift ios v IntranetClient MasterViewController swift
重写func为segue准备UIStoryboardSegue发送方如果segue标识符showDetail让控制器segue的目的地作为UINavigationController topViewController作为DetailViewController,如果让公告发送方作为Announcement控制器announcementPrimaryKey公告primaryKey controller navigationItem leftBarButtonItem self splitViewController displayModeButtonItem控制器navigationItem leftItemsSupplementBackButton true
1 代替设置对象,我们设置一个整数

DetailViewController要求服务器提供完整的公告正文

完整的Swift ios v IntranetClient DetailViewController swift
导入UIKit类DetailViewController UIViewController UIWebViewDelegate AnnouncementFetcherDelegate IBOutlet弱变量titleLabel UILabel IBOutlet弱WebView UIWebView IBOutlet弱var activityIndi​​catorView UIActivityIndi​​catorView let fetcher AnnouncementFetcher MARK LifeCycle覆盖func viewView自定义f公告自我公告,如果让标签为自己的标题标签标签文本公告标题,如果为让webView自身,webView,让正文公告的主体,自己的hideActivityIndi​​cator webView loadHTMLString body baseURL,否则为self hideActivityIndi​​cator var anno uncement公告didSet自configureView VAR announcementPrimaryKey诠释didSet自showActivityIndi​​cator自提取程序fetchAnnouncement自announcementPrimaryKey MARK私有方法FUNC showActivityIndi​​cator要是让activityIndi​​catorView自activityIndi​​catorView activityIndi​​catorView startAnimating FUNC hideActivityIndi​​cator要是让activityIndi​​catorView自activityIndi​​catorView activityIndi​​catorView stopAnimating MARK UIWebViewDelegate FUNC webViewDidFinishLoad web视图的UIWebView自hideActivityIndi​​cator FUNC webView的web视图的UIWebView didFailLoadWithError错误错误错误self hideActivityIndi​​cator标记AnnouncementFetcherDelegate函数funcannouncementFetchingFailed func公告获取的公告公告自我公告公告

它使用了新的提取程序

完整的Swift ios v IntranetClient AnnouncementFetcher swift
365bet地区导入基础类AnnouncementFetcher NSObject URLSessionDelegate弱var委托AnnouncementFetcherDelegate让构建器AnnouncementBuilder func fetchAnnouncement primaryKey Int允许URL URL字符串365bet地区Api serverUrl 365bet地区Api Announcements Path primaryKey let req NSMutableURLRequest URL req setValue 365bet地区Api VersionHTTP(1)let config URLSessionConfiguration默认为let会话URLSession配置config委托自己的委托类委托队列OperationQueue main let任务会话dataTask带有req的URL请求数据响应错误,如果错误为nil如果让委托人自行委托代理人announcementFetchingFailed任务恢复,则获取其他通知

我们向构建器添加了一种将网络数据转换为单个Announcement对象的方法

完整的Swift iOS v IntranetClient AnnouncementBuilder m
func公告来自JSON数据数据公告,如果让dict json作为字典,则让json尝试使用带有数据选项的JSONSerialization jsonObject如果让title dict标题为String,而primaryKey dict id为Int,则使公告公告Announcement primaryKey primaryKey标题标题,如果让body dict体为String公告主体,则返回公告返回nil

还有一个委托协议,用于指示是否已获取公告

完整的Swift ios v IntranetClient AnnouncementFetcherDelegate swift
协议AnnouncementFetcherDelegate类func公告FetchingFunc公告Fetched公告Announcement

结论

365bet地区借助365bet地区轻松的API版本控制,我们现在可以支持两个运行不同版本的iOS应用程序

您需要365bet地区帮助吗

365bet地区服务:

免费咨询

OCI 365bet地区团队包括365bet地区联合创始人检查我们的并向发展和维护365bet地区的工程师学习

Grails OCI团队