카테고리 보관물: iOS

iOS 삽질 : iOS 11에서 SearchBar 높이 변경 이슈

iOS 11에서 UISearchBar의 높이가 기존 44에서 56으로 변경되었습니다. 대부분의 경우 큰 문제는 없지만 네비게이션바와 함께 사용하는 경우 높이 변경으로 인해 문제가 발생했습니다.

네비게이션바의 titleView에 UISearchBar를 사용하는 경우 네비게이션바의 높이인 44를 넘어가게 되어 검색이후의 화면에서 네비게이션바 하단에 검정색 공백이 생기는 문제가 있었는데요.

현상

아래 개발화면의 스샷은 왼쪽의 첫번째 화면의 네비게이션바에 UISearchBar가 있고 두번째 화면이 검색결과를 눌렀을때 이동하는 화면입니다.

첫번째 화면과 두번째 화면의 네비게이션 바의 높이가 달라진것을 볼 수 있고 이로 인해 두번째 화면의 네비게이션바 하단에 검정색 공백이 추가됩니다.

이는 앞서 이야기 했던 UISearchBar의 높이가 44에서 56으로 변경된것에 따른 영향으로 첫번째 화면에서는 네비게이션바의 높이가 UISearchBar의 높이에 맞게 56으로 늘어났고 두번째 화면에서는 이전화면의 높이인 56픽셀보다 적은 44픽셀의 네비게이션바를 보여주면서 12픽셀 만큼의 검정색 공백이 생기는 문제입니다. iOS에서 똑똑하게 변경해주면 되는데 이게 처리가 안되더라구요.

해결

우선 첫번째 실패한 해결방법은 SearchBar의 높이를 44로 강제 설정하는것입니다. frame 정보를 변경하는것으로는 안되고 heightAnchor 값을 설정해줘야합니다. 아래 코드를 적용합니다.

if #available(iOS 11.0, *) {
    searchBar.heightAnchor.constraint(equalToConstant: 44).isActive = true
}

이렇게 하면 높이가 44로 잘 변경되지만 UISearchBar에 글자를 입력하기위해 포커스를 가져가면 UISearchBar가 살짝 내려오는 문제가 생깁니다. 이 문제는 어떻게 해결할 방법이 없더라구요 ㅜㅜ

살짝 내려오니까 내가 뭔가를 수정하면 되겠구나 싶어서 삽질을 계속해서 해봤지만 무슨수를 써도 안되더라구요 ㅜㅜ

최종적으로 해결한 방법은 UISearchBar를 감싸는 UIView를 하나 만들고 이 뷰의 frame 값을 강제로 지정하는 것입니다.

class SearchBarContainerView: UIView {
    let searchBar: UISearchBar
    init(customSearchBar: UISearchBar) {
        searchBar = customSearchBar
        super.init(frame: CGRect.zero)
        addSubview(searchBar)
    }

    override convenience init(frame: CGRect) {
        self.init(customSearchBar: UISearchBar())
        self.frame = frame
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        searchBar.frame = bounds
    }
}

let searchBarWrapper = SearchBarContainerView(customSearchBar: searchBar)
searchBarWrapper.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: 44)
navigationItem.titleView = searchBarWrapper

이렇게 하면 해결! 첫번째 방법이 깔끔하고 정석대로 해결하는것 같은데 제대로 동작하지 않으니 두번째 방법을 사용할 수 밖에 없었습니다. iOS 개발하다 보면 이런식으로 해결해야 하는 경우가 있더라구요.

삽질지수

이번 삽질의 삽질 지수는 1입니다. (삽질지수 범위 1 5 단계) 처음부터 원인을 알고 있었고 해결방법만 찾으면 되는데 인터넷에 있는 방법중에 되는게 있어서 다행이었죠.

이거 때문에 하루가 그냥 사라졌네요 ㅜㅜ 첫번째 방법으로 깔끔하게 해결하려고 계속 삽질 했던게 원인이었어요. 그래도 해결할 수 있어서 다행이었어요. 해결하지 못하면 네비바 아래에 검색바를 위치시켜야되서 내키지 않았거든요.

참고자료

게시글의 아마존, iTunes 링크들을 통해 구매를 하시면 제휴(Affiliate) 프로그램에 의해 저에게 일정 금액이 적립될 수 있습니다. ^_____^

iOS 삽질 : 인터페이스 빌더에서 설정한 색상이 제대로 표시 안됨

인터페이스빌더(interface builder) 혹은 스토리보드(storyboard)에서 디자이너간 준 색상 코드를 입력했는데 이상하게 코드로 입력한 색상과 다르게 보이는 현상이 있었습니다.

코드로 입력한건 잘되는데 인터페이스 빌더에서 직접 지정한 색상의 경우에만 원하는 색상이 표시되지 않았죠. 그래서 찾다 보니 Color Space 의 문제임을 알게 되었습니다.

이거는 맥에서 애플이 제공하지 않는 모니터를 사용할때 Picker 를 이용해서 색상을 선택하면 나오는 문제랑 비슷한데요. 같은 색상 코드라고 해도 Color Space 에 따라서 다르게 표현되는거죠.

하지만 저는 Xcode 에서 Generic RGB를 맞게 설정하고 hex 코드를 입력했음에도 원하는 색상이 표시되지 않았습니다.

xcode 색상 선택기

xcode 색상 선택기

그래서 혹시나 Xcode 가 저기에 지정한 Color Space 를 무시하고 자기 맘대로 하는건 아닐까 싶어서 인터페이스 빌더 파일을 소스보기로 봤더니 역시나 Color Space 를 자기 마음대로 지정하고 있었습니다. Generic RGB 가 당연히 일반 RGB 스페이스라고 생각했는데 이건 애플의 버그 아닌가 싶어요…

- <color key="textColor" red="0.45098039215686275" green="0.4823529411764706" blue="0.51764705882352935" alpha="1" colorSpace="calibratedRGB"/>
+ <color key="textColor" red="0.45098039215686275" green="0.4823529411764706" blue="0.51764705882352935" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>

위 코드의 윗부분은 색상이 잘못 나올때 이고 아랫 부분은 색상이 제대로 나올때 입니다. 보면 colorSpace 값이 calibratedRGB으로 지정 되었을때 색상이 잘못나오는것을 볼 수 있습니다.

그래서 colorSpace 는 custom 으로 변경하고 customColorSpace 를 sRGB로 변경해서 원하는 색상을 표현 할 수 있게 되었습니다.

검색하다보니 Xcode 8에서는 제가 변경한걸로 기본 스페이스가 바뀌었다는데 이상하게 새로 만든 파일에서 계속 이런식으로 적용되더라구요. 이거 때문에 디버깅 뷰 까지 열어가면서 색상 코드 디버깅 하기는 처음이었습니다.

전체 프로젝트에서 저런식으로 잘못 표시된 색상이 있을까 싶어 검색해봤더니 어마어마하게 많더군요… 그래서 아래 명령어로 한번에 변경했습니다.

$ grep -rl 'colorSpace="calibratedRGB"' 폴더/* | xargs sed -i '' 's/colorSpace="calibratedRGB"/colorSpace="custom" customColorSpace="sRGB"/g'

이렇게 해도 나중에 또 색상 지정할때 이런 문제가 발생할 수 있는 여지는 있습니다. 그건 바로 Color Picker 에서 색상을 지정해두고 사용했을때 그 색상을 사용하면 색상을 추가할때의 colorSpace 로 다시 변경됩니다. 그래서 저는 이런식으로 기존에 저장해두고 사용하던 색상을 다 덮어 버렸습니다.

색상 피커 저장된 색상 지움 ㅋ

색상 피커 저장된 색상 지움 ㅋ

지우는 방법을 몰라서 그냥 안쓸거 같은 색상으로 덮어 버렸어요.

http://stackoverflow.com/a/27283783 글을 보면 칼라 피커에서 hex 코드를 입력하면 Color Space가 원복된다는 이야기도 있네요… 제 문제도 이 경우에 해당하는것 같아요. 이런식이면 인터페이스 빌더에서 색상입력하지 말라는거나 다름없지 않나요…. RGB 코드 따로 입력하는게 얼마나 귀찮은데

애플의 이 정책은 나름 모니터마다 같은 색상을 보게하려는 의도라고는 하던데 이것 때문에 오히려 원하는 색상을 표시 못하는 경우가 더 많은거 아닌가 하는 생각이 들었습니다.

제가 제대로 이해하지 못하고 삽질을 해결한것 같아서 여전히 찝찝합니다. 더 좋은 방법을 알고 계신분은 댓글로 알려주세요 ~

덧. 아마도 이 문제는 아이맥이나 애플이 만든 모니터를 사용한다면 발생하지 않을것도 같아요. 색상 피킹하는 툴은 아이맥이나 애플이 만든 모니터에서는 괜찮았거든요…

게시글의 아마존, iTunes 링크들을 통해 구매를 하시면 제휴(Affiliate) 프로그램에 의해 저에게 일정 금액이 적립될 수 있습니다. ^_____^

iOS 삽질 : 릴리즈 빌드에서만 런타임에 에러 발생

앱을 만들다가 Debug 에서 잘 실행되는것을 확인하고 TestFlight 에 올려서 테스트 하는데 앱을 시작하자 마자 크래시가 발생하는 문제가 발생했습니다.

발생한 에러는 다음과 같았습니다

2017-04-03 15:12:19.049 daangn[48068:1047076] Unknown class _TtC6daangn17NotificationTabVC in Interface Builder file.
Could not cast value of type 'UIViewController' (0x112194288) to 'daangn.NotificationTabVC' (0x10bdc09c8).

이렇게 저렇게 해결 방법을 찾다보니 Swift 의 Release Scheme의 Optimization Level 을 ‘Fast, Whole Module Optimization’ 에서 None 으로 변경하면 잘된다는 글을 발견하고 그대로 해봤더니 정말이지 Release scheme 에서도 에러가 발생하지 않았습니다.

좀더 정확히는 ‘Fast, Whole Module Optimization’ 이 아닌 ‘None’, ‘Fast, Single-File Optimization’ 로 변경하면 앱이 종료되지 않았습니다.

Optimization 의 버그 인가 싶어서 에러 메시지에서 못찾는다고 나온 클래스만 별도의 IB 로 분리하거나 스토리보드로 분리해보고 Derived Data 도 지우고 Clean Build 도 해봤지만 문제는 해결되지 않았습니다.

이렇게 하루내내 삽질을 하다가 원인을 찾았는데 그건 제가 사용하는 라이브러리의 문제 였습니다. 저는 좌우로 스크롤되는 VC를 구현하기위해 XLPagerTabStrip 이라는 라이브러리를 사용하는데 이 라이브러리에서 사용하는 코드 구현 방식에서 어쩔수 없이 오류가 발생하는 부분이 있었습니다.

해결 방법은 생각보다 간단한데요. AppDelegate 에서 앱이 시작되고 XLPagerTabStrip 을 사용하는 코드가 호출되기 전에 XLPagerTabStrip 을 사용하는 VC를 한번 호출해주면됩니다.

let _ = NotificationTabVC(nibName: nil, bundle: nil)

위와 같이 한번 호출해주면 실제 사용할때 에러가 발생하지 않습니다. 이 에러는 이슈에도 올라와 있는데 Swift 의 Interface Builder 의 Generic 지원 문제로 인해 발생하는 문제 였습니다.

이런 삽질은 할때 마다 너무 힘드네요. 안그래도 Swift 빌드 느린데 빌드를 몇십번씩 하면서 테스트 하려니 중간에 다른것도 못하고 멍하니 화면만 바라보면서 이번엔 잘되라 하면서 기도하고 있으니 시간도 아깝구요.


진정한 삽질이란 이런걸까요? 작년 11월의 제가 이와 동일한 문제를 이 블로그에 iOS 삽질로 적었었네요… 글 다 작성하고 나서 아래 관련게시글로 나와서 알게됬습니다. ㅜㅜ https://code.iamseapy.com/archives/36

게시글의 아마존, iTunes 링크들을 통해 구매를 하시면 제휴(Affiliate) 프로그램에 의해 저에게 일정 금액이 적립될 수 있습니다. ^_____^

Swift 빌드가 제일 느린컴퓨터는 12코어 MacPro?

Swift 컴파일러가 느린건 잘 알려진 사실입니다. 조금이나마 개발속도를 빠르게 하기 위해 여러가지 꼼수들이 공유되고 있죠. 하지만 더 비싼 맥을 사면 다 해결될것 같지 않나요? 여기 그렇지 않다는 정보를 드리려고합니다.

링크드인에서 게시한 The best hardware to build with Swift is not what you might think 글에서는 맥프로, 아이맥, 맥미니, 맥북프로를 이용해 빌드 속도가 얼마나 차이가 나는지 확인한 결과를 공유했습니다.

결과는 충격적이게도… 맥프로가 제일 느립니다!

링크드인에서는 2015년 중반쯤 테스트를 통해 CPU 코어와 스레드 갯수가 빌드 타임에 많은 영향을 미치는것을 보고 모바일 개발자의 빠른 개발을 위해 기존의 4코어 맥북프로 대신 12코어 맥프로를 제공해 2-3배 정도의 속도 향상 효과를 얻었다고 합니다. 12코어 맥프로는 한대에 9백만원!!!(메모리와 그래픽카드 업그레이드까지 했다면 1천 2백만원!)

그러다가 최근 모바일 개발자들이 그 비싼 12코어 맥프로 대신 맥북프로를 사용하고 있는것을 보고 왜 맥프로 안쓰냐고 했더니 맥북프로가 더 빠르다고 했다네요.

2015년의 결과를 뒤로 하고 다시 테스트 해봤더니 충격적이게도 12코어 맥프로는 Swift 빌드 하는데 있어서 맥미니보다도 느린 성능을 발휘하고 있었습니다. (자세한 그래프는 링크드인 블로그를 참고하세요.)

빌드가 빠른 순서대로 나열하면 다음과 같습니다. “4코어 아이맥 27인치” > “4코어 맥북프로” > “2코어 맥미니” > “12코어 맥프로” 🙁

이런 문제가 발생한 이유는 Swift 컴파일러의 버그때문인것으로 보입니다. Compilation gets slower when allowed more concurrent jobs 요 이슈가 해결되면 맥프로도 빠르게 빌드 할 수 있겠네요 ~

게시글의 아마존, iTunes 링크들을 통해 구매를 하시면 제휴(Affiliate) 프로그램에 의해 저에게 일정 금액이 적립될 수 있습니다. ^_____^

iOS 삽질 : Unknown class xxx in Interface Builder file

아이폰 개발은 하루하루가 삽질의 연속이네요… 오늘의 삽질은 클래스 파일을 찾을수 없다고 하면서 앱이 종료(크래시)되는 현상을 고치면서 발생했습니다.

인터페이스 빌더의 ViewController에 새로 만든 VC를 지정 했고 개발 빌드까지 아무 이상이 없었습니다. 그런데 테스트 플라이트를 통해 받은 Production 빌드에서만 앱이 크래시 나길래 Xcode에 아이폰을 연결해 실행하니 앱이 종료될때 다음과 같은 메시지가 있었습니다.

daangn[19282:4846848] Unknown class _TtC6daangn15MyArticlesTabVC in Interface Builder file.
Could not cast value of type 'UIViewController' (0x1b453af60) to 'daangn.MyArticlesTabVC' (0x1072c0028).
daangn[19282:4846848] Could not cast value of type 'UIViewController' (0x1b453af60) to 'daangn.MyArticlesTabVC' (0x1072c0028).

MyArticlesTabVC는 분명히 존재하는 파일이고 UIViewController 하위 클래스가 맞습니다. 그리고 개발 빌드에서는 아무 문제가 없었죠 ~

제가 문제를 겪은 VC는 XLPagerTabStrip 라이브러리의 특정 VC를 상속받은 거였고 이와 비슷한 문제가 이슈에 등록되어 있었습니다.

Can’t connect ‘containerView’ and ‘buttonBarView’ outlets #141

상속받는 클래스가 Generic 클래스 인데 Xcode 문제로 인터페이스 빌더의 커스텀 클래스에는 Generic 클래스를 지정 할 경우 이와 같은 오류가 발생한다고 하네요. 해결은 간단하게도 AppDelegae의 didFinishLaunch 함수 제일 위쪽에 다음과 같은 코드를 추가해서 했습니다.

let _ = MyArticlesTabVC(nibName: nil, bundle: nil)

이문제는 이전에도 한번 경험해봐서 알고는 있었는데 그때는 개발 빌드에서도 앱이 종료 되었었는데 이번에는 개발빌드는 정상동작하고 Production 빌드에서만 문제가 발생해서 찾기 어려웠습니다.

참고문서

게시글의 아마존, iTunes 링크들을 통해 구매를 하시면 제휴(Affiliate) 프로그램에 의해 저에게 일정 금액이 적립될 수 있습니다. ^_____^

컴파일러 친화적으로 Swift 코딩 하기 싫다

swift

swift

swift는 생긴지 얼마 안된 언어지만 iOS, macOS 개발을 위해서는 어쩔수 없이 사용하게 되는데요. 개발을 하다보면 제일 문제되는건 swift 언어 컴파일 시간 문제입니다.

Xcode 에서 아이폰 개발을 하다보면 이전에 Obj-C 사용하던것 보다 컴파일이 느려서 시뮬레이터로 실행하면서 코드 수정하는 전체 프로세스를 느리게 만들정도죠.

그래서 swift로 개발할때는 고려해야 될게 생겼습니다. 컴파일러 친화적으로 코딩하기! ㅜㅜ 컴파일러의 컴파일 속도를 올려주는 팁들이 몇개 있는데 이걸 잘지키면 컴파일 속도가 빨라집니다.

제가 참고로한 2개의 글에 자세히 설명되어 있으니 읽어보시기를 추천드립니다.

대표적인거 몇개 소개해드리겠습니다.

rightView?.bounds.width ?? 0

위와 같이 값이 nil 이면 기본값을 제공하는 연산자는 코드 가독성도 좋아서 많이 사용하죠 ~ 그런데 이걸 아래와 같이 풀어 쓴다면 컴파일 속도가 얼마나 빨라질까요?

var width: CGFloat = 0
if let rightView = rightView {
 width = rightView.bounds.width
}

빌드 타임이 99.4% 빨라졌다네요… 5238ms 에서 32.4ms 로 빨라져요. (위의 예제는 벤치마크 코드의 일부라서 실제로 위의 코드로 하면 격차가 줄어들수도 있어요. 전체 코드는 링크 참고)

이런코드 100번 쓰면 5초 * 100 = 500초… 8분정도 느려지는거네요. 8분을 아끼기 위해서 1줄 코드를 4줄짜리로 바꾸면 100줄 짜리 코드가 400줄 될거구요.

이외에도 아래와 같은 것들이 있어요

  • 배열에 값을 추가할때 + 연산자 대신 append를 사용하면 97.9% 속도 향상!(1250.3ms -> 25.5ms)
  • 삼항 연산자를 사용하지 않으면 빌드타임 92.9% 향상!(239ms -> 16.9ms)

이런…. 다들 코드 가독성 향상이나 편리함을 이유로 코드에서 자주 사용하는것들이네요…

Swift 3에서는 그래도 이런것들이 조금 개선되었다고 합니다.

자 여러분의 선택은? 가독성을 포기하고 1줄코드를  3줄씩 늘려서 쓰고 컴파일 속도를 얻을것인가? 컴파일 속도를 포기하고 가독성이나 편리함을 추구할것인가요? 다른 옵션으로 iOS 8 지원을 포기하고 swift 3으로 넘어가는 선택도 있겠네요. 아니면 컴퓨터를 맥프로(맥북프로 아닙니다… 쓰레기통 맥 말하는거에요) 최고급 사양으로 바꾸는 선택지도 있겠네요 ㅎㅎ

저는 컴파일 속도를 포기했습니다… swift 3에 컴퓨터 사양 높이는걸 고려하겠어요

옛날 옛적에 ~ C로 코딩할때는 컴파일러 속도를 빠르게 하기위해 코딩 습관도 컴파일러 친화적으로 하고 컴파일러가 새로 나올때마다 공짜점심을 먹었다는 이야기를 들었는데… 요즘 세상에 이런 이야기를 할줄은 몰랐네요 ㅜㅜ 개발자들을 위해 Swift 컴파일러가 좀더 빠른 속도로 개선되었으면 좋겠습니다.

참고 자료

게시글의 아마존, iTunes 링크들을 통해 구매를 하시면 제휴(Affiliate) 프로그램에 의해 저에게 일정 금액이 적립될 수 있습니다. ^_____^

애플 푸쉬서비스(APNs) 토큰 방식 인증 추가

애플 플랫폼에서 개발하다 보면 유독 인증서를 많이 이용하는것을 볼수 있습니다. 보안 때문이라고는 하지만 구글 같은곳에서도 인증에 키를 사용하는데 애플은 인증서를 사용하는 경우가 대부분이죠. 이로 인해 대부분의 애플 개발 시작하기는 인증서 파일 만들기, 인증서 파일 등록하기,  여러대의 컴퓨터에서 인증서 사용하기 등 의 문서가 많습니다.

그런데 애플이 애플 푸쉬서비스(Apple Push Notification Service)에 토큰 기반 인증을 지원한다고 하네요!!

애플 뉴스 : Token Authentication Now Available for Push Notifications

그동안 애플 푸쉬서비스를 사용하려면 인증서를 애플 개발자 사이트에서 다운로드 받아서 openssl로 한번 파일 변환하고 이를 푸쉬 전송하는 서버에서 푸쉬 보낼때 전송했어야 했는데요. 

이제는 인증서 대신 토큰으로만 전송할 수 있게 되었습니다. 또한 인증서는 1년의 유효기간을 가지고 있어서 1년에 한번씩 갱신하는것을 까먹을경우 푸쉬가 전송되지 않는 문제도 있었는데 토큰은 유효기간이 없습니다 !!

푸쉬 인증서 타입에 토큰이 추가됨

토큰이라서 문자열이지만 파일로 받는다

만료기간이 없다


토큰을 생성해보니 p8 확장자를 가진 파일을 다운로드 하게 하는데요. 파일을 열어보면 문자열을 확인할 수 있습니다. FCM(aka GCM)처럼 단순 키 문자열은 아니라 뭔가 낚인것도 같지만… 그전에 인증서 발급할때 openssl 로 했던거 생각하면 이게 어딘가요 ㅜㅜ

푸쉬 서버를 직접 운영하고 있다면 이 새로운 기능을 위해 별도 구현을 해야되구요. 저처럼 외부 푸시 서비스를 이용하는경우에는 해당서비스에서 지원하기를 기다리면 되겠습니다 ~ (카카오 푸쉬 사용하고 있는데 해주시면 감사히 잘쓰겠습니다 ㅎㅎ)

참고로 APNs 서버 API 문서(APNs Provider API)를 보면 인증에 HTTP/2 + JWT(JSON Web Token)를 사용한다고 되있습니다. 이게 JSON으로 인증하는 표준기술이라던데 저도 어떻게 동작하는지는 모르겠는데 애플도 사용한다고 하니 괜찮은가보다라는 생각이 드네요. 

앞으로 개발자 인증서나 앱별로 별도로 만들어야 되는 프로파일도 없애주면 좋겠네요. 애플은 개발자들 불편한거 오래 방치하다가 풀어준단 말이죠… 그러면 저를 포함한 개발자들의 와우 효과는 더 커지구요 ㅎㅎ 

게시글의 아마존, iTunes 링크들을 통해 구매를 하시면 제휴(Affiliate) 프로그램에 의해 저에게 일정 금액이 적립될 수 있습니다. ^_____^

애플서버에서 권한허용 문제로 빌드 실패하는 경우

Xcode 8로 업데이트후 애플 서버에 빌드를 제출하면 잠시후 아래와 같은 메시지로 실패했다고 메일이 오는 경우가 있다. 애플 빌드서버에서 실패하는거라 실제로는 Xcode 8 이 아니라 iOS 10을 지원하는 경우 발생한다고 보는게 맞겠다.

This app attempts to access privacy-sensitive data without a usage description. The app's Info.plist must contain an NSPhotoLibraryUsageDescription key with a string value explaining to the user how the app uses this data.

This app attempts to access privacy-sensitive data without a usage description. The app's Info.plist must contain an NSCameraUsageDescription key with a string value explaining to the user how the app uses this data.

This app attempts to access privacy-sensitive data without a usage description. The app's Info.plist must contain an NSAppleMusicUsageDescription key with a string value explaining to the user how the app uses this data.

This app attempts to access privacy-sensitive data without a usage description. The app's Info.plist must contain an NSContactsUsageDescription key with a string value explaining to the user how the app uses this data.

This app attempts to access privacy-sensitive data without a usage description. The app's Info.plist must contain an NSCalendarsUsageDescription key with a string value explaining to the user how the app uses this data.

This app attempts to access privacy-sensitive data without a usage description. The app's Info.plist must contain an NSBluetoothPeripheralUsageDescription key with a string value explaining to the user how the app uses this data.

This app attempts to access privacy-sensitive data without a usage description. The app's Info.plist must contain an NSMicrophoneUsageDescription key with a string value explaining to the user how the app uses this data.

This app attempts to access privacy-sensitive data without a usage description. The app's Info.plist must contain an NSMotionUsageDescription key with a string value explaining to the user how the app uses this data.

이는 카메라나 달력등 개인정보 관련된 권한을 사용할때만 Info.plist에 설정하면 되는 코드인데 사용하지 않는데도 애플 빌드서버에서 이런 오류를 발생하는 경우가 있다.

확인해보니 애플서버에서는 빌드할때 개인정보 관련코드가 외부 라이브러리에 있더라도 이걸 체크해서 무조건 위와 같이 오류를 발생한다.

나는 PermissionScope 라는 라이브러리를 사용하는데 이거는 앱 사용자에게 현재 권한을 보여주고 쉽게 권한을 요청하기에 좋은데 라이브러리 특성상 모든 개인정보 권한 코드를 가지고 있다. 라이브러리 사용할때 특정 개인정보는 사용하지 않으면 사용자가 볼일이 없는데도 라이브러리에 코드가 있다는 이유로 애플 빌드서버에서는 이런 오류를 발생시킨다.

이 문제를 고치려면 이와 같은 라이브러리를 사용하지 않거나 위에 언급된 키들에 대해 모두 문자열을 추가하면된다. 그런데 라이브러리에서 내부적으로 사용하지 않는 코드가 들어있을수도 있는데 이걸 다 어떻게 체크하나 ㅜㅜ 애드몹 같은 경우도 달력이나 몇개의 개인정보에 접근하는 코드가 있는데 이건 개발자가 적용할때 안쓴다고 하면 안쓰는건데…. 그래서 그냥 속편하게 모든 키를 다 추가하면 된다. 어차피 사용자가 안볼 문구이니까

게시글의 아마존, iTunes 링크들을 통해 구매를 하시면 제휴(Affiliate) 프로그램에 의해 저에게 일정 금액이 적립될 수 있습니다. ^_____^

Xcode 8 에서 쓸모없는 로그 출력안하게 하기

Xcode 8을 설치하고 시뮬레이터에서 앱을 실행하면 이전과 달리 엄청난 로그들을 만나게 됩니다. 대략 아래와 같은 로그들이죠…

2016-09-20 18:43:53.720846 daangna[49804:209191] [] nw_endpoint_flow_attach_protocols [6.1 31.13.68.12:443 in_progress socket-flow (satisfied)] Attached flow protocol
2016-09-20 18:43:53.720996 daangna[49804:209191] [] nw_endpoint_resolver_receive_report [6 graph.facebook.com:443 in_progress resolver (satisfied)] received child report:[6.1 31.13.68.12:443 in_progress socket-flow (satisfied)]
2016-09-20 18:43:53.721119 daangna[49804:209191] [] nw_connection_endpoint_report [6.1 31.13.68.12:443 in_progress socket-flow (satisfied)] reported event flow:start_connect
2016-09-20 18:43:53.725114 daangna[49804:209191] [] nw_socket_handle_socket_event Event mask: 0x800
2016-09-20 18:43:53.725334 daangna[49804:209191] [] nw_socket_handle_socket_event Socket received CONNECTED event
2016-09-20 18:43:53.725523 daangna[49804:209191] [] nw_socket_setup_notsent_lowat Set TCP_NOTSENT_LOWAT(16384)
2016-09-20 18:43:53.725712 daangna[49804:209191] [] nw_endpoint_flow_protocol_connected [6.1 31.13.68.12:443 in_progress socket-flow (satisfied)] Output protocol connected
2016-09-20 18:43:53.726224 daangna[49804:209191] [] nw_endpoint_flow_connected_path_change [6.1 31.13.68.12:443 ready socket-flow (satisfied)]
2016-09-20 18:43:53.726454 daangna[49804:209191] [] nw_endpoint_flow_connected_path_change [6.1 31.13.68.12:443 ready socket-flow (satisfied)] Connected path is satisfied
2016-09-20 18:43:53.726645 daangna[49804:209191] [] nw_endpoint_resolver_receive_report [6 graph.facebook.com:443 in_progress resolver (satisfied)] received child report:[6.1 31.13.68.12:443 ready socket-flow (satisfied)]
2016-09-20 18:43:53.726839 daangna[49804:209191] [] nw_connection_endpoint_report [6.1 31.13.68.12:443 ready socket-flow (satisfied)] reported event flow:finish_connect
2016-09-20 18:43:53.727092 daangna[49804:209191] [] nw_connection_endpoint_report [6 graph.facebook.com:443 ready resolver (satisfied)] reported event flow:finish_connect
2016-09-20 18:43:53.727326 daangna[49804:209191] [] nw_endpoint_resolver_receive_report [6 graph.facebook.com:443 ready resolver (satisfied)] received child report:[6.1 31.13.68.12:443 ready socket-flow (satisfied)]
2016-09-20 18:43:53.727563 daangna[49804:209191] [] nw_connection_endpoint_report [6.1 31.13.68.12:443 ready socket-flow (satisfied)] reported event flow:changed_viability
2016-09-20 18:43:53.727758 daangna[49804:209191] [] nw_connection_endpoint_report [6 graph.facebook.com:443 ready resolver (satisfied)] reported event flow:changed_viability
2016-09-20 18:43:53.728074 daangna[49804:209175] [] __tcp_connection_start_block_invoke 6 sending event TCP_CONNECTION_EVENT_CONNECTED in response to state ready and error (null)
2016-09-20 18:43:53.728255 daangna[49804:209175] [] tcp_connection_event_notify 6 event: TCP_CONNECTION_EVENT_CONNECTED, reason: nw_connection event, should deliver: true
2016-09-20 18:43:53.728673 daangna[49804:209175] [] nw_endpoint_start_tls_while_connected [6.1 31.13.68.12:443 ready socket-flow (satisfied)]
2016-09-20 18:43:53.728852 daangna[49804:209175] [] nw_endpoint_start_tls_while_connected [6.1 31.13.68.12:443 ready socket-flow (satisfied)] Using CoreTLS
2016-09-20 18:43:53.729044 daangna[49804:209175] [] nw_endpoint_start_tls_while_connected [6.1 31.13.68.12:443 ready socket-flow (satisfied)] Set custom TLS client queue
2016-09-20 18:43:53.729243 daangna[49804:209175] [] nw_endpoint_start_tls_while_connected [6.1 31.13.68.12:443 ready socket-flow (satisfied)] Set custom TLS prepare handler
2016-09-20 18:43:53.729432 daangna[49804:209175] [] nw_endpoint_start_tls_while_connected [6.1 31.13.68.12:443 ready socket-flow (satisfied)] Set custom TLS message handler
2016-09-20 18:43:53.729621 daangna[49804:209175] [] nw_endpoint_start_tls_while_connected [6.1 31.13.68.12:443 ready socket-flow (satisfied)] Attached TLS protocol to connected flow
2016-09-20 18:43:53.729784 daangna[49804:209175] [] nw_endpoint_resolver_receive_report [6 graph.facebook.com:443 ready resolver (satisfied)] received child report:[6.1 31.13.68.12:443 in_progress socket-flow (satisfied)]
2016-09-20 18:43:53.730001 daangna[49804:209175] [] nw_connection_endpoint_report [6.1 31.13.68.12:443 in_progress socket-flow (satisfied)] reported event flow:start_secondary_connect
2016-09-20 18:43:53.730186 daangna[49804:209175] [] nw_connection_endpoint_report [6 graph.facebook.com:443 in_progress resolver (satisfied)] reported event flow:start_secondary_connect
2016-09-20 18:43:53.730400 daangna[49804:209175] [] nw_endpoint_resolver_receive_report [6 graph.facebook.com:443 in_progress resolver (satisfied)] received child report:[6.1 31.13.68.12:443 in_progress socket-flow (satisfied)]
2016-09-20 18:43:53.730580 daangna[49804:209175] [] nw_connection_endpoint_report [6.1 31.13.68.12:443 in_progress socket-flow (satisfied)] reported event flow:start_connect
2016-09-20 18:43:53.730777 daangna[49804:209175] [] nw_connection_endpoint_report [6 graph.facebook.com:443 in_progress resolver (satisfied)] reported event flow:start_connect
2016-09-20 18:43:53.730938 daangna[49804:209175] [] nw_endpoint_flow_protocol_connected [6.1 31.13.68.12:443 in_progress socket-flow (satisfied)] Transport protocol connected
2016-09-20 18:43:53.731079 daangna[49804:209175] [] nw_endpoint_resolver_receive_report [6 graph.facebook.com:443 in_progress resolver (satisfied)] received child report:[6.1 31.13.68.12:443 in_progress socket-flow (satisfied)]
2016-09-20 18:43:53.731277 daangna[49804:209175] [] nw_connection_endpoint_report [6.1 31.13.68.12:443 in_progress socket-flow (satisfied)] reported event flow:finish_transport
2016-09-20 18:43:53.731424 daangna[49804:209175] [] nw_connection_endpoint_report [6 graph.facebook.com:443 in_progress resolver (satisfied)] reported event flow:finish_transport
2016-09-20 18:43:53.746985 daangna[49804:209191] [] nw_endpoint_flow_protocol_connected [6.1 31.13.68.12:443 in_progress socket-flow (satisfied)] Output protocol connected
2016-09-20 18:43:53.747630 daangna[49804:209191] [] nw_endpoint_flow_connected_path_change [6.1 31.13.68.12:443 ready socket-flow (satisfied)]
2016-09-20 18:43:53.747850 daangna[49804:209191] [] nw_endpoint_flow_connected_path_change [6.1 31.13.68.12:443 ready socket-flow (satisfied)] Connected path is satisfied
2016-09-20 18:43:53.748068 daangna[49804:209191] [] nw_endpoint_resolver_receive_report [6 graph.facebook.com:443 in_progress resolver (satisfied)] received child report:[6.1 31.13.68.12:443 ready socket-flow (satisfied)]
2016-09-20 18:43:53.748310 daangna[49804:209191] [] nw_connection_endpoint_report [6.1 31.13.68.12:443 ready socket-flow (satisfied)] reported event flow:finish_connect
2016-09-20 18:43:53.748607 daangna[49804:209191] [] nw_connection_endpoint_report [6 graph.facebook.com:443 ready resolver (satisfied)] reported event flow:finish_connect
2016-09-20 18:43:53.748830 daangna[49804:209175] [] __tcp_connection_start_block_invoke 6 sending event TCP_CONNECTION_EVENT_TLS_HANDSHAKE_COMPLETE in response to state ready and error (null)
2016-09-20 18:43:53.749050 daangna[49804:209175] [] tcp_connection_event_notify 6 event: TCP_CONNECTION_EVENT_TLS_HANDSHAKE_COMPLETE, reason: nw_connection event, should deliver: true
2016-09-20 18:43:53.749295 daangna[49804:209175] [] tcp_connection_get_statistics DNS: 3ms/4ms since start, TCP: 10ms/17ms since start, TLS: 18ms/34ms since start
2016-09-20 18:44:07.686676 daangna[49804:209118] subsystem: com.apple.UIKit, category: Touch, enable_level: 0, persist_level: 0, default_ttl: 1, info_ttl: 0, debug_ttl: 0, generate_symptoms: 0, enable_oversize: 1, privacy_setting: 2, enable_private_data: 0
2016-09-20 18:44:07.687387 daangna[49804:209118] subsystem: com.apple.UIKit, category: Gesture, enable_level: 0, persist_level: 0, default_ttl: 1, info_ttl: 0, debug_ttl: 0, generate_symptoms: 0, enable_oversize: 1, privacy_setting: 2, enable_private_data: 0
2016-09-20 18:44:07.688917 daangna[49804:209118] subsystem: com.apple.UIKit, category: GestureExclusion, enable_level: 0, persist_level: 0, default_ttl: 1, info_ttl: 0, debug_ttl: 0, generate_symptoms: 0, enable_oversize: 1, privacy_setting: 2, enable_private_data: 0
2016-09-20 18:44:58.968517 daangna[49804:209882] [] nw_socket_handle_socket_event Event mask: 0x2
2016-09-20 18:44:58.968960 daangna[49804:209883] [] tcp_connection_cancel 6
2016-09-20 18:44:58.969395 daangna[49804:209882] [] nw_socket_handle_socket_event Socket received READ_CLOSE event
2016-09-20 18:44:58.969753 daangna[49804:209882] [] nw_endpoint_handler_cancel [6 graph.facebook.com:443 ready resolver (satisfied)]
2016-09-20 18:44:58.970003 daangna[49804:209882] [] nw_endpoint_handler_cancel [6.1 31.13.68.12:443 ready socket-flow (satisfied)]
2016-09-20 18:44:58.970305 daangna[49804:209882] [] nw_endpoint_flow_protocol_disconnected [6.1 31.13.68.12:443 cancelled socket-flow (null)] Output protocol disconnected
2016-09-20 18:44:58.970517 daangna[49804:209882] [] nw_resolver_cancel_on_queue 0x618000103210
2016-09-20 18:44:58.970791 daangna[49804:209882] [] -[NWConcrete_tcp_connection dealloc] 6

이 로그들을 없애려면 “Edit Scheme” 에서 “Run” > “Arguments” > “Environment Variables” 에 다음과 같은 값을 추가해야합니다.

Name 에는 OS_ACTIVITY_MODE 를 추가하고 Value 에는 disable 을 입력후 왼쪽 체크박스를 체크한 상태로 둡니다.

Xcode 8 log fix

Xcode 8 log fix

이 현상은 애플이 의도적으로 넣은건지 아닌지 모르겠지만 꽤나 귀찮습니다.

참고자료

게시글의 아마존, iTunes 링크들을 통해 구매를 하시면 제휴(Affiliate) 프로그램에 의해 저에게 일정 금액이 적립될 수 있습니다. ^_____^