Created
July 9, 2019 04:27
-
-
Save WF-Chiang/fbaebc7533ae48b463941471ac7c1acf to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// SwiftAdvanceTests.swift | |
// SwiftAdvanceTests | |
// | |
// Created by wuufone on 2018/9/17. | |
// Copyright © 2018年 wuufone. All rights reserved. | |
// | |
import XCTest | |
@testable import SwiftAdvance | |
class SwiftAdvanceTests: XCTestCase { | |
func testBoundInt() { | |
XCTAssertEqual(-9223372036854775808, Int.min) | |
XCTAssertEqual(9223372036854775807, Int.max) | |
XCTAssertEqual((pow(Decimal(2), Int(63))-1 as NSDecimalNumber).intValue, Int.max) | |
} | |
func testFormat() { | |
XCTAssertEqual(10000000, 1000_0000) | |
/* 指數 (exponent, exp)。 e 表示十進位;p 表示二進位。*/ | |
XCTAssertEqual(100.1, 1.001e2) | |
XCTAssertEqual(100.1, 1001e-1) | |
XCTAssertEqual(0x4, 0x1p2) | |
} | |
func testTypeAlias() { | |
typealias Location = CGPoint | |
XCTAssertEqual(Location(), CGPoint()) | |
} | |
func testOptional() { | |
var aVariable: Any? | |
XCTAssertNil(aVariable) | |
aVariable = EMPTY | |
XCTAssertNotNil(aVariable) | |
} | |
func testOptionalBinding() { | |
class MyView: UIView { | |
var button: UIButton? | |
} | |
let myViewInstance = MyView() | |
guard let button = myViewInstance.button else { | |
XCTAssertNil(myViewInstance.button) | |
return | |
} | |
XCTAssertNil(button) | |
} | |
func testIfLetSentence() { | |
let aValue: String? = nil | |
if let anotherValue = aValue { | |
XCTFail("\(anotherValue) 應該是 nil,此行不該被執行。") | |
} | |
guard aValue == nil else { | |
XCTFail("\(String(describing: aValue)) 應該是 nil,此行不該被執行。") | |
return | |
} | |
} | |
func testFunctionWithOptionalReturn() { | |
func findSomething(name: String) -> NSObject? { | |
return nil | |
} | |
var somethingDone: Bool = false | |
if let foundThing = findSomething(name: "Stuff") { | |
print("This is a say way to use \(foundThing).") | |
somethingDone = true | |
} | |
XCTAssertFalse(somethingDone) | |
} | |
func testOptionalChaining() { | |
class Person { | |
var job: Job? | |
} | |
class Job { | |
var title: String | |
init(title: String) { | |
self.title = title | |
} | |
} | |
let employee = Person() | |
let engineer = Job(title: "engineer") | |
employee.job = engineer | |
/* not use optional chaining | |
var title: String = "" | |
if employee.job != nil { | |
title = employee.job.title | |
} | |
XCTAssertEqual("engineer", title) | |
*/ | |
// 使用 optional chaining (very gracefully) | |
if let title = employee.job?.title { | |
XCTAssertEqual("engineer", title) | |
} else { | |
XCTFail() | |
} | |
} | |
func testConvertingErrorsToOptionalValues() { | |
func someThrowingFunction() throws -> String { return "test" } | |
// 不使用 do / catch 的話: Errors thrown from here are not handled | |
do { | |
let result = try someThrowingFunction() | |
XCTAssertEqual("test", result) | |
} catch {} | |
// 使用 try? (very gracefully) | |
XCTAssertEqual("test", try? someThrowingFunction()) | |
// 在確定不會產生 Error 的情形下使用 | |
XCTAssertEqual("test", try! someThrowingFunction()) | |
} | |
func testClassPropertiesWithOptional() { | |
class SomeClass { | |
var property1: Any? | |
var proeprty2: Any? | |
} | |
} | |
func testClassHasProperties() { | |
class Book { | |
private(set) var title: String? | |
init(title: String) { | |
self.title = title | |
} | |
} | |
let aBook = Book(title: "Learning Swift") | |
XCTAssertEqual("Learning Swift", aBook.title) | |
// aBook.title = "Learning Swift V2" | |
} | |
func testComputedProperty() { | |
class Rect { | |
var width: Float | |
var height: Float | |
var area: Float { | |
return self.width * self.height | |
} | |
init (width: Float, height: Float) { | |
self.width = width | |
self.height = height | |
} | |
} | |
let aRect = Rect(width: 5, height: 2) | |
XCTAssertEqual(10, aRect.area) | |
} | |
func testClassHasPropertiesSwiftStyle() { | |
class Book { | |
var title: String? = nil | |
private var _isbn: String? = nil | |
var isbn: String? { | |
get { return _isbn } | |
set { | |
if _isbn == nil { // _isbn 只有在為 nil 時才能被變更 | |
_isbn = newValue | |
} | |
} | |
} | |
} | |
let aBook = Book() | |
aBook.title = "Learning Swift" | |
aBook.isbn = "1111111111" | |
XCTAssertEqual("1111111111", aBook.isbn) | |
aBook.isbn = "2222222222" // 試圖變更 isbn 值 | |
XCTAssertEqual("1111111111", aBook.isbn) // 斷言:isbn 不會改變 | |
} | |
func testWatchProperties() { | |
class Book { | |
var oldTitle: String? = nil // 舊標題的值 | |
private var _title: String? = nil | |
var title: String? { | |
get { return _title } | |
set { | |
oldTitle = _title | |
_title = newValue | |
} | |
} | |
} | |
let aBook = Book() | |
aBook.title = "學習 Swift 基礎" | |
XCTAssertEqual("學習 Swift 基礎", aBook.title) | |
aBook.title = "學習 Swift 進階" | |
XCTAssertEqual("學習 Swift 基礎", aBook.oldTitle) | |
XCTAssertEqual("學習 Swift 進階", aBook.title) | |
} | |
func testWatchProperitesWithObserver() { | |
class Book { | |
var oldTitle: String? = nil | |
var newTitle: String? = nil | |
private var _title: String? = nil | |
var title: String? { | |
willSet { | |
newTitle = newValue | |
} | |
didSet { | |
oldTitle = oldValue | |
} | |
} | |
} | |
let aBook = Book() | |
aBook.title = "學習 Swift 基礎" | |
XCTAssertEqual("學習 Swift 基礎", aBook.title) | |
aBook.title = "學習 Swift 進階" | |
XCTAssertEqual("學習 Swift 基礎", aBook.oldTitle) | |
XCTAssertEqual("學習 Swift 進階", aBook.title) | |
} | |
func testPropertyObservers() { | |
class WebForm { | |
var oldEmail: String? = nil | |
var newEmail: String? = nil | |
var email: String { | |
willSet { | |
self.newEmail = newValue | |
} | |
didSet { | |
self.oldEmail = oldValue | |
} | |
} | |
init(email: String) { | |
self.email = email | |
} | |
} | |
// 使用建構子設值,並不會執行 willSet 和 didSet | |
let webForm = WebForm(email: "old@abc.com") | |
XCTAssertEqual(webForm.oldEmail, nil) | |
XCTAssertEqual(webForm.newEmail, nil) | |
// 使用 email 特性的隱含 setter 時,會執行 willSet 和 didSet | |
webForm.email = "new@abc.com" | |
XCTAssertEqual(webForm.oldEmail, "old@abc.com") | |
XCTAssertEqual(webForm.newEmail, "new@abc.com") | |
} | |
func testCreateEmptyArray() { | |
let emptyArray = [String]() | |
XCTAssertTrue(type(of: emptyArray) == Array<String>.self) | |
let emptyArray2 = Array<String>() | |
XCTAssertTrue(type(of: emptyArray) == type(of: emptyArray2)) | |
} | |
func testWalkThroughArray() { | |
// devices 為陣列 | |
// iPhone 索引值為 0;iPad 為 1; iPod Touch 為 2 | |
let devices = ["iPhone", "iPad", "iPod Touch"] | |
// 使用 for-in 語法遍歷陣列中的每個元素 | |
var device: String? | |
for _device in devices { | |
device = _device | |
} | |
// 斷定: device 的值為陣列的最後一個元素 | |
XCTAssertEqual("iPod Touch", device) | |
/* One-Sided Ranges */ | |
for _device in devices[...1] { // 只遍歷索引小於等於 1 的元素 | |
device = _device | |
} | |
XCTAssertEqual("iPad", device) | |
for _device in devices[..<1] { // 只遍歷索引小於 1 的元素 | |
device = _device | |
} | |
XCTAssertEqual("iPhone", device) | |
for _device in devices[1...] { // 只遍歷索引大於 1 的元素 | |
device = _device | |
} | |
XCTAssertEqual("iPod Touch", device) | |
} | |
func testOtherArrayOperation() { | |
var someInts = [Int]() | |
XCTAssertTrue(someInts.isEmpty) // 以 isEmpty 驗證陣列為空 | |
// 陣列元素的增加 | |
someInts.append(100) | |
XCTAssertEqual(100, someInts[0]) | |
// 以單一值,填充給指定個數元素至陣列 | |
let location = Array(repeating: 0, count: 3) | |
XCTAssertEqual([0, 0, 0], location) | |
// 陣列的加法 | |
XCTAssertEqual([0, 1, 2, 3], [0, 1] + [2, 3]) | |
// XCTAssertEqual([0, 1, 2], [0, 1, 2, 3] - [3]) // 減法行不通 | |
// 陣列元素的移除 | |
someInts = [0, 1, 2, 3] | |
someInts.remove(at: 3) | |
XCTAssertEqual([0, 1, 2], someInts) | |
} | |
func testEmptySet() { | |
let emails: Set<String> = Set<String>() | |
XCTAssertTrue(emails.isEmpty) | |
} | |
func testEachOneSetElementIsUnique() { | |
var emails: Set<String> = Set<String>() | |
XCTAssertTrue(emails.isEmpty) | |
emails.insert("tom@abc.com") | |
emails.insert("mac@abc.com") | |
XCTAssertTrue(emails.count == 2) | |
// 無法加入重復的元素 | |
let (success, _) = emails.insert("mac@abc.com") | |
XCTAssertFalse(success) | |
XCTAssertTrue(emails.count == 2) | |
} | |
func testSupersetAndSubset() { | |
let emails: Set<String> = ["tom@abc.com", "mac@abc.com"] | |
let email: Set<String> = ["tom@abc.com"] | |
XCTAssertTrue(email.isSubset(of: emails)) | |
XCTAssertTrue(emails.isSuperset(of: email)) | |
} | |
func testSetOperation() { | |
let emails: Set<String> = ["tom@abc.com", "mac@abc.com"] | |
var email: Set<String> = ["tom@abc.com"] | |
// 交集 | |
XCTAssertEqual(emails.intersection(email), ["tom@abc.com"]) | |
// 差集 | |
XCTAssertEqual(emails.subtracting(email), ["mac@abc.com"]) | |
// 對稱差 | |
email.insert("kim@abc.com") | |
XCTAssertEqual(emails.symmetricDifference(email), ["mac@abc.com", "kim@abc.com"]) | |
// 聯集 | |
XCTAssertEqual(emails.union(email), ["tom@abc.com", "mac@abc.com", "kim@abc.com"]) | |
// 不交集 | |
let newEmail: Set<String> = ["new@abc.com"] | |
XCTAssertTrue(emails.isDisjoint(with: newEmail)) | |
XCTAssertTrue(newEmail.isDisjoint(with: emails)) | |
} | |
func testGenerateSetFromSet() { | |
let emails: Set<String> = ["tom@abc.com", "mac@abc.com"] | |
let moreEmails = NSMutableSet(set: emails) | |
moreEmails.add("joe@abc.com") | |
XCTAssertEqual(3, moreEmails.count) | |
XCTAssertTrue(emails.isSubset(of: moreEmails as! Set<String>)) | |
} | |
func testSortedSet() { | |
let emails: Set<String> = ["tom@abc.com", "mac@abc.com"] | |
var sortedEmails = Array<String>() | |
for e in emails.sorted() { | |
sortedEmails.append(e) | |
} | |
XCTAssertEqual("mac@abc.com", sortedEmails.first) | |
XCTAssertEqual("tom@abc.com", sortedEmails.last) | |
} | |
func testCreateEmptyDictionary() { | |
let emptyDictionary = [String:Float]() | |
XCTAssertTrue(type(of: emptyDictionary) == Dictionary<String, Float>.self) | |
let emptyDictionary2 = Dictionary<String, Float>() | |
XCTAssertTrue(type(of: emptyDictionary) == type(of: emptyDictionary2)) | |
} | |
func testSimpleDictionary() { | |
var simpleMembers: [Int: String] = [1: "John", 2: "Tom", 3: "Doris"] | |
XCTAssertTrue(simpleMembers[2] == "Tom") | |
} | |
func testComplexDictionary() { | |
var members: [[String: String]] = [ | |
["name": "John", | |
"email": "john@abc.com", | |
"id": "1"], | |
["name": "Tom", | |
"email": "tom@abc.com", | |
"id": "2"], | |
["name": "Doris", | |
"email": "doris@abc.com", | |
"id": "3"], | |
] | |
XCTAssertEqual(members[0]["email"], "john@abc.com") | |
XCTAssertEqual(members[1]["name"], "Tom") | |
XCTAssertEqual(members[2]["id"], "3") | |
} | |
func testDictionaryUpdate() { | |
var members: [[String: String]] = [ | |
["name": "John", | |
"email": "john@abc.com", | |
"id": "1"], | |
["name": "Tom", | |
"email": "tom@abc.com", | |
"id": "2"] | |
] | |
members[1].updateValue("tony@abc.com", forKey: "email") | |
members[1].updateValue("Tony", forKey: "name") | |
XCTAssertEqual(members[1]["name"], "Tony") | |
} | |
func testThroughOutDictionary() { | |
let members: [[String: String]] = [ | |
["name": "John", | |
"email": "john@abc.com", | |
"id": "1"], | |
["name": "Tom", | |
"email": "tom@abc.com", | |
"id": "2"], | |
["name": "Doris", | |
"email": "doris@abc.com", | |
"id": "3"], | |
] | |
var memberNames: Set<String> = Set<String>() | |
for member in members { | |
let memberInfo: [String: String] = member as [String: String] | |
for (key, value) in memberInfo { // 使用 Tuple + for-in 結合 | |
if key == "name" { | |
memberNames.insert(value) | |
} | |
} | |
} | |
XCTAssertEqual(memberNames, Set<String>(arrayLiteral: "John", "Tom", "Doris")) | |
} | |
func testTupleGetElementByIndex() { | |
let aTuple = (1, "someone's name", true) | |
XCTAssertEqual("someone's name", aTuple.1) | |
} | |
func testTuplePositionParameters() { | |
let (_, _, z) = (1, 2, 3) | |
XCTAssertEqual(3, z) | |
let (_, y, _) = (1, 2, 3) | |
XCTAssertEqual(2, y) | |
} | |
func testTupleHasParameterLabel() { | |
let vector3D = (x: 1, y: 2, z: 3) | |
XCTAssertEqual(2, vector3D.y) | |
} | |
func testCompareBetweenTuples() { | |
let vector3D = (x: 1, y: 2, z: 3) | |
XCTAssertEqual(2, vector3D.y) | |
let anotherVector3D = (x: 1, y:2, z: 4) | |
XCTAssertTrue(vector3D < anotherVector3D) | |
} | |
func testTupleWithSwitch() { | |
let xValue = Int(arc4random_uniform(5) + 1) | |
let yValue = Int(arc4random_uniform(5) + 1) | |
let point = (xValue, yValue) | |
var descriptionOfPoint: String = "" | |
switch point { | |
case (0, 0): | |
descriptionOfPoint = "該點為原點" | |
case (0, _): | |
descriptionOfPoint = "該點在 Y 軸上" | |
case (_, 0): | |
descriptionOfPoint = "該點在 X 軸上" | |
case let (x, y) where x == -y: // Value Bindings + where clause | |
descriptionOfPoint = "該點在第四象限上" | |
case let (x, y): // Value Bindings | |
descriptionOfPoint = "該點(\(x),\(y))不在任何軸上" | |
} | |
print(descriptionOfPoint) | |
XCTAssertEqual(descriptionOfPoint, "該點(\(xValue),\(yValue))不在任何軸上") | |
} | |
func testStructureIsImmutable() { | |
struct S { | |
var aValue: Int? | |
} | |
var s1 = S() | |
s1.aValue = 1 | |
var s2 = s1 | |
s2.aValue = 2 | |
XCTAssertEqual(1, s1.aValue) | |
} | |
func testClassIsMutable() { | |
class C { | |
var aValue: Int? | |
} | |
let c1 = C() | |
c1.aValue = 1 | |
let c2 = c1 | |
c2.aValue = 2 | |
XCTAssertEqual(2, c1.aValue) | |
} | |
func testSimpleClosure() { | |
var result: String? = nil | |
let aSimpleClosure = { () -> Void in | |
result = "A Simple Closure." | |
} | |
aSimpleClosure() | |
XCTAssertEqual("A Simple Closure.", result) | |
} | |
func testSimplestClosure() { | |
var result: String? = nil | |
let aSimplestClosure = { | |
result = "A Simplest Closure." | |
} | |
aSimplestClosure() | |
XCTAssertEqual("A Simplest Closure.", result) | |
} | |
func testClosureAsFunctionParameter() { | |
func setResult(_ aClosure: ()->Void) { | |
aClosure() | |
} | |
var result: String? = nil | |
let aSimplestClosure = { | |
result = "A Simplest Closure." | |
} | |
setResult(aSimplestClosure) | |
XCTAssertEqual("A Simplest Closure.", result) | |
} | |
func testClosureAsReturnType() { | |
func chineseToEnglish(word: String) -> String { | |
if word == "您好" { | |
return "Hello" | |
} | |
return "無法翻譯" | |
} | |
func chineseToNihongo(word: String) -> String { | |
if word == "您好" { | |
return "こんちは" | |
} | |
return "無法翻譯" | |
} | |
func getTranslator(from: String, to: String) -> ((String) -> String) { | |
if from == "中" && to == "英" { | |
return chineseToEnglish | |
} else if from == "中" && to == "日" { | |
return chineseToNihongo | |
} | |
return {(String) -> String in return "無法翻譯"} | |
} | |
let translator = getTranslator(from: "中", to: "英") | |
XCTAssertTrue(translator("您好") == "Hello") | |
XCTAssertTrue(translator("大家好") == "無法翻譯") | |
} | |
func testTrailingClosure() { | |
func executeClosure(_ closure: () -> String) -> String { | |
return closure() | |
} | |
let result = executeClosure() { | |
return "Hello" | |
} | |
XCTAssertEqual("Hello", result) | |
} | |
func testTailingClousre2() { | |
func anotherFunction(_ arg1: String, _ arg2: ()-> String) -> String{ | |
return "\(arg1), \(arg2())" | |
} | |
XCTAssertEqual ("Hello, World", anotherFunction("Hello") { return "World" }) | |
} | |
func testKeepArgs() { | |
var args: [String] = [String]() | |
func aFunction(_ arg: String) { | |
args.append(arg) | |
} | |
aFunction("Hello") | |
XCTAssertEqual(1, args.count) | |
} | |
func testKeepClosures() { | |
var closures: [()->String] = [()->String]() | |
func aFunction(_ closure: @escaping ()->String) { | |
closures.append(closure) | |
} | |
aFunction() { | |
return "Hello." | |
} | |
XCTAssertEqual("Hello.", closures.first!()) | |
} | |
func testAutoClosure() { | |
var steps = [String]() | |
func readFile(_ filename: String) -> String { | |
steps.append("==> ①") | |
return "hello" | |
} | |
func aFunctionIncludeAutoClosure(filename: String, fileContent: @autoclosure () -> String) { | |
steps.append("==> ②") | |
if filename != "" { | |
steps.append("==> ③") | |
XCTAssertEqual("hello", fileContent()) | |
} | |
} | |
steps.append("==> ④") | |
let filename = "aFile.txt" | |
aFunctionIncludeAutoClosure(filename: filename, fileContent: readFile(filename)) | |
XCTAssertEqual(steps, ["==> ④", "==> ②", "==> ③", "==> ①"]) | |
} | |
func testCommonFunction() { | |
func greet(myFriend person: String) -> String { | |
return "Hello, \(person)" | |
} | |
XCTAssertTrue(greet(myFriend: "Tom") == "Hello, Tom") | |
} | |
func testFunctionUseArgNameAsLabel() { | |
func greet(person: String) -> String { | |
return "Hello, \(person)" | |
} | |
XCTAssertTrue(greet(person: "Tom") == "Hello, Tom") | |
} | |
func testFunctionIgnoreLabel() { | |
func greet(_ person: String) -> String { | |
return "Hello, \(person)" | |
} | |
XCTAssertTrue(greet("Tom") == "Hello, Tom") | |
} | |
func testFunctionHasDefaultParameterValues() { | |
func welcome(name: String = "Guest") -> String { | |
return "Hello, \(name)." | |
} | |
XCTAssertEqual("Hello, Guest.", welcome()) | |
} | |
func testVariadicParameters() { | |
func addTotal(_ numbers: Int...) -> Int { | |
var total: Int = 0 | |
for number in numbers { | |
total += number | |
} | |
return total | |
} | |
XCTAssertEqual(10, addTotal(2, 3, 4, 1)) | |
} | |
func testInOutParameters() { | |
func swap(number1: inout Int, number2: inout Int) { | |
let tempNumber = number1 | |
number1 = number2 | |
number2 = tempNumber | |
} | |
var n1 = 5, n2 = 3 | |
swap(number1: &n1, number2: &n2) | |
XCTAssertEqual(3, n1) | |
XCTAssertEqual(5, n2) | |
} | |
func testDiscardableResultMethod() { | |
@discardableResult | |
func aMethod() -> String { | |
return "hello." | |
} | |
aMethod() // 若沒有加上 @discardableResult 會出現告警:Result of call to 'aMethod()' is unused. | |
} | |
func testDefer() { | |
var aNumber = 0 | |
func aFunction() { | |
defer { | |
aNumber = 0 | |
} | |
aNumber = 1 | |
aNumber = 2 | |
XCTAssertEqual(2, aNumber) | |
} | |
aFunction() | |
XCTAssertEqual(0, aNumber) | |
} | |
func testInitializerWithoutInheritance() { | |
class A { | |
var result: String = "a" | |
init(newResult: String) { // 指定建構子 | |
self.result = newResult // 由於 A 不為任何類的子類,所以不需要呼叫 super。 | |
} | |
convenience init() { // 便利建構子 | |
self.init(newResult: "") // 便利建構子呼叫指定建構子 | |
} | |
required init(aBool: Bool) {} | |
} | |
let a = A() // 以便利建構子生成 A 的實例 | |
XCTAssertEqual("", a.result) | |
} | |
func testInitializerWithInheritance() { | |
class A { | |
var result: String = "a" | |
init(newResult: String) { | |
self.result = newResult | |
} | |
convenience init() { | |
self.init(newResult: "") | |
} | |
required init(aBool: Bool) {} | |
} | |
class B: A { | |
var number: Int? | |
// 繼承自父類的建構子 | |
override init(newResult: String) { | |
super.init(newResult: "結果: \(newResult)") | |
} | |
// 可失敗建構子 (Failable Initializer) | |
init?(number: Int?) { | |
super.init(newResult: "b") | |
if number == nil { return nil } | |
self.number = number | |
} | |
// 便利建構子的覆寫不需要宣告 override | |
// 便利建構子不會被繼承 | |
convenience init() { | |
self.init(newResult: "b") | |
} | |
// 由於父類 (A) 有宣告此建構A子,子類必須也加上 | |
required init(aBool: Bool) { | |
super.init(aBool: aBool) | |
} | |
} | |
let b = B() | |
XCTAssertEqual("結果: b", b.result) | |
XCTAssertNil(B(number: nil)) | |
} | |
func testDeInitializer() { | |
class A { | |
var aProperty: String = "" | |
} | |
class B { | |
var aAInstance: A = A() | |
deinit { | |
aAInstance.aProperty = "B is over." | |
} | |
} | |
var b = B() | |
let a = b.aAInstance | |
XCTAssertEqual("", a.aProperty) | |
b = B() // 取代舊的 B 實例 | |
XCTAssertEqual("B is over.", a.aProperty) | |
} | |
func testReferenceDeadLock() { | |
class Book { | |
var author: Author? | |
} | |
class Author { | |
var book: Book? | |
} | |
var aBook: Book? = Book() | |
let aAuthor: Author? = Author() | |
aBook!.author = aAuthor | |
aAuthor!.book = aBook | |
aBook = nil | |
XCTAssertNotNil(aAuthor!.book) | |
} | |
func testWeakReference() { | |
class Book { | |
var author: Author? | |
} | |
class Author { | |
weak var book: Book? | |
} | |
var aBook: Book? = Book() | |
let aAuthor: Author? = Author() | |
aBook!.author = aAuthor | |
aAuthor!.book = aBook | |
aBook = nil | |
XCTAssertNil(aAuthor!.book) | |
} | |
func testLazyStoredProperties() { | |
class Animal { | |
lazy var isLive: Bool = true // 在特性前加上 lazy 關鍵字 | |
var isHappy: Bool = true | |
} | |
let animal = Animal() | |
// 為了能夠檢查在記憶體裡的狀況,這裡需使用 Swift 的反射 (reflect) 語法 | |
let mirror = Mirror(reflecting: animal) | |
// 取出物件的第一個特性,先證明為 isLive 特性 | |
XCTAssertEqual("isLive.storage", mirror.children.first?.label) | |
// 並證明其值仍為 nil,還沒被設定為 true | |
XCTAssertEqual("nil", String(describing: (mirror.children.first?.value)!)) | |
// 取出物件的另一個特性,應該是 isHappy,並證明其值已經被設定為 true | |
XCTAssertEqual("true", | |
String(describing: (mirror.children[mirror.children.index(after: mirror.children.startIndex)].value))) | |
// 取用 isLive 特性 | |
let _ = animal.isLive | |
// 取出物件的第一個特性,先證明為 isLive 特性 | |
XCTAssertEqual("isLive.storage", mirror.children.first?.label) | |
// 並證明其值被設定為 true (為 optional 型別) | |
XCTAssertEqual("Optional(true)", | |
String(describing: (mirror.children.first?.value)!)) | |
} | |
func testTypePropertiesAndMethods() { | |
class Util { | |
static var inch: Float = 0.0 | |
static func centWith() -> Float { | |
return inch * 2.54 | |
} | |
static func centWith(inch: Float) -> Float { | |
return inch * 2.54 | |
} | |
} | |
Util.inch = 2 | |
XCTAssertEqual(5.08, Util.centWith()) | |
XCTAssertEqual(2.54, Util.centWith(inch: 1)) | |
} | |
func testBoringExample() { | |
class SomeClass { | |
var someProperty: String = "" | |
func getSomeValue() -> String { | |
return "hello" | |
} | |
} | |
let aInstance = SomeClass() | |
aInstance.someProperty = aInstance.getSomeValue() | |
XCTAssertEqual("hello", aInstance.someProperty) | |
} | |
func testAssignClosureResultToProperty() { | |
class SomeClass { | |
let someProperty: String = { | |
return "some value" | |
}() | |
} | |
XCTAssertEqual("some value", SomeClass().someProperty) | |
} | |
func testAssignInstanceProperty() { | |
class SomeClassForProperty: NSObject { | |
override var description: String { | |
return "some value" | |
} | |
} | |
class SomeClass { | |
let someProperty: String = SomeClassForProperty().description | |
} | |
XCTAssertEqual("some value", SomeClass().someProperty) | |
} | |
func testExtension() { | |
XCTAssertEqual("Hello".toChinese(), "您好") | |
} | |
func testExtensionImplementProtocol() { | |
XCTAssertEqual("Hello".sayFrenchHello(), "Bonjour") | |
} | |
func testOptionalProtocol() { | |
class SomeClass: AOptionalProtocol {} | |
XCTAssertNotNil(SomeClass()) | |
} | |
func testClassOnlyProtocols() { | |
class SomeClass: SomeClassProtocol {} | |
/* | |
由於 SomeClassProtocol 是繼承自 AnyObject 這個協定, | |
所以無法使用下列代碼讓結構/列舉宣告實作 SomeClassProtocol。 | |
struct SomeStructure: SomeClassProtocol {} | |
或 | |
enum SomeEnum: SomeClassProtocol {} | |
錯誤訊息如下: | |
Non-class type 'SomeStructure' | |
cannot conform to class protocol 'SomeClassProtocol' | |
*/ | |
} | |
func testSeveralTypesEnum() { | |
enum Emotion { | |
case happy, sad | |
case laugh() | |
case eat(foodName: String) /* Associated Values */ | |
func brief() -> String { | |
switch self { | |
case .eat(foodName: let fName): | |
return "有些人心情不好會大吃\(fName)一頓" | |
default: | |
return "" | |
} | |
} | |
} | |
let eatEmotion: Emotion = Emotion.eat(foodName: "牛排") | |
XCTAssertEqual(eatEmotion.brief(), "有些人心情不好會大吃牛排一頓") | |
} | |
func testEnumRawValue() { | |
enum Season: Int { | |
case Spring, Summer, Fall, Winter | |
} | |
XCTAssertEqual(Season(rawValue: 1), Season.Summer) | |
XCTAssertEqual(Season(rawValue: 3), Season.Winter) | |
} | |
func testEnumOfCustomRawValue() { | |
enum Swith: Int { | |
case ON = 1, OFF = 0 | |
} | |
XCTAssertEqual(Swith(rawValue: 1), Swith.ON) | |
} | |
func testGenerics() { | |
func add<Item>(item1: Item, item2: Item) -> Any { | |
if item1 is Int { | |
return (item1 as! Int) + (item2 as! Int) | |
} | |
if item1 is Double { | |
return (item1 as! Double) + (item2 as! Double) | |
} | |
if item1 is String { | |
return "\(item1)\(item2)" | |
} | |
return EMPTY | |
} | |
XCTAssertEqual(2, add(item1: 1, item2: 1) as! Int) | |
XCTAssertEqual(2.5, add(item1: 0.5, item2: 2) as! Double) | |
XCTAssertEqual("11", add(item1: "1", item2: "1") as! String) | |
} | |
func testErroHandler() { | |
enum RequestError: Error { | |
case wrongFormat | |
case deadNetwork | |
func simpleDescription() -> String { | |
if self == RequestError.wrongFormat { | |
return "wrong format" | |
} | |
if self == RequestError.deadNetwork { | |
return "dead network" | |
} | |
return "unknown" | |
} | |
} | |
func checkNetworkAvailable() -> Bool { | |
return true | |
} | |
func request(parameter: Dictionary<String, String>?) throws -> (String, String) { | |
if parameter == nil || parameter?.keys.count == 0 { | |
throw RequestError.wrongFormat | |
} | |
if !checkNetworkAvailable() { | |
throw RequestError.deadNetwork | |
} | |
return ("200", "OK") | |
} | |
XCTAssertThrowsError(try request(parameter: Dictionary<String, String>()), | |
"Parameter dictionary is empty") { (error) in | |
XCTAssertTrue(error is RequestError) | |
} | |
var errorReason: String? = nil | |
do { | |
let response = try request(parameter: Dictionary<String, String>()) | |
print(response) | |
} catch RequestError.wrongFormat { | |
errorReason = RequestError.wrongFormat.simpleDescription() | |
} catch RequestError.deadNetwork { | |
errorReason = RequestError.deadNetwork.simpleDescription() | |
} catch { | |
errorReason = "Unknown" | |
} | |
XCTAssertEqual("wrong format", errorReason) | |
let response = try? request(parameter: Dictionary<String, String>()) | |
XCTAssertNil(response) | |
} | |
func testIf() { | |
let number1 = 10 | |
let number2 = 9 | |
let number3 = 8 | |
var result = 0 | |
/* 以下三個例子是同義的 */ | |
if number1 > number2 { | |
if number2 > number3 { | |
result += 1 | |
} | |
} | |
if (number1 > number2) && (number2 > number3) { | |
result += 1 | |
} | |
if number1 > number2, number2 > number3 { | |
result += 1 | |
} | |
XCTAssertEqual(3, result) | |
if (number1 < number2) || (number2 > number3) { | |
result += 1 | |
} | |
XCTAssertEqual(4, result) | |
} | |
func testNilCoalescingOperator() { | |
var aInt: Int? | |
aInt = Int("1") | |
XCTAssertEqual(1, aInt ?? 0) | |
aInt = Int("a") | |
XCTAssertEqual(0, aInt ?? 0) | |
} | |
func testTernaryConditionalOperator() { | |
let score = 65 | |
XCTAssertEqual("考試通過", ((score >= 60) ? "考試通過" : "考試不及格")) | |
} | |
func testClosedRange() { | |
var result:Int = 0 | |
for number in 1...10 { | |
result += number | |
} | |
XCTAssertEqual(55, result) | |
} | |
func testHalfOpenRange() { | |
var result:Int = 0 | |
for number in 1..<11 { | |
result += number | |
} | |
XCTAssertEqual(55, result) | |
} | |
func testMultilineString() { | |
let articleTexts = """ | |
歡迎一起來學習 Swift "最新版"! | |
Swift 是蘋果官方發佈的程式語言。 | |
""" | |
XCTAssertNotEqual("歡迎一起來學習 Swift \"最新版\"!Swift 是蘋果官方發佈的程式語言。", articleTexts) | |
XCTAssertEqual("歡迎一起來學習 Swift \"最新版\"!\nSwift 是蘋果官方發佈的程式語言。", articleTexts) | |
} | |
func testSpecialCharacters() { | |
let blackHeart = "\u{2665}" | |
XCTAssertEqual(blackHeart, "♥") | |
} | |
func testGetFirstCharacterFromString() { | |
let greeting = "您好,有什麼能為您服務的?" | |
XCTAssertEqual("您", greeting[greeting.startIndex]) | |
} | |
func testCountStringLength() { | |
let greeting = "您好,有什麼能為您服務的?" | |
XCTAssertEqual(greeting.count, greeting.endIndex.encodedOffset) | |
} | |
func testSubStringWithBefore() { | |
let greeting = "您好,有什麼能為您服務的?" | |
XCTAssertEqual("?", greeting[greeting.index(before: greeting.endIndex)]) | |
} | |
func testFromIntToStringIndex() { | |
let greeting = "您好,有什麼能為您服務的?" | |
XCTAssertEqual("?", greeting[greeting.index(before: String.Index(encodedOffset: greeting.count))]) | |
} | |
func testGetIndexPositonOfCharacter() { | |
let greeting = "您好,有什麼能為您服務的?" | |
let indexOfComma = greeting.index(of: ",") | |
XCTAssertEqual(indexOfComma, String.Index(encodedOffset: 2)) | |
} | |
func testStringReverse() { | |
let greeting = "您好,有什麼能為您服務的?" | |
XCTAssertEqual("?的務服您為能麼什有,好您", String(greeting.reversed())) | |
} | |
func testStringInsert() { | |
var welcome = "hello" | |
// 插入字元 | |
welcome.insert(",", at: welcome.endIndex) | |
XCTAssertEqual("hello,", welcome) | |
// 插入字串 | |
welcome.insert(contentsOf: " world.", at: welcome.endIndex) | |
XCTAssertEqual("hello, world.", welcome) | |
} | |
func testStringRemove() { | |
var welcome = "hello, world." | |
welcome.remove(at: welcome.index(before: welcome.endIndex)) | |
XCTAssertEqual("hello, world", welcome) | |
welcome.removeSubrange( | |
welcome.index(welcome.endIndex, offsetBy: -(", world".count)) | |
..< | |
welcome.endIndex) | |
XCTAssertEqual("hello", welcome) | |
} | |
} | |
protocol LearningLanguage { | |
var languageName: String {get set} | |
var description: String {get set} | |
func recite() | |
} | |
protocol LearningLanguageWithMutaingKeyword { | |
var languageName: String {get set} | |
var description: String {get set} | |
mutating func recite() | |
} | |
@objc protocol AOptionalProtocol { | |
@objc optional func aOptionalMethod() | |
} | |
extension String { | |
func toChinese() -> String { | |
if self == "Hello" { | |
return "您好" | |
} | |
return "Unknown" | |
} | |
} | |
protocol LearningFrench { | |
func sayFrenchHello() -> String | |
} | |
extension String: LearningFrench { | |
func sayFrenchHello() -> String { | |
if self == "Hello" { | |
return "Bonjour" | |
} | |
return "Unknown" | |
} | |
} | |
protocol SomeClassProtocol: AnyObject {} // AnyObject 為 protocol | |
class JustForGenerics: XCTestCase { | |
func testTypeConstraintGenerics() { | |
func add<Item: Addable>(item1: Item, item2: Item) -> Item { | |
return item1 + item2 | |
} | |
XCTAssertEqual(2, add(item1: 1, item2: 1)) | |
XCTAssertEqual("11", add(item1: "1", item2: "1")) | |
} | |
} | |
protocol Addable { | |
static func +(lItem: Self, rItem: Self) -> Self | |
} | |
extension Int: Addable {} | |
extension String: Addable { | |
static func +(lItem: String, rItem: String) -> String { | |
return "\(lItem)\(rItem)" | |
} | |
} | |
view rawSwift4.swift hosted with ❤ by GitHub |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment