2013년 4월 25일 목요일

iOS 프로그래밍 팁 - NSThread 사용하기

알티베이스 메뉴얼 앱을 최초 개발 할 때의 일이다.
기본 아이디어가 알티베이스 메뉴얼 pdf 파일이 여러 개이고
사용자는 이 여러 개의 pdf 파일에서 검색어 하나로 찾고 싶어 할 것이라는 가정 하에

찾을 대상에 pdf 파일 선택 화면을 만들고
선택된 pdf 파일에서 검색어로 키워드를 찾는 것이다.

 pdf에서 text 를 검색하는 것은 상당한 시간이 걸리는 작업이고
이를 여러 파일에 대해서 하려니 UI가 그동안 얼어 버린다.

해서 thread로 구현 해야 했다.


- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
{
    [searchBar resignFirstResponder];
    [self.view bringSubviewToFront:webActInd];

    [webActInd startAnimating];
    [resultFilename removeAllObjects];
    [resultThumbImg removeAllObjects];
    [resultPageNum removeAllObjects];
    
    [tableView_ reloadData];
    //스레드
    searchText = [NSString stringWithString:searchBar.text];
    [NSThread detachNewThreadSelector:@selector(searchDoJob) toTarget:self withObject:nil];
    
    
    //[webActInd stopAnimating];
}


알티베이스 메뉴얼 버젼 1.0에서 검색 버튼을 눌렀을 때 코드이다.
NSThread 를 사용하여 구동 될 메서드 searchDoJob을 설정 한다.




- (void) searchDoJob
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    
    //pdfSearch = [[[PDFSearch alloc]init]autorelease];
    
    NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
    
    NSString *text = [NSString stringWithString:searchText];
    
    //모든 파일이름을 루프로 돌려 검색 한다.
    
    for (NSInteger j = 0; j< [filename_ count]; j++)
    {
        NSString * filename = [filename_ objectAtIndex:j];
        if ([userDefault boolForKey:filename])
        {
//            NSURL *pdfURL = [[NSBundle mainBundle] URLForResource:filename withExtension:@"pdf"];
//            
//            MFDocumentManager *documentManager = [[MFDocumentManager alloc]initWithFileUrl:pdfURL];



            
            
//            CGPDFDocumentRef document =  CGPDFDocumentCreateWithURL(( CFURLRef)pdfURL);
//            for (NSInteger i=1; i< CGPDFDocumentGetNumberOfPages(document); i++ )
//            {
//                CGPDFPageRef PDFPage = CGPDFDocumentGetPage(document, i);
//                
//                if ([pdfSearch page:PDFPage containsString:text] )
//                {
//                    NSString * pageNumStr= [NSString stringWithFormat:@"%i",i];
//                    [resultFilename addObject:filename];
//                    [resultPageNum addObject:pageNumStr];
//                    
//                    [resultThumbImg addObject:[self imageFromPDFWithDocumentRef:document andPageNum:i]];
//                    
//                    if ([resultFilename count] > 100)
//                        break;
//                    
//                    [tableView_ reloadData];
//                }
//                
//            }
//            CGPDFDocumentRelease(document);
        }
        
        if ([resultFilename count] > 100)
            break;
        
    }
    [webActInd stopAnimating];
    [pool release];
}

선택된 파일들 (filename_ ) array에서 CGPDocuemntRef를 이용해서 주석처리된 부분이 실제로 PDF파일에서 문자열 검색을 하는 부분이고 결과로 pdf 파일 명과 페이지 번호 그리고 썸네일 이미지를 생성하는 부분이다.

현재 1.5버젼 이상 부 터는 fastpdfkit 라이브러리를 사용하기 때문에 관련 코드는 모두 주석 처리 되었다.

이렇게 수행 시간이 오래 걸리는 작업을 처리 하기 위해서는 NSThread가 필요하다.

2013년 4월 24일 수요일

parallels desktop 제거 후 런치패드에 남아 있는 windows7 Applications 폴더 제거 하기

parallels desktop 8 trial을 설치해보았다.

역시 Virtual Box보다 한수위 인 듯 하다.
VirtualBox의 seamless 모드는 시작 메뉴와 바가 mac os 상단에 떡하니 표시 되는것이 거슬려서
사용 하지 않았는데 parallels의 기본 모드는 완전히 통합되어 windows 응용 프로그램이 마치
mac용 응용프로그램과 같이 되고 너무 좋은 듯 했다.

하지만 90000원, 적어도 84000원 이라는 금액의 압박

바로 언인스톨
그냥 Applications 폴더에서 제거 했다.

dock에 남아 있는 windows7 Applications폴더는 그냥 제거 했다.
그럼 끝인 줄 알았는데......

Launchpad 화면에 떡하니 남아 있는 windows7 Applications

헐.....

이거 어떻게 지우지

http://macnews.tistory.com/385
런치패드에 finder 넣기에서 맨 아래 제거 하기...

해서 이 방법으로 잘된다.

sudo sqlite3 ~/Library/Application\ Support/Dock/*.db "DELETE from apps WHERE title='windows7 Applications';" && killall Dock

터미널에서 위와 같이 입력하면 windows7 Applications 폴더를 제거 할 수 있다.

2013년 4월 16일 화요일

iOS 프로그래밍 팁, jSON 객체를 주고 받는 클라이언트 개발

서버 <-----> iOS 클라이언트 관에 데이터 통신을 하는 앱 개발에 필요한 방법을 정리한다.



1. jSON 객체를 주고 받는다.
2. HTTP 프로토콜을 이용한다.
3. jSON 객체 string은 다음에 과정을 거친다.
jSON string --> base64Enc --> AES256Enc --> byte2Hex String --> HTTP 통신 --> 서버
서버 --> HTTP 통신 ---> hex2byte --> AES256Dec --> base64Dec --> JSON string


4. 필요한 모듈
이를 위해 필요한 것은 아래의 NSString 및 NSData 카테고리 클래스를 구해야 한다.



NSString+Base64.m / h

https://github.com/kyoshikawa/ZUtilities/blob/master/NSData%2BAES256.m

NSData+AES25.m / h

https://github.com/nicklockwood/Base64

NSData+Base64.m / h


5. 인크립트 디크립트 함수를 구현 하는 crypto클래스를 작성한다.
... 생략 ....



//string -> base64Enc -> aes256Enc -> byte2hex
- (NSString *) encryptString:(NSString *)textString {
    NSString * base64Enc = [self base64Encode:textString];
    NSData * aes256enc = [self encryptString:base64Enc withKey:[self generateKey]];
    return [self byteToHexString:aes256enc];
}

// hex2byte-> aes256Dec --> Base64Dec --> string
- (NSString *) decryptString:(NSString *)textString {
    NSData * data = [self hexToBytes:textString];
    NSString * aes256dec = [self decryptData:data withKey:[self generateKey]];
    return [self base64Decode:aes256dec];
}

... 생략 ...



6. jSON객체를 핸들링 하기 위한 모듈을 준비한다.
json 객체를 이용하기 위해서는 

http://superloopy.io/json-framework/

에서 jSON 모듈이 필요하다.


7. 서버에 요청 하기 위한 jSON객체 생성과 인크립트를 한다.


NSArray *keys = [NSArray arrayWithObjects:@"graduateNo", @"name", @"alumniName", nil];
    NSArray *objects = [NSArray arrayWithObjects:txtFieldNumber.text, txtFieldName.text, txtFiledAlumiName.text, nil];
    NSDictionary *jsonDictionary = [NSDictionary dictionaryWithObjects:objects forKeys:keys];
    
    NSString *jsonString =[jsonDictionary JSONRepresentation];



위와 같이 key와 key값을 NSArray와 NSDictionary 를 이용하여 jsonString을 얻어 낼수 있다.

인크립트 메서드를 가지고 있는 crypto클래스를 이용해서 전송에 필요한 데이터를 구성한다.


8. 서버에 전송
NSMutableURLRequest,  NSURLConnection 을 이용해서 서버에 전송한다.

NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:
                                    [NSURL URLWithString:[NSString stringWithFormat: @"%@/api/xxxx.api",
                                                          SERVER_URL]]
                                                                cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:5.0];
    
    [request addValue:@"application/x-www-from-urlencoded" forHTTPHeaderField:@"content-type"];
    [request setHTTPBody:jsonData];
    [request setHTTPMethod:@"POST"];
    [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
    [request setValue:[NSString stringWithFormat:@"%d",[jsonData length]] forHTTPHeaderField:@"Content-Length"];
    
    NSURLConnection *connection = [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
        
    if(connection) {
receiveData = [[NSMutableData alloc] init];
}

9. 서버로 부터 응답 처리


- (void)connection:(NSURLConnection *)connection didReceiveResponse:

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error

- (void)connectionDidFinishLoading:(NSURLConnection *)connection

응답에 관련된 메서드를 각각 구현한다.

서버에 응답 데이터에 대해서는
didReceiveResponse 에서 NSData 를 append 하고

connectionDidFinishLoading
에서 완성된 데이터를 crypto 클래스의 decryp 메서드를 이용해서 json 스트링으로 변환 한다.

마지막으로 JSONValue 를 통해서 NSDictionary로 변환 하여 
필요한 값을 valeuForKey로 얻어 낸다.











2013년 4월 15일 월요일

디바이스 좌표계를 OpenGL 좌표계로 변환


OpenGL의 좌표계는 화면에 중심에 0,0 으로 부터 좌측 -1.0 우측 +1.0
위로 +1.0 아래로 -1.0 이더라

마치 수학시간 좌표계

이게 지금까지 컴퓨터 그래픽 좌표계와 달라서 혼돈스럽다.


gwidth
gheight

가 설정되어 있다고 가정 하고 아래에 함수를 이용해서 x,y를 입력하면
OpenGL에서 사용하는 화면에 중앙을 기준으로 float -1.0 ~ +1.0까지의 x, y
좌표 체계로 변환 하는 함수이다.

  void convertDeviceXY2OpenglXY(int x, int y, float * ox, float  * oy)
  {
      int w = gwidth;
      int h = gheight;
      *ox = (float)((x - (float)w/2.0)*(float)(1.0/ (float)(w/2.0)));
      *oy = -(float)((y - (float)h/2.0)*(float)(1.0/ (float)(h/2.0)));
  }

를 이용 할수 있다.


https://github.com/sparrowapps/CohenSutherlandLineClipp

코헨서덜런드 라인클립핑 알고리즘을 mac os에서 OpenGL로 구현할때 
이함수를 이용 하였다.

2013년 3월 20일 수요일

iOS 프로그래밍 팁 - Custom URL Scheme

custom URL scheme 기능은

사파리에서 특정 URL 을 입력하면 앱이 활성되게 할수 있다.

아래 apple 사이트에서

Implementing Custom URL Schemes 

항목을 참고하면 이해가 갈것이다.


http://developer.apple.com/library/ios/#documentation/iphone/conceptual/iphoneosprogrammingguide/AdvancedAppTricks/AdvancedAppTricks.html

여기를 참조 하여 xxxxx-info.plist 파일을 수정 하는 방법을 쉽게 이해 할수 있다.


http://iosdevelopertips.com/cocoa/launching-your-own-application-via-a-custom-url-scheme.html


실제로 xxxxx-info.pilist 파일에 내용을 확인 해보면
다음과 같다.


<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>sparrowapps</string>
</array>
</dict>
</array>


사파리에서  주소창에 sparrowapss://를 입력 하면 해당 앱이 활성된다.

2013년 3월 14일 목요일

2007 lenovo Thinkpad T60의 부활

이 글을 T60으로 쓴다면 스크린 샷 이랑 해서 좀 더 자세히 쓸수 있겠지만
이글은 2012 mac mini로 작성하고 있다.

2007년 당시 170만원에 육박하는 가격으로 lenovo Thinkpad T60을 구입했다.
지금은 2013년 3월이니까 벌써 6년이 지났다.

당시의 최신의 CPU와 엄청난 용량의 메모리 최고의 성능이라 자부 했지만
지금은 그냥 오래된 노트북이다.

2012 late mac mini 의 엄청난 속도에 비하면 느리고 답답하다.

인터넷 쇼핑을 하기에도 부족한 성능이다.

메롬 플랫폼은 램이 2GB를 두개 꽂아도 3GB로 인식 한다.
OS가 64bit여도 소용이 없다.

그래서 1GB, 2GB를 꽂아서 T60과 구형 맥미니를 사이좋게 3GB 머신으로 만든지
오래 되었다.

메모리가 3GB나 되지만 느린 120GB 5400rpm 하드 디스크 그리고 CPU로 인해 답답하다.

시스템 메모리를 2기가로 줄이고 1기가를 램 디스크로 설정해서

인터넷 익스플로러의 temp를 램 디스크로 설정
램 디스크에 readyboost 설정
환경 변수 TEMP와 TMP의 경로도 램 디스크로 설정

이렇게 설정을 하니 인터넷 속도가 훨씬 빨라 졌다.

잠시 SSD를 구매할까 고민했던,..
아니면 버리고 새로 노트북을 장만할까.. 고민 했던...
그냥 더 쓰자..
어짜피 난 mac을 쓰고
이건 그냥 인터넷 쇼핑 용이니까...



2013년 2월 14일 목요일

iOS 프로그래밍 팁 - NSLog() 사용시 릴리즈 빌드시 제거하기

NSLog() 자체가 릴리즈 빌드에서 저절로 사라 질까?

그랫으면 좋으련만

그럼 NSLog()를 사용한곳 앞뒤로 #ifndef ~ #endif 로 해야 하나?

아니면 NSLog()를 매크로로 만들고 DebugLog() ?

NSLog()를 사용한 곳을 모두 replace 해서 DebugLog()로 바꿔야 하나 ?

가장 간단한 해답을 찾았다.

그리고 DEBUG=1  같은 것을 설정 할 필요도 없이


앱이름-Prefix.pch 

파일에

#ifndef __OPTIMIZE__
#    define NSLog(…) NSLog(__VA_ARGS__)
#else
#    define NSLog(…) {}
#endif


를 추가해주면 된다.