개발자로서 프리랜서 생활을 시작하면서, 일이 없을 때 공부할 겸 내가 필요한 기능이 들어간 앱을 만들었다. 지금까지 5개를 만들었는데, 1개는 더 이상 스스로도 필요하다고 생각지 않아 접었다.


기능적으로나, 사용성으로 봤을 때도 내가 만든 앱보다 뛰어난 앱이 많지만 그냥 내가 필요한 건 내가 만들어 쓴다는 생각으로 일이 없을 때는 공부할 겸, 나름 틈틈이 유지보수를 하려고 노력했었다. 그런데 작년에는 그러한 유지보수를 거의 하지 못했다. 


그중에서 한 앱에 유독 피드백을 잘 해주는 중국인 친구가 있었다. 정말 서로 아는 친구는 아니지만 매 릴리즈마다 해결되지 않고 늘어만가는 버그에도 질리지 않고 정성껏 피드백을 해주는 고마운 분이셨다. 크리스마스 즈음에는 한글로 '즐거운 성탄절 보내시고 새해 복 많이 받으세요'라는 메시지도 남겼던 잊을 수 없는 내 앱의 로열 고객(!)이었다.


그분이 2015년 12월 초쯤에 업데이트된 앱의 문제점 3가지를 메일로 피드백을 했었다. 그중 두 가지는 그럭저럭 해결을 했지만 나머지 하나는 하다 하다 못해 해결을 못하고, 또 생업에 밀려 당장은 해결하지 못하지만 꼭 해결하겠다.라고 답장을 썼었다.


그리고 시간은 훌쩍 흘러 2017년 6월이 되었다. 중간에도 2-3번 정도의 패치 정도의 업데이트는 있었지만 그 문제는 여전히 해결하지 못한 채 까맣게 잊고 있었는데 소스 관리를 bitbucket에서 github로 옮기면서 남은 이슈들을 보다가 그 해결하지 못한 이슈를 발견했고, 1년 반이 넘어서야 패치를 하고 업데이트를 했다.


업데이트 내역에 해당 문제에 대한 내용을 적고 너무 미안한 마음에 그분 이름을 적고 너무 늦어져서 미안하다고 적었다. 솔직히 1년 반이나 지났고 같은 기능을 하는 앱 중 훨씬 뛰어난 앱들이 많았기에 그분이 아직까지 이 앱을 쓸 거라고는 생각은 안 했지만 그래도 미안한 마음을 어떻게든 전하고 싶었달까.


그런데 오늘 메일이 왔다.




뭔가 가슴이 찡해지면서 반가운 마음에 곧바로 답장을 썼다. 너무 늦어져서 미안하고, 굳이 변명하자면 돈 버느라 개인적으로 만든 앱들을 전혀 관리하지 못했다... 주절주절..


답장을 보내고 5분 정도 지났을 때, 그러니까 이 기분을 어디다가 적어놓고 싶어서 여기에 끄적이고 있을 때 또 곧바로 짧은 답장이 왔다. 나름대로의 상상을 펼쳐 의역을 하자면 이렇다.


'미안할게 뭐 있어. 돈 버는 건 중요한 일이야. 네가 잘 있는 것 같아서 좋네~'


기분이 참 좋다. 

Posted by 52

댓글을 달아 주세요

iTunes Connect에 앱을 올리려고 하니까 'Missing iOS Distribution signing identity  for...' 관련 에러가 발생했다. 갑자기 왜 이러나 싶어 인증서를 새로 발급받고 온갖 삽질을 했지만 문제는 애플에서 제공하는 인증서의 2016/2/14일부로 만료되어서 발생하는 문제였다.


"Uploading archive error: "Missing iOS Distribution signing identity  for..."


조치로는 만료된 WWDR 인증서를 지우고, 다음 경로에서 (링크) 2026년까지 유효한 새 인증서를 받아 갱신하면 된다.


애플에서의 공지도 있었다.


Apple Worldwide Developer Relations Intermediate Certificate Expiration


이런 사실도 모르고 엄한 인증서들만 지우고 받고 삽질을 했다. 

Posted by 52

댓글을 달아 주세요

c_standard_lib

Logs 2016. 2. 11. 20:25

페이스북 C++, OpenSource 스터디 모임의 글을 보다가 MSVC의 기반이 되었다는 Dinkumware의 c standard library source가 Github에 공개되어 있다는 사실을 알았다. 이게 원래 공개가 되어 있던 건지 아니면 이번에 처음으로 공개된 것인지는 잘 모르겠다만.


오호라.라고 혼자 감탄하고 재빠르게 소스를 탐색하여 맨 처음 찾아본 것은 문자열 처리 함수들이었다. STRING이라는 디렉터리에 차곡차곡 '함수명. c'의 형태로 구현되어 있었다.


문자열 처리 함수를 맨 처음 보고자 했던 이유는 개발 초짜 때 C 관련 시험을 보든, 기술 면접을 보든, 그리고 세월이 흘러 내가 초짜 프로그래머를 면접 볼 때 빼놓지 않고 단골처럼 등장했던 strcpy를 구현해봐라, 또는 strlen을 구현해봐라 등등의 문제를 풀 때도, 문제를 낼 때도 늘 궁금했었기 때문이다. 이 함수를 맨 처음으로 구현한 라이브러리는 과연 어떻게 구현을  했을까? 라는 궁금증.


strcpy는 다음과 같이 구현되어 있다.







strlen은 다음과 같이 구현되어 있다.








색다를 것 없는 어찌 보면 뻔한 구현이긴 하지만, 어쩐지 느낌에 특별히(!) 매우 깔끔하고 간결하다. 뭔가 피겨스케이팅으로 치면 연아님의 모습을 보는 것 같고 농구로 치면 마이클 조던의 플레이를 보는 느낌이랄까;; 유명하고 역사적인(?) C 라이브러리 구현 소스라는 사실로 인해 콩깍지가 껴 버린 것 같다.


함수 당 3중 정도의 짧은 구현인데 많은 - 생뚱맞은 - 생각들이 든다. strlen에서의 입력받은 char*를 받는 변수명은 왜 sc일까. sc는 뭐의 약자일까? string count의 약자일까. 언젠가 strcpy를 구현해보라고 해서 칠판에 끄적끄적 구현했더니 함수 내에 필요해서 선언한 변수명을 지적하며 왜 변수명을 그렇게 지었냐 라는 지금 생각해보면 좀 이상한 딴지를 받았던 기억이 나서 그런가. 그 딴지를 걸었던 분은 저 위의 strcpy의 인자로 들어오는 s1, s2를 갖고도 딴지를 걸 것 같다. dest, src 로 짓는게 어때? 라며. :) 물론 이름을 잘 짓는 것은 매우!! 중요하다!


생각난 김에 memcpy와 memmove도 보았다.






예상한 대로의 구현이다. 근데 왜 이리 특별하게(!) 깔끔하고 간결해 보이는지. 근데 su1, su2는 뭐의 약자일까. 왜 자꾸 이딴 생각만 나는지 모르겠다. 뭔가 사소한 것에도 트집잡으려는 시험관의 마음이 드는 것 같다. @_@


그리고 memcpy이야기가 나오면 늘 따라나오는 memmove 함수는 이렇게 구현되어 있다.






주석에 safely라고 특별히 강조하는  듯하다. ㅎ 원본과 대상의 메모리 영역이 겹칠 경우 처리하는 부분을 보니 또 혼자 옛 생각이 가득 난다. 여기에도 sc라는 변수명이 쓰인걸로 봐선 sc는 string count의 약자는 아님에 분명하구나. :(


별 것 아닌데 저 소스들 잠깐 보는 것만으로 재미있다. 

Posted by 52

댓글을 달아 주세요

iOS용 갈무리 UI/UX 변경 및 엔진 개선 작업 막바지에 매번 내 아이폰에서만 테스트하다가 소영의 아이폰으로 테스트를 했는데 SafariViewController로 웹 페이지를 연 후, Swipe to Dismiss 동작으로 창을 닫으면 그 후론 앱이 먹통이 되어버리는 문제를 발견했다.


내 아이폰에서는 발생하지 않아서 코드에 문제가 있나 싶어 열심히 디버깅을 해 봤는데 SafariViewController를 통해 웹 페이지를 연 후, 좌상단 Done 버튼이 아닌 Swipe to Dismiss 동작으로 창을 닫은 다음 다시 SafariViewController를 열려고 시도하면 Freeze 되는 것을 확인했다. 좀 더 정확히 이야기하면 Swipe to Dismiss 제스처를 사용하여 SafariViewController를 닫으려고 한 이후부터 제대로 ViewController가 소멸되지 않고 먹통이 되기 시작하는 것이었다. 그래서 내 쪽 코드의 문제는 아니라는 것을 확인한 후 검색해 보니 iOS 9.2.x 버전의 버그다.


링크 : SFSafariViewController in iOS 9.2


해당 Thread의 댓글을 보면 임시 해결책을 제시하고 있다. 일단 Swipe to dismiss 제스쳐를 강제로 막아야 하는 것이 관건인데 NavigationController 안에 Safari ViewController를 우겨넣어(!) 버리고 띄우는 방법이다.


Posted by 52

댓글을 달아 주세요

iCloud를 이용한 Coredata 사용은 굉장히 단순해서 편해 보이지만, 많이 답답하다. 


Coredata 저장을 iCloud를 이용하도록 설정하고 난 후에는 진행 상황에 대한 피드백을 얻을 수 있는 방법이 없어 난감한 경우가 생긴다. 이번에 그러한 사례를 두 건 만나게 되어 기록해 놓는다.


앱이 시작되고 iCloud 저장소를 통한 Coredata를 설정이 시작되면 로그창에서 아래와 같은 로그를 확인할 수 있다.


Using local storage: 1


이 상태는 아직 완전히 iCloud를 통한 Coredata 사용 준비가 완료된 상태가 아니다. iCloud 저장소에 연결되기 전에 임시로 생성된 로컬 스토리지를 사용하고 있다는 의미이다. (링크)


저 1의 값이 0으로 바뀌어 로그창에 찍혀야 모든 준비가 끝난 상황이다. 그런데 개발자 입장에서 'Using local storage : 0'이 찍히는 상태, 다시 말해 사용 준비 완료된 상태를 체크할 수가 없다. 내가 만든 앱에서는 'Using local storage' 값이 0으로 바뀌는 것에 대한 통지를 받을 수 있는 방법이 없다는 말이다. 물론 일반적인 상황에서는 저 값이 1이든 0이든 상관하지 않고 쓰면 되지만 초기화 과정에서 오류가 발생했을 때가 문제다.


진행 과정 중에 문제가 발생하면 앱에 피드백을 해주면 좋은데 디버그 시 디버그 창에 오류가 발생했다는 메시지와 함께 60초 후, 또는 120초 후에 재시도를 한다는 로그가 뜰 뿐이다.  디버그 창에 나오는 메시지가 아니라면 앱은 오류가 발생했지만 그 상황을 인지하지 못하고 그저 iCloud가 잘 돌아가고 있겠거니- 하고 있을 뿐이다.


시작 시 초기화 하는 과정에서 문제가 발생하는 사례가 두 건이 있었다.


1. 앱 강제 종료 후 다시 실행 하였을 때 iCloud를 사용하기 위한 초기화 실패

: 'failed finishing setup for store during asynchronous iCloud initialization~' 가 포함된 로그를 확인할 수 있다. 저런 에러 메시지 이후 60초 이후 다시 시도 한다고 하는데 60초 지나 다시 실행해도 같은 오류가 발생하며 그 다음엔 120초 후에 다시 실행하겠다는 메시지를 볼 수 있다. 120초 후에도 역시나 오류가 발생한다. 이 상황에서 앱을 다시 한번 강제 종료한 이후 재 실행하면 최초 실행 시 초기화 하는 작업을 진행하여 정상적인 상태가 된다.


검색해 보면 iCloud 서버와 동기화를 하거나 또는 동기화할 데이터가 남아있는 상황  - 어떤 transaction이 완전히 완료되지 않은 상태 -  에 강제종료를 하는 경우 이런 상황에 빠지게 되는 것으로 보이는데 조치법을 열심히 찾아봤지만 'ManagedObjectContext의 내용 변경 시 performBlock이나 performBlockWait를 사용하는 비동기 방식을 사용하여 최대한 그런 상황을 만들지 말라.' 가 일반적인 내용이었다. 하지만 완벽한 해결책은 아니다. 더 큰 문제는 저 상황에 빠졌을 때 앱 입장에서는 iCloud가 오류를 냈는지 알 수가 없기 때문에 대처할 수가 없다는 점이다.


2. 최초 실행 시 초기화 실패

: 'initial sync notification returned an error (Error Domain=BRCloudDocsErrorDomain Code=12 "The operation couldn’t be completed. (BRCloudDocsErrorDomain error 12.)")' 와 비슷한 로그를 확인할 수 있으며 60초 후에 다시 시도한다는 메시지가 나온다. 다행히 이 경우는 60초 후 재 시도시 대부분 정상적으로 초기화가 완료된다. 그런데 그 60초 동안이 문제다. 앱 입장에서는 60초 후에 재 시도하는 상황에 대해서 전혀 알 수가 없다. 앱이 실행하자마자 iCloud 저장소의 내용을 복원해야 하는 상황에서 60초나 아무런 반응이 없다면 (개발시엔 로그창에라도 찍히나 일반적인 사용자의 경우는 아무런 정보를 받지 못한 상황에서) 이래저래 난감하다. 하다못해 iCloud 서버 동기화 준비 중이라는 메시지라도 사용자에게 보이고 싶지만 이 역시 완벽한 해결책을 찾을 수 없었다.


며칠 이와 관련한 해결책을 고민하다가 1번의 경우는 앱 종료 시 appdelegate 내의 applicationWillTerminate 에서 앱을 처음 실행할 때 기기 내의 iCloud 저장 공간을 생성하고 초기화 할 수 있도록 로컬에 생성된 파일을 날려버리는 무식한 방법 말고는 아직 해결책을 찾지 못했고 2번의 경우는 '최초 실행 시' 라는 가정하에 준비 완료된 상황을 잡을 수 있는 방법을 찾을 수 있었다.


일반적인 상황에서는 몇 가지 정보를 받아볼 수 있도록 되어 있는데 Notification으로 다음의 세 가지 상황을 받아 볼 수 있긴 하다.


* NSPersistentStoreDidImportUbiquitousContentChangesNotification : iCloud에 저장된 데이터가 변경되었음을 알림

* NSPersistentStoreCoordinatorStoresWillChangeNotification : 저장소의 데이터를 변경할 것이라고 알림.

* NSPersistentStoreCoordinatorStoresDidChangeNotification : 저장소의 데이터가 변경되었다고 알림.


여기서 NSPersistentStoreCoordinatorStoresDidChangeNotification 을 통해 알려주는 정보 중 'NSPersistentStoreUbiquitousTransitionTypeKey'라는 키로 좀 더 자세한 상황을 받을 수 있다는 것을 알게 되었다. 


    NSNumber *transitionType = [notification.userInfo objectForKey:NSPersistentStoreUbiquitousTransitionTypeKey];

    int theCause = [transitionType intValue];

    

    switch (theCause) {

        case NSPersistentStoreUbiquitousTransitionTypeAccountAdded: {

            NSLog(@"NSPersistentStoreUbiquitousTransitionTypeAccountAdded");

            // account was added

        }

            break;

        case NSPersistentStoreUbiquitousTransitionTypeAccountRemoved: {

            NSLog(@"NSPersistentStoreUbiquitousTransitionTypeAccountRemoved");

            // account was removed

        }

            break;

        case NSPersistentStoreUbiquitousTransitionTypeContentRemoved: {

            NSLog(@"NSPersistentStoreUbiquitousTransitionTypeContentRemoved");

            // content was removed

        }

            break;

        case NSPersistentStoreUbiquitousTransitionTypeInitialImportCompleted: {

            NSLog(@"NSPersistentStoreUbiquitousTransitionTypeInitialImportCompleted");

            // initial import

        }

            break;

            

        default:

            break;

    }



NSPersistentStoreUbiquitousTransitionTypeInitialImportCompleted 가 저장소 초기화가 완료되었다는 정보인데 이를 체크하면 최초 실행 시 초기화되는 시점을 확인할 수가 있다. 이 Notification이 온 후에 'Using Local Storage : 0'으로 바뀌면서 준비가 완료된다. 그나마 다행인 점은 최초 실행 시 문제가 없다면 대부분의 경우 다음 실행 시에는 같은 오류가 발생하는 일은 없어 보인다는 점이다. (강제 종료 시 1번 상황이 발생하지만 않는다면)


iCloud를 이용하는 앱이 한 두개가 아닌데 내가 발견한 문제에 대한 완벽한 해결책이 보이지 않는 것으로 보아 일반적인 상황은 아니고 아마도 나의 잘못된 iCloud 사용 방법이 문제가 아닌가 라는 생각을 하고 있다. 그래서 놓친 부분이 있을까 싶어 Using the SQLite Store with iCloud 문서를 차근차근 읽어보는 중이다.


그렇다고 하더라도 진행상황에 대한 피드백이 미흡하다는 점은 아쉽고 답답하다.




Posted by 52

댓글을 달아 주세요

갈무리 앱을 사용하는 유저께서 폰 자체의 화면 고정 기능과 상관없이 앱 자체에 세로 고정 기능을 넣어달라는 요청이 왔다. 아마도 화면 방향 고정을 해제한 상태에서 앱을 사용하는 경우 조금만 흔들려도 화면이 가로세로로 왔다갔다 하는 것이 불편하기 때문에 요청하셨던 것으로 보인다.


그래서 작업을 시작했다. 


ViewController 기반의 Class에서 


- (BOOL) shouldAutorotate

- (UIInterfaceOrientationMask) supportedInterfaceOrientations


를  Override 하여 작업을 해야 하는데, 갈무리 앱의 경우는 게시물 리스트가 보이는 MasterViewController에서 위의 두 함수를 Override 한다고 해도 아무런 변화가 없다.


화면에는 보이지 않지만 UINavigationController가 화면 구성의 시작이기 때문에 이 부분을 수정해야 하기 때문이다. 상속받은 Class를 만들고 위의 두 함수를 Override 하여 세로 고정과 관련한 코드를 추가한 후, StoryBoard상의 루트인 NavigationViewController를 새로 만든 Class로 교체하여 세로 고정 기능을 완성했다.


수정 후, 테스트를 하다 보니 근래에 iOS9 환경에서 게시물을 SafariViewController로 볼 수 있도록 기능을 추가했었는데 이 SafariViewController는 세로 고정이 되지 않는 것을 알게 되었다. 아이폰 가로 보기 상황에서 게시물 리스트는 세로 고정으로 나오는데 게시물은 가로로 나오는 이상한 상태. 어찌 보면 당연한 현상이다.


이 역시  SafariViewController를 그대로 사용하지 말고 이를 상속받은 Custom Class를 만든 후, 위에 언급한 두 개의 함수를 Override 하니 해결이 가능했다.


안드로이드 버전 갈무리에도 같은 기능을 추가해 봐야겠다.

Posted by 52

댓글을 달아 주세요

XCode7에서의 Storyboard는 크게 3가지의 개선점이 있다.


* 스토리 보드 분리

* 하나의 스토리 보드를 여러개의 sub 스토리 보드로 나눌 수 있다. 이는 여러 개발자 / 디자이너가 동시에 한 프로젝트에서 개발할 경우 나눠진 부분별로 각자 개발을 할 수 있는 이점이 생긴다.

* Scene dock에 supplementary view를 추가함으로서 view controller에 추가적인 view를 생성할 수 있다.

* 네비게이션 바에 여러 개의 버튼을 추가할 수 있다.


스토리 보드 분리 및 supplementary view 추가 기능은 새로운 기능 추가라고 본다면 세번째는 개선인데 내 입장에서는 제일 반가운 개선이다. 네비게이션 바에 버튼을 하나 이상 추가하기 위해서는 지금까진 직접 코딩하여 추가 했어야 하는데 이젠 스토리보드 상에서 마우스로 찍- 끌어 댕기면 버튼이 쉽게 추가가 된다. 아래 이미지 처럼.




스토리 보드 분리 및  supplementary view 관련해서는 아래의 글을 보면 쉽게 이해할 수 있을 듯.


링크 : iOS 9 Storyboards Tutorial: What’s New in Storyboards?


Posted by 52
TAG Xcode, 개발

댓글을 달아 주세요