개인적으로 iOS개발할때 불편하다고 생각되는것 중 하나가 CoreData를 이용한 개발이다.

간단한 테이블뷰어정도는 제공해줘도 될듯한데 XCode에 포함시키지 않았을까? 

애플의 사소한 배려가 없는 상황에서 지금까지는 그때끄때 무식하게 코딩으로 직접 만든 나만의 프로그램으로 해결하곤 했다. 

뭐 테이블뷰로 대충 붙여놓은 간단한 프로그램이긴 하지만.. 귀찮은 일이긴 했지만 자주 사용했으므로 나름 유용한 방법이긴했다. 

물론 앱스토어에 유용한 툴도 있겠지만 거의 파견형태로 일하는 나로서는 내 맥북도 아닌데 아무 어플리케이션이나 깔수 없었다.

그것이 무식한 방식을 선택한 이유이기도 했다.


그런데....무식하면 몸이 고생한다고... 정말 간단한 SQL만으로도 검색할 수 있는 방법이 있었다. 

sqlite명령어를 쓰면 되는데 왜 그생각을 지금까지 못했을까..


어쨋거나 반성은 나중에 하고 일단 메모해 둔다.


1. 당연한 얘기지만 확인해야할 파일을 작업폴더로 복사한 후 터미널을 실행시켜 해당 폴더로 이동한다.

2.sqlite를 실행한다. sqlite>라는 커서가 나타나면 성공.

sqlite3




3. 사용할 데이타베이스를 지정한다. (혹시 실행경로와 파일경로가 다를경우 풀패스를 적어주면 된다.)

sqlite3 데이타베이스 파일명




4. 종료할 경우 (명령어의 경우는 .(점) 으로 시작한다는 것에 주의)

sqlite>.exit 또는 .quit




5. SQL명령어는 ;를 마지막에 붙여준다.

*실제로 XCode에서 정의한 테이블명, 컬럼명 앞에 Z가 붙어 있다.

select * from 테이블명;




6. 검색시 컬럼명을 같이 출력하고 싶을때에는 헤더출력설정을 ON으로 해준다.

sqlite> .headers ON



7. 줄맞춤이 필요한 경우

sqlite> .explain ON



8. 기타 필요한 명령어

#데이타베이스 접속확인

sqlite> .database

#전체 테이블명 확인

sqlite> .tables


물론 도음말은 

sqlite> .help





Posted by 악당잰 트랙백 0 : 댓글 0

댓글을 달아 주세요

iOS5부터인가 나왔던 기능으로 알고 있는데 block코딩과 더불어 생소한 문법 중 한놈이다.

굳이 써야되는 필요성을 느끼지 못한놈인지라 넘겨두고 있었는데 최근 비동기처리가 필요한 플젝을 하면서

어쩔수 없이 조금 공부하게 되었다.

정말 간단하게 구현되고 편리하므로 아마도 자주 써줘야겠다.

어쨌든 내가 아는한도내에서 두가지 정도 메모해 둔다.


첫번째 제일 자주 쓰는 구문. 이건 싱글톤 처리할때 쓰는 패턴이다. 다른방법도 있지만(인스턴스가 nil인지 아닌지 비교해서 분기해주는...)

어쩐지 이게 더 멋있어 보인다. 

쓰는 방법은 dispatch_once_t 라는 변수를 선언해주고 dispatch_once라는 메소드에 초기화 처리를 해주면 메소드 이름처럼 

딱 한번만 실행한다. 그러니 결과적으로 싱글톤이 됨.


+ (AVAudioPlayerManager*)sharedAVAudioPlayerManager {

    static dispatch_once_t pred = 0;

    __strong static id _vrObject = nil;

    dispatch_once(&pred, ^{

        _vrObject = [[self alloc] init];

    });

    return _vrObject;

}


두번째로 비동기를 위해 스레드 처리할 경우. 예를들어 메인스레드에서 시간이 걸리는 처리를 해버리면 

당연히 화면이 멈추는 현상이 나타난다. 왜냐면 화면처리는 메인스레드에서 처리되므로.. 

바꾸어 말하면 시간 걸리는건 메인스레드에서 처리하지 않는것이다. 다만 화면상태를 바꾸고 싶을땐 메인스레드에서 처리

해야 하므로 그 부분만 메인스레드로 넘기는 것이 포인트.


1.일반큐와 메인큐를 각각 선언한다. 

2.스레드를 사용하기 위해 dispatch_async 라는 메소드를 선언하고 일반큐를 파라메터로 넣는다.

3. 다음 처리로직안에 화면제어를 할 필요가 있는 처리에 대해선 메인큐(메인스레드)로 넘겨준다.


정말 간단하다. 백문이불여일견! 직접 소스를 보자.


dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_queue_t mainQueue = dispatch_get_main_queue();

    dispatch_async(globalQueue, ^{

    

        //시간이 걸리는 처리를 한다고 치자.

        int count = 0, i;

        for(i = 0; i < 10000; i++){

            NSLog(@"count = %d", count);

            count++;

        }

        

        //진행상태를 화면에 표시해주기 위해 메인스레드에 화면변경로직을 코딩.

        dispatch_async(mainQueue, ^{

            [self updateProcress:[self logStringWithCount:count]];

        });

        

        //또 시간이 걸리는 처리가 있다고 한다면...

        for(i = 0; i < 10000; i++){

            NSLog(@"count = %d", count);

            count++;

        }


        //끝났을 경우의 처리를 해준다. 여기서도 메인스레드로 처리해줌.

        dispatch_async(mainQueue, ^{

            [self updateProcress:[self logStringWithCount:count]];

            [_indicatorView stopAnimating];

            _startButton.hidden = NO;

        });

    });


Posted by 악당잰 트랙백 0 : 댓글 0

댓글을 달아 주세요

인류문명의 발달은 잉여노동력 그리고 적절한 환경에 의한 결과라는 제레미다이아몬드라는 유명한 아저씨의 주장이 요 며칠간 가깝게 다가왔다.

지금 마치 내상황이 잉여노동력(할일없음) + 적절한 환경(프로젝트 끝나감) 이므로 저아저씨 말대로라면 뭔라도 해야 될 상황이다.


프로젝트는 슬슬 끝나가고 할일은 없고.. 인터넷 서핑도 하루이틀이지.. 

잉여력이 넘치고 넘치던 찰나 결국 남는 잉여력을 소스코드를 이쁘게 고치는데 사용키로 했다.


지금까지 딜리게이트을 남발하면서 막개발을 해 왔는데 이번기회에 iOS5부터인가 지원되지 시작한 blocks비동기처리 방식를 써볼까? 라는 생각이 든것이다.


이제까지 문법자체가 생소해서 쓰고 싶지 않았는데 시계바늘을 본드에 붙여놓은듯 시간이 가질 않으니 어쩔수 없다.

결과적으론 익숙해지는데 조금 시간이 걸렸으나 한번 적용해보니 코드량이 상당히 줄어드는걸 체감했다.

더이상 딜리게이트를 남발하지 않으리...


그럼 일단 block방식으로 코딩하기 위한 문법부터 살펴보자.


블럭구문 기본적인 선언방법

^(파라메터,...) {

    //실행로직

};

 

블럭구문을 대입할 경우의 코딩

반환값 (^변수명)(파라메터,...) = ^(임시 파라메터...) {

    //실행로직

};


여기까지만 보면 어떻게 하라는 얘기인지 모를것이다. 직접 짜보자.


블럭구문으로 test라는 함수작성

int (^test)(int) = ^(int x) {

    return x + x;

};

 

// 결과출력

NSLog(@"%d", test(10));



블럭구문을 typedef로 정의할땐 이렇게 한다. 일반적으로 사용하는 패턴이므로 숙지해 두자.

typedef 반환값 (^변수명)(파라메터,...) 


블럭구문내에 변수사용하기.

__block를 변수앞에 붙이면 블럭구문내에서 값변경이 가능하다.

물론 붙이지 않아도 참조는 가능.


__block int x = 300;

void (^print)(void) = ^(void) {

    x++;

    NSLog(@"%d", x);

};


자.. 여기까지는 기본적인 사항이고 실제로 하고싶은 블럭구문을 메소드에 넘겨주는 걸 해보자.

- (void)example:(double (^)(int, int))func {

int i = 3;

int j = 4;


if(func(i,j) > 5) {

  //처리1...

}else {

  //처리2...

}

}


이런식으로 쓴다. 파라메터로 받은 블럭구분 결과로 처리가 가능하다. 이전엔 이와같은 처리를 딜리게이트로 해결했었다.


실제 호출될 경우에 블록구문내 처리를 하도록 코딩하자.

[self example:^(int n1, int n2) {

//실행처리

    return i+j;

}];


자 여기서 아까 언급했던 typedef 를 상기해보자.

그렇다 메소드에 넘기는 블럭구문을 간단히 정의하여 메소드길이를 줄일수 있다.


//이렇게 정의한 다음..

typedef double (^calcHandler)(int a, int b);



//이렇게 바꾸어 쓸수 있다.

- (void)example:(calcHandler)func {

...

}


딜리게이트의 경우는 프로토콜 정의해주고 그걸 받는쪽에서 위임받아서 처리해주는 구문까지 해야하므로 상당히 코딩량이 많아지는데 반해서 블럭은 상당히 코드를 줄어든다.


조금 설명이 부족한거 같아 한가지 예를 더 들어보자. 

어떤 특정 메소드를 실행이 끝난뒤 후처리를 해주는 메소드를 만들어보자.

블록구문이 사용된 우리가 흔히보는 메소드이다.

[self.navigationController dismissViewControllerAnimated:(BOOL) completion:^{

        //TODO: 

        //화면이 닫힌후 후처리.

    }];

    


먼저 블럭구문부터 관찰해보면 반환값, 파라메터도 없는 블럭이다.

^{ 

//TODO: 

}


위와 같은 처리를 하는 calcTime라는 이름의 메소드를 만들어보자. 그리고 이 메소드가 실행이 완료되면 실행되는 블럭을 만들어 보자.


우선 메소드 선언.

- (void)calcTime:(void ^(void)handler;


그리고 메소드 정의

- (void)calcTime:(void ^(void)handler {


//무쟈게 오래걸리는 처리가 있다고 치자..

//...

//처리가 다 끝난부분에서 블럭구문을 실행하게 한다.

handler();

}

    

    

실제 호출하여 사용할때에는..

[self calcTime:^{

//이메소드 끝날때 처리할 내용 코딩..

}];



써놓고 보니 별 내용없네.. 

테스트하면서 코딩한게 아니라서 소스에 오류가 있을수 있으므로 참고하시길..


Posted by 악당잰 트랙백 0 : 댓글 0

댓글을 달아 주세요

최근에 갑느님이 알아먹기 쉽게 하기위한 목적으로 데모용 프로젝트를 만들어야 했다.

아무리 데모용이지만 어느정도 형태는 갖추어야 하기에..여기저기 이미지를 다른앱에서 긁어모아 

만들기 시작했다.

지금 프로젝트엔 디자이너님이 없는 관계로..


아무튼 가져온 이미지를 문제없이 잘 사용하고 있었는데(적어도 시뮬레이터에는...) 정작 디바이스에 넣어보니 

콘솔에 지저분한 에러로그를 토해내며 XCode가 반항을 하기 시작했다.

「While reading ... pngcrush caught libpng error」


분명 컴파일 에러이면서 앱은 실행되는 그런 이중적 태도를 취하면서 위에 로그대로 해당이미지는 표시하지 않는 짜증나는 현상이었다.

혹시 다른앱에서 퍼온 이미지라서 그러나? 뭔가 락이 걸려있는 이미지인가? 뒤가 구린생각에 오만가지 생각을 다봤지만

그런건 들어본적이 없기에 구글링+스택플로우 에서 찾아보기로 했다.


두시간정도 삽질하니 아래의 두가지정도의 패턴이 발견되었다.


첫번째..

해당파일이 프로젝트 리소스 파일 타겟에서 빠져있는경우.

정말 이경우라면 문제는 간단히 해결.그러나 내가 이런 초보적인 실수를 할리가 없어라고 자신없게 외치며 확인을 해보니 이문제는 아니었음.

참고로 확인방법은 해당파일을 선택한 후 오른쪽 프로파티창에 Target Membership 에서 해당 타겟을 

체크해 되어 있는지 확인하면 됨. 안되 있음 해당 타겟에 체크해면 해결.

 



그리고 두번째..

인터레이스(インタレース)가 적용된 이미지파일일 경우.


인터레이스가 존재자체가 몰랐기에 구글링 해보니 이미지 저장방식의 한종류라고 함.

간단히 예를들어 웹페이지를 열었을때 이미지가 처음에는 뿌옇게 보이다가 다운로드가 진행되면서 

점점 선명해지는 이미지.. 바로 그런이미지로 저장하는 방식이란다.


어쨋든 인터레이스로 되어 있는 이미지는 XCode에서 받아들이지 못하니 변경해주야 한다.

물론 포토샾으로 해주면 깔끔하겠지만 회사에서 준 맥에는 당연하게도 포토샾이 없고 그렇다고 gimp는 사용할 줄 모르고..

더군다나 데모용이었기 때문에 맥 기본 이미지뷰어에서 대충 해결하기로 했다.


해당파일을 이미지뷰어에서 연 후 내보내기에서 알파값속성체크를 제거후 저장한다.




근데 신기한건.. 다른 앱에서 가저온 이미지인데 왜 안된걸까? 

Posted by 악당잰 트랙백 0 : 댓글 0

댓글을 달아 주세요

프로젝트를 마치고 납품할때 설계서를 납품하기도 하지만 종종 고객에 따라 Doxyzen형태의 문서를 요구 하는 경우가 있다.

예전 자바프로젝트의 경우 이클립스에서 Doxyzen용으로 자동으로 코멘트를 생성하주는 편리한 기능이 있었는데 (물론 템플릿 자동생성성)이 기능을 XCode에서도 사용할 수 있다.

상훈아 고마워~ 


이 방법을 몰라 이전 프로젝트는 코멘트를 다 손코딩으로 했었는데 무식하면 몸이고생한다.


방법은 간단. 아래의 설명을 따라하길 바란다.


1.일단 아래링크에서 파일을 하나 다운받는다.

http://www.duckrowing.com/2011/05/14/using-the-doxygen-helper-in-xcode-4/

download the service. <-- 여기에서 다운



2.다운받은 파일은 .tar 로 압축되어 있는데 압축을 풀면 workflow라는 확장자의 파일이 나타난다.

이렇게 쇠파이프를 들고 있는 로봇이다.



3. 자 그러면 이 로봇을 더블클릭하여 실행한다.

* 실행하면 동시에 이 로봇이 말도 없이 사라지는데 놀라진 말자. 중요! 나중에 등록시 이놈의 이름(Document Code)이 필요하므로 외워두자. 


4. 시스템 환경설정에서 키보드를 선택하고 왼쪽에 어플리케이션을 선택하 "+" 버튼을 눌러 아래의 그림과 같이 등록한다.

여기서 등록할때 이름을 3번에서 외워두었던 Document Code를 넣어준다.

단축키를 등록하게 되는데 내경우는 cmd+shift+D 로 지정하였다.


5. 여기까지 되었으면 XCode로 돌아가보자.

XCode가 실행중 이었다면 종료후 다시 시작해야 적용이 된다.


메소드를 선택한 후 아까 지정했던 단축키를 누르면 아래와 같이 Doxyzen용 템플릿이 나타난다.







Posted by 악당잰 트랙백 0 : 댓글 0

댓글을 달아 주세요

iPhone앱안에 있는 /document  저장공간내 파일을 제어할때 필요한 메소드를이다. 내용이야 간단하지만 만들려고 하면 여간 귀찮은게 아니라서 만들어 놓고 쓸 생각이다. 급조한거라 좀 부실하지만 다듬어가면서 업데이트해야지.


이번 프로젝트의 경우 특이하게 파일관련 기능을 자주 썻지만(드롭박스 같은 클라우드 API를 포함해서) 다음 프로젝트에선 어떨지 모르겠다.



1. 파일복사.

- (BOOL)copyFile:(NSString *)srcPath destination:(NSString *)destPath error:(NSError **)error {

    

    NSFileManager *fileManager = [NSFileManager defaultManager];

    BOOL isDirectory;

BOOL result = NO;

    

    if([self isExistFile:srcPath isDirectory:&isDirectory] == YES) {

        

        if (isDirectory) {

            NSString *preFolderPath = [destPath stringByDeletingLastPathComponent];

            

            [fileManager createDirectoryAtPath: preFolderPath

                   withIntermediateDirectories: YES

                                    attributes: nil

                                         error: error];

            result = [fileManager copyItemAtPath:srcPath toPath:destPath error:error];

            

            return result;

            

        } else {

            result = [fileManager copyItemAtPath:srcPath toPath:destPath error:error];

            

            return result;

        }

        

    }else {

        *error = [NSError errorWithErrorCode:ErrorCodeFileNotFoundError];

        

        return NO;

    }

    

}


2. 파일삭제

- (BOOL)removeFile:(NSString *)filePath error:(NSError **)error {

    

    NSFileManager *fileManager = [NSFileManager defaultManager];

BOOL isDirectory;

    BOOL result = NO;

    

    if([self isExistFile:filePath isDirectory:&isDirectory] == YES) {

        

        if (isDirectory) {

            NSArray *contents = [fileManager contentsOfDirectoryAtPath:filePath error:error];

            for(int i=0;i<[contents count];i++) {

                NSString *childFileName = [contents objectAtIndex:i];

                NSString *nextFilePath =[[filePath stringByAppendingString:@"/"] stringByAppendingString:childFileName];

                

                result = [self removeFile:nextFilePath error:error];

            }

            

            result = [fileManager removeItemAtPath:filePath error:error];

            

            return result;

            

        } else {

            result = [fileManager removeItemAtPath: filePath error:error];

            

            return result;

        }

        

    } else {

        *error = [NSError errorWithErrorCode:ErrorCodeFileNotFoundError];

        

        return NO;

        

    }

}


3. 이름변경하기

- (BOOL)renameFile:(NSString *)filePath newfilename:(NSString *)newFilePath error:(NSError **)error {

    

    NSFileManager *fileManager = [NSFileManager defaultManager];

    BOOL isDirectory;

    BOOL result = NO;

    

    if([self isExistFile:filePath isDirectory:&isDirectory] == YES) {

        

        if (isDirectory) {

            NSString *preFolderPath = [newFilePath stringByDeletingLastPathComponent] ;

            [fileManager createDirectoryAtPath: preFolderPath

                   withIntermediateDirectories: YES

                                    attributes: nil

                                         error: error];

            

            result = [fileManager moveItemAtPath:filePath toPath:newFilePath error:error];

            

            return result;

            

        }else {

            NSString *preFolderPath = [newFilePath stringByDeletingLastPathComponent] ;

            

            [fileManager createDirectoryAtPath: preFolderPath

                   withIntermediateDirectories: YES

                                    attributes: nil

                                         error: error];

            

            result = [fileManager moveItemAtPath:filePath toPath:newFilePath error:error];

            return result;

        }

        

    } else {

        *error = [NSError errorWithErrorCode:ErrorCodeFileNotFoundError];

        return NO;

    }

    

}


4. 파일이동하기.

- (BOOL)moveFile:(NSString *)srcPath destination:(NSString *)destPath error:(NSError **)error {

    

    NSFileManager *fileManager = [NSFileManager defaultManager];

BOOL isDirectory;

    BOOL result = NO;

    

    if([self isExistFile:srcPath isDirectory:&isDirectory] == YES) {

        

        if (isDirectory) {

            NSString *preFolderPath = [destPath stringByDeletingLastPathComponent] ;

            [fileManager createDirectoryAtPath: preFolderPath

                   withIntermediateDirectories: YES

                                    attributes: nil

                                         error: error];

            

            result = [fileManager moveItemAtPath:srcPath toPath:destPath error:error];

            

            return result;

            

        }else {

            NSString *preFolderPath = [destPath stringByDeletingLastPathComponent] ;

            

            [fileManager createDirectoryAtPath: preFolderPath

                   withIntermediateDirectories: YES

                                    attributes: nil

                                         error: error];

            

            result = [fileManager moveItemAtPath:srcPath toPath:destPath error:error];

            return result;

        }

        

    } else {

        *error = [NSError errorWithErrorCode:ErrorCodeFileNotFoundError];

        return NO;

    }

}



5. 파일정보보기

- (NSDictionary *)showPropertyFile:(NSString *)filePath {

    

    NSError *error = nil;

    NSDictionary *attr = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:&error];

    

    if (error == nil) {

        return attr;

    }

    

    return nil;

    

}


6. 새폴더 만들기.

- (BOOL)createFolder:(NSString *)currentDirectory folderName:(NSString *)foldername error:(NSError **)error {

    

    NSFileManager *filemanager = [NSFileManager defaultManager];

    

    NSString *newFolderPath = [currentDirectory stringByAppendingFormat:@"/%@",foldername];

    BOOL isDirectory;

    

    if([self isExistFile:newFolderPath isDirectory:&isDirectory] == YES) {

        

        if (isDirectory) {

            return NO;

        }else {

            BOOL result = [filemanager createDirectoryAtPath: newFolderPath

                                 withIntermediateDirectories: YES

                                                  attributes: nil

                                                       error:error];

            

            return result;

        }

        

    }else {

        BOOL result = [filemanager createDirectoryAtPath: newFolderPath

                          withIntermediateDirectories: YES

                                           attributes: nil

                                                error:error];

        

        return result;

    }

}




Posted by 악당잰 트랙백 0 : 댓글 0

댓글을 달아 주세요

며칠전 운영체제인 iOS7과 iPhone5s와 iPhone5c라는 두가지 디바이스 버전이 발표되었다. 신제품이 출시되면 소비자들은 즐겁겠지만 개발자는 마냥 즐겁지는 않다. 만들어 놓은 앱이나 지금 진행중인 플젝에 반영해야 하기 때문이다.(일이 늘어난다는 얘기임.)

마치 군복무중에 눈이 내리는 장면을 상상하면 딱 맞을듯 싶다.


내 경우는 스마트폰 개발 특성상 안드로이드 개발자들과 일을 같이 하는데 ( 뭐 안드개발자들 바쁘면 도와주기도 한다) 이 디바이스별 대응이라는게 참 손이 많이가는 작업이다. 테스트를 다 할수 없는 거고 안된다고 크레임들어오고.. 대책이 없는거다.


그나마 다행이랄까 애플에서 개발자를 배려해 XCode5의 새로운 기능이 추가되었다.

디바이스별 이미지를 관리하는 어셋기능(Asset)이다.


테스트 해보니 기존소스와도 호환성이 있어 예전방식으로 코딩해도 기본적으로는 인식하는듯 하다.

어쨋든 서론이 길어졌지만 직접 해보지 않으면 답답하니 샘플을 만들어 보자.


우선 샘플 프로젝트를 만들고 UIImageView를 배치 IBOutlet로 선언후 연결해 놓는다.




그리고 이미지명을 "animal" 로 지정해서 불러들인다.

그리고 XCode5에 새로생긴 Images.xcassets 폴더를 선택한 후 오른쪽 마우스를 클릭하여 Image Set 을 추가한다.

참고로 폴더를 만들수도 있고 이외 여러가지 메뉴가 있다.




기본으로 Image라는 이름으로 추가되므로 이 이름을 "animal"로 변경해준다.




위의 작업을 해주면 프로젝트내에 폴더가 생성되는데 이곳에다가 사용할 이미지를 복사한다.



펭귄, 코알라, 고양이들을 복사해주자.



복사함과 동시에 XCode상에 추가된 이미지를이 보이기 시작한다. 

오른쪽 프로파티에 속성을 그림과 같이 변경해준다.



각각 동물들을 필요한 곳에 드롭다운 해준다. 




이제 시뮬레이터 버전을 변경해 가면서 표시되는 이미지를 확인해보자. 역시 예상대로 잘 표시된다.



Asset기능과 AutoLayout기능을 적절히 사용하면 디바이스 화면사이즈에 대응하는것이 그리 귀찮은 일은 아닐것 같다.

예전방식에 익숙해서 AutoLayout기능은 쓰질 않았었는데 나중에 한번 더 연구해 볼생각이다.



Posted by 악당잰 트랙백 0 : 댓글 0

댓글을 달아 주세요


이왕 AdHoc배포를 한다면 아이폰 스타일로 다운로드 페이지를 만들어 보자.



아래는 html소스이다.

HTML은 지식이 없어 여기저기 소스를 갇다붙였더니 지저분하다. 뭐 잘 보이기만 하면 되지머~ 

<!DOCTYPE html>

<html>

<head>

  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> 

  <title>Test Applicationダウンロードページ</title>

  <style type="text/css">

    body {

      background: url(bkg.png) repeat #c5ccd4;

      font-family: Helvetica, arial, sans-serif;

    }

    .congrats {

      font-size: 16pt;

      padding: 6px;

      text-align: center;

    }

    .desc {

      font-size: 10pt;

      padding: 6px;

      text-align: left;

    }

    .step {

      background: white;

      border: 1px #ccc solid;

      border-radius: 14px;

      padding: 4px 10px;

      margin: 10px 0;

    }

    .instructions {

      font-size: 12pt;

    }

    .arrow {

      font-size: 15pt;

    }

    .icon_image {

      border: 1px #ccc solid;

      border-radius: 10px;

    }

    table {

      width: 100%;

    }

  </style>

</head>

<body>


<div class="congrats">Test Application</div>

<div class="step">

  <table><tr>

    <td class="instructions"><b>Test Application<b><br /><font size="2px" color="#ccc">ver.0.9.0<br />2013/08/15</font></td>

    <td width="24" class="arrow">&rarr;</td>

    <td width="57" class="imagelink">

      <a href="itms-services://?action=download-manifest&url=http://devkjcode.com/app/TestApplication.plist">

        <img class="icon_image" src="testApplication-icon.png" height="57" width="57" />

      </a>

    </td>

  </tr></table>

</div>

<div class="desc"> 

<p>初めての方は下記のリンクをタップしてProvisioningをダウンロードしてください。<br />

<a href="http://devkjcode.com/app/TestApplication.provisioning">Provisioningをダウンロード</a></p>

</div>

</body>

</html>


배경용 이미지는 아래 이미지를 사용하면 된다.





Posted by 악당잰 트랙백 0 : 댓글 0

댓글을 달아 주세요

개발중인 앱이 런타임중에 먹통이 되는 현상이 발생하여 디바이스 로그를 까봤더니 <Error>: Max open files: 78 라는 놈이 다량으로 출력되어 있었다.


앱이 먹통이 되는 문제가 이 에러랑 직접적인 관련은 없을거라 생각하면서도 저 찝찝한 에러로그 때문에 원인을 찾아보기로 했다.

가볍게 시작한 일이 하루종일 삽질이되고...

 모든문제를 해결해주는 전지전능한 스택플로우 마저도 명확한 답이 없었다. 있다 싶으면 케이스별로 다 답변도 달랐다.


찾아보면 찾아볼수록 심오한 Objective-C의 세계로 빠져들어 포기하려던 찰나 우연치 않게 해결할 수 있었다.

문제는 뚱딴지 같이 Provisioning설정..

빌드셋팅에서 프로비져닝을 정성것 다시 설정해주니 그 지저분한 에러가 싹 없어졌다.


하루종일 삽질해서 해결됬으니 다행이지만.. 아직도 먹통이되는 문제는 미해결.



Posted by 악당잰 트랙백 0 : 댓글 0

댓글을 달아 주세요

최근의 앱들은 대부분 서버와의 연동이 필요하게 되었다. 서버의 API를 이용함으로 인해서 발생되는 여러가지 에러를 NSError카테고리를 만들어 관리하면 편하다.

그로인해 서비스쪽(API를 이용한 비지니스 로직)과 화면쪽이 명확하게 분리되는 장점도 있다.


NSError+Service.h


#import <Foundation/Foundation.h>


#define KJErrorDomain @"com.kj-code.KJError"


/**

 * @brief enum KJErrorエラー定義

 */

typedef enum {

    KJErrorCodeNone = 0,                    ///< エラーなし

    KJErrorCodeInternalError,               ///< 内部エラー

    KJErrorCodeAuthenticationError,         ///< 認証エラー

    KJErrorCodeResponseStatusError,         ///< 応答エラー

KJErrorCode;


/**

 * @brief KJCodeエラーカテゴリークラス

 */

@interface NSError (Service)


/**

 * @brief NSErrorインスタンスを返却します。

 * 

 * @param KJErrorCode 定義したエラーコード

 * @param NSString エラーメッセージ文字列

 *

 * @return NSError インスタンス

 */

+ (NSError*)errorWithKJErrorCode:(KJErrorCode)code localizedDescription:(NSString*)localizedDescription;


/**

 * @brief NSErrorインスタンスを返却します。

 * @details KJErrorCodeに定義されているエラーメッセージがセットされます。

 *

 * @param KJErrorCode 定義したエラーコード

 *

 * @return NSError インスタンス

 */

+ (NSError*)errorWithKJErrorCode:(KJErrorCode)code;


@end




NSError+Service.m


#import "NSError+Service.h"


static NSString* _KJ_CODEDESCRIPTION[] = {

    @"",

    @"KJError.InternalError",

    @"KJError.AuthenticationError",

    @"KJError.ResponseStatusError",

};


@implementation NSError (Service)


+ (NSError*)errorWithKJErrorCode:(KJErrorCode)code localizedDescription:(NSString*)localizedDescription {


    NSDictionary *userInfo = [NSDictionary dictionaryWithObject:localizedDescription forKey:NSLocalizedDescriptionKey];

    

    NSError *error = [[self class] errorWithDomain:KJErrorDomain code:code userInfo:userInfo];

    return error;

}


+ (NSError*)errorWithKJErrorCode:(KJErrorCode)code {


    NSString *description = _KJ_CODEDESCRIPTION[code];

    NSDictionary *userInfo = [NSDictionary dictionaryWithObject:description forKey:NSLocalizedDescriptionKey];

    

    NSError *error = [[self class] errorWithDomain:KJErrorDomain code:code userInfo:userInfo];

    return error;

}




사용할 때는  이렇게 사용한다.



NSError *error = [NSError errorWithKJErrorCode:KJErrorCodeInternalError

                                                      localizedDescription:message];



Posted by 악당잰 트랙백 0 : 댓글 0

댓글을 달아 주세요