Share
Sign In
Swift | 학습 내용 정리
iOS 개발자로 성장하면서 학습한 내용을 글로 풀어 기록합니다. 제 글에 대한 어떤 리액션도 다 완전 환영!!
[Swift - UIKit, CoreLocation] iOS에서 사용자 정보 권한과 위치 권한 획득
iOS, iPadOS와 같이 애플 기기에서 돌아가는 운영체제는 사용자의 개인정보 보호에 아주 민감하다는 것을 많은 기사와 사례를 통해서 알 수 있다. 애플은, 어떤 기기에서 돌아가는 앱이든, 사용자의 앱 사용 경험을 헤치지 않으면서 유저가 권한 부여에 동의한 정보에 대해서만 앱이 활용할 수 있는 강력한 통제 시스템을 구축하고 있다. 사용자 > 기술 이라는 기본적인 모토에서 앱이 사용자의 정보 통제권을 쉽게 가질 수 없도록 만든다. iOS의 메이저 버전이 올라가면서 사용자가 앱에 줄 수 있는 개인 정보 통제권 유형을 *더 세분화 하고 있다. * 위치 정보를 허용한다고 했을 때, 어느 순간부터 아래와 같은 선택지를 사용자에게 제공해주었다. (iOS13+) - 허용하지 않음 - 한 번만 허용 - 앱을 사용할 때만 허용 - 항상 허용 정리하면, 앱을 만들 때 사용자가 부여한 권한의 정도가 어떻게 되는지를 계속 신경써야 한다는 것이고, 사용자가 갑자기 해당 정보에 대한 권한을 차단하지는 않았는지를 계속 인앱 상황에서 모니터링 하고 있어야 한다는 점이다. 위치 정보에 대한 권한을 획득하고 획득한 위치 기반으로 현재 날씨를 보여주는 간단한 앱을 만들어 보면서 위치 서비스에 대한 권한 획득을 경험해봤다. 순서대로 기록해본다. 간단히 위치 정보 활용 동의받고 이용하기 info.plist에서 Privacy 항목 활성화하고 사용자 위치 정보를 수집하는 이유가 담긴 문구 수정하기 해당 항목에 작성하는 문구가 추후에 권한을 획득하는 Alert의 메시지에 반영된다. 위치 정보를 왜 수집하고, 어떻게 사용되는지 등에 대해 최대한 자세히 작성해야 앱 출시에 제한이 없다. 사용자가 애플 기기 시스템에서 위치 서비스에 대한 권한을 켜뒀는지 확인하기 CoreLocation 패키지를 가져오고 CLLocationManager 클래스의 인스턴스를 생성하여 CLLocationManagerDelegate 로 위치 권한을 획득하는 역할을 VC에 위임해준다. .locationServicesEnabled 라는 타입 메서드로 유저가 시스템 상에서 위치 서비스를 활성화했는지 확인한다. else 구문으로 빠지면, 보통 alert 등을 통해서 시스템 설정에서 위치 서비스를 켜달라는 식으로 사용자의 선제 액션을 유도할 수 있다. (혹은 이동시킬 수도 있겠다.) 사용자가 승인해준 우리 앱에 대한 위치 정보 활용 권한에 따라 위치 데이터를 사용한다. CLLocationManager가 가지고 있는 authorizationStatus 열거형을 통해 유저가 승인한 권한에 따른 분기를 처리해준다. .notDetermined : 뜻 그대로 '정해진 것이 없는' 상태다. 권한 획득 모달에서 유저가 권한을 선택하기 전의 상태다. 즉, 앱에서 아직 유저의 위치 정보를 전혀 사용할 수 있는 상태가 아니다. .denied : 권한 부여에 '거절'당한 상태다. 유저가 설정 앱에서 우리 앱에 대한 위치 서비스를 다시 부여하거나 모달에서 다시 권한을 부여하기 전까지 유저 위치 정보에 대한 서비스는 제공할 수 없다. .authorizedWhenInUse : 앱을 사용하는 동안 위치 정보를 사용할 수 있는 권한이 부여됐다. 임시적으로 한 번 할 수도 있고, 앱을 사용하는 동안 부여받을 수도 있다. .authorizedAlways : 앱이 백그라운드 상태여도 위치 정보를 사용할 수 있는 권한을 부여한다. *내가 위치 기반 서비스를 이용해본 경험상, 앱이 사용되는 동안 허용으로 설정한 다음 일정 기간이 지난 다음 백그라운드 상황에서 '항상 허용' 권한을 요청하는 모달이 뜨곤했다. .notDetermined 상태에서 매니저의 .requestWhenInUseAuthorization 메서드를 호출해서 모달을 띄워준다. authorizationStatus에 따라 권한 변화를 감지하고, 권한 획득 후 정보 사용이 시작된 순간 앱 서비스를 위한 로직이 돌아간다. locationManagerDidChangeAuthorization 메서드가 권한이 변경되는 순간을 감지한다. 이 메서드 내부에서는 처음부터 권한에 대한 검증이 필요하다. 개발자는 유저가 어떤 권한을 부여했는지 알 수 없기 때문이다. didUpdateLocations 값은 .startUpdatingLocation 메서드가 동작하는 순간에 위치 정보를 업데이트 하기 시작한다. .desiredAccuracy 속성에 부여한 정확성 값에 따라 위치가 어떻게 변경되었는지를 알아서 감지해서 위치 정보를 업데이트하여 배열로 돌려준다. 위치 정보를 한 번만 사용하고 추후에 업데이트 하지 않는 앱이라면, .stopUpdatingLocation 메서드로 위치 정보 업데이트를 종료할 수도 있겠다. 정리해보면, 유저 Privacy 정보를 획득하기 위해서는 시스템 상으로 우리가 사용하고자 하는 개인 정보 서비스를 유저가 활성화 해뒀는지 확인해야 한다. 비활성화 상태라면, 우리는 권한 자체를 요청할 수도 없다. 1에 대한 서비스를 활성화한 상태라면, 어느정도 권한을 줄 수 있는지 물어봐야 한다. 이 단계에서 유저가 거부(.denied)한다면 우리는 정보를 활용할 수 없다. 설정에서 유저가 권한을 활성화 해주길 기다려야 한다. 2에서 유저가 특정 상태로 권한을 부여해줬다면 그 권한 범위내에서 정보를 활용할 수 있다.
한결
[Swift - UIKit] iOS에서의 기본적인 Notification System
알림센터나 사이드 푸쉬 등으로 특정 블록 형태의 UI가 뜨는 것을 Notification 이라고 한다. 카카오톡에서 친구가 채팅을 보냈을 때, 쿠팡에서 특가 상품이 떴을 때, 주변에 토스를 사용하는 사용자가 발견되었을 때와 같은 상황에서 주로 알림이 온다. 이런 기본적인 '알림'이라고 하는 것을 다룰 수 있는게 iOS Notification System이다. 내장된 User Notification Framework를 이용하여 쉽게 알림을 구현할 수 있다. 리모트 방식의 알림은 서버를 통해 인증된 유저에게만 보여줄 수 있기 때문에 APNs 라고하는 서비스를 거쳐야 한다. Notification System의 두 가지 방식 Notification 방식은 Local, Remote 형태가 있다. 이름에서 두 방식의 차이를 대강 알 수 있다. Local 알림에 전달할 내용이 전부 앱에 들어있다. 루틴한 내용이나 비슷한 컨텐츠를 전달해주는 용도로 사용된다. (디데이, 루틴, 투두 어플 등) Remote 서버단에서 보내는 알림을 컨트롤하는 형식. (푸시 알림이라고도 함) (서버에서 데이터가 오기 때문에) 유저가 알 수 없는 시간에 서로 다른 컨텐츠를 전달해주는 용도로 사용된다. 광고, 개인화된 추천, 채팅 알림(ex. 카카오톡 채팅별 알림) 등의 알림이 리모트 형식이다. Notification System에서 유의해야 할 점 Notification이 동작하기 위해서는 반드시 유저에게 권한 동의를 받아야 한다. 아이폰의 여러 앱을 써보면 알겠지만, 앱을 처음 실행할 때나 앱을 실행시키고 있는 도중에도 계속 '알림'에 대한 권한을 획득하려고 하는 뷰가 나타난다. 어떤 앱에서는 '권한이 어떤 시점에서 부여되지 않았다'는 사실을 알려주는 alert을 보여줄 때도 있다. 애플에서는 알림에 대한 유저 동의 여부를 강하게 체크한다. 무분별하게 알림을 내뱉는 앱은 앱의 사용 경험을 넘어서 아이폰의 사용 경험 자체를 헤칠 수 있기 때문에 애플에서 권한 획득을 중요하게 생각하는 것 같다. 커머스나 앱 자체로 비즈니스 가치가 발생하는 서비스들은 사용자에게 알림 권한을 획득하기 위해서 노력한다. 앱이 종료된 시점에도 우리 서비스가 있고, 우리 서비스를 통해 새로운 이점을 얻을 수 있다는 사실을 알림을 통해 간편하게 전달할 수 있기 때문이다. 더불어, 애플은 Notification에 대해서 아래와 같은 정책을 운영하고 있다. Notification은 고유한 identifier를 가진다. 하나의 앱에서 최대 64개의 identifier를 운영할 수 있다. 64개의 idendifier를 넘기면 앱이 종료될 수 있다. (무분별한 알림을 방지하는 장치인 것 같다.) 64개를 넘기기 전에 알림을 리프레시 할 수 있는 앱 내부 정책, 코드 관리가 필요해 보인다. TimeInterval로 반복할 수 있는 최소 시간은 60초다. 60초 보다 적은 시간 단위로 동일한 알림을 반복할 수는 없다. (역시 무분별한 반복적인 알림을 방직하는 장치로 보인다.) Notification은 기본적으로 앱의 background에서 동작한다. background는 앱이 켜져있지 '않은' 상태를 의미한다. 반대는 foreground인데, delegation을 통해서 앱이 실행중인 상태에서도 Notification이 오도록 설정할 수는 있다. Notification Center나 배너 상태 등에서 유저가 Notification 자체를 인지했는지는 알 수 없다. 유저가 해당 알림을 터치했을 경우에만 확인할 수 있다. UNUserNotificationCenter를 이용해서 간단하게 Notification을 구현해보자 AppDelegate 단에서 앱이 런칭되는 시점에 Notification에 대한 권한을 획득하는 코드를 추가하자. UNAuthorizationOptions를 이용해서 획득하고자 하는 알림 형식을 정해줄 수 있다. UNMutableNotificationContent, UNTimeIntervalNotificationTrigger, UNNotificationRequest 클래스로 Notification을 위한 인스턴스를 생성하여 알림을 제어하는 코드를 작성할 수 있다.
한결
[Swift - UIKit] UIViewController 위에 여러 Protocol을 적용하여 동작하는 다양한 뷰 만들기
코드 베이스로 뷰를 그리는 연습을 하기 전, SB + Outlet 코드로 뷰를 그리는 연습을 하면서 최종적으로 만들어 본 TravelProject에 대한 정리 글입니다. 전체 코드는 해당 링크에서 볼 수 있습니다. Swift를 학습하면서 정말 다양한 뷰를 직접 만들어보고 있다. 24년 5월 27일부터 30일까지 스토리보드 기반으로 UIViewController 위에서 코드로 여러 화면을 그리는 연습을 했다. 그 과정에서 나 스스로 '이건 꼭 기록으로 남겨봐야겠다.' 하는 점들이 있어서 남겨본다. 1. UIViewController 위에 커스텀한 TableView 올려보기 (with. XIB) UIViewController에 UITable을 핸들링하기 위한 Delegation, Datasource 프로토콜을 적용해준다. *Protocol을 통해 delegation을 위임하는 것에 대한 내용은 요 링크로 짧게 작성해봤다. UITable에 들어갈 커스텀한 TableCell을 작업하기 위해 Swift 파일과 XIB 파일을 함께 생성해준다. XIB에서 작업한 커스텀 셀을 재사용이 가능한 셀로 만들기 위해 정확한 Identifier를 지정해줘야 한다. 이 부분 역시 UITableViewCell 클래스를 상속받는 클래스들이 identifier라는 멤버 변수를 가질 수 있도록 protocol + extension 조합으로 코드를 미리 작성해두면 편하다. UIViewController 위에 올려진 UITableView의 인스턴스에 커스텀한 Cell을 연결하고, 어떤 행에 어떤 셀을 어떻게 보여줄 것인지를 위임받은 코드에 설정해준다. 코드로 한 번 보자. 위 코드 형태가 Swift에서 테이블 뷰를 그리는 가장 기본적인 방법이다. cellForRowAt 에서 커스텀하게 생성한 TableViewCell 클래스의 인스턴스를 as 키워드로 정확하게 캐스팅 해줘야 해당 클래스 내부에 구현한 멤버 요소들을 불러와 사용할 수 있다. 2. 연결된 VC에 데이터 넘겨주기 테이블 뷰에서 특정 셀을 터치하여 선택할 경우, 연결된 페이지 뷰(UIViewController)로 데이터를 전달해야하는 경우가 있다. 여러 방법이 있겠지만, 가장 쉽다고 생각하는 방법은 해당 VC의 멤버 변수(Outlet 변수 포함)에 값을 할당하는 것이다. 테이블의 특정 셀이 선택되어 연결된 VC 인스턴스가 Present되기 전에 반드시 데이터 자체를 VC의 내부 멤버들에 할당하는 코드가 반영되어야 한다. VC가 Present되면 ViewDidLoad 메서드가 호출되고, 해당 뷰에 있는 각각의 뷰 객체들의 속성 값이 반영되어 있지 않으면 알 수 없는 nil 값을 만나서 앱이 종료된다. 다른 SB에 있는 VC를 찾아 navigating하는 방법에 대해서는 요 링크에 작성해뒀다. 코드로 다른 VC를 연결해주는 방법은 오히려 더 간단하다. 그냥 VC 인스턴스를 생성해주면 된다. 유저의 액션에 의해서 VC간 데이터가 잘 전송되면 다양한 형태의 UI로 앱을 구성하기 좋다. 3. UISearchBar + UISegmentedControl + UITableView 조합으로 필터링된 데이터 보여주기 유저가 원하는 조건으로 데이터를 필터링해서 보여줘야 할 경우가 많다. 검색을 할 수도 있고, 메뉴를 여러개 생성할 수도 있고, 조건 자체를 미리 여러 개 생성해 둘 수도 있다. 이번 프로젝트를 하면서 iOS에 내장된 SegmentedControl, SearchBar를 통해 데이터를 필터링 해볼 수 있었다. 물론 미리 정의된 Protocol을 불러와 역할을 ViewController에 위임하는 형식으로 코드를 작성했다. 필터링 할 데이터셋 확인하기 데이터를 필터링하려면 데이터가 어떤 형식으로 생겼는지 알아야 한다. 데이터는 기본적으로 (관계성이 있는 정형화되었다면) 동일한 키를 서로다른 값으로 채우는 형식이다. 이런 데이터를 특정 기준으로 필터링 하는데 가장 좋은 기준은 Bool 타입을 찾는 것이다. City라는 구조체는 domestic_travel (국내 여행 여부) 라는 키(사실 멤버 변수명이지만)에 Bool 타입의 값이 들어와야 한다. 필터링하기 딱 좋은 기준이다. 해당 키를 기반으로 .filter 메서드를 활용해 국내 여행 / 비국내 여행을 구조체 내에서 계산형 변수로 반환해줄 수 있다. View 단에서 로직을 세울 필요가 없다. 세그먼트의 선택에 따라서 필터링된 데이터 할당하기 데이터 처리가 준비되었다면, 이제 세그먼트 요소별 선택 값만 잘 지정해주면 된다. UISegmentedControl 객체에 내장된 setTitle 메서드로 세그먼트 선택지 이름을 부여할 수 있다. UIButton에 액션 타겟 함수를 지정해주는 것과 동일하게, 세그먼트 인스턴스에도 값이 변경될 때마다 불러낼 액션 타겟 함수를 지정할 수 있다. .selectedSegmentIndex 라는 접근자로 필터링 해두었던 데이터를 맵핑만 잘 해주면 된다. 필터링 된 데이터를 테이블에 보여주려면, 테이블 reload 함수를 호출하면 된다. 기준이 명확하다면, 열거형으로 기준을 명확하게 정의해주는 것도 좋을 것 같다. 검색어에 따라서 필터링된 데이터 보여주기 UISearchBar 역시 VC에 그 역할을 위임할 수 있다. (UISearchBarDelegate) extension 키워드로 VC에 서치바에 대한 역할 코드를 구분하여 작성하면 코드를 관리하기 편하다. 서치바에서 입력이 어떻게 이루어졌을 때, 검색어를 어떻게 처리해줄 지를 결정하는 것이 로직의 핵심이다. 아래 목록 중에 밑줄을 그어둔 부분이 서치바에서 내가 고려한 유저 이벤트다. 유저가 보고 싶은 데이터를 키워드로 검색했을 때 (검색 완료) → 해당 키워드를 포함하는 or 키워드와 일치하는 데이터를 필터링해서 보여준다. 유저가 서치바에 입력할 때마다 데이터를 검증하면서 해당하는 내용이 있으면 필터링해서 보여준다. 유저가 서치바에 입력한 데이터를 다 지우면, 처음 데이터를 다시 보여준다. 검색어를 어떻게 필터링 할 것인지는 개발자의 뜻에 달려있는 것 같다. 양이 정해진 데이터에서 검색을 할 때는 매번 필터링을 거쳐도 괜찮겠지만, 검색에 따라 API를 호출하여 외부 서버와 통신을 해야한다면 필터 함수에 적절한 지연이 필요해보인다. 1~3을 통해 서치바 + 세그먼트 컨트롤로 데이터를 필터링하여 뷰를 다르게 보여준 예시는 아래 영상과 같다. 4. MapKit을 이용해서 지도상에 주소의 위치를 표시하고, UITextField에 PickerView 삽입하여 위치 데이터 선택하기 iOS에서는 자체 내장 패키지로 MapKit을 지원한다. Apple Map이 있기 때문에, 해당 지도에 구현되어 있는 내부 API를 활용하여 앱 위에서 지도를 그려갈 수 있다. VC에서는 위에서 계속 해왔던 대로 Delegation을 잘 해주면 된다. MapKit을 활용하기 위해서는, 다른 어떤 웹 지도와 동일하게 latitude, langitude 값이 필요하다. 위치에 대한 이름도 같이 기본적으로 표시하면 참 좋겠다. MKMapViewDelgete 프로토콜에 내장된 setRegion API를 이용해서 위도, 경도, 주소의 이름 값으로 지도에 위치를 찾고, 반경 얼마만큼으로 보여줄 지를 결정할 수 있다. 이 API를 이용해서 주소값을 알고 있으면 동일 지도의 위치로 이동시켜주는 간단한 메서드를 작성해서 이용했다. setAddressOnMap 위치 이름, 위도, 경도 순서대로 데이터를 관리하면 좋을 것 같아서 Tuple 타입을 이용했다. 사실 데이터의 명시성을 더 높이기 위해서는 구조체의 인스턴스를 이용하는게 더 좋았지 싶다. MKPointAnnotation 인스턴스를 이용해서 해당하는 주소 포인트에 Pin (Apple이 만들어주는)을 꼽을수도 있다. setAddressOnMap 메서드를 PickerView 선택 액션에서 호출될 수 있도록 코드를 작성하면 딱 깔끔할 것 같다. UITextField에 UIPickerView를 InputView로 넣어준다. 그리고, 텍스트 필드의 UI를 마치 버튼인 것처럼 꾸며주면 그럴싸한 UI가 된다. 코드로 살펴보면 정말 별거 아닌 기능인데, 생각보다 UX적으로 신경써야 할 것이 많았던 것 같다. iOS 학습을 시작한지 2~3주차 시점에 스토리보드와 여러 View 클래스들을 이용해서 어느정도 동작하는 다양한 뷰를 반복적으로 만들어 보면서 확실히 개발 매커니즘에 조금은 익숙해질 수 있었다. 현재는 코드베이스로 UIKit 기반의 앱을 만드는 연습에 매진하고 있다. SB에서 코드베이스로 넘어온다고 코드의 대격변이 일어나지는 않기 때문에, 이번에 기록한 것들이 좋은 자양분이 될 것 같다.
한결
[Swift - System] Swift의 Sandbox 시스템과 UserDefaults.
App Sandbox 시스템 App Sandbox 시스템은 유저의 데이터를 보호하는 목적으로 커널 레벨에서 앱 자체의 데이터 접근을 제어할 수 있는 기술이다. (말이 좀 어렵다.) Swift는 기본적으로 C언어 - Objective-C 언어를 기반하기 때문에 다른 가상 머신 없이 바로 커널에 명령을 쏠 수 있다. 이 과정에서 앱이 동작하는데 필요한 특정 목적의 데이터(ex. 유저의 개인 정보라던지, 네트워크 연결성 등)를 위험에서 보호한다. 위험은 다양할 수 있는데, 앱 규모가 커질수록 더 많은 위험에 노출될 수 있겠다. 샌드박스에 보관하는 데이터를 앱과 어떻게 상호작용 할지를 개발자단에서 컨트롤 하면서, 유저로부터 올 수 있는 문제를 사전에 컨트롤 할 수 있다. 이 데이터를 앱이 사용한다는 것을 유저에게 특정 *인지 이벤트로 인식시키고, 앱이 그 데이터만 활용한다는 명확성을 부여해야 하는 시스템이 샌드박스 시스템이다. 명시적으로 허락 받지 않은 접근은 런타임이 차단한다. *내가 이해한 인지 이벤트는 다음과 같다. 우리가 앱을 설치하고 처음 켰을 때, 런치 스크린 이후에 각종 동의를 받는 경험을 했을 것이다. 이런 데이터에 대한 접근 및 활용 동의가 샌드박스를 이용해 인앱에서 데이터를 관리할 수 있는 권한을 부여한다고 볼 수 있다. - 위치 기반 데이터 활용 동의 - 사진 데이터 접근 및 활용 동의 우리가 앱스토어에서 특정 앱을 설치하면 iOS는 해당 앱을 위한 샌드박스 영역을 생성한다. 위 이미지처럼 Bundle, Data, iCloud 를 위한 공간이 생성된다. 쉽게 생각해서 유저의 의도와는 상관없이 앱의 운영을 위해 읽기, 쓰기를 위한 문서 저장 공간(애플은 데이터 파일을 Documents라고 한다 보통)을 만드는 것이다. 외부 다른 앱이나 사용자가 접근하기 힘든 공간을 이용해서 앱의 보안을 높이는 시스템이라고 볼 수 있다. 앱을 실행하면, 앱은 iOS 전체 파일관리 시스템에서 자기자신의 샌드박스를 바라본다. 샌드박스에서 필요한 데이터를 활용한다. (읽고, 쓰고, 업데이트하고 다 한다) 앱을 종료한다. 해당 앱을 위한 샌드박스 위치를 자체적으로 변경한다. 앱을 다시 실행한다. 해당 앱을 위한 변경된 샌드박스 위치를 바라볼 수 있게 한다. 즉, 특정 앱을 위한 샌드박스가 늘 같은 위치에 있는 것이 아니다. User Defaults 샌드박스 내부에 해당 앱을 위한 user's defaults database가 생성된다. 이 데이터베이스는 문서형 NoSQL 데이터베이스처럼 데이터를 키-값 형태로 저장한다. 정해진 여러 필드가 있고, 하나의 데이터가 해당 필드의 값들을 가지는 관계형 데이터베이스 형태는 아니다. UserDefaults로 관리되는 데이터는 앱이 가동되거나 어떤 이벤트를 처리할 때, 기본적인 상태들을 관리한다. (공식 문서에 따르면 - determine an app’s default state at startup or the way it acts by default.) 핵심 사용 방식은 공식 문서의 다음 문장으로 정리될 것 같다. UserDefaults 클래스가 데이터를 바라보고 있다가 캐싱을 통해 데이터를 앱에 필요하게 세팅한다. 당연히 서비스 레이어에 민감하게 적용될 수 있는 데이터나 처리는 좋지 않다. At runtime, you use UserDefaults objects to read the defaults that your app uses from a user’s defaults database. set 메서드를 통해서 키-값 형태로 값을 저장하고, UserDefaults 클래스의 인스턴스를 생성해서 저장한 값을 키로 조회할 수 있다. Dictionary를 사용하는 방식과 뭔가 비슷하다. 저장한 값은 UserDefault의 standard 메서드를 연결하여 조회할 수 있다. standard 메서드에는 불러온 값을 어떤 타입으로 가지고 올 것인지 정의할 수 있는 메서드가 함께한다. UserDefaults에 저장한 값이 정수형이라면 integer(forKey: String) 형태의 메서드를 사용하면 되는 것이다. 아직은 iOS 기반으로 앱 개발을 막 배우는 단계라서 앱 운영을 위한 데이터 관리 체계를 이정도로 정리해본다. iCloud나 외부 API를 통한 데이터 관리, 다른 앱과의 데이터 주고받기 (ex. iOS 기본 앱인 건강 앱의 데이터를 나이키 런클럽 앱에서 활용하는 방식) 방식을 더 공부해보면 이해하는 폭이 넓어질 것 같다. 참고한 문서 👇
한결
[Swift - UIKit] UIViewController에서의 View life Cycle API
UIViewController에서 View 객체를 로드하고 (그리고) 어떤 페이지로 전환되어 그 객체가 사라지는 과정을 관리하는 시스템을 내가 학습하기로는 View Life Cycle 이라고 한다. View 객체의 생명주기를 관리하는 UIViewController 클래스의 내장 오픈 메서드(API라고 부르겠다)는 여러가지가 있지만, 대표적으로 위 이미지의 5가지가 대표적일 것 같다. viewWillAppear viewIsAppearing viewDidAppear viewWillDisappear viewDidDisappear 사실, API의 prefix에 붙은 키워드만 보더라도 View 객체가 어떤 상태일지에 따라 서로 다른 API가 동작한다는 것을 쉽게 유추해 볼 수 있을 것 같다. (will, -ing, did) 모달이나 페이지 전환에 따라 특정 View 객체가 어떤 생명 주기를 가지는지를 각 API에 print 구문으로 확인해봤다. XCode로 앱을 처음 빌드할 때. UIController의 viewDidLoad가 가장 먼저 앱을 그리는 로직을 수행한다. 그리고 나서 순서대로 willAppear → isAppearing → didAppear API가 호출된다. (prefix 만으로도 쉽게 유추가 가능하다.) 버튼 액션으로 모달 페이지가 떠오를 때. 앱 화면 하단에서 떠오른 모달 페이지가 직전 View를 가리더라도, 해당 View 객체 생명 자체를 사라지게 하지 않는다. 즉, 이전 화면을 완전히 가리지 않는다면 해당 View는 생명선을 유지한다는 뜻이다. 해당 모달을 내리더라도 willAppear, isAppearing, didAppear API가 콘솔에 전혀 찍히지 않는다. 모달 페이지에서 특정 액션을 발생시켜서 뒤에 있는 View에 어떤 화면 전환이 필요하면, 그걸 처리하는 로딩 로직을 만들어야 할 것 같다. 버튼 액션으로 페이지 자체가 전환될 때. 화면이 Push되어서 완전 새로운 화면이 이전 View를 가릴 경우, 해당 View 객체는 생명을 다한다. 순서대로 willDisAppear, didDisappear API가 호출된다. 즉, 화면이 전환될 경우는 화면이 꺼짐으로 발생하는 데이터 저장이나, 업데이트 로직이 해당 생명주기 API에서 처리되어야 할 것이다.
한결
[Swift - UIKit] Alert, ActionSheet 구현하기
들어가면서. 웹이나 모바일 앱을 사용할 때, 유저에게 특정 정보를 '확인' 받거나 유저가 한 행동에 대해서 '주의'를 주거나 어떤 이유에서든 앱 내부에서 유저에게 정보를 주고 싶을 때 보통 Alert 라고 하는 요소를 활용한다. 웹 브라우저 중앙 상단에서 팝업 되거나, 모바일 앱 중앙 영역에서 작은 박스 형태로 팝업 되는 경우가 보통이다. iOS에서 동일한 역할을 하는 Alert, ActionSheet는 UIAlertController 클래스의 인스턴스로 생성한다. 문서를 살펴보면 알겠지만, UIAlertController는 기본적으로 UIViewController를 상속받는다. Alert 요소에 반영된 버튼을 통해 유저의 특정 행동(이벤트)을 받기 때문에 당연히 Control 요소가 있다. 이 말은 @IBAction을 코드로 지정해줄 수 있다는 것이겠다. 그러면 정말 간단하게 iOS Alert를 만드는 기록을 남겨본다. Alert 만들기. UIAlertController 클래스로 우리가 만들려고 하는 Alert 인스턴스의 속성들을 지정하여 생성하면 된다. 생성한 Alert 인스턴스에 액션을 추가해준다. 액션이라고 해서 거창한 것 같지만, Alert에 '확인', '취소'와 같은 버튼을 추가하는 것이라고 생각하면 편하다. 인스턴스에 addAction 메서드를 이용해서 등록하고 싶은 액션을 연결하면 된다. UIAlertAction 클래스로 인스턴스를 생성해 인자로 넘겨주면 된다. UIAlertAction의 style 속성에는 기본적으로 .default, .cancel, .destructive 라고 하는 미리 지정된 Enum의 값이 있다. Alert 버튼으로는 보통 '확인'을 받기 때문에 특정 액션을 잘 컨트롤 하지 않겠지만, *handler 속성에 특정 액션을 주입할 수도 있다. *내가 파악한 바로는, handler 속성에는 액션 객체들의 묶음이 오는데, { key in actions } 형태로 작성해서 등록하는 여러 액션을 순환하며 순차적으로 동작하는 방식인 것 같다. 공식 문서에 보면, Alert에 대한 스타일링, 액션을 지정하고 present 메서드를 이용해서 Alert 인스턴스를 등록하라고 되어 있다. 그러면 하면 된다. After configuring the alert controller with the actions and style you want, present it using the present(_:animated:completion:) method. 위 1 ~ 3의 코드를 통해 Alert를 띄워보면 아래와 같다. 아주 가볍게 잘 동작한다. ActionSheet 도 거의 동일하다. iPhone을 사용하면, Alert와 기능은 거의 비슷한데 기기의 아래에서부터 올라오는 Bottom-Up-Sheet이 있다. 이걸 ActionSheet이라고 한다. (안드로이드에서는 Dialog라고 한다고 한다.) Alert와 동일하게 문구, 버튼을 추가할 수 있다. 형태는 개발자가 지정하기 나름이다. UIAlertController에서 preferredStyle 속성의 값을 .actionSheet으로 변경해주면 된다.
한결
[Swift - UIKit] NavigationItem에서 #selector로 메서드 바인딩
UINavigationItem 아웃렛 위에 UIBarButtonItem을 설정하기 위해서는 다양한 방식을 사용할 수 있다. 버튼 UI기 때문에 버튼의 액션을 컨트롤 할 필요가 있다. 즉, Button에 target-action 설정이 필요하다. UIBarButtonItem 클래스 인스턴스를 코드로 생성해보면 삽입해야 하는 action 속성의 타입이 Selector 로 나오는 경우를 볼 수 있다. 이 경우에 @objc 라고하는 어트리뷰트를 붙인 메서드를 등록하게 된다. 어트리뷰트 이름에서 유추할 수 있듯이 해당 속성은 Objective-C Structure에 등록된 구조체 타입이다. Objective-C에서 동적인 무언가를 만들어내는 메서드를 바인딩 할 때 사용되었던 구조체다. Objective-의 경우런타임단에서 메서드를 바인딩하여 동적인 처리를 할 수 있게 해주는데, Swift에서는 컴파일 단계에서 메서드를 바인딩하기 때문에 Swift에서 Selector 타입을 사용하기 위해서 @objc 라고 하는 어트리뷰트가 필요해진 것이라고 보면 되겠다. (나는 그렇게 이해했다.) 정리하자면, Swift에서 Objective-C에 의존적인 API를 활용하기 위해서 @objc 라고 하는 어트리뷰트를 자주 사용하게 될 수 있다. 이는 Swift 컴파일러가 해당 어트리뷰트를 이해할 수 있게 도와주는 역할을 하는 것이라고 쉽게 이해하면 되겠다. 참고한 문서 👇
한결