CHAPTER 2 - INTRODUCTION TO SWIFT

dumdum·2021년 5월 9일

intro

playground: offers a place to experiment and play around with our code before including it in our applications.
어플을 바로 만드는것보다, playground으로 먼저 만들면, 어떻게 프로그램하고, fundamental frameworks로부터 이득을 얻을지 알 수 있음.

Listing 2-1: Playground template
instruction
import: 우리 코드에 framework의 pre-programmed code를 넣을꺼다.
var: text를 memory에 store.

import UIKit

var str = "Hello, playground"

2.2 Variables

variables: memory에 저장된 값을 표현하는 이름.

  • var이 정의되면, 이것의 이름은 유지된 채로 메모리에 저장된 값은 변한다.
  • 이름을 통해 값을 메모리에서 어디에 저장했는지 기억 할 필요 없이, 값을 저장하거나 retrieve할 수 있다.

Memory

  • computer의 memory는 huge honeycomb와 같고, 2가지의 state가 가능하다.( activated, deactivated)
  • 각각의 cell은 information의 작은 단위이고, cell의 sequence를 combine함으로써 다양한 state를 표현 할 수 있다.
  • 하나의 cell을 bit, 8bit의 group를 byte라고 부른다.

Primitive Data Types

primitive types : 프로그래밍 언어에서 data의 단위를 정의함. => 항상 같은 크기.
- => computer가 얼마나 많은 메모리가 사용되었는지 알 수 있음.
- int:no fractional component.
- 8byte -> swift, int8(1byte), int16(2byte), int32(4byte), int64(8byte)
- 이때, 맨 앞은 부호를 나타내기 위해 사용된다. 따라서, int8의 경우 -128~127의 범위를 갖는다.
- Uint: 부호가 할당되지 않는값. 마찬가지로, Uint8, Uint16...이 있다.
- float: 4byte
- double: 8byte

Declaration and Initialization

Declaration

  • 메모리에 데이터를 저장하고 싶을때, data type과 일치하는 type의 변수를 선언해야한다.
  • syntax [var name: type]
    - 메모리에 type에 해당하는 만큼의 공간을 reserve하고, 그 공간에 name을 assign한다.

Listing 2-2: Declaring variables

var mynumber: Int //creates a variabel called mynumber of type Int

Initialization

  • memory는 reusable resource이므로 딴애가 썼던 공간을 얘가 쓰게 될 수도 있다. 따라서, 새로운 변수가 메모리의 공간을 차지하기 위해서는 declaration한 후에 value를 넣어줘야한다.
  • => variable has a valid value all the time
  • syntax [name = value]

Listing 2-3: Initializing variables

var mynumber: Int
mynumber = 5

Listing 2-4: Declaring and initializing variables in the same statement

var mynumber: Int = 5 //one line of 2-3 code

Listing 2-5: Assigning a new value to a variable

var mynumber: Int = 5
mynumber = 87

variable은 constant가 아니므로 새로운 value값이 드갈 수 있다. => replace됨.

Listing 2-6: Declaring variables of different types

var mynumber: Int = 5
var myfavorite: Float = 14.129

변수가 한번 선언되면 우린 선언될떄의 type을 유지해야한다.

Listing 2-7: Declaring variables without specifying the type

var mynumber = 5
var myfavorite = 14.129

type을 정해주지 않고도 선언 할 수 있다.
swift는 datatype를 inter해서 변수를 생성한다.

Listing 2-8: Assigning variables to variables

var mynumber = 5
var myfavorite = mynumber //int

Arithmetic Operators

  • +addition, - substraction, * multiplication, / division, % modulus
  • 곱하기, 나누기 먼저하고 더하기, 빼기하므로 순서 정하고 싶으면 괄호해라.

Listing 2-9: Assigning the result of an operation to a variable

var mynumber = 5 + 10  // 15

Listing 2-10: Performing operations in variables of different type

var mynumber = 2 * 25  // 50
var anothernumber = 8 - 40 * 2  // -72
var myfraction = 5.0 / 2.0  // 2.5

어떻게 type을 정할 지 swift가 추론 쉽게 하기 위해 double로 그냥 두는게 좋다.

Listing 2-11: Inferring the type from an operation

var myfraction1 = 5.0 / 2.0  // 2.5
var myfraction2 = 5 / 2.0  // 2.5
var myfraction3 = 5 / 2  // 2

두 data type의 연산의 경우 가장 복잡한 data type(double)으로 할당한다.
따라서 myfraction3의 경우 모두 int이므로 output역시 int가 된다.
Listing 2-12: Calculating the remainder

var remainder1 = 11 % 3  // 2
var remainder2 = 20 % 8  // 4
var remainder3 = 5 % 2  // 1

Listing 2-13: Adding numbers to variables

var mynumber = 5
var total = mynumber + 10  // 15

Listing 2-14: Performing operations on the variable's current value

var mynumber = 5
mynumber = mynumber + 10  // 15

Listing 2-15: Modifying the variable's value using incremental operators

var mynumber = 5
mynumber += 4  // 9

+=: variable = variable + number
-=: variable = variable - number

Constants

  • memory는 switche들의 sequence이고, 엄청나게 많은 switch가 있다.
  • 변수가 어떤공간을 차지하고있는지 알기 위해서 우리는 각 변수에 addresses를 할당한다.
  • addresses는 각각의 byte에 따른 consecutive number이다.
  • primitive type의 경우 fixed-size이므로 addresses를 예측하기 쉽다.
  • => constant를 사용하면 값 역시 fixed-size가 되어, 아래의 장점이 적용된다.
    - secure way to store
    - help the system manage ethe memory
    - improving the performance of our app
    - ensuring a value does not change over time
    - 따라서, 변하지 않는 값은 무조건 let으로 선언하는게 좋다.
  • syntax [let name = number] variable의 syntax에서 var대신 let을 사용한다.
    Listing 2-16: Declaring and initializing a constant
let mynumber = 5

2.3 Data Types

table(character의 경우 unicode)에 일치하는 information을 보여줌.

Characters

  • emoji역시 character로 저장 가능.
    Listing 2-17: Declaring and initializing a Character variable
var myletter: Character = "A"

" " 를 사용하여 character를 초기화.

Strings

character의 string역시 변수다~
Listing 2-18: Declaring and initializing a String variable

let mytext: String = "My name is John"

Listing 2-19: Concatenating strings

var mytext = "My name is "
mytext = mytext + "John"  // "My name is John"

Listing 2-20: Concatenating strings with the + and += operators

let smileyface = "😀"
var mytext = "My name is John "
mytext += smileyface  // "My name is John 😀"

+=연산 여기서도 가능~

Listing 2-21: Including variables in strings

let age = 44
let mytext = "I am \(age) years old"  // "I am 44 years old"

Listing 2-22: Performing operations inside strings

let age = 44
let mytext = "I am \(age * 12) months old"  // "I am 528 months old"

Listing 2-23: Including special characters in a string

let text1 = "This is \"my\" age"  // "This is "my" age"
let text2 = #"This is "my" age"#  // "This is "my" age"

string 내에서 따옴표 쓰고싶을땐 backslash뒤에 "를 붙여주거나 양옆에 #

Listing 2-24: Generating multiple lines of text

let twolines = "This is the first line\nThis is the second line"
let multiline = """
This is the first line
This is the second line
This is the third line
"""

triple quotes로 여러줄 string만들기.

Booleans

  • store only two variable: true, false
  • purpose: identifying a condition의 process를 simplify.
    - 만약 condition의 state를 int로 정의하면, 어떤 숫자가 뭘 가리키는지 외워둬야함.
    - Conditionals, Loops에서 특히 좋음.
    Listing 2-25: Declaring a Boolean variable
var valid = true

Optionals

  • 초기화를 어떻게 해야할 지 모르겠으면 type이름 뒤에 ?를 붙이면된다.
  • optional type: 모든 data type가능. 값이 있거나, 텅 비거나.

Listing 2-26: Declaring an optional variable of type Int

var mynumber: Int?

Listing 2-27: Assigning new values to optional variables

var mynumber: Int?
mynumber = 5

Listing 2-28: Using nil to empty a variable

var mynumber: Int?
mynumber = 5
mynumber = nil

nil: empty state, indicate the absense of a value.

Listing 2-29: Unwrapping an optional variable

var mynumber: Int?
mynumber = 5
var total = mynumber! * 10  // 50

optional variable을 읽기위해서는, 이걸 unwrap해야하는데, variable 이름 뒤에 !를 붙이면된다.

  • unwarp 하기 전에 variable이 nil인지 아닌지 확인해줘야한다. nil값이 저장되어있을경우 error가 발생한다. conditional statemet로 이 조건을 확인하게된다.

Listing 2-30: Assigning an optional to another optional

var mynumber: Int?
mynumber = 5
var total = mynumber

만일 값을 읽어서 use하는게 아니라 그냥 다른 optional에 assign할때는 ! 안 써도 된다.

Listing 2-31: Declaring implicitly unwrapped optionals

var mynumber: Int!
mynumber = 5
var total = mynumber * 10  // 50

optional이 값을 갖고 있다는건 알지만, 어떤 값으로 초기화됐는지 모를경우.
예를들어, app이 실행되자마자 시스템으로부터 값을 받는 variable이 있다고 하자.
우린 이 variable를 선언할 때 값을 할당하진 않지만 user가 앱 실행시 값이 생긴다는건 안다.
이때 이 변수를 implicity unwrapped optionals로 선언하면된다.
2-31처럼, system은 이 변수를 optional로 알고있다가, 우리가 이 값을 사용할때 자동으로 unwrap한다. (사용시 !를 안 붙여도 된다. )

Tuples

  • 하나이상의 같거나 다른 type의 값들의 그룹.
  • () 괄호 안에 콤마를 통해 구분된다.
  • tuple 값을 읽기위해, 제일 앞에 값을 0으로 index가 부여된다. index를 통해 우리가 원하는 값에 access할 수 있다.
  • 튜플은 주로 function과 함께 사용한다.
    - code의 different pieces사이에서 정보를 공유할때 사용한다.
    - persistent data, complex data structure 를 저장할때는 collection을 사용한다.

Listing 2-32: Declaring a tuple with two values

var myname: (String, String) = ("John", "Doe")

Listing 2-33: Declaring a tuple with values of different type

var myname = ("John", "Doe", 44)

Listing 2-34: Reading a tuple

var myname = ("John", "Doe", 44)
var mytext = "\(myname.0) is \(myname.2) years old" // "John is 44 years old"

index를 통해 값 읽기.
Listing 2-35: Changing the value of a tuple

var myname = ("John", "Doe", 44)
myname.0 = "George"
var mytext = "\(myname.0) is \(myname.2) years old"

index를 통해 값 넣기. 새로운 값은 원래의 값과 같은 type이어야한다.
Listing 2-36: Declaring names for the values of a tuple

var myname = (name: "John", surname: "Doe", age: 44)
var mytext = "\(myname.name) is \(myname.age) years old"

index에서 어떤번호가 무엇을 가리키는지 기억하기 힘드므로, 각각의 값에 이름을 정해주고, 접근한다.
Listing 2-37: Creating multiple variables from the values of a tuple

var myname = ("John", "Doe", 44)
var (name, surname, age) = myname
var mytext = "\(name) \(surname) is \(age) years old"

tuple의 값을 튜플에 복사할 수도 있다.
Listing 2-38: Ignoring some of the values of a tuple

var myname = ("John", "Doe", 44)
var (name, _, age) = myname
var mytext = "\(name) is \(age) years old"

만일 몇몇의 값들만 필요하다면, 안 쓰는 값은 _(underscore)로 두면 된다.

2.4 Contidionals and Loops

  • 지금까지 배운것들은 각각의 instruction들을 sequence로 작성하고, 각 statemente들을 제시된 순서대로 한번만 실행한다.
  • 이러한 sequential flow를 break하기 위해 conditionals, loop이 만들어졌다.
  • 얘네는 group of instructions를 반복적으로 수행한다.

If and Else

  • if 뒤의 조건이 참일 경우에만 { }사이의 명령어들을 실행한다.
  • { } braces는 swift의 construction에서 많이 적용된다. 얘는 얘네만의 (own) variable들로 independent processing한다. 이 construction는 block이라 불린다.
  • Listing 2-39: Comparing two values with if
var age = 19
var message = "John is old"

if age < 21 {
   message = "John is young"
}
  • age: check value
  • message: condition에 따라 modify
  • if 문이 age와 21을 비교하는 연산을 수행하면, 그리고 이 condition이 참이면, { } 를 실행하고, message는 새로운 값이 할당됨.

comparison operator

  1. == : left가 right랑 같니?
  2. != : left가 right랑 다르니?
  3. : left가 right보다 크니?

  4. < : left가 right보다 작니?
  5. = : left가 right보다 크거나 같니?

  6. <= : left가 right보다 작거나 같니?

Listing 2-40: Comparing two values with the <= operator

var age = 21
var message = "John is old"
if age <= 21 {
   message = "John is young"
}

Listing 2-41: Conditions with Boolean values

var underage = true
var message = "John is allowed"
if underage {
   message = "John is underage"
}

만일 2개의 값만 필요하다면, boolean값을 통해 condition을 정의 할 수 있다. 얘는 바로 state를 return하므로 comparing이 필요 없다.

Listing 2-42: Using logical operators

var underage = true
var message = "John is underage"
if !underage {
   message = "John is allowed"
}

만일 statement가 false일 경우 수행하게 하고싶다면, condition의 state를 toggle시키는 logical operator !를 사용하면 된다.

logical operator

  1. ! NOT : toggle state
  2. && AND: check two condition, 둘다 true일때만 return true
  3. || OR: check two condition, 둘중 하나가 true일때 return true
  4. 연산의 순서는 왼쪽에서 오른쪽으로 이다. 순서를 명확하게 해주고싶으면 ( )해라.

Listing 2-43: Using logical operators to check several conditions

var smart = true
var age = 19
var message = "John is underage or dumb"

if (age < 21) && smart {
   message = "John is allowed"
}

condition은 괄호를 해주는게 좋다.

optional

  • optional의 경우 warp되어있으므로 바로 compare할 수 없다.
  • 이 값이 nil인지 확인 한 후 unwrap해야 쓸 수 있다.

Listing 2-44: Checking whether an optional contains a value or not

var count = 0
var myoptional: Int? = 5

if myoptional != nil {
   let uvalue = myoptional!
   count = count + uvalue  // 5
}

optional은 unwrap하기 전에 항상 값을 갖고있어야하므로, 2-44를 좀 더 편리하게 하는 방법이 있다. optional binding
Listing 2-45: Using optional binding to unwrap an optional variable

var count = 0
var myoptional: Int? = 5
if let uvalue = myoptional {
   count = count + uvalue  // 5
}

myoptional이 값을 갖고있을경우, uvalue상수에 그 값이 할당되고 { }이 실행된다. myopt가 nil이면 실행 안 된다.
Listing 2-46: Checking multiple conditions with Optional Binding

var count = 0
var myoptional: Int? = 5
if let uvalue = myoptional, uvalue == 5 {
   count = count + uvalue  // 5
}

여러 optional를 동시에 unwrap하고싶다면 expression들을 ,콤마로 구분해서 선언해야한다.
만일 myoptional에 값이 있따면, 그 값을 uvalue에 할당하고, uvalue가 5와 같다면 { }를 실행해라.

if else

  • 2 blocks로 표현된다.
  • 만약이게아니면 저걸해라 그런 느낌.
    Listing 2-47: Using if else to respond to both states of the condition
var mynumber = 6
if mynumber % 2 == 0 {
   mynumber = mynumber + 2  // 8
} else {
   mynumber = mynumber + 1
}

Listing 2-48: Concatenating if else instructions

var age = 19
var message = "The customer is "
if age < 21 {
   message += "underage"  // "The customer is underage"
} else if age > 21 {
   message += "allowed"
} else {
   message += "21 years old"
}

ternary operator

  • condition ? true vlaue : false value
    Listing 2-49: Implementing the ternary operator
var age = 19
var message = age < 21 ? "Underage" : "Allowed"  // "Underage"

Listing 2-50: Unwrapping an optional with a ternary operator

var age: Int? = 19
var realage = age != nil ? age! : 0  // 19

ternary로 optional를 unwrap할 수도 있다.
age가 nil이 아니면, age를 unwrap해서 realage에 할당하고, nil이면 0(default value)를 대입해라.

nil-coalescing operator

Listing 2-51: Unwrapping an optional with the nil-coalescing operator

var age: Int?
var maxage = age ?? 100  // 100

ternary랑 비슷하지만 얘는 unwrap내가 안 해줘도 된다.
age에 값이 할당되어있으면 age를 maxage에 대입하고, age가 nil이면 100으로 초기화해라.

Switch

  • if, else statement로 많은 conditon들을 check할 수 있지만, 얘네는 read, maintain이 어렵다.
  • 따라서, several condition을 verify해야한다면, switch instruction을 사용하는게 좋다.
  • switch는 value와 case keyword으로 나열된 value를 비교해서 일치하는 값에 해당하는 statement를 실행한다.
  • case statement는 가능한 모든 case들에 대해 작성하는게 좋지만, 그러지 못할 경우 default statement를 맨 끝에 적어줘야한다.

Listing 2-52: Checking conditions with switch

var age = 19
var message = ""
switch age {
   case 13:
      message = "Happy Bar Mitzvah!"
   case 16:
      message = "Sweet Sixteen!"
   case 21:
      message = "Welcome to Adulthood!"
   default:
      message = "Happy Birthday!"  // "Happy Birthday!"
}

Listing 2-53: Checking multiple conditions per case

var age = 6
var message = "You go to "
switch age {
   case 2, 3, 4:
      message += "Day Care"
   case 5, 6, 7, 8, 9, 10, 11:
      message += "Elementary School"  // "You go to Elementary School"
   case 12, 13, 14, 15, 16, 17:
      message += "High School"
   case 18, 19, 20, 21:
      message += "College"
   default:
      message += "Work"
}

하나 이상의 값들을 콤마로 구분해 case statement를 작성할 수 있다.

Listing 2-54: Matching a tuple in a switch statement

var message = ""
var ages = (10, 30)

switch ages {
   case (10, 20):
      message = "Too close"
   case (10, 30):
      message = "The right age"  // "The right age"
   case (10, 40):
      message = "Too far"
   default:
      message = "Way too far"
}
  • swtich statement는 tuple, string등 더 복잡한 data type에서도 사용 될 수 있다.
  • tuple의 경우, complex matching pattern을 위한 추가적인 option이 있다.
  • 2-54의 경우 항상 10에 대한 두번쨰값(index1)을 비교한다.
    Listing 2-55: Matching only the second value of a tuple
var message = ""
var ages = (10, 30)

switch ages {
   case (_, 20):
      message = "Too close"
   case (_, 30):
      message = "The right age"  // "The right age"
   case (_, 40):
      message = "Too far"
   default:
      message = "Way too far"
}

만일 어떤값과 비교하든 상관이 없다면 _ (underscore)를 사용한다.

Listing 2-56: Capturing values with constants

tuple값을 constant로 capture해서 case statement 내에서 접근 가능하게 한다.
; capture a value in a constant to be able to access it from the instruction of the case.

var message = ""
var ages = (10, 20)

switch ages {
   case (let x, 20):
      message = "Too close to \(x)"  // "Too close to 10"
   case (_, 30):
      message = "The right age"
   case (let x, 40):
      message = "Too far to \(x)"
   default:
      message = "Way too far"
}
  • 2-56의 경우, x라는 constant를 만들어서 tuple의 first value에 할당했다.
  • 우린 이를통해 그 값에 access할 수 있고, case statement내에서 customize해서 사용 할 수 있다.
  • 여기서는, string에 value를 추가했다.

Listing 2-57: Comparing values with where

clause where를 사용하여 추가적인 조건들을 check할 수 있다.

var message = ""
var ages = (10, 20)

switch ages {
   case let (x, y) where x > y:
      message = "Too young"
   case let (x, y) where x == y:
      message = "The same age"
   case let (x, y) where x < y:
      message = "Too old"  // "Too old"
   default:
      message = "Not found"
}

tuple의 값을 capture해서 tuple값들으 서로 비교할 수 있다.

While and Repeat While

  • conditional의 경우 condition을 만족하는 statement를 한번만 실행했지만, 여러번 실행하고 싶을떄도 있다.
  • while문의 경우 조건을 확인하고, 조건이 true일때까지 statement를 실행한다.

Listing 2-58: Using while to create a loop

var counter = 0
while counter < 5 {
   counter += 1
}

조건이 false가 되는 순간 statement는 실행되지 않는다. 만일 한번이라도 while문을 실행시키고 싶다면 repeat while statement를 작성하면된다.
Listing 2-59: Using repeat while to create a loop

var counter = 10
repeat {
   counter += 1
} while counter < 5

conter값은 5보다 크지만, while의 조건이 block의 뒤에 있으므로 repeat문은 조건이 체크되기 전에 실행된다.

For In

  • purpose: iterate over collections of elements.
  • for in loop을 실행하기 전에, system은 collection의 elements을 sequential order로 읽고, constant로 값을 assign한다. constant는 block내의 statements에서 처리된다.
  • for in loop이 수행되기위한 condition은 collection의 끝에 도달하는 것이다.
  • syntax [ for constant in collection { }]
    - constant: capture the value of each element:
    - collection: we want to iterate over. 우리가 반복하고 싶은 만큼.

Listing 2-60: Using for in to iterate over a string

var mytext = "Hello"
var message = ""

for letter in mytext {
   message += message != "" ? "-" : ""
   message += "\(letter)"
}

for in loop을 통해 mytext에서 한 charcter씩 순서대로 letter constant에 assign해서 { }를 실행한다.

  • 첫번째 statement에서 ternary operator를 사용해여 message가 empty인지 아닌지 검사하고, 만약 empty가 아니면 - character를 message의 끝에다 넣는다.
  • 두번째 statemet에서 letter의 지금 값을 message의 끝에 추가한다.
    - 예를들어, 처음엔 H가 할당되고, 이때 첫번쨰 statement에서 message는 empty이므로 message는 아무것도 추가되지 않다가, 두번쨰 statement에서 H값이 추가된다. 그 담에 e가 할당되면 첫번쨰 statement에서 -가 message에 추가되어 H-가 되고, 두번째 statement에서 e가 추가되어 message = "H-e"가 된다.
    - 최종적으로 for in loop가 끝나면, message = "H-e-l-l-o"가 된다.
  • Stings는 character값들의 collection이고, letter은 character이다.
    - character은 direct로 String이 될 수 없으므로 우린 letter를 string으로 먼저 바꿔주어야한다.
    - 이를 위해 for in loop의 두번쨰 statement에서 string interpolation을 사용했다. (letter) (ex. Listing 2-21)

Listing 2-61: Iterating over a string without reading the characters

var mytext = "Hello"
var counter = 0

for _ in mytext {
   counter += 1
}
var message = "The string contains \(counter) letters"  // 5

만일 constant를 block내에서 사용하지 않는다면, constant이름 대신에 _ (underscore)를 사용해주면된다.

Control Transfer Statements

Loop는 때때로 condition의 state와 관계없이 interrupt되어야한다.

  • continue: block 내에서 이후의 statement들을 무시하고 다시 loop를 시작하게 한다.
  • break: loop를 빠져나오게한다.
    Listing 2-62: Jumping to the next cycle of the loop
var mytext = "Hello"
var counter = 0

for letter in mytext {
   if letter == "l" {
      continue
   }
   counter += 1
}
var message = "The string contains \(counter) letters"  // 3

letter=l일 경우, counter += 1를 무시하고 mytext의 다음 character를 letter에 assign해서 for문을 다시 돌린다.
Listing 2-63: Interrupting the loop

var mytext = "Hello"
var counter = 0

for letter in mytext {
   if letter == "l" {
      break
   }
   counter += 1
}
var message = "The string contains \(counter) letters"  // 2

letter=l일 경우, for문을 끝낸다.
Listing 2-64: Ignoring values in a switch statement

var age = 19
var message = ""

switch age {
   case 13:
      message = "Happy Bar Mitzvah!"
   case 16:
      message = "Sweet Sixteen!"
   case 21:
      message = "Welcome to Adulthood!"
   default:
      break
}

break는 switch statement의 실행을 취소하는데도 유용하게 사용된다.
모든 가능한 값들을 case로 두면 좋겠지만, 그게 불가능할 땐 break instruction을 통해 그 외 값들을 무시 할 수 있다.

Guard

if else와 비슷하지만, condition이 false일 경우 조건을 실행한다.
guard가 if else보다 나은 점은 condition에서 define한 variable이나 constant를 block의 외부에서도 읽을 수 있다는것이다.
또한, loop에서 break, continue하기 위해 넣기도 좋다.
가독성을 위해 사용
return instruction과 같이 쓰이기도한다.
Listing 2-65: Interrupting a loop with guard

var mytext = "Hello"
var counter = 0

for letter in mytext {
   guard letter != "l" else {
      break
   }
   counter += 1
}
var message = "The string contains \(counter) letters"  // 2
```*
profile
Noteing WIll

0개의 댓글