FitCast: 기온별 옷차림
구글 검색 "기온별 옷차림"

간절기만 되면 항상 외출 전에 "기온별 옷차림"을 검색하여 날씨 앱과 번갈아가면서 보게 된다.

이때마다 위젯으로 제공하면 좋겠다는 생각을 해서 어플을 찾아보면 앱스토어에 있는 어플들은 UI가 내 마음에 썩 들지는 않는달까..

그래서 이번에 직접 만들어보았다.


 

‎Fitcast: 기온별 옷차림

‎- 외출시간을 설정하여 해당 시간의 평균기온을 알 수 있습니다. - 24시간의 일기예보를 1시간 단위로 확인할 수 있습니다. - 위젯을 통해 현재 기온과 그에 맞는 옷차림을 확인할 수 있습니다.

apps.apple.com

2023년의 겨울은 따듯함과 극한 추위가 함께 있는 겨울이었다.

분명 4계절이 뚜렷한 건 알겠는데, 일주일 단위로 계절이 바뀌는 느낌이랄까..

그래서 더더욱 "기온별 옷차림"을 많이 검색해 보았다.

 

Fitcast 스크린샷

 

기본적으로 시간단위 날씨정보와 외출시간을 설정하여 해당 구간의 평균기온이 나오게 하였다.

미리 저장해 둔 기온별 옷차림이 평균기온에 맞게 화면 중앙에 나오는 형태이다.

 

크게 복잡한 로직이 요구되지 않는 앱이어서 3-4주면 만들겠거니 시작했는데, 두 달이나 걸려버렸다.

심지어 처음 구상했던 다른 지역 날씨 조회 기능은 빠진 상태로 출시하였다...

현재는 사용자의 위치를 조회하여 해당 지역 정보만 조회가 가능하다.

 

개인 프로젝트를 할 때마다 마주하는 순간이 바로 내가 구상했던 기능들을 다 구현하기 위해 데드라인을 미룰 것인가, 아니면 미구현된 부분은 추후에 업데이트하기로 하고 현재 구현된 부분까지만 마무리하고 배포할 것인가에 대한 혼자만의 줄다리기가 발생하게 된다는 점이다. 

 

현재 대한민국 날씨가 굉장히 변덕스럽다는 점을 감안해서 이번에는 일단 출시를 하고

내가 직접 사용해 보면서 불편한 점들을 개선해 나가는 쪽으로 결정하여 배포를 우선시하였다.

 

프로젝트를 진행하면서 처음에 가장 신경 썼던 부분은 iOS 순정 앱과 비슷한 UI 구성과 룩이었다.

아이폰 순정 날씨 위젯과 나란히 놓았을 때 위화감이 없었으면 해서 그렇게 구성하려 했고,

배경화면 색상과 UI구성을 최대한 흉내 내어 보았다.

 

이번에는 별도의 외부 라이브러리는 사용하지 않았고, 요즘 많이 사용한다는 SwiftUI 프레임워크를 이용하였다.

UIKit을 대체하기 위해 나온 건 아니지만, 확실히 UIKit에 비해 직관적이고 쉬워졌다고 생각한다. 

 

다음으로 서비스의 핵심인 WeatherKit.

날씨 정보를 가져오는 데 사용한 애플 측에서 제공하는 API다.

async / await 표현을 이번에 처음 마주해서 당황했는데,

다행히도 자주 도움을 받는 naljin님의 글 덕분에 기본적인 지식은 이해하였다.

 

다음 핵심인 WidgetKit. 마찬가지로 애플 측에서 제공하는 위젯 제작 프레임워크이다.

이번 프로젝트의 주요 목적이라고 할 수 있는 위젯 기능의 구현을 위해 필요했다.

당연하게도 처음 사용하다 보니 사용법을 이해하는데에 시간이 많이 걸렸는데, 그만큼

iOS에서 위젯이 어떻게 작동하는지 평소의 궁금증이 해결되어서 좋았다.

 

 

GitHub - hyun083/Fitcast

Contribute to hyun083/Fitcast development by creating an account on GitHub.

github.com

깃허브에도 신경쓰면서 작업을 했다. 아직도 서투르긴 하지만 우연히 좋은 Commit massage 작성법을 발견해서 이전에는 그냥 작업 끝나고 눌러대던 커밋버튼을 나름대로 작업의 구간과 의도를 나타내기 위해 메세지를 작성하고 정리해가면서 작업하였다.


노마드 코더 - 아이디어가 있다구? 창의력을 위한 조언 한마디

사실 이전까지만해도 "이미 있는 서비스네" 라는 결론으로 시작하지 않고 묻어둔 개인 프로젝트가 몇가지 있다.

Fitcast 또한 마찬가지로 이미 같은 기능을 제공하는 앱이 많이 출시되어있는 상태임에도 생각을 바꾸고 프로젝트를 시작하게된 계기가 유튜브에서 저 썸네일을 발견하고서 부터다.

 

간간히 즐겨보는 노마드 코더님의 영상인데, 요약하면 창의적인 서비스에 대한 이이디어가 떠오르더라도 이미 누군가 생각했을 가능성이 높으니, 해당 서비스가 존재하는지 구글링 하지 말고 아이디어에 대해서만 집중하여 나만의 창의성을 부여하라는 얘기다.

 

이미 있는 서비스인 것을 알지만 경쟁 서비스보다 내가 만든 서비스가 조금이라도 더 나은 점이 있다면 그것으로 차별점을 두는 것이 의미있지 않을까 라는 결론에 도달했다.

노마드코더 - 전설의 프로그래머 형님의 찐 공부법!

또하나 재밌게 본 영상이 천재들의 코딩 공부법.

Swift 창시자라는 썸네일이 내 시선을 사로잡았다.

 

개발실력을 쌓기 위해 책을 보고 암기하지말고 무언가를 만들어보고 그 과정에서 마주한 문제들을 해결하기 위해 지식들을 찾아가면서 성장하라는 얘기이다.

 

사실 개인 프로젝트를 시작하라는 문구보다 책을 보고 암기하지 말라는 문구가 더 와닿았다. 실제로 나는 정반대로 프로젝트보단 강의영상 시청 위주의 학습을 이어오고 있었고, 이것이 동기와 흥미가 금방 식어버린다는 것을 최근에 느꼈기 때문이다.

 

개발자는 현실의 문제를 IT기술로 해결하는 사람이라고 생각한다. 따라서 크건 작건 현실 속의 불편함을 해결해 나가는 과정을 통해 성장하는 것이 개발자로서의 흥미와 동기를 잃지 않는 방법이 아닐까 생각하여 프로젝트를 시작한 것이다.

 

작성하고 보니 나는 주위의 환경에 크게 영향을 받는 타입이라고 생각한다.

그분들에게 도움을 받았듯이 나도 언젠가 글속에서 언급한 개발자 분들 처럼 남들에게 도움을 주는 그런 사람이 되고 싶다.

https://www.youtube.com/watch?v=nqNLrpMQrU8 

Lazy Sequences in Swift Explained (Performance Tips) – iOS

유튜브 채널 iOS Academy에서 발견한 영상. lazy sequence에 관련된 영상이다. 재밌게 시청해서 가져왔다. 

 

lazy

우선 lazy 키워드에 대한 간략한 설명을 하자면 말 그대로 lazy하게 변수를 할당하는 키워드이다. 대다수의 언어가 그렇겠지만 swift에서 변수, 상수의 경우 우선적으로 초기화가 이루어져야지만 컴파일이 완료된다. 하지만 lazy의 경우, 해당 변수의 초기화가 완료되었다고 판단하고 컴파일이 우선적으로 이루어진다. 이후 lazy로 생성된 변수에 접근하게 될 경우에 뒤늦게 초기화가 이루어지게 된다. 해당 부분의 경우 cs193p swift강의를 보면서 처음 마주했는데, 사실 당시에는 필요성이 크게 와닿지 않았다. 

 

lazy sequence

앞선 얘기는 lazy stored property에 관련된 이야기였고 이번글은 lazy sequence에 관련된 이야기를 하려 한다. swift에서 collection 타입의 자료들을 다루게 되면 가장 많이 다루게 되는 부분이 고차함수가 아닐까 싶다. rest-api를 사용할 때 반환된 요청을 map() 혹은 filter()와 같은 고차함수들을 통해 파싱을 하게 되는데, 이때 입력되는 자료의 수가 많다면 성능 저하가 일어나기 쉬울 것이다. 해당 자료들을 모두 사용하게 된다면 어쩔 수 없으나 대다수의 경우 해당 자료에서 일부분의 자료만 뽑아내어 사용자에게 보여주는 일이 많다. 여기서 lazy sequence를 통해 어느 정도의 성능개선을 이루어낼 수 있다.

 

0부터 1,000,000이 담긴 배열을 생성, map() 메소드를 통해 원소값들을 가공한 뒤 필요한 자료를 호출한 모습이다. 1,000,001 개의 원소를 탐색, 마지막으로 threeTimesArr에 결과를 저장하는 과정이 더해져 1,000,002번의 자료 접근이 일어나게 된다. 곱셈이라는 단순한 작업이었지만 처리해야 할 자료의 수가 많고 더욱 복잡한 자료의 연산이 일어난다면 성능 저하를 유발하게 된다.

 

이번엔 lazy를 이용한 연산이다. 앞서 말했듯이 lazy는 해당 자료에 접근이 일어날 경우에 초기화가 이루어지게 된다. lazy를 통한 map()메소드는 마찬가지로 자료의 접근이 일어날 때에 필요한 부분에서 연산이 일어나게 되고, 자료를 반환하는 모습을 볼 수 있다. 따라서 arr [50]의 자료접근, threeTimesArr[50]에 자료를 저장하는 과정 총 두 번의 접근을 통해 값을 출력하는 모습을 보여준다. 개발과정에서 이러한 lazy sequence를 통해 성능개선을 이루어낼 수 있지 않을까 싶다.

 

이상 lazy 키워드를 통한 성능개선에 관련된 이야기였으며 아마도 iOS 개발을 하면서 정말 유용하게 사용할 수 있는 미세먼지팁이 되지 않을까 싶다. 혹시나 틀린 정보를 적었다면 성장중인 아기개발자를 위해 따끔한 지적 부탁드립니다.

foreground
사용자와의 상호작용을 처리하기 위해 CPU를 포함한 시스템 자원할당에 높은 우선순위를 가지게 됩니다.
시스템은 이러한 리소스 할당을 위해 백그라운드 상태의 앱을 종료하기도 합니다.

background
사용자 이벤트를 받기 어려운 상태입니다. 
가능한 적은 시스템 자원을 사용해야 하고, 시스템에 의해 종료되기도 합니다.

 

 

Apple Developer Documentation

 

developer.apple.com

The current state of your app determines what it can and can’t do at any time. For example, a foreground app has the user’s attention, so it has priority over system resources, including the CPU. By contrast, a background app must do as little work as possible, and preferably nothing, because it’s offscreen. As your app changes from state to state, you must adjust its behavior accordingly.

 

iOS 앱의 생명주기에 관련된 용어다. iOS는 총 다섯 개의 상태(state)가 존재하며, 생명주기에 따라 상태가 변화한다. 개발자는 앱의 생명주기를 관리하며 각 상태에 따른 적절한 동작들을 정의할 수 있다.

 

애플의 공식 문서에 따르면 foreground 상태의 경우 사용자의 상호작용을 최우선으로 취급해야 하기에, 시스템 자원을 최우선으로 할당받게 된다. 반면 background 상태의 경우 화면에 보이는 상태가 아니기 때문에, 가능한 적은 일을 해야 한다.

 

app-based life-cycle events

Not running

앱이 실행되지 않았거나, 완전히 종료되어 동작하지 않는 상태

Inactive

앱이 실행되면서 foreground 상태에 진입하지만 어떠한 이벤트도 받지 않는 상태. 앱의 상태전환과정에서 잠깐 머무는 단계

Active

앱이 실행 중이며, foreground에 있고, 사용자의 상호작용을 받고 있는 상태

Background

앱이 background에 있으며, 다른 앱으로 전환되거나, 홈버튼을 눌러 나갔을 때의 상태. 일정 시간이 지나면 Suspended 상태로 전환

Suspended

앱이 background상태에서 특별한 작업이 없을 경우 해당단계로 진입. 앱은 메모리상에 올라가 있지만, 아무 일도 하지 않기 때문에 배터리를 사용하지 않는다. iOS의 자원할당에 따라서 이 상태의 앱은 메모리에서 해제될 수 있다.

 

각 단계마다 UIKit은 UIApplicationDelegate 객체를 통해 생명주기 이벤트를 발생시켜 개발자에게 알려주는데, 해당 부분을 통해 개발자는 앱의 생명주기에 따른 앱의 동작을 제어하게 된다.

 

여기까지 iOS13까지의 앱 생명주기에 대한 내용이며, iOS13 이후에는 약간의 변화가 생겼다. iOS13 업데이트부터 아이패드에서 멀티윈도우를 지원하기 시작했고, 하나의 앱 프로세스에서 하나 이상의 UI를 제공할 수 있게 되었다. 따라서 프로세스의 생명주기와 UI화면의 생명주기 부분을 나누어서 관리하기 시작했고, UI화면 관리 부분을 애플 측은 "scene-based life-cycle"로 정의했다.

 

scene-based life-cycle events

해당 부분에 대한 설명을 적으려면 주제를 조금 벗어나게 되므로 다음글에 이어서 적도록 하겠다.

 

참고 블로그

https://icksw.tistory.com/178

https://icksw.tistory.com/137

프로그램 실행을 위한 진입 지점을 가리키는 어노테이션입니다.

 

[iOS] 앱이 시작할 때 main.c 에 있는 UIApplicationMain 함수에 의해서 생성되는 객체는 무엇인가?

Apple Developer Documentation developer.apple.com iOS 개발자라면 가장 근본적으로 알아야 할 App life Cycle에 관련된 질문이다. 질문에 대해 단답식으로 대답하자면 아래와 같이 대답할 수 있다. "UIApplication 객

hyun083.tistory.com

지난 글과 주제가 같은 글이다. 

 

사용자가 iOS앱을 클릭하면, UIKit 프레임 워크 안에 숨겨진 main() 함수가 수행되고, 뒤이어 UIApplicationMain() 함수가 수행되는데, 이때 UIApplicationMain() 함수를 호출하는 어노테이션이 바로 @UIApplicationMain 어노테이션이다. iOS 탬플릿을 생성하면 기본적으로 생성되는 파일 중 "appDelegate.swift"에서 해당 어노테이션이 자동으로 생성되는 것을 볼 수 있다. xcode12 기준으로 해당 어노테이션은 @main으로 변경되었다.

*swiftUI로 프로젝트를 생성한 경우, "OOOOApp.swift" 파일에서 찾아볼 수 있다.

 

 

GitHub - apple/swift-evolution: This maintains proposals for changes and user-visible enhancements to the Swift Programming Lang

This maintains proposals for changes and user-visible enhancements to the Swift Programming Language. - GitHub - apple/swift-evolution: This maintains proposals for changes and user-visible enhance...

github.com

@main: Type-Based Program Entry Points
A Swift language feature for designating a type as the entry point for beginning program execution. Instead of writing top-level code, users can use the @main attribute on a single type. Libraries and frameworks can then provide custom entry-point behavior through protocols or class inheritance.

 

애플이 작성한 swift-evolution 문서를 확인해 보면 프로그램을 실행하기 위한 진입지점으로 유형을 가리키는 기능이라고 적혀있다. 뒤에 이어지는 내용에는 사용자는 탑 레벨 코드를 적는 것 대신, 유형에 @main 속성을 사용할 수 있다고 한다. 완벽하게 이해되지는 않지만 뒤에 이어지는 글이 큰 힌트를 준다. "프로토콜과 클래스 상속을 통한 진입지점의 커스텀이 가능하다."

 

UIApplicationMain() 함수는 애플리케이션 객체와 애플리케이션 델리이트를 생성하고 이벤트 루프를 설정한다. 이때에 @main 어노테이션이 붙어있는 클래스(AppDelegate)를 인스턴스화하여 전달해 주게 되는 것이고, 이러한 과정에서 해당 클래스의 상속 클래스 이름과 채택한 프로토콜의 이름을 전달하는 것이 @main의 역할이라고 생각하면 될 것 같다.

 

Apple Developer Documentation

 

developer.apple.com

iOS 개발자라면 가장 근본적으로 알아야 할 App life Cycle에 관련된 질문이다. 질문에 대해 단답식으로 대답하자면 아래와 같이 대답할 수 있다.

"UIApplication 객체가 생성됩니다."

사용자가 앱 아이콘을 눌러 앱을 실행하게 되면 대략 사진과 같은 형태의 생명주기를 통해 메모리에 할당되었다가 소멸된다. 여기서 이번 글에 눈여겨볼 부분은 바로 UIApplicationMain() 호출 부분이다. swift이전에 obj-c로 작성되었던 앱은 c언어 기반이었기에 앱이 실행되면 운영체제가 가장 먼저 main.m 파일 안에 main() 함수를 호출하여 앱이 시작되었다고 한다.

 

swift가 c언어 기반은 아니지만 obj-c와 함께 사용되기 위한 언어여서인지, 동일하게 main() 함수를 호출을 해야하는데, 우선 xcode로 생성한 iOS 개발 템플릿에는 main.swift 파일은 보이지 않는다. 이유는 UIKit 프레임워크 안에 main() 함수를 숨겨놓았다고 한다. 따라서 앱이 실행되면 가장 먼저 운영체제가 개발자는 찾아볼 수 없는 main() 함수를 호출하고, main() 함수는 뒤이어 UIApplicationMain() 함수를 호출하여 UIApplication 객체가 생성이 되는 것이다. 그렇다면 UIApplication은 무엇일까?

 

UIApplication

 

Apple Developer Documentation

 

developer.apple.com

The centralized point of control and coordination for apps running in iOS.

문서에 따르면 iOS에서 실행중인 앱의 제어와 조정을 담당하는 중앙지점이라고 적혀있다. 즉 여기서부터 실직적인 앱이라고 부를 수 있고, 해당 객체가 생성되고 나서야 개발자가 작성한 코드대로 이벤트 처리 등 앱의 동작을 제어할 수 있는 것이다.

 

모든 iOS앱은 단 하나의 UIApplication 인스턴스를 가지며, 개발자가 원한다면 UIApplication의 shared 프로퍼티를 통해 객체에 접근할 수 있다고 한다. 사실 무슨 말인지는 정확하게 와닿지 않아서 이 부분에 대해서는 나중에 시간을 들여 더 깊이 알아봐야겠다.

 

참고 문서 및 블로그

https://atelier-chez-moi.tistory.com/29

https://zeddios.tistory.com/538

https://blog.naver.com/soojin_2604/222423840595

App Thining

 

https://help.apple.com/xcode/mac/current/#/devbbdc5ce4f

To see this page, you must enable JavaScript. Pour afficher cette page, vous devez activer JavaScript. Zur Anzeige dieser Seite müssen Sie JavaScript aktivieren. このページを表示するには、JavaScript を有効にする必要があります。

help.apple.com

애플리케이션을 설치할 때, 앱스토어와 운영체제가 디바이스의 환경에 맞게 설치하는 설치 최적화 기술이다.

필요한 만큼의 리소스만 다운로드하기에 적은 디스크 사용량, 빠른 다운로드를 제공한다.

해당 기술에는 slicing(슬라이싱), bitcode(비트 코드), on-demand resource(주문형 리소스)가 있다.

 

slicing

Slicing is the process of creating and delivering variants of the app bundle for different target devices and operating system versions.

슬라이싱은 여러 가지 디바이스와 운영체제를 위한 앱 번들의 variants(변형)을 생성 및 제공하는 과정을 뜻한다. variants(변형)에는 각 디바이스와 운영체제가 필요로 하는 실행 가능한 아키텍처와 리소스가 담겨있다고 한다. 개발자가 앱스토어 커넥트에 풀버전의 앱 빌드를 업로드하게 되면 앱스토어는 자동적으로 variants를 생성하고 전달한다고 한다.

유저가 앱스토어에서 앱을 다운로드하게 되면, 유저의 디바이스와 운영체제 버전에 맞는 variants를 다운로드하게 된다.

 

bitcode

Bitcode is an intermediate representation of a compiled program.

비트코드는 컴파일된 프로그램의 중간 표현이라고 한다. 즉, 기계 코드도 아니고, 프로그래밍 코드도 아닌 그 중간의 형태라고 보면 된다. 개발자는 앱스토어 커넥트에 자신의 앱에 비트 코드를 포함시켜 올릴 수 있으며 그러한 비트 코드는 컴파일되어 앱스토어에 연결된다. 애플은 업로드된 비트 코드를 통해서 개발자의 새로운 버전의 앱 업로드 없이도 앱 바이너리를 최적화할 수 있다고 한다.

*앱 바이너리 : 기계코드가 포함된 파일

 

자세한 부분까지 이해하기 쉽지 않으나, 본인이 보기엔 디바이스의 프로세서 아키텍처의 변경(32bit, 64bit)과 같은 부분에서 비트 코드가 없다면, 개발자가 새로 컴파일하여 새로운 앱을 배포해야했으나, 기계어도, 프로그래밍 언어도 아닌 중간 형태의 비트 코드를 통해서 새로운 아키텍처의 변경에도 자동적으로 최적화가 이루어진다는 얘기 같다. 추가적인 정보는 더 찾아봐야 할 거 같다.

 

on-demand resource

On-demand resources are resources—such as images and sounds—that you can tag with keywords and request in groups, by tag.

주문형 리소스는 이미지, 음원과 같은 앱의 리소스를 키워드로 태그 해놓고, 태그를 통해 그룹으로 요청이 가능한 리소스다.

앱스토어는 사용자에게 현재 필요한 리소스를 관리하고 다운로드하게 한다. 또한 주문형 리소스의 슬라이싱 작업을 통해 variants의 최적화 작업을 진행한다고 한다. 이러한 주문형 리소스는 다음과 같은 이점을 제공한다.

  • 앱의 사이즈가 작아지게 되고, 이를 통해 더 빠른 다운로드를 제공, 사용자가 앱의 최초 실행 경험을 개선하게 된다.
  • 사용자가 앱을 체험하면서 필요하게 되는 주문형 리소스들을 백그라운드에서 다운로드하게 된다.
  • 운영체제가 더 이상 필요로 하지 않는 주문형 리소스들은 제거하게 되고, 이로 인해 저장공간을 절약한다.

대표적인 예시를 들 수 있는 경우가 게임의 추가 리소스 다운이다. 일반적으로 이미지와 음원이 많은 게임 앱의 경우, 최초 실행을 위한 최소한의 리소스를 우선적으로 받고, 게임 실행 후 필요한 부분만 리소스를 추가적으로 다운로드하여 저장공간 절약, 실행을 위한 다운로드 시간 감소 등 대용량 앱 사용에 더욱 쾌적한 환경을 제공할 수 있다.

 

참고 자료 및 블로그

https://zeddios.tistory.com/655

https://jiseok-zip.tistory.com/entry/iOSApp-Thinning#toc-정의%20▾ 

https://developer.apple.com/library/archive/documentation/IDEs/Conceptual/iOS_Simulator_Guide/TestingontheiOSSimulator/TestingontheiOSSimulator.html

 

Testing and Debugging in Simulator

Retired Document Important: The information in this document is deprecated in Xcode 9. For Xcode 9 and later, see Simulator Help by choosing Help > Simulator Help in Simulator. Testing and Debugging in Simulator Simulator is a great tool for rapid prototyp

developer.apple.com

시뮬레이터에서 할 수 없는 동작들에 대해 생각해보았다. 시뮬레이터 관련 공식문서는 찾았지만 구버전의 xcode 시뮬레이터 관련 문서였고, 최근 버전의 레퍼런스 페이지에서는 시뮬레이터 관련 문서를 찾지 못했다. 일단 구버전 문서의 기준에 따르면 다음과 같은 동작들이 불가능하다.


Hardware Differences

Though most of the functionality of devices can be simulated in Simulator, some hardware features must be tested directly on a device. The hardware features that are not simulated as of iOS 8.2 are:

  • Motion support (accelerometer and gyroscope) are unsupported.
  • Audio and video input (camera and microphone) are unsupported.
  • Proximity sensor
  • Barometer
  • Ambient light sensor

API Differences

Simulator APIs don’t have all the features that are available on a device. For example, the APIs don’t offer:

  • Receiving and sending Apple push notifications
  • Privacy alerts for access to Photos, Contacts, Calendar, and Reminders
  • The UIBackgroundModes key
  • Handoff support

In addition, Simulator doesn’t support the following frameworks:

  • External Accessory
  • IOSurface
  • Media Player
  • Message UI
  • In UIKit, the UIVideoEditorController class

한 가지 의문인 점은 하드웨어에서의 오디오 입력 부분이다. 현시점 시뮬레이터 14.1 버전에서는 입출력 관련 메뉴가 따로 있다. 해당 메뉴를 통해 오디오 입력을 맥북의 마이크로 지정할 수 있고, 실제로 입력이 가능한지 텍스트 입력을 해보았다.

영어는 물론 한국어로도 잘 인식이 되는 모습을 확인할 수 있었다. 시뮬레이터가 오디오 입력을 지원하니 아마도 테스트용 앱 개발을 통해서도 입력을 지원하지 않을까 싶다. 나머지 가속도, 근접, 조도 센서와 같은 부분은 당연히 지원하지 않을 것으로 보인다. 기압계(barometer)도 적혀있는데, 아이폰에 운동량 측정을 위해 아이폰6부터 들어간 센서라고 한다. 해당 센서를 통해 개발자는 대략적인 고도를 알아낼 수 있다고 한다. 이런 부분은 처음 알았다.

이외에도 시뮬레이터의 메뉴들을 좀 더 찾아보니 페이스아이디 지원 부분도 있었다. 맥북에는 당연하게도 페이스 아이디 센서가 없으니 일치, 불일치와 같은 조건 상황만 지원하는 것으로 보인다. 

 

그리고 정보를 찾다가 많은 분들이 마우스로만 조작해야하므로 핀치 줌인/아웃 동작을 할 수 없다고 적어놨던데, 틀린 정보다. 옵션 키를 누르면 투 포인트로 제어할 수 있다. 해당 부분은 스탠퍼드 swift 강의로 유명한 cs193p 강의에서 확인했던 기능이다. 해당 부분은 본인도 지도 관련 앱을 개발할 당시 잘 사용했던 기능이다. 

option키를 누르고 있으면 x,y축 반전으로 동작하는 하나의 포인터가 추가되고, shift를 추가로 누르게 되면 평행이동하는 포인터로 제어가 가능하다.

이상 요약하자면 시뮬레이터로 할 수 없는 것들은 다음과 같다.

하드웨어

  • 가속도, 자이로스코프센서
  • 비디오 입력(카메라)
  • 근접센서
  • 기압 센서
  • 조도센서

API

  • 애플 푸시 알림
  • 사진, 연락처, 캘린더, 리마인더 등 개인 정보 접근
  • The UIBackgroundModes key
  • 핸드오프 기능

API 측면에는 적지 않은 더 많은 기능들이 적혀있는데 하나하나 확인하기에는 시간이 걸릴 것 같으므로 천천히 알아봐야겠다.

해당 질문은 첫 iOS 개발 면접에서 마주했던 질문이라 기억에 강하게 남아있는 질문이다. 우선 swift 레퍼런스 문서를 보면 다음과 같이 정의를 찾을 수 있다.

Frame

The frame rectangle, which describes the view’s location and size in its superview’s coordinate system.

Bounds

The bounds rectangle, which describes the view’s location and size in its own coordinate system.

Declaration

var bounds: CGRect { get set }
 
두 개념 모두 뷰의 위치와 사이즈를 반환하는 CGRect 타입 변수이다. 
하지만 frame은 해당 뷰가 속해있는 상위 뷰의 좌표계를 기준으로 정해진 크기와 좌표이고, bounds의 경우는 자기 자신의 좌표계를 기점으로 정해진 크기와 좌표이다. 
 

What is View?

UIView

An object that manages the content for a rectangular area on the screen.

뷰가 무엇인지부터 설명하자면 사용자의 앱 위에 그려지게 되는 Label, Switch, Button들과 같은 요소들을 모두 view라고 정의한다. SwiftUI 혹은 UIKit을 통해서 생성할 수 있으며, 이러한 뷰들은 UIView라는 클래스로 생성된 객체들이다. 해당 객체들을 통해 유저와의 상호작용이 가능하다.

 

Create a View

let rect = CGRect(x: 10, y: 10, width: 100, height: 100)
let myView = UIView(frame: rect)

뷰를 생성하기 위해선 크기와 좌표가 필요하며 이러한 좌표와 크기는 CGRect라는 형태로 표현된다. 크기와 좌표가 주어지면 해당 조건에 맞게 화면 위에 그려지게 되는 것이다.

 

CGRect

A structure that contains the location and dimensions of a rectangle.

Creating Rectangle Values

init(origin: CGPoint, size: CGSize)
Creates a rectangle with the specified origin and size.
init(x: Double, y: Double, width: Double, height: Double)
Creates a rectangle with coordinates and dimensions specified as floating-point values.
init(x: Int, y: Int, width: Int, height: Int)
Creates a rectangle with coordinates and dimensions specified as integer values.
init(x: CGFloat, y: CGFloat, width: CGFloat, height: CGFloat)
Creates a rectangle with coordinates and dimensions specified as CGFloat values.

그렇다면 CGRect는 무엇일까? 위에서 얘기했듯이 뷰의 크기와 좌표계를 담는 구조체이며, 초기화하는 방법에는 여러 가지 방법이 있지만 기본적으로 CGFloat이라는 실수 형태의 자료형을 통해 크기와 좌표를 표현한다.

여기서 CG는 CoreGraphics의 약자로 그래픽 관련 즉 화면 표현에 대한 자료들은 모두 CG로 시작하는 자료형을 사용하니 알아두면 처음 보는 자료형에 대해서도 어떠한 용도로 쓰이는지 유추할 수 있다.

 

iOS의 좌표계 시스템은 뷰의 좌측 상단이 원점(0,0)이며, 화면의 수직면이 y 축이며 수평면이 x 축이다. 즉 y값이 증가하게 되면 아래로 이동하고 x값이 증가하게 되면 우측으로 이동하게 된다. 뷰에 관한 자세한 설명은 해당 글에 따로 정리해두었다.

 

frame과 bounds의 사이즈는 항상 똑같은가?

 

직관적으로 둘의 차이점에 대해 알 수 있는 예시이다. B가 기울어진 상태에서 frame의 경우, 상위 뷰 A의 좌표계에서 차지하는 크기는 좌표를 기준으로 사각형을 그려 표현하게 때문에 B의 frame은 Point(140, 65), Size(320, 320)이 반환되며 bounds의 경우 Point(0,0), Size(200,250)이 반환된다.

 

결론

frame : 상위 뷰의 좌표계 기준으로 좌표 원점과 크기를 반환
bounds : 자기 자신의 좌표계 기준으로 좌표 원점과 크기를 반환

+ Recent posts