Swift 5.1 관련하여 간단하게 개념만 집고가자!
- Declaring constants and variables
- Numeric type conversion
- String
- Tuple
- Optionals
- Collection Types: Array
- Collection Types: Dictionary
- Collection Types: Set
- Control Flow: Loops
- Control Flow: Conditionals
- Functions
- Closures
- Enumerations
- Structs
- Classes
- Inheritance
- designated & Convenience Initializers
- Extensions
- Error Handling
- Coding Protocols
- Access Control
- 스위프트에서의 물음표와 느낌표
1. Declaring constants and variables
let double: Double = 2.0
let inferredDouble = 2.0
var mutableInt: Int = 1
2. Numeric type conversion
let integerValue = 8
let doubleValue = 8.0
// error: type mismatch
//let sum = integerValue + doubleValue
let sum = Double(integerValue) + doubleValue
3. String - TODO: string 비교
let helloWorld = "Hello, world"
let helloWorldProgram = """
dfdf
dfdf
"""
// emptyString
let emptyString = "" // Using string literal
let anotherEmptyString = String()
var mutableString = "Swift"
mutableString += " is awesome!"
print("This is my opinion: \(mutableString)")
4. Tuple
- 여러 값을 group화 할 수 있는 자료형 => Tuples are useful for simple groups of related values
- decompose - let (code, reason) = httpError
- underscore (_) : ignoring parts of the tuple
- 인덱스 숫자로 접근하여 값을 가져올 수 있음
- element에 이름 추가 가능
// Group multiple values into a single compound value
// : a number and a human-readable description.
let httpError = (503, "Server Error")
// decompose
let (code, reason) = httpError
let codeByIndex = httpError.0
let reasonByIndex = httpError.1
// _ : ignoring parts of the tuple
let (_, justTheReason) = httpError
// access using index numbers
print("The status code is \(http404Error.0)")
/-----------------------------------
// element 이름 추가 가능
let http200Status = (statusCode: 200, description: "OK")
print("The status code is \(http200Status.statusCode)")
5. Optionals
- 해당 자료형 값 또는 nil 값을 갖는 변수
var catchphrase: String? // optional String
let count3: Int? = catchphrase?.count // optional Int (catchphrase가 nil일 수도 있으니까요)
- ! (forced unwrapping opration) : nil이 아니면 unwrapped optional, nil이면 crash
- optional binding: if let, guard let
- ?? : coalescing operator
- ? : chaing operator
- implicitly unwarpped optionals : 강제적으로 optional unwrap
// optional: String or nil 값을 갖는 변수
var catchphrase: String? // called optional String
catchphrase = "Hey, everybody?"
// Forced uwrapping operation(!)
// catchphrase이 nil이 아닐 경우 count 값을 가짐, nil이면 crash
let count1: Int = catchphrase!.count
// Optional binding
// catchphrase?count : optional Int
// optional Int에 값이 포함되어 있으면 count라는 constant 생성
if let count = catchphrase?.count {
print(count)
}
// Coalescing operator (??) - coalesce: 합치다
//catchphrase가 nil이 아니면 count2는 cathphrase의 count을 넣음; nil이면 0
let count2: Int = catchphrase?.count ?? 0
// Chaining operator (?)
// Optional Int: catchphrase의 count or nil
let count3: Int? = catchphrase?.count
// Implicitly unwrapped optionals (implicit: 절대적인)
let forcedCatchphrase: String! = "Hey, everybody?"
let implicitCatchphrase = forcedCatchphrase // 값을 넣을 때 ! mark 필요 없음
6. Collection Types: Array
- contains
- append, insert
- remove
- arr[0]
// let collection : reassign과 element 바꾸는 것 안됨
let immutableArray: [String] = ["Alice", "Bob"]
// 타입 추론: mutableArray [String]
var muttableArray = ["Eve", "Frank"]
let isEveThere = immutableArray.contains("Eve")
let name: String = immutableArray[0]
// index가 out of range이면 crash
mutableArray[1] = "Bart"
// immutableArray[1] = "Bart" // Error: 바꿀 수 없음
mutableArray.append("Ellen")
mutableArray.insert("Gemma", at: 1)
let removedPerson = mutableArray.remove(at: 1)
mutableArray = ["Ilary", "David"]
mutableArray[0] = "John"
7. Collection Types: Dictionary
- key가 존재하지 않으면 nil return : optional 값 리턴
- dic["name], removeValue(forKey: )
let immutableDict: [String: String] = ["name", "Kirk", "rank": "captain"]
var mutableDict = ["name": "Picard", "rank": "captain"]
// key가 존재하지 않으면 nil return (String? - optional String)
let name2: String? = immutableDic["name"]
// update value for key
mutableDic["name"] = "Janeway"
// add new key and value
mutableDic["ship"] = "Voyager"
// delete by key, 해당 key가 nil이 아니면
let rankWasRemoved: String? = mutableDic.removeValue(forKey: "rank")
8. Collection Types: Set
- list와 비슷하지만 중복된 item은 ignore
- contains, insert, remove
// Set: 중복된 item은 ignore
let immutableSet: Set = ["chocolate", "vanilla", "chocolate"]
var mutableSet: Set = ["butterscotch", "strawberry"]
immutableSet.contains("chocolate")
mutableSet.insert("green tea")
// 해당 item을 찾지 못하면 nil return
let flavorWasRemoved: String? = mutableSet.remove("strawBerry")
9. Control Flow: Loops
// Iterator list or set
for item in listOrSet {
print(item)
}
// Iterator over dictionary
for (key, value) in dictionary {
print("\(key) = \(value)")
}
for i in 0...10 {
print(i) // 0 to 10
}
for i in 0..<10 {
print(i) // 0 to 9
}
var x = 0
while x < 10 {
x += 1
print(x)
}
repeat {
x -= 1
print(x)
} while(x > 0)
10. Control Flow: Conditionals
let number = 88
if (number <= 10) {
} else if (number > 10 && number < 100) {
} else {
}
// Ternary operator (ternary: 3으로 이루어진)
let height = 100
let isTall = height > 200 ? true : false
// guard: program의 제어를 변형하기 위해
for n in 1...30 {
guard n % 2 == 0 else {
continue
}
print("\(n) is even")
}
let year = 2012
switch year {
case 2003, 2004:
print("Panther or Tiger")
case 2010:
print("Lion")
case 2012...2015:
print("Mountain Lion through El Captain")
default:
fatalError("Not already classified")
}
11. Functions
_: argument label 제외
// void function
func sayHello() {
print("Hello")
}
// function with parameters
func sayHello(name: String) {
print("Hello \(name)!")
}
// function with default parameters
func sayHello(name: String = "Lorenzo") {
print("Hello \(name)!")
}
func sayHello(name: STring = "Lozenzo", age: Int) {
print("\(name) is \(age) years old!")
}
// using just the non default value
sayHello(age: 35)
// function with parameters and return value
func add(x: Int, y: Int) -> Int {
return x + y
}
let value = add(x: 8, y:10)
// single expression만 포함할 경우 return 키워드 제거 가능
func multiply(x: Int, y: Int) -> Int {
x + y
}
// arguments label 명시
func add(x xVal: Int, y yVal: Int) -> Int {
return xVal + yVal
}
// TODO: argement label 제외
func add(_ x: Int, y: Int) -> Int {
return x + y
}
let value = add(8, y: 10)
// parameter로 다른 함수 접근
func doMath(operation: (Int, Int) -> Int, a: Int, b: Int) -> Int {
return operation(a, b)
}
12. Closures: TODO: 실행하기
// TODO:
let adder: (Int, Int) -> Int = {(x, y) in x + y}
// argument의 약어 사용
let square: (Int) -> Int = {$0 * $0}
// 함수에 closure 전달
let addWithClosure = doMath(operation: adder, a: 2, b: 3)
13. Enumerations
- allCases : CaseIterable protocol을 채택하면 사용 가능
- rawValue
- associated value(연관값): enum case문에 추가 정보를 덧붙일 수 있음
case text(String)
- switch문으로 associated value를 캐치할 수 있다.
case .text(let value):
enum Taste {
case sweet, sour, salty, bitter, umami
}
let vinegarTaste = Taste.sour
// Iterating through an enum class
enum Food: CaseIterable {
case pasta, pizza, hamburger
}
for food in Food.allCases {
print(food)
}
// enum with String raw values
enum Currency: String {
case euro = "EUR"
case dollar = "USD"
case pound = "GBP"
}
let euroSymbol = Currency.euro.rawValue
print("The currency symbol for Euro is \(euroSymbol)")
// enum with associated values
// associated value(연관값):
// 열거형의 케이스(case)에 추가적인 정보를 덧 붙일 수 있는 독특한 방법
enum Content {
case empty
case text(String)
case number(Int)
}
// matching enumeration values with a switch statement
let content = Content.text("Hello")
switch content {
case .empty:
print("Value is empty")
case .text(let value): // String value 추출
print("Value is \(value)")
case .number(_): // Int value ignore
print("Value is a number")
}
14. Structs
- member initializer 자동 생성됨
struct User {
var name: String
var age: Int = 40
}
// member initilizer는 자동적으로 생성됨.
let john = User(name: "John", age: 35)
// member initilizer는 기본 파라미터 값을 사용.
// TODO: User()이렇게 해도 돼?
let dave = User(name: "Dave")
print("\(john.name) is \(john.age) years old")
15. Classes
- 반드시 하나의 designated initializer를 가져야 함
- computed property
- deinit: object의 리소스가 해제될 때 수행
class Person {
let name: String
// class initializer(designated initializer)
init(name: String) {
self.name = name
}
// using deinit to perform object's resources cleanup
deinit {
print("Perfom the deinitialization")
}
var numberOfLaughts: Int = 0
func laugh() {
numberOfLaughts += 1
}
// define a computed property
var isHappy: Bool {
return numberOfLaughs > 0
}
}
let david = Person(name: "David")
david.laugh()
let happy = david.isHappy
16. Inheritance
- override computed property
- final: subclass를 방지하기 위해 사용
class Student: Person {
var numberOfExams: Int = 0
// override isHappy computed property providing additional logic
override var isHappy: Bool {
numberOfLaughs > 0 && numberOfExams > 2
}
}
let ray = Student(name: "Ray")
ray.numberOfExams = 4
ray.laugh()
let happy = ray.isHappy
// final: subclass를 방지하기 위해 사용
final class Child: Person { }
17. designated & Convenience Initializers
- designated initializer: class는 반드시 하나 가져야 함
- convenience initializer: 옵션
// class는 반드시 최소한의 하나의 designated initilizer를 가져야 함
// convenience initilizer는 옵션
// TODO: designated initilizer 정의
class ModeOfTransportation {
let name: String
// designated initilizer 정의
init(name: String) {
self.name = name
}
// convenience initilizer 정의
convenience init() {
// 내부 designated initilizer에게 위임
self.init(name: "Not classified")
}
}
class Vehicle: ModeOfTransportation {
let wheels: Int
init(name: String, wheels: Int) {
self.wheels = wheels
// superclass의 designated initilizer로 위임
super.init(name: name)
}
// override superclass convenience initilizer
override convenience init(name: String) {
// 내부 designated initilizer에게 위임
self.init(name: name, wheels: 4)
}
}
18. Extensions
- 존재하는 class, structure, enumeration, protocol 타입에 새로운 기능 추가
- TODO: Category와 extension의 차이점은?
extension String {
// String type 확장 - String instance가 truthy or falsy인지 판단
var boolValue: Bool {
if self == "1" {
return true
}
return false
}
}
let isTrue = "0".boolValue
19. Error Handling
- Error protocol 채택하여 enum으로 정의
- do catch 문으로 에러 캐치
- try? : 해당 값에 error가 던져지면 nil로 반환 (optional 반환)
try! : 해당 값에 error가 던져지면 런타임 에러(앱이 죽는다는 얘기... 절대로 에러가 발생하지 않을 경우만 사용)
// error 상속받아 enum으로 기술
enum BeverageMatchineError: Error {
case invalidSelection
case insufficientFunds
case outOfStock
}
func selectBeverage(_ selection: Int) throws -> String {
return "Waiting for beverage"
}
// do clause의 code에 의해서 error가 던져지면 처리
let message: String
do {
message = try selectBeverage(20)
} catch BeverageMatchineError.invalidSelection {
print("Invalid selection")
} catch BeverageMatchineError.insufficientFunds {
print("Insufficient funds")
} catch BeverageMatchineError.outOfStock {
print("Out of stock")
} catch {
print("Generic error")
}
// try? error가 던져지면, 그 expression의 값은 nil로 처리
let nillableMessage = try? selectBeverage(10)
// try! : 런타임에 error를 받으면 error를 받음.
let throwableMessage = try! selectBeverage(10)
20. Coding Protocols
- Codable: Decodable + Encodable protocol
- JSONDecoder: JSON String -> Data
- JSONEncoder: Data -> JSON String
import Foundatation
// Codable: Decodable, Encodable protocol
struct UserInfo: Codable {
let username: String
let loginCount: Int
}
// CustomStringConvertible 채택
extension UserInfo: CustomStringConvertible {
var description: String {
return "\(username) has tried to login \(loginCount) time(s)"
}
}
// string literal
let json = """
{"username" : "David", "loginCount" : 2}
"""
// JSONDecoder를 사용하여 JSON serialize : JSON string -> Data
let decoder = JSONDecoder()
// String을 data(struct)로 표현
let data = json.data(using: .utf8)!
let userInfo = try! decoder.decode(UserInfo.self, from: data)
print(userInfo)
// JSONEncoder 사용하여 struct serialize :
let encoder = JSONEncoder()
let userInfoData = try! encoder.encode(userInfo)
// Data -> JSON String으로 표현
let jsonString = String(data: userInfoData, encoding: .utf8)!
print(jsonString)
21. Access Control
- public
- fileprivate
- private
- access관련이 없을 경우
// module(framework or application): 코드가 배포의 single unit
// public: 다른 모듈에서 접근 가능
public class AccessLevelsShowcase {
public var somePublicProperty = 0
// ?
var someInternalProperty = 0
// 소스 파일을 정의한 것으로부터 접근 가능
fileprivate func someFilePrivateMethod() { }
//
private func somePrivateMethod() { }
}
22. 스위프트에서의 물음표와 느낌표
스위프트에서 이 두 가지 구두 문자 (? 와 !)는 사용된 모든 용법에서 공통된 의미를 가지고 있음
물음표 : 무언가 확실하지 않다는 뜻
느낌표 : 경고 표지판 같은 용도, 잠재적으로 위험 동작을 수행한다.
옵셔널의 언랩핑과 마찬가지로 몇 라인 코드를 더 작성해야한다 할지라도 try!대신에 try와 try?를 사용할 것을 권장합니다. 물론 try!와 옵셔널 강제 언랩핑이 필요한 경우가 분명 존재합니다.
Ref.
Ray Wenderlich website
'iOS > Swift' 카테고리의 다른 글
[Swift 5.2] Methods - Apple Documentation (0) | 2020.03.05 |
---|---|
[Swift] ExpressibleByStringLiteral, CustomStringConvertible (0) | 2020.02.18 |
[Swift4.1] map, flapMap, compactMap (0) | 2019.12.11 |
[Swift 5.2] Properties - Apple Documentation (0) | 2019.12.08 |
[Swift] if let vs. guard let (0) | 2019.11.29 |