# Swift 文法筆記
###### tags: `Swift`
# Swift
```篇章1```
## Swift基本語法
### 宣告
```swift=
//一般宣告
let 用來宣告常數
var 用來宣告變數
//因為是動態語言,所以資料型態會自己設定
let f = 0
//手動設定的話:
let f:Float = 0 //設成Float型態
//甚至可以使用中文和表情符號作為變數及常數命名
let 香蕉 = "Banana"
## 型別轉換 ##
//Swift屬於強型別(Strongly Typed)代表運算式中的型態要一致
let n = Int("100") //由字串轉整數
let s = String(20) //由數字轉字串
//字串轉二進位
let data = "Hello".data(using: .utf8)
//二進位轉字串
let str = String(data:data!, encoding: .utf8)
## as ##(型態中的轉換)
class Animal {}
class Dog: Animal {}
let d = Dog()
let k = d as Animal //在後面做轉換
## Any and AnyObject ##(特殊資料型態)
var list: [Any] = []
list.append("hello")
list.append(10)
list.append(20.3)
list.append(["key":5])//字典型態
## as! ##
//取出資料 需要先轉成正確的型態 使用as!
print((list[3] as! [String: Int])["key"]!)
```
### for-in
- for-in 陣列、數字、字串
- where 有條件的使用for-in
- reverse()
- strid函數使用
```swift=
//陣列
for city in ["台北","台中","高雄"]{
print(city)
}
//數字 不包含最後一個字
for n in 0...10{
print(n)
}
//字串
for c in "hello world"{
print(c)
}
## 有條件的使用for-in ##
//加入 where 做條件
for n in 0...10 where n%2 ==0{
print(n)
}//結果0,2,4,6,8,10
//加入reverse使用
for n in (0...10).reversed() where n%2==0{
print(n)
}//結果 10,8,6,4,2,0
//加入另外一個函數strid(from:to:by:)
for n in stride(from: 10, to: -1, by: -2){
print(n)
} //因為不包含to: 所以要10-0,才會to:-1
```
### While
do{
...
}while()
變成
repeat{
...
}while
```swift=
//Repeat-While 即 do while
var n = 0
repeat {
print(n)
n = n+1
}while n<10
```
其他的都一樣
### if
都一樣
### Switch-case
- case範圍
- 0...2
- enum使用
``` swift=
let n =10
switch n{
case 0...2:
//範圍在0-2之間
}
## enum ##(列舉型態)
enum Direction{
case north
case east
case south
case west
case unknown
}
//舉例:
var d:Direction = .south
switch d{
case .north:
print("朝北")
case .east:
print("朝東")
...
default:
print("無法確定方向")
}
//好處:型態名稱可以省略
//原本:Direction.south 變 .south
```
``新``
### guard
guard:會先過guard檢查,沒有符合就跳出
``` swift=
//舉例:
var n = 10
func somefunc(){
guard n>0 else{
print("n不符")
return
}
print("ok")
}
當然也可以用 if 辦到 :)
```
### 不同版本的ios須執行不同的程式碼
```swift=
if #available(iOS 12,*){
//在iOS 12 以上的程式碼
}else if #available(iOS 11,*){
...
}else if #....{
....
}
```
## Swift靈魂
``新``
### Nil
**Swift 語言的靈魂**
**! 跟 ?**
?(表示其值==有可能是nil==)
! (表示其值==不可能是nil==)
> 沒有加上問號的,代表變數或常數裡面只存放一個==非Nil==的值
輸出看到Optional這個字
>代表這個變數或是常數有可能是nil也可能不是nil,==只是現在剛好不是nil而已==
``` swift=
let n = Int("10")
print(n) //Optional(10)
```
一開始的變數、常數或函數傳回值在宣告就加上問號,後面所有要接的變數或常數其資料型態也要加上問號。
驚嘆號保證其值不會是nil,
```swift=
//因為print出來的資料只會有非nil的部分,故沒有Optional字樣
let n = Int("10")!
print(n) //10
//因為print出來的資料只會有非nil的部分,故沒有Optional字樣
let n = Int("10")
print(n!) //10
```
:::warning
補充Optional:
Optional是一個enum型別,裡面定義了兩個狀況
- 一個名為none,用來放nil
- 一個名為some,用來放非nil
**驚嘆號用來取用some內的內容,如果取到nil,程式會當掉**
:::
``新``
### if let
延續上面的問題,用來檢查右邊的值(n)是否為nil,如果不是,則宣告會給一個常數k(按照下面的例子),在if的範圍內k不用加!
理解上:
```swift=
//理解版本
if let k = n{
//n不是nil
}else{
//n是nil
}
```
實際上:
```swift=
//為了避免多想一個名字,通常等號兩邊都會相等
if let n = n{
//...
}else{
//....
}
```
### 給個預設值
某個變數或是常數為nil,我們可以使用連續兩個問號??來設定一個預設值,例如當n為nil,x得到0
```swift=
let n = Int("ABC") //得到nil
let x = n ?? 0 //因為x得到nil,故將其設一個預設值為0
```
:::danger
未來有可能存入nil,宣告時就必須在資料型態後方加上問號,而取出其值時必須加入驚嘆號(避免為nil)
:::
## 函數
### 宣告跟傳回值
#### 宣告:
以func為開頭
```swift=
func saveDatat(){
//....
}
```
``新``
#### 傳回值:
函數後方加入「->」符號與傳回值型態,並在函數使用return將值傳回
```swift=
func getData() -> String{
//....
return "hi"
}
```
### 參數與參數名稱
argument label 給外部參考用
parameter name 給內部計算用
```swift=
// argument label: number | parameter name: value
// ˇarg ˇpar
func square(number value: Float) -> Float{
return value* value
}
let ans = square(number:5)
//ans:25
//如果argument label 與 parameter name一樣時,可以將兩者合併
func square(value: Float) -> Float{
return value * value
}
```
如果不想加入argument label
```swift=
//使用_ 作為代替argument label
func square (_ value: Float) -> Float{
return value * value
}
```
如果遇到函數有兩個以上的參數
```swift=
//兩個參數的用逗號隔開
func add(_ value:Float, _ value2:Float) -> Float{
return value1 * value2
}
val = add(3,5)
//val:15
```
### Call by address
在資料型態前面加上==inout==該參數就是傳址呼叫
呼叫參數要加上&
```swift=
//call by address
func inc(_ value:inout Int){
value = value+1
}
var n = 0
inc(&n)
print(n)
```
### 參數預設
在參數型態後面加入=加預設
```swift=
func hi(who: String = "anonymous"){
print("hi,\(who)")
}
hi() //印出hi, anonymous
hi(who:"John")//印出 hi, John
```
``新``
### 參數不固定
使用...來宣告一個參數數量不固定的函數,傳進函數的參數會轉成陣列型態
```swift=
//計算1+2+3+4+5
func sum(_ values: Float ...) -> Float{
var total: Float = 0
for n in values{
total += n
}
return total
}
print(sum(1,2,3,4,5))
```
``新``
### 閉包Closure
將一個程式區段當作函數的參數或是類別的屬性來傳遞
```swift=
func inc(value: Int) -> Int{
return value+1
}
func dec(value: Int) -> Int{
return value-1
}
func f(_ value:Int, op:(Int)->Int)->Int{
return op(value)
}
//可以看到f這個函數的第二個參數op,其資料型態為(Int)->Int
//代表要放入另外一個函數,函數的類型是接收一個整數型態的參數,然後回傳一個整數型態
//接收(Int) -> Int回傳
print(f(10,op:inc)) //11
print(f(3,op:dec)) //2
```
#### Inline closure
呼叫f()時,在op的位置就直接填inc或dec即可
如果inc或dec這樣的函數沒有預先實做並給予一個名字,而直接寫在參數op的位置,就稱inline closure
```swift=
//重點在in,in後面接計算式
let n = f(5, op:{ (value) -> Int in
return value * value
})
//原本
func f(_ value:Int, op:(Int)->Int){
return op(value) //return 一個func
}
print(n) //25
```
#### Trailing closures
在op位置的closure可以放到f()函數的最後方,稱trailing closure
```swift=
//差異語法 f(5){ ... } vs. f(5,op:{ ... })
let n = f(5){ (value) -> Int in
return value * value
}
print(n) //25
```
``新``
### 使用$0作為closure中參數名稱
使用$0,$1,$2作為closure中代替參數名稱
```swift=
let n =f(5){
return $0 * $0
}
print(n) //25
```
```swift=
//completion是閉包,這個閉包接收兩個參數,回傳一個Void
func doSomething(completion: (_ str: String, _ err:Error?) -> Void){
completion("hello", nil)
}
```
:::danger
重要!常用! :arrow_down:
跟上面的搭配用
:::
``重要``
```swift=
///呼叫completion 將"hello"字串及nil回傳給呼叫者
doSomething { (str,err) in
if err != nil{
print("失敗: \(err!.localizedDescription)")
return
}
print(str) //印出hello
}
```
### 字串
字串為字元陣列,資料型態為String,以雙引號開始跟結束。
使用三個雙引號來支援多行字串,例如:
```swift=
let s = """
aa
bb
cc
"""
```
#### 兩個字串相加
- 使用+號
- append()函數
```swift=
let s = "abc" + "def"
或
var s = "abc"
s.append("def")
```
#### 將字串與數字相加
要先將數字轉成字串才行
- String()
- \\() 括號內除了可以放數字外,也可以放變數跟常數,容易把數字、變數或常數與原本的字串合併起來
```swift=
"abc" + String(10)
或
// \()為特殊指令
"abc\(10)"
```
#### 檢查是否為空字串
- s1 == ""
- isEmpty{}
```swift=
let str = ""
//isEmpty 成立就會做{}的內容
if str.isEmpty{
print("str 為空字串")
}
```
#### 字串取值
不能直接str[3] 這樣來取值
1. 需要先將字串轉換成陣列
- 損耗記憶體位置
2. 使用String.Index型態的特殊索引取值
像是:
1.
```swift=
//將字串轉型成矩陣
let str = "Hello"
var arr = Array(str)
print(arr[4])
```
2.
```swift=
//使用String.Index型態的特殊索引值
str[.init(encodedOffset: 4)] //o
或
str[str.index(str.startIndex, offsetBy: 4)]
```
### 常用的屬性跟函數
1. .count 字串長度
```swift=
//str = "abcdef"
print(str.count) // 6
```
2. startIndex、endIndex
```swift=
print(str.startIndex.encodedOffset) // 0
print(str.endIndex.encodedOffset) // 6
```
3. index(_:offsetBy:) 索引值加上偏移量
```
//從第二個字元開始第三個字元
//str.index(起點,offset:偏移量)
let begin = str.index(str.startIndex, offset:1)
let end = str.index(begin, offset:3)
print(str[begin..<end]) //bcd
4. contains()
```swift=
if str.contains("ddd"){
...
}
```
5. replacingOccurences(of:with:) :字串取代
```swift=
if str.contains("cde"){
let newstr = str.replaceOccurences(of:"cde", with: "")
print(newstr) //abf
}
```
6. trimmingCharacters(in:) :頭尾去空白
```swift=
var str = " \tabcdef \n\n "
str = str.trimmingCharacters(in:.whitespaceAndNewlines)
print(str) //abcdef
```
7. uppercased()、lowercased():轉換大小寫
8. hasPrefix(_:)、hasSuffix(_:) :判斷字串是否由某的字串的開頭或結尾
```swift=
if str.hasPrefix("ab"){
//ab開頭
}
if str.hasSuffix("ef"){
}
```
9. split(separator:) :分割字串,分割後以矩陣型態傳回
```swift=
let str = "ab,cd,ef"
let arr = str.split(seperator:",")
```
##### 參考自ios13程式設計實戰