Xcode11 새로워진 ScrollView 만들기

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.


프로젝트를 진행하면서 scroll view를 구현할 일이 생겼는데, 그냥 뷰컨트롤러 위에 스크롤뷰를 올리고 (0,0,0,0)을 하는데 적용은 되지않고 계속 빨간줄만 뜬다.

찾아보니 이번 Xcode 11 버전에서 인터페이스 빌더로 스크롤뷰를 만들때 contentLayoutGuide, frameLayoutGuide가 기본적으로 활성화 되도록 추가되었다고 한다.

Xcode 11 Release Notes

Interface Builder

Content and Frame Layout guides are supported for UIScrollView and can be enabled in the Size inspector for more control over your scrollable content. (29711618)

릴리즈 노트를 보면 content 및 frame 레이아웃 가이드는 UIScrollView에서 지원되며, 스크롤 가능한 컨텐츠를 보다 효과적으로 제어할 수 있도록 Size inspector에서 활성화할 수 있다고 나와있다.

Size inspector에서 확인해보면 Content Layout Guides 체크박스가 추가되어있습니다. 스크롤뷰 생성시 기본적으로 활성화 되어있기 때문에 따로 체크할 필요는 없습니다.

contentLayoutGuide

ScrollView의 변환되지 않은 컨텐츠 사각형을 기반으로 하는 레이아웃 가이드
ScrollView의 컨텐츠 영역과 관련된 오토 레이아웃 제약 조건을 만드려면 이 레이아웃 가이드를 사용하면 된다

frameLayoutGuide

ScrollView의 변환되지 않은 프레임 사각형을 기반으로 하는 레이아웃 가이드
컨텐츠 사각형 반대로 ScrollView 자체의 프레임 사각형을 포함하는 오토 레이아웃 제약 조건을 만드려면 이 레이아웃 가이드를 사용하면 된다

Interface Builder로 ScrollView 만들기

ScrollView 추가

ScrollView를 추가하고 화살표를 눌러 ContentLayoutGuide와 FrameLayoutGuide가 포함되어있는걸 확인할 수 있다.

가장 간단하게 스크롤뷰를 구현하는 방법은 size inspector에 체크되어있는 content layout guids를 해제하면 이전에 했던 방법처럼 적용이 가능해진다.

ScrollView Constraints 설정

ScrollView를 추가했으니 알맞게 제약을 설정해준다.

우선 릴리즈된 xcode에서는 safe area에 맞춰 (0,0,0,0)을 해주면 아래와 같이 빨간줄이 뚝! 뜨게된다.

Content Layout Guide 설정

ScrollView안에 컨텐츠를 넣기 위한 상위 View 하나를 추가해준다.
이때 View의 제약조건을 ScrollView에 걸지 않고 Content Layout Guide에 걸어준다.

여전히 빨간줄이 떠있다.

Frame Layout Guide 설정

이제 Frame Layout Guide 에 Equal Widths 나 Equal Heights 제약을 추가해 고정시킬 방향을 설정한다.

  • Equal Widths는 가로길이를 Frame Layout Guide에 고정시켜 세로 스크롤이 필요할때 사용
  • Equal Heights는 세로길이를 Frame Layout Guide에 고정시켜 가로 스크롤이 필요할때 사용

새롭게 릴리즈된 xcode를 살펴보았으나.. 나는 스크롤뷰에 있어서 사실 더 편한 건지는 잘 모르겠다.

프레임워크와 라이브러리 차이점

|

개인 공부 내용을 정리한 글입니다.
잘못된 내용이 있다면 편하게 댓글 남겨주세요!


프레임워크

프로그램 개발에 투입되는 개발자들이 늘어남에 따라서(특히 객체지향 프로그래밍) 다양성 또한 비례되어 늘어나고, 전체 시스템의 통일 & 일관성의 부족함을 느끼게 되었다. 그래서 개발자의 자유를 제한하는 대신 일정한 테두리 안에서 일관되고 유지 보수를 쉽게 개발할 수 있는 환경인 프레임워크를 도입했다. 즉 프레임워크는 기본적인 뼈대가 이미 완성되어 있고 규칙이 존재하는 개발환경 을 생각하면 쉽게 이해 가능하다.

우리가 꼭 개발이 아니더라도 무언가를 조립하고자 물건을 구매했을때 기본적인 뼈대는 주어지는 상태에서 물건을 조립하게 된다. 책장을 조립한다고 하면 기본적으로 책장을 조립하는 뼈대는 비슷할 것이고 그 뼈대 안에서 원하는 책장을 조립해나가는 과정을 생각해보도록 하자. 즉, 특정한 틀을 만들어 놓고 거기에 살을 붙여 놓음으로써 프로그램을 만들 때 작업시간을 줄여주게 된다. 뼈대가 이미 만들어져 있어 거기에 살만 덧붙이면 완성이 되도록 공통된 함수 또는 클래스를 미리 만들어 놓는 것을 의미한다.

  • 개발자들이 따라야 할 가이드라인을 가진다.
  • 개발할 수 있는 범위가 정해져있다.
  • 개발자를 위한 다양한 도구들이 지원된다.

프레임워크는 뼈대나 기반구조. 프로그래밍을 진행할 때 필수적인 코드, 알고리즘 등과 같이 어느 정도의 구조를 제공해주기 때문에 프레임워크를 사용하는 프로그래머는 이 프레임워크의 뼈대 위에서 코드를 작성하여 프로그램을 개발하면된다.

프레임워크 장단점

  • 장점
    1. 개발 편의성 증가로 시간 단축에 용이
    2. 오류의 폭을 감소
    3. 코드 품질의 보장(어느정도)
    4. 유지보수에 용이
  • 단점
    1. 프레임워크 의존도가 높아지면 개발 능력이 저하됨
    2. 개발자의 자유도 저하

라이브러리

개발을 하다보면 공통적으로 반복되어 사용되는 기능들이나 특정한 기능들을 자주 마주하게 되고 그때마다 새로 알고리즘 혹은 코드를 짜는 것은 매우 비효율적이다. 따라서 잘 만들어진 모듈화된 코드를 가져다 쓰는 것이 간편하고 안정적인 필요와 수요가 생기며 만들어진 것으로 개발 시 활용 가능한 도구들을 모아 모듈화한 것 이라고 이해하면 된다.

잘 만들어진 네이게이션이 있다고 하자. 그러면 우리는 네비게이션을 만들기 위해 처음부터 하나하나 기능을 구현하기 보다는 잘 만들어진 네이게이션 기능을 쏙쏙 내가 원하는 곳에 넣어주면 된다. 즉 여러 라이브러리중에서 우리가 원하는 함수, 클래스들을 가져다 쓰는 것을 의미한다.

라이브러리는 특정 기능에 대한 도구 또는 함수들의 집합. 프로그래머가 어떠한 기능을 수행하기 위해서 도움을 주는 또는 필요한 것을 제공해주는 역할을하는 것이죠. 간단히 정리를 하자면 프로그램 기능 수행을 위해 활용 가능한 도구의 집합이다.

둘 사이의 차이점

프레임워크는 개발자들의 환경을 제한하는 대신 일정한 환경을 제공한다. 그러나 라이브러리는 내가 우너하는 코드를 내가 원할 때 원하는 곳에 가져다 넣으면 된다. 이때 환경은 없고 오로지 의도와 목적성만이 존재한다.

프레임워크는 단지 미리 만들어 둔 반제품이나, 확장해서 사용할 수 있도록 준비된 추상 라이브러리의 집합이 아니다.
프레임워크가 어떤 것인지 이해하려면 라이브러리와 프레임워크가 어떻게 다른지 알아야 한다.

라이브러리를 사용하는 애플리케이션 코드는 애플리케이션 흐름을 직접 제어한다.
단지 동작하는 중에 필요한 기능이 있을 때 능동적으로 라이브러리를 사용할 뿐이다.

반면에 프레임워크는 거꾸로 애플리케이션 코드가 프레임워크에 의해 사용된다.
프레임워크에는 분명한 제어의 역전 개념이 적용되어 있어야 합니다.

애플리케이션 코드는 프레임워크가 짜놓은 틀에서 수동적으로 동작해야 합니다.

프레임워크는 전체적인 흐름을 자체적으로 가지고 있어 프로그래머는 그 안에서 필요한 코드를 작성한다. 반면에 라이브러리는 프로그래머가 전체적인 흐름을 가지고 있어 라이브러리를 자신이 원하는 기능을 구현하고 싶을 때 가져다 사용할 수 있다. 흐름에 대해서 잘 이해하시는 것이 가장 중요하다.

간단히 프레임워크는 가져다가 사용한다는 것보다는 프레임워크라는 특정 공간에 들어가서 사용한다는 느낌이 더 강하다고 말할 수 있으며 라이브러리는 라이브러리 자체를 가져가 사용하고 호출하는 용도로 사용된다고 생각하시면 쉽게 이해하실 수 있을 것이다.

개발 디자인패턴(Singleton, MVC)

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.


디자인패턴이란?

개발에서 디자인 패턴이란, 다양한 개발 환경에서도 비슷한 문제가 발생할 수 있는데 이 비슷한 문제들을 해결하는 정형화된 해결책을 의미한다. 즉, 개발에서 비슷한 문제를 해결하는 일종의 템플릿이나 개발패턴을 말한다고 생각하면 쉽다. 이 패턴을 적용해 코드의 가독성, 효율성, 디버깅, 협업 등이 쉬워지게 만들수 있다.

Singleton 패턴

싱글턴 패턴은 어떤 클래스의 인스턴스가 단 하나만 존재하도록 강제하는 패턴을 의미한다. 앱 전체에서 해당 클래스의 인스턴스는 딱 하나만 만들어질 수 있게끔 설계하여 여러 객체들이 이를 참조하도록 하는 방식이다. 이는 데이터 자체가 하나의 원본으로 유지되어야 하거나, 인스턴스를 만드는 것이 리소스가 많이 드는 일이기 때문에 등 여러 이유로 사용되어지는 패턴이다.

UserDefault.standard  // 앱의 디폴트 설정상태를 정의, 저장하는 공간으로 하나의 앱에는 하나의 설정 인스턴스만 존재
UIApplication.shared  // 앱의 유저이벤트에 대응하는 라우팅을 담당하여 특성에 따라 하나의 인스턴스만 존재
UIScreen.main  // 유저가 들고있는 아이폰의 스크린자체를 의미하며 스크린이 하나니 하나의 인스턴스만 존재
FileManager.default  // 유저의 아이폰 하드드라이브에 저장된 파일들에 접근하는 것으로 하나의 인스턴스만 존재

싱글턴을 만들 때 가장 중요한 것은 인스턴스가 단 하나만 만들어질 수 있도록 확실하게 구조를 짜야한다는 것이다. 이를 위해서는 해당 인스턴스를 외부에서 새로 생성할 수 없도록 막아야 한다. init()을 private으로 선언해 외부에서 인스턴스를 만들 수 있는 여지를 완벽하게(원천적으로) 차단하는 방법이 있다.

class SingleTon {
  statie let shared = SingleTon()
  private init() {}
}

오직 타입프로퍼티인 shared를 통해서만 인스턴스를 활용할 수 있을 것이다.

MVC 구조 패턴

MVC구조(Model-View-Controller)는 프로그램을 짜는 대표적인 디자인패턴 중 하나이다. MVC의 Model에는 앱의 비즈니스 로직이나 데이터를 담당하는 코드를 담아놓고, View에는 사용자에게 보여지는 모든 객체들을 다루는 코드를 담아놓으며, Controller에는 Model과 View 사이 중간자 역할을 하는 코드를 담아놓는다. 이러한 패턴을 MVC 패턴이라고 칭한다.

이렇게 코드를 MVC 패턴으로 짜는 이유는 Model과 View 코드들을 각각 분리함으로써 범용성과 재사용성을 높이고, 각각의 역할을 분리하여 개발과 유지보수를 쉽게 하기 위함이다. Model과 View 코드는 분리되어야 하지 않는데 해당 코드들이 섞이기 시작한다면 해당 코드들의 범용성이 떨어져 다른곳에 재사용도 불가능할 뿐더러 코드 구조가 꼬여버려 분석이나 디버깅 및 확장이 어려운 코드가 되어버릴 위험이 커진다.

Model

앱의 비즈니스 로직과 데이터를 관장하는 영역이다. 앱의 두뇌역을 하는 것으로 생각하면 쉬는데 계산기앱이라면 계산하는 로직과 관련된 데이터가, 채팅앱이라면 채팅 기능 관련 로직과 데이터가 Model 영역에 포함되게 된다. 서버로부터 데이터를 불러오거나 데이터 정합성을 관리하는 것도 Model의 역할이다.

View

UI. 유저에게 데이터를 보여주거나, 유저 인터렉션(UI)을 담당하는 영역이다. UI와 UI로직을 담당한다고 보면 된다. ‘어떻게’ 보여질지에 대해서만 관장하며 어떤 것을 보여줄 지에 대해서는 알지 못한다. 단순히 보여지는 영역을 어떻게 어떤 모양으로 보여줄지만 담겨있지 그 안의 세부적인 데이터나 무엇이 넘어오는지에 대해서는 View에서 알 수 없음을 의미한다. 즉, 자신의 Controller가 누군지, 어떤 데이터가 보여지는 지 등에 대해서는 알 수 없다.

Controller

View와 Model을 이어주는 역할을 담당한다. View와 Model은 서로 독립적이어야 하기에 중간에서 Controller가 중간다리 역할을 해주게 된다. Controller는 View와 Model 사시에서 자유로이 소통을 하지만 반대로 View와 Model은 자신에게 연결되어있는 Controller가 누군지 알 수가 없다.


Controller to View: Outlet

Controller가 View에게 말을 걸기 위해서는 outlet 을 사용한다. 스토리보드에서 코드로 레이블이나 테이블뷰를 당겨오면 IBOutlet이 생기게 되는데 이게 바로 Controller가 View에게 말을 걸기 위해 해당 레이블이나 테이블 뷰 등의 인스턴스를 만드는 것을 의미한다. 스토리보드에서 코드영역으로 드래그를 하기에 View가 Controller에게 말을 거는 것처럼 보이겠지만 사실 그 반대이다. IB는 Interface Builder의 약자로 IBOutlet을 통해 Controller는 View에게 ‘너가 보여줄 이미지는 이것이다’ 등과 같은 구체적인 지시를 하는 것으로 이해해보자.

@IBOutlet weak var nameLabel: UILabel!
@IBOutlet weak var ageTextField: UITextField!

View to Controller

IBAction

View는 Controller의 꼭두각시일 뿐이지만 때에 따라 View가 Controller에게 말을 걸 수도 있다. 이는 슈퍼갑갑갑갑인 사용자가 View를 통해 메시지를 던질 수도 있기 때문이다. 간단하게 버튼을 생각하면 되는데 사용자가 바라보면 뷰에서 어떤 버튼을 눌러 이루어지는 메시지가 있을때 해당 버튼이 해야하는 일을 정해줘야 한다.

이를 처리하는 방법 중 하나가 Target-Action이다. Controller가 자신에게 과녁(Target)을 설정해놓고 View에게 그 과녁에 쏠 화살(Action)들을 건네주는 벙법이다.

@IBAction func nextBtn() {
  let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
  let vc: SecondViewController = storyboard.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController

  self.navigationController?.pushViewController(vc, animated: true)
}

버튼이 눌러져 Action이 발생하면 본 Controller의 nextBtn() 메서드가 Target으로 대응하게 될 것이다.

Delegation

두번째 방법은 Delegation Protocol을 사용하는 것이다. Controller class 안에 View class의 인스턴스를 만드는 IBOutlet처럼 View class 안에 Controller class를 만드는 것과 유사하다. 다만 class 대신 Protocol이 활용되는 것으로 Delegation은 View가 자신에 대한 제어권을 Controller에게 위임하는 protocol로서 앱을 사용하는 유저가 다음 5가지 행동을 하는 경우에 대한 권한이 위임된다.

  • will
  • did
  • should
  • data at
  • count

Controller to Model: 인스턴스

Model class의 인스턴스를 만든다.

Model to Controller: Notification 및 KVO

View가 실질적으로 Controller에게 말을 걸 수 없듯(다이렉트로) Model도 Controller에게 바로 말을 걸수는 없다. Model은 자신의 데이터가 변할때와 무엇인가 알려줄 필요가 있을 때, 라디오 스테이션처럼 자신의 주파수에 맞춘 Controller 및 Model들에게 관련 안내를 하는 구조로 교신을 하게 된다. 이러한 과정은 딕셔너리처럼 Key, Value를 관찰하는 Key-Value Observation(KVO)를 통해 수행되며 변경사항이 있을 경우 통지(Notification)가 가게 된다.

MVC의 단점

1. Massive View Controller

모델에 넣기도 애매하고 뷰에 넣기도 애매한 코드들은 모두 컨트롤러에 들어가게 되다보니 컨트롤러가 비대해지게 된다.

2. View와 Controller가 너무 친하다.

애플의 MVC 패턴은 기존 MVC패턴과는 조금 다르게 View와 Controller가 강하게 연결되어 있어 ViewController가 거의 모든일을 다 한다. ViewController에서는 Controller가 View의 라이프사이클에 관여하기 때문에 View와 Controller를 분리하기 어렵다. 그래서 앱을 테스트할 때 Model은 따로 분리되어 테스트 할 수 있어도 View와 Controller는 강하게 연결되어 있어 각각 테스트하기 어렵다고 한다.

참고한 블로그 참고한 블로그

iOS Application state(iOS 앱의 생명주기)

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.


일반적으로 생명주기를 생각해보면 우리가 홈버튼을 누르거나, 전화가 왔을때와 같이 앱이 화면상에 보이지 않는 Background 상태, 화면에 앱이 올라와있는 Foreground 상태를 떠올릴 수 있다. 제대로 된 앱을 만들기 위해서는 이 생명주기를 정확히 이해하는 것이 중요하며, 이러한 생명주기를 이해하기 이전에 iOS앱이 시작할 때 일어나는 일들을 짧게나마 이해하는 것 또한 중요하다. » 참고할 글: View Controller의 생명주기

더 근본적으로 이야기해서 생명주기에 대해 간략하게 정리해보자면,

생명주기라는 것은 앱의 최초 실행부터 앱이 완전히 종료되기까지 앱이 가지는 상태와 그 상태들 사이의 전이를 뜻한다. 앱의 상태는 앱이 현재 어떠한 것을 할 수 있는 가를 결정한다. 예로 들어, Foreground 상태의 애플리케이션은 유저와 직접 상호작용하기 때문에 시스템 자원 사용 등에서 우선순위를 갖는다. 반면 Background 상태의 앱은 스크린에 드러나지 않기 때문에 작업을 하지 않거나, 하더라도 최소한의 작업만 수행해야 한다. 앱의 상태가 변하게 되면, 이 상태에 따라 앱의 행동을 조절해줘야 하는데, 이 조절작업을 하기 위해 UIKit는 특정 위임객체(delegate object)에 메시지를 보내주게 된다.

iOS 13이후에는 SceneDelegate를 사용할 수 있다. 이는 햐나의 앱이 여러개의 UI를 띄울 수 있게 되면서, 이 UI가 별도의 생명주기를 가질 필요가 생겼기 때문으로 기존 AppDelegate가 수행했던 생명주기 관리의 많은 부분이 SceneDelegate로 옮겨가게 되었다. 여기서 AppDelegate는 앱의 초기 구동과 앱 전체에 관련된 이벤트의 처리정도 만을 담당하도록 기능이 축소되었다.
iOS 12 이하의 경우에는 여전히 생명주기 관리에 AppDelegate를 사용하며, iOS 13 이상이더라도 Scene을 지원하지 않게 만들었다면 여전히 AppDelegate를 사용한다.

Scene을 지원하지 않는 경우, 모든 생명주기 관련 이벤트들은 appDelegate에 전달된다. appDelegate객체는 앱의 모든 window를 관리하기 때문에 앱의 상태 변화는 앱의 모든 UI에 영향을 미친다.

Application state

iOS에서 앱은 5가지의 상태로 구분이 가능하며 항상 하나의 상태를 가지고 있다.

  • Not running(Unattached): 앱이 실행되지 않았거나 시스템에서 종료됨
  • In-Active: 앱이 Foreground에서 실행중이지만, 이벤트를 받을 수 없음 > 이 상태에 잠시 머물다가 다른 상태로 전이됨
  • Active: 앱이 화면에 떠 있을 때의 상태 > 이벤트를 받을 수 있음
  • Background: 앱이 백그라운드에서 코드를 실행중. 대부분의 앱은 일시중지 상태로 잠시 이 상태가 된다. 그러나 추가 시간을 요청하는 앱은 일정기간 동안 이 상태로 남아있을 수 있다. 또한 백그라운드로 직접 실행되는 앱은 비활성 상태 대신 이 상태로 전환된다
  • Suspended: 앱이 백그라운드에 있지만 코드를 실행하지 않음. 시스템은 앱을 자동으로 이 상태로 이동시키고 그렇게 하기 전에 앱에 알리지 않는다. 일시 중지 된 앱은 메모리에 남아는 있지만 코드는 실행되지 않는다. 메모리 부족상태가 발생하면 시스템은 예공ㅂㅅ이 일시 중단 된 앱을 제거하여 메모리를 확보한다.

사용자나 시스템이 새로운 scene을 만들어달라고 요청하면, UIKit는 이를 만들어 unattached 상태로 만든다. 사용자가 요청한 scene은 곧장 Foreground inactive 상태를 거쳐 active 상태가 되고, 시스템이 요청한 scene은 보통 Background 상태가 되어 이벤트를 처리한 뒤에 Foreground로 올라온다. 사용자가 앱의 UI를 닫게 되면, 해당하는 scene은 UIKit에 의해 Background 상태로 갔다가 Suspended 상태가 된다. UIKit는 언제든지 이 Background나 Suspended 상태인 scene을 회수해 unattached 상태로 되돌려 놓을 수 있다.

View Controller Life Cycle

앱의 라이프사이클은 앱을 터치해서 실행시키고 완전히 종료되기 까지 아래 단계로 요약 해볼 수 있다.

  1. main 함수 실행
  2. main 함수는 UIApplicationMain 함수를 호출
  3. UIApplicationMain함수는 앱의 본체에 해당하는 객체인 UIApplication 객체를 생성
  4. nib 파일을 사용하는 경우나, Info.plist 파일을 읽어들여 파일에 기록된 정보를 참고해 그 외에 필요한 데이터를 로드
  5. App Delegate 객체를 만들고 앱 객체와 연결해 Main를 만드는 등 실행에 필요한 준비
  6. 실행 완료를 앞두고 앱 객체가 App Delegate에게 application:didFinishLaunchingWithOptions: 메시지를 보냄

상태전이

사용자의 동작에 따라 앱의 상태가 변경된다.

  1. 사용자가 앱을 실행한다: Not running » In-Active » Active
  2. 앱 실행 도중 홈버튼을 누른다: Active » In-Active » Background
  3. 앱을 다시 켠다: Background » Active
  4. 앱이 백그라운에 있다가 Suspended 상태로 전이: Active » In-Active » Background » Suspended

앱이 In-Active 상태가 되는 시나리오를 설명해보자

우선 일반적으로 이런경우는 흔하지 않다.

가장 흔한 예시는 우리가 네이버에서 기사를 읽고있을때 기사를 카카오톡으로 공유하려고 해보자.
이때 네이버 앱 위로 카카오톡 앱이 실행되는데, 이때 네이버 앱은 In-Active 상태가 되어진다고 생각하면 된다.

Notification Center와 Notification +예제

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.


Notification

등록된 노티피케이션에 노티피케이션 센터를 통해 정보를 전달하기 위한 구조체  » 게임을 생각하면 쉬움(크아에서 물풍선에 갇히는 범위)

주요 프로퍼티

// name : 알림을 식별하는 태그
var name: Notification.Name

// object : 발송자가 옵저버에게 보내려고 하는 객체. 주로 발송자 객체를 전달하는 데 쓰임
var object: Any?

// userInfo : 노티피케이션과 관련된 값 또는 객체의 저장소
var userInfo: [AnyHashable : Any]?

예) 특정 행동으로 인해 작업이 시작되거나 완료되는 시점에 다른 인스턴스로 노티피케이션이 발생 시 필요한 데이터를 같이 넘겨 줄 수 있다. 간단한 예로 네트워킹을 이용하는 애플리케이션이라면 네트워킹이 시작 및 완료되는 시점, 음악 및 동영상 재생 등에도 재생이 끝나는 시점에 관련 정보를 넘겨 줄 수 있다.

NotificationCenter

등록된 옵저버에게 동시에 노티피케이션을 전달하는 클래스이다. NotificationCenter 클래스는 노티피케이션을 발송하면 노티피케이션 센터에서 메세지를 전달한 옵저버의 처리할 때까지 대기한다. 즉, 흐름이 동기적(synchronous)으로 흘러가게 되어 노티피케이션을 비동기적으로 사용하려면 NotificationQueue를 사용하면 된다

기본 노티피케이션 센터 얻기

// default : 애플리케이션의 기본 노티피케이션 센터
class var `default`: NotificationCenter { get }

옵저버 추가 및 제거

실습

Notification을 보낼 Request.swift 파일을 만들어준다.

import Foundation

// Notification 이름을 하나 만들어줌
let DidReceiveFriendNotification: Notification.Name = Notification.Name("DidReceiveFriend")

func requestFriend() {
    guard let url: URL = URL(string: "https://randomuser.me/api/?results=20&inc=name,email,picture") else {return}

    let session: URLSession = URLSession(configuration: .default)
    let dataTask: URLSessionDataTask = session.dataTask(with: url) { (data: Data?, response: URLResponse?, error: Error?) in

        if let error = error {
            print(error.localizedDescription)
            return
        }

        guard let data = data else {return}
        do {
            let apiResponse: APIResponse = try JSONDecoder().decode(APIResponse.self, from: data)

            // userInfo 딕셔너리에 friends와 받아온 results값을 실어서 보내도록 한다
            NotificationCenter.default.post(name: DidReceiveFriendNotification, object: nil, userInfo: ["friends": apiResponse.results])

        } catch(let err) {
            print(err.localizedDescription)
        }
    }
    dataTask.resume()
}

그리고 notification center를 통해 보낸 request를 받아주는 함수를 viewController에 작성해준다.

// request.swift에서 center통해 보냈으니 받아주는 함수
@objc func didReceiveFriendNotification(_ noti: Notification) {
    // friends를 vc의 프로퍼티에 셋팅해준다음 테이블뷰 리로드 해줘야 함
    // noti.UserInfo에 실어서 보냈던 정보를 friends라고 해서 해당 정보를 받아오도록 한다
    // UserInfo가 있는지 없는지 모르니까 ?로 까본다
    guard let friends: [Friend] = noti.userInfo?["friends"] as? [Friend] else { return }

    // 위에서 만들어준 friends를 넣어주면 됨
    self.friends = friends

    // notification을 발송하는 쓰레드와 받는 쪽도 같은 쓰레드에서 동작하도록 해야한다.
    // 따라서 메인쓰레드에서 동작해야하는 쓰레드는 반드시 해당 코드로 작성해주어야 한다.
    // 혹은 notification 센터를 호출할때 메인쓰레드에서 호출하도록 하면 되기도 한다.
    DispatchQueue.main.async {
        self.tableView.reloadData()
    }
}

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.

    // Notification을 받을 것이라고 센터에 알려줌
    // 내가 받고(self) 어떤 notification을 함수로 받을 것인지, 어떤 이름으로 가져온 것을 받을 것인지를 등록
    // notification 센터에 등록을 하고 notification이 발생하면 해당 메서드를 통해 알려주면 해당 메서드가 실행될 것이라고 알려줌
    NotificationCenter.default.addObserver(self, selector: #selector(self.didReceiveFriendNotification(_:)), name: DidReceiveFriendNotification, object: nil)
}

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    requestFriend()
}

notification은 잘 사용하면 굉장히 좋은 무기지만 잘못 사용하게 되면 굉장히 헷갈리게 된다.

따라서 해당 관련된 것은 상수로 잘 정리해놓고 key값도 정리하는것이 좋으며, 위 실습에서의 코드에서만 보아도 코드가 굉장히 분산히 되어보이는데, 그렇기 때문에 코드가 간단한 경우에는 안쓰는 것이 효율적이지만 한번에 여러 인스턴스에게 notification을 전달해야한다면 이를 사용하는 것이 좋다.