IOS Apps 开发(Swift)(7)——Define Your Data Model

来源:互联网 发布:网站域名到期怎么回事 编辑:程序博客网 时间:2024/05/19 10:41

前言:网上一直没有找到用Swift开发IOS的好的教程,所以找了官网的文档翻译一下算了。如有错误欢迎指正。博主首发CSDN,mcf171专栏。

博客链接:mcf171的博客

原文链接:Define Your Data Model

——————————————————————————————

在本次课程中,我们将定义和测试 FoodTracker 的数据模型。一个数据模型是用来展现anpp的信息结构。

学习目标

在本次课程中,你可以了解到:

  • 创建一个数据模型
  • 在自定义类中创建一个可失败的初始化器
  • 理解可失败和不可失败的初始化器
  • 通过运行单元测试来检测数据模型

创建数据模型

现在我们要创建一个数据模型来存储我们的菜品场景信息。因此我们需要定义一个简单的类,包括名字、照片和打分

创建一个新的数据模型类

1、选择 File > New > File (or press Command-N).

2、在左边的对话框选择iOS下的Source 

3、选择Swift文件,点击下一步

我们之前创建RatingControl类的时候是使用不同的步骤(iOS > Source > Cocoa Touch Class),这是因为现在我们是创建一个基类,不需要从其他类继承。

4、输入名字Meal

5、选择保存位置同时勾选测试


6、点击Create

Xcode将创建一个Meal.swift的文件

在Swift中,我们能通过字符串来保存名字,使用UIImage保存图片,使用Int保存打分。因为图片不一定有,所以我们对于UIImage使用Optional类型

定义菜品的数据类型

1、返回标准编辑框



2、打开Meal.swift

3、修改import

import UIKit

默认情况下Swift文件引入的是 Foundation 框架,因此我们可以使用 Foundation 数据结构。 因为我们接下来是会用来自UIKit 框架的类,所以我们需要将UIKit引入到我们的代码中。同时引入 UIKit 也允许我们访问Foundation中的数据,所以我们可以所见为一行代码。

4、在import语句下写如下代码

class Meal {    // MARK: Properties        var name: String    var photo: UIImage?    var rating: Int}

代码定义了基本的属性值。

5、在上述代码下,添加初始化器

// MARK: Initialization init(name: String, photo: UIImage?, rating: Int) {}

初始化器是在一个类进行实例化的时候调用的方法。

6、在初始化器中加入以下代码

// Initialize stored properties.self.name = nameself.photo = photoself.rating = rating

如果没有使用正确的值来创建Meal会发生什么呢?比如说一个空的名字或者一个负数分数。我们需要返回一个nil来表明这个对象不能被创建。并且设置默认值。

7、在初始化器的最后我们添加 if 语句来检测是否合法

// Initialization should fail if there is no name or if the rating is negative.if name.isEmpty || rating < 0 {    return nil}

因为初始化器现在可能返回 nil ,所以我们需要表明在初始化器表明

8、点击错误提示,选择 fix-it来修补错误


init?(name: String, photo: UIImage?, rating: Int) {

可失败的初始化器的声明形式就是如上所示,也就是意味初始化器可能返回空值。

到目前为止,我们的初始化器应该如下:

// MARK: Initialization init?(name: String, photo: UIImage?, rating: Int) {    // Initialize stored properties.    self.name = name    self.photo = photo    self.rating = rating        // Initialization should fail if there is no name or if the rating is negative.    if name.isEmpty || rating < 0 {        return nil    }}

里程碑:选择Product > Build (or pressing Command-B)编译工程。虽然我们还没有使用这个类,但是可以通过编译来检测是否我们有语法上的错误。

测试数据

尽管我们的数据模型已经建立了,但是我们还没有将这个数据模型整合到我们的App中。因此很难说我们就百分百的正确完成了这个数据模型。我们可能会在运行时碰到一些我们没有考虑到的边界情况。

查看FoodTracker的单元测试文件

1、打开 FoodTrackerTests文件夹。展开它


2、打开FoodTrackerTest.swift


花几分钟理解一下下述代码

import UIKitimport XCTest class FoodTrackerTests: XCTestCase {        override func setUp() {        super.setUp()        // Put setup code here. This method is called before the invocation of each test method in the class.    }        override func tearDown() {        // Put teardown code here. This method is called after the invocation of each test method in the class.        super.tearDown()    }        func testExample() {        // This is an example of a functional test case.        XCTAssert(true, "Pass")    }        func testPerformanceExample() {        // This is an example of a performance test case.        self.measureBlock() {            // Put the code you want to measure the time of here.        }    }    }

XCTest 框架是 Xcode的测试框架。单元测试本身也是一个类。FoodTrackerTests继承了XCTestCase。注释解释了了setUp和tearDown方法的用处


我们要写的主要代码是功能测试——检测是否能产生我们期望的值、和性能测试——我们的代码能否像我们期望的那样运行。因为我们没有写过任何性能密集型的代码,所以我们只需要测试功能性。

针对菜品对象初始化器写单元测试

1、在FoodTrackerTests.swift中删除测试模板

import UIKitimport XCTest class FoodTrackerTests: XCTestCase {    }

在本次课程中我们不需要任何模板

2、在最后一个花括号增加如下注释

// MARK: FoodTracker Tests

3、在注视下增加单元测试方法

// Tests to confirm that the Meal initializer returns when no name or a negative rating is provided.func testMealInitialization() {}

4、首先增加一个能通过的测试用例。

// Success case.let potentialItem = Meal(name: "Newest meal", photo: nil, rating: 5)XCTAssertNotNil(potentialItem)

XCTAssertNotNil用来测试Meal对象是否在初始化后为nil,也就是意味着初始化器成功的创建了一个Meal对象。

5、现在增加一个失败的测试用例

// Failure cases.let noName = Meal(name: "", photo: nil, rating: 0)XCTAssertNil(noName, "Empty name is invalid")

XCTAssertNIl 则断言一个对象是nil。在这个测试用例中对象是nil因为我们的没有输出名字。

6、现在增加一个测试用例,这个测试用里会返回一个nil的对象,但是我们断言对象不是nil

let badRating = Meal(name: "Really bad rating", photo: nil, rating: -1)XCTAssertNotNil(badRating)

最终我们的testMealInitialization方法为:

// Tests to confirm that the Meal initializer returns when no name or a negative rating is provided.func testMealInitialization() {    // Success case.    let potentialItem = Meal(name: "Newest meal", photo: nil, rating: 5)    XCTAssertNotNil(potentialItem)        // Failure cases.    let noName = Meal(name: "", photo: nil, rating: 0)    XCTAssertNil(noName, "Empty name is invalid")        let badRating = Meal(name: "Really bad rating", photo: nil, rating: -1)    XCTAssertNotNil(badRating)}

我们可以通过Command + U 来运行我们的单元测试。或者我们可以运行一个单独的测试。最后一个测试用例会报错,因为我们知道他是nil但是断言为非nil

运行 testMealInitialzation单元测试

1、打开FoodTrackerTests.swift 找到testMealInitialization

2、在测试方法的左边找到一个菱形


3、将我们的鼠标移动到菱形块上,从而变成一个小的运行按钮


4、点击这个按钮

里程碑:当app运行单元测试的时候,前面两个测试用例会通过,最后一个会失败


正如我们看到的,单元测试可以帮助我们捕捉代码的错误地方,如果我们期望在最后的测试用例中Meal对象不是nil,那么我们能够在测试中捕捉错误。

修复测试用例

1、找到FoodTrackerTests.swift文件中的testMealInitialization方法

2、将最后一行改为

XCTAssertNil(badRating, "Negative ratings are invalid, be positive")
testMealInitialization方法最后为:

// Tests to confirm that the Meal initializer returns when no name or a negative rating is provided.func testMealInitialization() {    // Success case.    let potentialItem = Meal(name: "Newest meal", photo: nil, rating: 5)    XCTAssertNotNil(potentialItem)        // Failure cases.    let noName = Meal(name: "", photo: nil, rating: 0)    XCTAssertNil(noName, "Empty name is invalid")        let badRating = Meal(name: "Really bad rating", photo: nil, rating: -1)    XCTAssertNil(badRating, "Negative ratings are invalid, be positive")}
里程碑:再次运行测试用例的时候,所有测试用例都通过了


测试用例是我们代码中必要的一部分,因为他可以帮厨我们捕捉到我们可能忽视的错误。每个测试都需要检测特殊的,基本的行为。付过我们写的测试用例是非常长和复杂的,那么非常难追踪到发生错误的准确位置

ps:本课程工程文件Download File












0 0
原创粉丝点击