Swift 5. 스위프트 일지 (2)

키보드의 Return key로 앱 동작하기

  1. ViewController에 UITextFieldDelegate protocol 추가
    1. UITextField에 속한 여러 메소드를 사용하기 위함
  2. viewDidLoad()에 textfield IBOutlet의 delegate 추가 (textField.delegate = self)
    1. 이를 통해 textfield와 현재 viewcontroller 사이에 연결이 생김

자주 쓰이는 관련 함수/메소드
searchTextField.endEditing(true) -> 키보드를 dismiss하는 메소드
textFieldShouldReturn() -> 유저가 return 버튼을 누르는 이벤트 시 호출되는 함수
textFieldShouldEndEditing() -> endEditing(true)메소드 호출 직후 진짜 edit을 끝낼지 validate하는 함수
textFieldDidEndEditing() -> edit 상태가 끝난 뒤 호출되는 함수

UITextFieldDelegate 함수들은 자동적으로 해당 View 안에 있는 textfield들의 이벤트를 읽어서 반응함.
따라서 여러개의 textfield가 한 화면에 있는 경우 잘 구분해줘야 함

Protocol

Bird 클래스를 상속받은 Eagle과 Penguin 이란 클래스가 있다고 하자. 이 때 Bird 클래스에 새들의 기본적인 공통점인 fly() 함수를 만들어 놓았다면, 펭귄은 날 수 없음에도 불구하고 Bird 클래스를 상속 받았기 때문에 fly가 가능해진다. 이는 프로그램의 의도치 않은 동작으로 이어지기 쉽다. 또한 struct는 상속을 받을 수 없기 때문에 앞으로 모든 새들은 class로만 정의 되야 한다.

참고) C++와 같은 몇몇 프로그래밍 언어에서는 다중상속이 기본적으로 제공되지만 Swift는 하나의 클래스만 상속받을 수 있다. (프로토콜은 다중 채택 가능)

즉,
이유 1: class, struct 상관없이 무조건 상속 가능한 data structure가 필요
이유 2: 여러가지 behaviour (e.g. 새의 경우 ‘생물’로써의 behavior와 ‘날개 달린 것’의 behaviour가 공존)의 모듈화 필요

따라서 protocol이란 개념이 도입됐고, abstract class와 형태와 사용방법은 비슷하지만 본질은 말 그대로 ‘규약’을 class/struct에 포함하기 위함이다.

protocol은 function body를 가질 수 없고 선언만 할 수 있다.

Delegate Pattern

View에 있는 component (e.g. textfield, progress bar)의 변화를 custom ViewController에 전달할 때 간단하고 reusable하게 만들기 위해 사용되는 패턴이다.

Delegate protocol을 사용해서 구현한다.

예시
UITextField 라는 component엔 UITextFieldDelegate 타입의 delegate이라는 프로퍼티가 있다. (다른 타입의 delegate protocol은 못들어온다)
이 프로퍼티에 우리의 ViewController를 할당하기 위해 우리는 UITextFieldDelegate 프로토콜을 채택하고, 우리가 원하는 UITextField의 delegate에 스스로를 넣는다 (i.e. searchTextField.delegate = self)
이제 UITextField가 유저로부터 여러 인풋을 받을 때 protocol 메소드들 (delegate.textFieldDidBeginEditing() 등등)을 알아서 실행시킨다.
우리는 유저의 다양한 인풋에 대한 처리를 직접 구현할 필요없이 프로토콜에서 필요한 함수만 가져와서 사용하면 된다.

Delegate Pattern을 직접 구현할 때

Delegate Protocol과 해당 Delegate를 프로퍼티로 가지는 클래스를 보통 같은 파일안에 넣는다.
필수적으로 “ViewController의 update function” didUpdate뭐뭐()”error가 난 경우의 UI 핸들링 function” didFailGetting뭐뭐() 을 넣어주자

URLSession으로 http 통신

  1. URL object를 만든다.
  2. URLSession을 만든다.
  3. URLSession에 task를 정의하고 resume()을 통해 통신 시작

JSON decoding 하는 법

  1. JSONDecoder object를 만든다.
  2. 해당 오브젝트의 decode() 메소드를 try catch 블록안에 넣어서 디코드한다
  3. 디코드될 파일 구조에 맞춰서 Codable 프로토콜을 채택한 struct/class를 생성한다.
    json file의 키값과 프로퍼티르 이름을 반드시 맞춰줘야 한다!

꿀팁! Function auto completion에서 안에 parameter가 클로져일 때 클로져 부분을 선택한 후 엔터를 누르면 자동으로 trailing closure로 만들어줌 ㅎㅎ

Method Naming Convention

  1. delegate 프로퍼티를 가진 오브젝트는 ‘_ ‘를 붙여서 parameter name을 skip시킨다.
  2. External parameter name을 가독성을 위해 적절히 사용한다

⭐️⭐️ Handling Async

Future value를 비동기처리 하지 않고 그냥 사용하면 아래와 같은 에러가 발생할 것이다

“UILabel.text must be used from main thread only” Error

Completion handler 안에서 UI를 업데이트 할 때 발생하는 runtime error다.

이번 예시에서 completion handler는 URLSessions.dataTask에 들어있는 클로져였다.

Networking과 같은 long running task는 백그라운드에서 돌아간다. 만약 이런 task를 main thread가 하게 된다면 정보를 가져올 때 까지 UI를 포함한 앱 전부 멈춘것 처럼 보일 것이기 때문이다. 따라서 background fetch가 완료돼서 네트워크 정보를 받아올 때까지 기다려야 한다.

결국 Async operation을 처리하는 Swift의 방법을 찾으면 된다!

DispatchQueue

Swift는 DispatchQueue를 통해 async한 작업을 처리한다. closure안에 async한 동작들을 넣어주면 된다.

DispatchQueue.main.async {
            //async 작업처리
 }

extension

사용처는 여러가지 있겠지만, protocol의 단점을 보완해주는데 효과적인 방법이다.
Protocol은 특정 class나 struct 그룹에 필요한 behaviour만 정의하기 위해 빈 함수들과 프로퍼티로 작성하는 상속용 구조체다.
따라서 function body가 없기에 이를 채택하는 모든 구조체가 동일한 function이 필요할 때 일일이 똑같은 코드를 반복해줘야 한다.
이 때 해당 protocol의 function body를 채워주기 위해 사용할 수 있다.

이 외에도 많은 양의 코드를 분리시키는 refactoring을 할 때도 요긴하다

⭐️⭐️ Code snippet 추가하기

자주 사용하는 코드를 자동 완성 시켜주는 code snippet을 개인적으로 추가할 수 있는 기능이다 완전 신박쓰!

  1. 자주 사용하고 싶은 코드 부분을 drag > 우클릭 > Create Code Snippet.. 클릭
  2. 그 다음 code snippet의 이름, description을 적고, 만약 code block 부분을 만들어주고 싶으면 ‘<#WRITE SOMETHING HERE #> ‘
  3. 마지막으로 code snippet을 호출할 때 사용할 키워드 (이 경우엔 mark)를 Completion 에 적어주고 Done을 클릭


4. Completion란에 입력했던 키워드를 Xcode에서 치면 직접 작성한 이름, 설명, 등이 나온다.

Navigation Stack

default segue는 modal transition (bottom bar처럼 화면이 밑에서 위로 나오며 이전 화면이 상단에 계속 보임.)을 구현한다.
화면을 아예 덮어버리고 navigation stack에 쌓으려면 아래 방법을 쓰자

  1. Initial screen을 담당하는 View Controller를 선택
  2. 화면 최상단 메뉴바에서 Editor > Embed in > Navigation Controller

⭐️⭐️⭐️⭐️⭐️ Cocoapods 설치 주의사항

위 reference에서 terminal을 Rosetta로 돌리는 부분은 안해줘도 상관이 없다.

Pod install후 에러가 많아진 경우

  • cocoapods에서 GitHub -> releases랑 pull request 확인하며 최신 버젼과 바뀐 코드 찾아보기
  • 특정 버젼업데이트 시 신텍스: pod 'CLTypingLabel', '~> 0.4.0’

pod의 minimum deployment version 찾는 법:
해당 pod의 GitHub > .podspec 파일 확인 > s.platform 확인

error를 UI에 반영하기

error.localizedDescription 이란 메소드를 사용하면 에러를 사용자의 국가에 따라 다른 언어로 보여준다 이를 UILabel을 통해서 화면에 반환하면 된다.

⭐️⭐️ Table View

  1. View Controller에 Table View 오브젝트를 추가한다.
  2. 그 안에 Table View cell을 추가한다.
  3. Table View cell의 identifier 명을 적고 오른 쪽 pane에서 여러 설정을 해준다.
  4. 해당 Table View의 IBOutlet을 ViewController에 가져오고, UITableViewDataSource와 UITableViewDelegate 프로토콜을 delegate 패턴으로 구현해준다. (pickerView와 유사)
    • UITableViewDataSource
      tableView 메소드 두개는 필수로 작성 되야 한다. 하나는 tableView cell의 갯수, 나머지 하나는 각 index에 들어갈 내용이다. 각 index는 메소드의 파라미터 indexPath로 부터 가져오며, 몇번째 row인지 알려면 indexPath.row를 해주면 된다.

    • UITableViewDelegate
      보통 DataSource 프로토콜은 특정 IBOutlet의 개수와 내용을 정해주고, delegate 프로토콜은 해당 IBOutlet과 상호작용할 떄 (e.g 스와이프나 터치를 할 떄) 쓰이는 것 같다.

.xib 파일을 이용한 TableViewCell customize

https://kimcomdong.tistory.com/entry/iOS-%EA%B8%B0%EB%B3%B8-%EA%B0%9C%EB%85%90-%EC%A0%95%EB%A6%AC-8-UITableVieCell-Cellxib <- 여기 설명 잘돼있음

Firebase

Firebase Auth:
https://firebase.google.com/docs/auth/ios/start?hl=ko&authuser=0

FireStore read/write:
https://firebase.google.com/docs/firestore/quickstart?hl=ko&authuser=0

Listen to realtime updates:
https://firebase.google.com/docs/firestore/query-data/listen?hl=ko&authuser=0

Firestore Security rules:
https://firebase.google.com/docs/rules/get-started?authuser=0&hl=ko

⭐️⭐️ 키보드 생성/제거시 화면 맞춰주기 "IQKeyboard"

https://cocoapods.org/pods/IQKeyboardManagerSwift
cocoapod나 Swift Package Manage사용해서 가져오면 됌

자주 쓰는 property

IQKeyboard 사용하기
IQKeyboardManager.shared.enable = true
자동교정용 툴바 없애기
IQKeyboardManager.shared.enableAutoToolbar = false
화면 누르면 키보드 내려가기
IQKeyboardManager.shared.shouldResignOnTouchOutside = true
(SPM 사용법: https://green1229.tistory.com/66)

개인 팁! SPM을 사용하면 가끔 엄청 낮은 버전이 latest major version으로 설정되며 add package 하면 에러가 뜬다. 만약 이 때 패키지를 추가했다면 반드시 제거하고, 아니라면 그냥 github의 release note에서 맘에 드는 최신 버전을 직접 입력하면 패키지가 잘 설치 된다.

Navigation bar 변화

ios15부터 navigation bar가 스크롤 시 자동으로 사라지며 투명화 된다.
관련 포스트를 밑에 첨부한다
https://www.zehye.kr/ios/2021/10/19/iOS_ios15_navigation_bar/
https://developer.apple.com/forums/thread/682420

ViewController의 Lifecycle

viewDidLoad() -> viewWillAppear() -> viewDidAppear() -> viewWillDisappear() -> viewDidDisappear()

리눅스 터미널 shortcut command 모음

https://github.com/0nn0/terminal-mac-cheatsheet

Hex To RGB UI color

https://www.uicolor.io/#/hex-to-ui

⭐️⭐️ SwiftUI info.plist랑 LaunchScreen 어디??

Xcode 13부터 SwiftUI에 info.plist랑 LaunchScreen이 사라져서 이유를 찾아봤다.
info.plist의 경우 target에서 추가해주면 파일이 생성되고, Launchscreen은 설정에서 만져야 한다

info.plist 관련
https://useyourloaf.com/blog/xcode-13-missing-info.plist/

launchScreen.storyboard 관련
https://useyourloaf.com/blog/dropping-launch-storyboards/
https://www.avanderlee.com/xcode/launch-screen/

Custom Fonts 적용

  1. 새로운 폰트 (.ttf) 파일 다운로드 (https://fonts.google.com/)
  2. Xcode에서 프로젝트 타겟파일 바로 밑에 ‘fonts’ 폴더(그룹)을 만들고 폰트 넣어주기
  3. info.plist (SwiftUI의 경우 target의 info 탭에 들어가야함) 가서 “Fonts provided by application” 딕셔너리 추가해주고 그 안에 확장자명을 포함한 폰트명 (e.g. Pacifico-regular.ttf) 추가
  4. Swift 파일에 확장자명을 제외한 폰트명 (“Pacifico-regular”)를 적용

SwiftUI에서 struct의 property 바꾸기

struct의 property는 기본적으로 immutable이어서 내부 메소드로 바꿀 수가 없다.
SwiftUI의 ContentView struct에서 유저 interaction을 통해 이런 변수들을 바꾸고 싶으면 변수 선언 앞에 “@State”를 붙여준다 (mutating keyword를 붙여줄수가 없어서)

⭐️⭐️ List와 Identifiable 프로토콜로 동적 길이 리스트 만들기 (SwiftUI)

Flutter의 Listview builder와 비슷하다. 대신 ListView의 각 item으로 들어갈 구조체는 반드시 Identifiable protocol을 채택해야하고,
Identifiable protocol엔 반드시 ‘id: String”라는 unique한 필드가 필요하다.
헌데 http networking을 할 때 json file을 받아오는 구조체의 모든 프로퍼티는 json key의 이름을 그대로 따라가야 한다.
따라서 우리는 id: String이라는 computed property를 만들어서 각 파일의 고유한 값을 가져오거나, indexing code를 스스로 가져와서 unique한 값을 부여해야 한다.

⭐️ Observer Design Pattern

후에 RxSwift를 통해 더욱 쉽게 만들 수 있겠지만 일단 지금은 맨땅에 부딪혀보자

  1. data를 fetch하고 받아오는 class로 ObservableObject를 채택한다
  2. fetch된 data를 받아오는 변수를 선언하고, 앞에 “@Published” keyword를 붙인다.
    1. 변수가 fetch된 data를 받을 때 Dispatch.main.async 안에 넣어줘서 main thread에서 안정적으로 받아올 수 있게 한다
  3. 해당 class의 instance를 생성할 때 “@ObservedObject”를 붙인다.
  4. 이제 Published variable의 값이 변할 때마다 자동으로 view에 반영된다.
  5. View가 load될 때 Observed class instance에서 data를 받아온다.
    1. SwiftUI의 경우 .OnAppear { classInstance.fetchData() }
    2. AutoLayout의 경우 viewDidLoad에 넣어주기

좋은 웹페이지 즐겨찾기