해당 자료: Do it! 스위프트로 아이폰 앱 만들기 입문, 송호정, 이범근 저,이지스퍼블리싱, 2023년 01월 20일
AppDelegate.swift
AppDelegate.swift은 iOS 앱의 핵심 파일 중 하나입니다. 이 파일은 앱의 생애주기(lifecycle)를 관리하고 앱의 전역적인 동작을 조정하는 역할을 합니다.
앱의 시작과 종료를 관리합니다: AppDelegate는 앱이 처음 시작될 때 호출되는 application(_:didFinishLaunchingWithOptions:) 메서드를 포함하고 있습니다. 이 메서드는 앱이 구동되는 초기 설정을 수행하고, 앱이 백그라운드에서 실행되는 동안 발생하는 이벤트를 처리합니다. 또한 앱이 종료될 때 호출되는 applicationWillTerminate(_:) 메서드를 통해 앱이 종료되기 전에 필요한 작업을 처리할 수 있습니다.
상태 변화를 관리합니다: 앱이 활성화되거나 비활성화될 때 발생하는 이벤트를 처리하는 메서드를 제공합니다. 예를 들어, 앱이 백그라운드로 이동할 때 호출되는 applicationDidEnterBackground(_:) 메서드는 앱이 백그라운드에서 실행되는 동안 필요한 작업을 처리할 수 있도록 합니다. 반대로, 앱이 다시 활성화될 때 호출되는 applicationWillEnterForeground(_:) 메서드는 앱이 다시 활성화될 때 필요한 작업을 처리할 수 있도록 합니다.
알림과 디바이스 이벤트 관리: AppDelegate는 푸시 알림, 로컬 알림 등과 같은 알림 이벤트를 처리하기 위한 메서드를 제공합니다. 또한 디바이스의 회전, 터치 등과 같은 이벤트에 대한 처리도 담당합니다.
다른 시스템 이벤트 관리: AppDelegate는 앱이 다른 시스템 이벤트에 응답할 수 있도록 합니다. 예를 들어, 앱이 백그라운드에서 실행 중인 동안 전화가 왔을 때 호출되는 application(_:didReceiveRemoteNotification:fetchCompletionHandler:) 메서드는 푸시 알림을 처리할 수 있도록 합니다.
이러한 기능들을 통해 AppDelegate.swift 파일은 앱의 전반적인 동작을 관리하고, 앱의 핵심 로직과 UI 요소들 사이의 연결 역할을 수행합니다.
SceneDelegate.swift
SceneDelegate.swift 파일은 iOS 13 이후 도입된 개념인 'Scene'와 관련된 기능을 처리합니다.
'Scene'은 앱의 사용자 인터페이스와 관련된 모든 데이터와 상태를 포함하는 객체로서, 여러 개의 'Scene'을 이용해 앱이 여러 개의 사용자 인터페이스를 동시에 관리하게 할 수 있습니다. 이를 통해 멀티 윈도우 사용이 가능해집니다. SceneDelegate.swift 파일에서는 주로 다음과 같은 기능을 처리합니다:
1. Scene의 생명주기 관리: Scene이 생성되거나 소멸될 때, 또는 활성화 또는 비활성화 될 때 호출되는 메서드들을 정의합니다. 예를 들어 `sceneDidBecomeActive(_:)` 메서드는 Scene이 활성화될 때 호출되며, `sceneDidEnterBackground(_:)` 메서드는 Scene이 백그라운드로 들어갈 때 호출됩니다.
2. 사용자 인터페이스 설정: `scene(_:willConnectTo:options:)` 메서드에서는 앱의 사용자 인터페이스를 설정합니다. 이 메서드는 Scene이 처음 만들어질 때 호출되며, 주로 초기 ViewController를 설정하는데 사용됩니다.
3. 상태 복원: `stateRestorationActivity(for:)` 및 `scene(_:willContinueUserActivityWithType:)` 같은 메서드를 통해 앱의 상태 복원을 관리할 수 있습니다. 이 기능은 앱이 중단된 후 다시 시작될 때 이전의 상태를 복원하는 데 사용됩니다.
4. 외부 액션 처리: `scene(_:openURLContexts:)` 메서드를 통해 앱이 외부에서 전달받은 URL을 처리할 수 있습니다. 이 기능은 다른 앱으로부터 데이터를 전달받아 처리하는 데 사용됩니다. 따라서 SceneDelegate.swift는 앱의 여러 Scene들의 생명주기를 관리하고, 사용자 인터페이스를 설정하고, 상태 복원을 처리하고, 외부 액션을 처리하는 등의 역할을 합니다.
import Foundation
func calcBMI(weight : Double, height : Double) -> String{
let bmi = weight / (height*height*0.0001) // kg/m*m
let shortenedBmi = String(format: "%.1f", bmi)
var body = ""
if bmi >= 40{
body = "3단계 비만"
} else if bmi >= 30 && bmi < 40 {
body = "2단계 비만"
} else if bmi >= 25 && bmi < 30 {
body = "1단계 비만"
} else if bmi >= 18.5 && bmi < 25 {
body = "정상"
} else {
body = "저체중"
}
return "BMI:\(shortenedBmi), 판정:\(body)"
}
print(calcBMI(weight:62.5, height: 172.3))
함수를 사용한 BMI 계산기
// bmiMode.swift
import Foundation
class BMI {
var weight : Double
var height : Double
init(weight:Double, height:Double){
self.height = height
self.weight = weight
}
func calcBMI() -> String {
let bmi=weight/(height*height*0.0001)// kg/m*m
let shortenedBmi = String(format: "%.1f", bmi)
var body = ""
if bmi >= 40{
body = "3단계 비만"
} else if bmi >= 30 && bmi < 40 {
body = "2단계 비만"
} else if bmi >= 25 && bmi < 30 {
body = "1단계 비만"
} else if bmi >= 18.5 && bmi < 25 {
body = "정상"
} else {
body = "저체중"
}
return "BMI:\(shortenedBmi), 판정:\(body)"
}
}
var han = BMI(weight:62.5, height:172.3)
print(han.calcBMI())
func add(x: Int, y: Int) -> Int {
return(x+y)
}
print(add(x:10, y:20))
let addf = { (x:Int, y:Int) -> Int in
return (x+y)
}
print(addf(x:3, y:4))
//--> print(addf(3,4)
//main.swift:11:11: error: extraneous argument labels 'x:y:' in call
//print(addf(x:3, y:4)
closure로 호출할 시에는 argument label이 필요하지 않습니다
func mul(a: Int, b: Int) -> Int {
return a * b
}
let multiply = {(a: Int, b: Int) -> Int in
return a * b
}
print(mul(a:10, b:20))
print(multiply(10, 20))
let add = {(a: Int, b: Int) -> Int in
return a + b
}
print(add(10, 20))
func math(x: Int, y: Int, cal: (Int, Int) -> Int) -> Int {
return cal(x, y)
}
var result = math(x: 10, y: 20, cal: add) //return add(10,20)
print(result)
result = math(x: 10, y: 20, cal: multiply)//return multiply(10,20)
print(result)
result = math(x: 10, y: 20, cal: {(a: Int, b: Int) -> Int in
return a + b
}) //클로저 소스를 매개변수에 직접 작성
print(result)
result = math(x: 10, y: 20) {(a: Int, b: Int) -> Int in
return a + b
}//trailing closure
print(result)
후행 클로저(trailing closure)
클로저가 함수의 마지막 argument라면 마지막 매개변수명(cl)을 생략한 후 함수 소괄호 외부에 클로저를 작성
func someFun(cl: () -> Void) {
}
// trailing closure를 사용 안하면
someFun(cl: {
//closure’s body
})
// trailing closure 사용
someFun() {
//trailing closure's body goes here
}
func math(x: Int, y: Int, cal: (Int, Int) -> Int) -> Int {
return cal(x, y)
}
result = math(x: 10, y: 20) {(a: Int, b: Int) -> Int in
return a + b
}//trailing closure
property는 저장 프로퍼티(stored property)과 계산 프로퍼티(computed property)
class Man{
var age : Int = 1 //stored property는 초기값이 있어야 함
var weight : Double = 3.5
}
class Man{
var age : Int? //stored property는 초기값이 있어야 함, nil
var weight : Double!
}
class Man{
var age : Int = 1
var weight : Double = 3.5
func display(){
print("나이=\(age), 몸무게=\(weight)")
}
class func cM(){
print("cM은 클래스 메서드입니다.")
}
static func scM(){
print("scM은 클래스 메서드(static)")
}
}
var kim : Man = Man()
kim.display() //인스턴스 메서드는 인스턴스가 호출
Man.cM() //클래스 메서드는 클래스가 호출
Man.scM() //클래스 메서드는 클래스가 호출
클래스명.클래스메서드()
타입 메서드 또는 클래스 메서드는 클래스 레벨에서 동작
타입 메서드는 인스턴스 메서드와 동일한 방법으로 선언하지만 class 나 static 키워드를 앞에 붙여서 선언
class Man{
var age : Int = 1 // init 초기화 시 초기값 생략가능
var weight : Double = 3.5 // init 초기화 시 초기값 생략가능
func display(){
print("나이=\(age), 몸무게=\(weight)")
}
init(age: Int, yourWeight : Double){
self.age = age // this 대신 self 사용가능
weight = yourWeight
} //designated initializer
}
//var kim : Man = Man() //오류
//init()을 하나라도 직접 만들면 default initializer는 사라짐
var kim : Man = Man(age:10, yourWeight:20.5)
kim.display()
클래스, 구조체, 열거형(enum) 인스턴스가 생성되는 시점에서 해야 할 초기화 작업
인스턴스가 만들어지면서 자동 호출됨
init 메서드(생성자)
init() { }
designated initializer
- 모든 프로퍼티(age, weight)를 다 초기화시키는 생성자
init()을 하나라도 직접 만들면 기본적으로 만들어지는 눈에 안보이는 default initializer는 사라짐
for var i = 0; i < 10; i+=1
{ // for i in 0..<10 로 수정해야 함
print(i)
}
-------------------------------------
for var i in 0..<10
{
print(i)
}
----------------------------------
for _ in 0..<5 // _ <- 사용 가능
{
print("Han")
}
-------------------------------------
let name = ["a","b","c","d"]
for i in name[2...]
{
print(i)
}
----------------------------------------------------------
let numberOfLegs = ["Spider": 8, "Ant": 6, "Dog": 4]
//dictionary는 key:value형식의 배열
for (animalName, legCount) in numberOfLegs
{
print("\(animalName)s have \(legCount) legs")
}
-------------------------------------
// 감소하는 경우
for i in (5..<0).reversed()
{
print(i)
}
-------------------------------------
// 2씩 증가하는 경우
for i in stride(from: 0, to: 10, by: 2)
{
print(i)
}
-------------------------------------
다른 언어들과 다른 Swift의 for문 사용법...
for i in 1..<10
{
if i > 5 break
//error: expected '{' after 'if' condition
print(i)
}
Swift에선 조건식 다음 한 줄만 있어도 { } 중괄호를 사용해야 한다.
var a = 1
var b = 2
var c = 3
var d = 4
if a < b && d > c {
print("yes")
}
if a < b, d > c {
print("yes")
}
Swift if문에서 ,(콤마)를 사용할 경우 AND와 동일하다
let someCharacter: Character = "z"
switch someCharacter
{
case "a":
print("The first letter of the alphabet")
case "z":
print("The last letter of the alphabet")
default:
print("Some other character")
}
-------------------------------------------------
let anotherCharacter: Character = "a"
switch anotherCharacter
{
case "a", "A":
print("A 글자")
default:
print("A글자 아님")
}
----------------------------------------------------
let weight = 60.0
let height = 170.0
let bmi = weight / (height*height*0.0001) // kg/m*m
var body = ""
switch bmi {
case 40...:
body = "3단계 비만"
case 30..<40:
body = "2단계 비만"
case 25..<30:
body = "1단계 비만"
case 18.5..<25:
body = "정상"
default:
body = "저체중"
}
print("BMI:\(bmi), 판정:\(body)")
Switch~Case 문, break문이 포함되어 있다.
Switch~Case 문에서 ,(콤마)는 OR와 동일하다
Switch~Case 문에서도 범위지정 매칭을 사용할 수 있다.
var temperature = 60
switch (temperature)
{
case 0...49 where temperature % 2 == 0:
print("Cold and even")
case 50...79 where temperature % 2 == 0:
print("Warm and even")
case 80...110 where temperature % 2 == 0:
print("Hot and even")
default:
print("Temperature out of range or odd")
}
where절은 다양한 식에 부가적인 조건을 추가하기 위하여 사용한다
값이 속하는 범위뿐만 아니라 그 숫자가 홀수인지 짝수인지도 검사
var value = 4
switch (value)
{
case 4:
print("4")
fallthrough
case 3:
print("3")
fallthrough
case 2:
print("2")
fallthrough
default:
print("1")
}
내장되어 있는 break문에 걸리지않고 순차적으로 실행되기 위해선 fallthrough 이라는 키워드를 사용하면 된다
Swift에서 Optional은 값이 있을 수도, 없을 수도 있는 상황을 표현하기 위해 사용됩니다. 이는 Swift의 안전성(safety)에 중점을 둔 설계의 일부입니다.
Optional은 다음과 같은 상황에서 유용합니다:
값이 없는 상황 표현: 변수나 함수가 값을 반환하지 않는 경우를 안전하게 처리할 수 있습니다. 예를 들어, 딕셔너리에서 키로 값을 조회할 때 해당 키가 존재하지 않으면 nil을 반환합니다.
초기화되지 않은 상태 표현: 객체의 초기화 과정에서 일부 속성 값이 즉시 사용 가능하지 않거나 나중에 설정될 수 있어야 하는 경우 Optional을 사용할 수 있습니다.
옵셔널 체이닝(Optional Chaining): 옵셔널 체이닝은 옵셔널에 포함된 하위 속성에 접근하려 할 때 nil 검사를 반복적으로 해야 하는 번거로움을 줄여줍니다.
옵셔널 바인딩(Optional Binding): if-let 또는 while-let 구문과 함께 사용하여 옵셔널의 값을 안전하게 추출할 수 있습니다.
옵셔널 강제 추출(Forced Unwrapping): 개발자가 확신할 때만 사용해야 합니다. 값이 없다면(runtime 시점에) 앱 크래시를 유발합니다.
nil 병합 연산자(Nil-Coalescing Operator): 옵셔널 값이 nil인 경우 기본값(default value)을 제공하는 방법입니다.
Optional 없이 모든 것들을 처리하려고 하면 앱 크래시와 같은 문제가 발생할 가능성이 많아집니다. 따라서 Swift는 이러한 문제들로부터 개발자와 사용자를 보호하기 위해 Optional 개념을 도입했습니다.
var x : Int?
x = 10
print(x)
//Optional(10)
- Optional형은 초기값이 없다면 nil 값이 들어가고 값을 대입해준다 하여도 Optional( ) 으로 랩핑된다
var x : Double = 10.0
var y : Int = Int(x)
print(Int("123"))
print(Int("hi))
//Optional(123)
//nil
var x : Int? //옵셔널 정수형 변수 x 선언
var y : Int = 0
x = 10 // 주석처리하면?
print(x) // Optional(10)
print(x!) // nil값일때 unwrapping 시 오류발생
print(y)
x = x!+2
y = x! //가능?
강제 추출 연산자 !는 피연산자와 꽉 묶이는(tightly associates) 높은 우선순위를 가진다.
즉, 대부분의 다른 연산자보다 먼저 계산되며, 다른 연산자와 함께 사용되는 표현식에서는 피연산자에 밀접하게(bind tightly) 결합되어, 표현식에서 다른 연산자와는 무관하게 즉시 적용된다.
예를 들어, let x = optionalValue! + 5 표현식에서 강제 추출 연산자 !는 덧셈 연산자 +보다 높은 우선순위를 가지므로, 옵셔널 값이 5에 더해지기 전에 먼저 추출된다. 그러나 옵셔널 값이 nil인 경우 런타임 오류가 발생할 수 있으므로, 가능한한 강제 추출 대신 옵셔널 바인딩 또는 옵셔널 체인을 사용하는 것이 좋다. 이렇게 하면 코드를 보다 견고하게 만들어 crash를 피할 수 있다
- 강제 언래핑하는 또 다른 방법으로, 옵셔널 바인딩(optional binding)을 이용하여 옵셔널에 할당된 값을 임시 변수 또는 상수에 할당
if let constantname = optionalName{
//옵셔널 변수가 값이 있다면 언래핑해서 일반 상수 constantname에 대입하고 if문 실행
//값이 없다면 if문의 조건이 거짓이 되어 if문을 실행하지 않음
}
if var variablename = optionalName {
//옵셔널 변수가 값이 있다면 언래핑해서 일반 변수 variblename에 대입하고 if문 실행
//값이 없다면 if문의 조건이 거짓이 되어 if문을 실행하지 않음
}
var x : Int?
x = 10
if let xx = x { //옵셔널 변수 x가 값(10)이 있으므로 언래핑해서 일반 상수 xx에 대입하고 if문 실행
print(x,xx)
}
else {
print("nil")
}
var x1 : Int?
if let xx = x1 { //옵셔널 변수 x1이 값이 없어서 if문의 조건이 거짓이 되어 if문 실행하지 않고 else로 감
print(xx)
}
else {
print("nil")
}
여러 옵셔널 변수를 한번에 언래핑하는 방법은 콤마 사용
var pet1: String?
var pet2: String?
pet1 = "cat"
pet2 = "dog"
if let firstPet = pet1, let secondPet = pet2
{
print(firstPet, secondPet)
}
else
{
print("nil")
}
- Implicitly Unwrapped Optional
형 다음에 ?가 아닌 !를 쓰는 옵셔널 형 // Int!, String!
When you use an implicitly unwrapped optional value, Swift first tries to use it as an ordinary optional value; if it can’t be used as an optional, Swift force-unwraps the value.
일반 옵셔널 값으로 사용하려고 하지만, optional로 사용할 수 없는 경우 Swift는 값을 강제로 품
Optional로 사용되지 않으면 자동으로 unwrap함
- 두 가지 Optional 형 : Int? vs Int!
let x : Int? = 1
let y : Int = x!
let z = x
print(x,y,z) //Optional(1) 1 Optional(1)
print(type(of:x),type(of:y),type(of:z))
//Optional<Int> Int Optional<Int>
let a : Int! = 1 //Implicitly Unwrapped Optional
let b : Int = a //Optional로 사용되지 않으면 자동으로 unwrap함
let c : Int = a!
let d = a //Optional로 사용될 수 있으므로 Optional형임
let e = a + 1
print(a,b,c,d,e) //Optional(1) 1 1 Optional(1) 2
print(type(of:a),type(of:b),type(of:c),type(of:d), type(of:e))
//Optional<Int> Int Int Optional<Int> Int
Nil-Coalescing Operator (Nil합병연산자) ??
옵셔널변수 ?? nil일 때 할당되는 값
옵셔널 변수의 값이 nil이면 ?? 다음 값으로 할당됨
옵셔널 변수의 값이 nil이 아니면 언래핑된 값이 나옴
let defaultAge = 1
var age : Int?
//age = 3
print(age) // optional(3)
var myAge = age ?? 1
//age가 nil이 아니므로 언래핑된 값이 나옴
print(myAge) // 3
- Swift에서 Optional 값을 처리하는 방법은 여러 가지가 있습니다. 이는 옵셔널 값이 nil일 수도 있다는 것을 명시적으로 표현하고, 이를 안전하게 처리할 수 있는 방법을 제공합니다. (GPT에서 발췌)
강제 추출 (Forced Unwrapping): 옵셔널 변수 뒤에 느낌표 (!)를 붙여서 값을 강제로 추출합니다. 값이 nil인 경우에는 런타임 에러가 발생하므로 사용에 주의해야 합니다.
swift
let optionalInt: Int? = 10
let forcedUnwrappedInt: Int = optionalInt!
옵셔널 바인딩 (Optional Binding): if-let 또는 guard-let 구문을 사용하여 안전하게 옵셔널 값을 추출할 수 있습니다.
swift
// if-let 사용 예시
if let unwrappedInt = optionalInt {
print(unwrappedInt)
}
// guard-let 사용 예시
guard let unwrappedInt = optionalInt else {
return
}
print(unwrappedInt)
Nil 병합 연산자 (Nil-Coalescing Operator): ?? 연산자를 사용하여 옵셔널 값이 nil일 경우 기본값을 제공할 수 있습니다.
swift
let nonOptionalValue = optionalValue ?? defaultValue
옵셔널 체이닝 (Optional Chaining): 옵셔널 값에 접근하는 연산 중 어느 하나라도 실패(nil)한다면 전체 표현식은 실패(nil)를 반환합니다.
swift
let lengthOfFirstName = user.firstName?.count
맵(Map)과 플랫맵(FlatMap) 함수를 활용한 안전한 변환 및 추출:
map 함수는 옵셔널 값이 존재하는 경우 해당 값을 변환한 후 다시 옵셔널로 감싸서 반환합니다.
flatMap 함수는 map과 비슷하지만, 결과값을 다시 감싸지 않아 중첩된(optional of optional) 형태를 방지합니다.
Implicitly Unwrapped Optionals: 앱의 생명주기 동안 한번 초기화된 후 nil이 아니라고 확신할 수 있는 상황에서 사용됩니다. 일반 변수처럼 사용 가능하지만, 실제로는 Optional입니다.
위와 같은 방법들을 통해 Swift에서는 옵셔널 값을 안전하게 처리할 수 있습니다. 이를 통해 런타임 에러를 방지하고, 코드의 안정성을 높일 수 있습니다.
var a = 2
var b = 2.0
print(a/b);
print(a+b);
//main.swift:4:8: error: binary operator '/' cannot be applied to operands of type 'Int' and 'Double' print(a/b);
var x : Int = 10;
let y : Int = 20;
y = 30;
//error: cannot assign to value: 'y' is a 'let' constant
var z = 3;
print(x)
print(y)
print(z)
//main.swift:8:12: error: '=' must have consistent whitespace on both sides
let은 상수를 선언할때 사용되며
위와 같이 초깃값이 있을 경우에는 컴파일러가 타입 추론(type inference)을 하므로 데이터 타입을 명시할 필요 없다
print(1.0, 2.0, 3.0, 4.0, 5.0, separator: " ... ") // Prints "1.0 ... 2.0 ... 3.0 ... 4.0 ... 5.0"
for n in 1...5 { print(n, terminator: "") } // Prints "12345"
var x = 10
print(type(of:x))
let s = MemoryLayout.size(ofValue: x)//8
let t = MemoryLayout<Int>.size
print(s, t)
//Int
//8 8
swift 에서는 Int형의 크기가 8byte 이다
데이터타입의 크기들은 플랫폼에 따라 다르다. Int형을 예시로 들자면 대부분의 언어와 컴파일러에선 4Byte로 인식하여 사용하지만 Swift 같은 경우 Int형의 크기를 8Byte로 사용한다.
var x : Int = 10
print(x)
print("x")
print("\(x)")
print("값은 \(x)입니다.")
print("Int32 Min = \(Int32.min) Int32 Max = \(Int32.max)")
10
x
10
값은 10입니다.
Int32 Min = -2147483648 Int32 Max = 2147483647
원하는 값을 문자열로 출력하기 위해선 \( ) 를 사용한다
var userName : String = "Kim" // : String 생략하는 것이 일반적임
var age = 20
var message = "\(userName)의 나이는 \(age)입니다."
print(message) // Kim의 나이는 20입니다.
문자열 보간(string interpolation)을 사용하여 문자열과 변수, 상수, 표현식, 함수 호출의 조합으로 만들 수도 있다
타입 어노테이션(type annotation)
Int 타입의 userCount 라는 이름의 변수를 선언
var userCount : Int = 10 // : Int가 type annotation
선언부에 타입 어노테이션이 없으면 스위프트 컴파일러는 상수 또는 변수의 타입을 식별하기 위하여 타입 추론(type inference) 사용
해당 상수 또는 변수에 값이 할당되는 시점에서 그 값의 타입을 확인하고 그와 같은 타입처럼 사용
var signalStrength = 2.231 // var signalStrength : Double = 2.231
let companyName = "My Company"
signalStrength라는 변수를 Double 타입(스위프트의타입 추론에서 모든 부동 소수점 수는 Double 타입)
companyName이라는 상수는 String 타입으로 간주
상수를 선언할 때도 타입 어노테이션을 사용하면 나중에 코드에서 값을 할당할 수 있다.
let bookTitle: String
var book = true
if book {
bookTitle = "iOS"
}
else {
bookTitle = "Android"
}
print(bookTitle)
상수에는 값을 한 번만 할당할 수 있다
튜플은 Swift에서 가장 강력한 기능 중 하나이며 여러 값을 하나의 개체에 일시적으로 묶는 방법이다.
튜플에 저장되는 항목들은 어떠한 타입도 될 수 있으며, 저장된 값들이 모두 동일한 타입일 필요도 없다
let myTuple = (10, 12.1, "Hi")
var myString = myTuple.2
print(myString)
print(myTuple.1,myTuple.2,myTuple.0)