OS X El Capitan & Xcode7 beta 버전

Programming/Objective-C 2015. 7. 2. 15:38



애플이 개발자들을 위해 내놓는 beta 버전은 일정 부분 잠재적 문제가 있음을 고려하고 사용해야 한다. 그러나 문제의 성격에서 개발자들의 편의에는 점점 소홀해진다는 생각을 지울 수가 없다.


OS X El Capitan을 업데이트하고 여러 가지 문제가 발생한다. 가장 큰 문제는 텍스트입력 필드에서 자음과 모음 조합이 이루어지지 않아서 메모장 같은 애플리케이션을 이용해서 문장을 작성하고 붙여넣기를 해야 하는 불편함이다. 영어는 당연히 문제가 없겠으나 한글과 같이 조합이 필요한 언어에서는 짜증이 나는 버그다. 이 문제는 최근에 올려진 beta2 버전에서도 여전히 고쳐지지 않았다.


두 번째 문제는 OS X El Capitan를 설치하면 기존에 사용하던 Xcode6 버전이 실행되지 않는다. OS X El Capitan를 설치할 경우 무조건 Xcode7 beta를 설치해야 한다. 이로 인하여 새로운 문제가 발생한다. Xcode7에서는 iOS 8.4 버전과 8.0등 몇몇 버전을 지원하지 않는다. 누락된 해당 버전 기기는 테스트 기기로 사용할 수가 없는 것이다. 


이 문제는 기존 Xcode6버전 패키지에 있는 버전별 폴더를 복사해서 아래 경로와 같이 Xcode7 내에 있는 동일한 폴더에 붙여넣기를 하는 것으로 해결할 수가 있다.

경로 : /Applications/Xcode-beta.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport


이러한 문제는 애플에서 내놓은 beta 버전이 더는 개발자들을 위한 버전이 아닐 수 있다는 인식을 심어 주기에 충분하다. 애플이 장기적인 안목에서 개발자들과 바람직한 협업(?!)을 유지하려면 일반인에게 공개되는 정식 버전의 점유율만큼, 개발자들의 beta 버전 설치율에도 신경 써야 하지 않을까.


보탬.

OS X El Capitan에서 Xcode 6 버전이 실행되지 않을 경우 아래와 같은 명령어를 터미널에서 실행하면 Xcode6 버전을 실행할 있습니다. Xcode를 종료하면 다시 명령어를 입력해야하니 임시적으로 사용할 수 있는 방법이다.


/Applications/Xcode.app/Contents/MacOS/Xcode </dev/null &>/dev/null &



    

설정

트랙백

댓글

요세미티와 iOS8을 이용해 디바이스 동영상 캡처하기

Programming/Objective-C 2014. 9. 25. 10:55

iOS8 업데이트와 함께 앱스토어에서 앱의 프리뷰 영상을 업로드 할 수 있도록 되었습니다. 기존에는 이러한 앱 영상을 만들려면 시뮬레이터를 이용한 녹화 방법 이외에 딱히 효과적인 방법을 찾기 어려웠습니다. 더욱이 디바이스 전용 하드웨어를 사용하는 앱의 경우에는 난감한 부분이었습니다. 그러나 요세미티의 QuickTime Player에서 디바이스 화면을 녹화할 수 있는 기능을 지원합니다. 


https://developer.apple.com/app-store/app-previews/


아래 링크가 좀 더 이해하기 쉽겠네요.

http://www.macworld.co.uk/how-to/iphone/create-video-for-your-app-on-app-store-3532435/


설명 드리면, 요세미티 베타 버전을 업데이트 하셨다면, 

1. 디바이스를 USB로 맥과 연결하고 QuickTime Player를 실행합니다.

2. 파일->새로운 동영상 녹화 선택

3. 동영상 화면에서 위 사진과 같이 녹화 버튼 옆에 화살표를 클릭.

4. 연결된 디바이스 중에 하나를 선택

5. 녹화

6. 끝.




    

설정

트랙백

댓글

iOS bitly.com을 통한 url 단축 코드

Programming/Objective-C 2013. 5. 29. 08:12

url 링크 스트링을 단축해주는 서비스들 중에 트위터에서 많이 사용되는 Bitly를 통한 url 단축 방법은 다음과 같다.


1. https://bitly.com/ 사이트에서 회원 가입을 한다. (페이스북이나 트위터가 로그인 되어 있으면 해당 계정으로 가입할 수 있음).

2. 오른쪽 상단 내 계정을 클릭, Settings 화면으로 이동하여 ADVANCED 탭을 선택.

3. 맨 아래 Legacy API Key에서 Show legacy API Key 를 클릭.

4. Login과 API Key를 복사해 둔다.


Objective-C 코드 :


-(NSString *)shortenedURL:(NSString *)url{
      NSString *bitLyLogin = @"yourLogin";
      NSString *bitLyAPIKey = @"yourAPIKey";
      NSString *shortenedURL = [NSString stringWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://api.bit.ly/v3/shorten?login=%@&apikey=%@&longUrl=%@&format=txt", bitLyLogin, bitLyAPIKey, url]] encoding:NSUTF8StringEncoding error:nil];
      
      return shortenedURL;
}


복사한 Login 스트링과 API Key를 넣으면 사용가능하다.


주의할 것은 Legacy API Key에서 API Key를 Reset하면 기존 API Key로는 사용할 수 없으므로 초기 설정된 값을 사용하거나

Reset한 API Key를 받아서 사용하도록 하자.


Facebook이나 Twitter로 가입을 한 경우, 초기 비밀번호를 설정해야 Bitly를 사용하는데 불편함이 없다. 위 코드로 링크를 단축할 경우 Bitly 사이트에서 단축한 원래의 url과 단축된 url을 확인할 수 있다. 또한, 해당 url로 얼마나 클릭이 이루어졌는지도 그래프로 확인이 가능하다.

    

설정

트랙백

댓글

iPhone, iPad : iOS font list

Programming/Objective-C 2012. 10. 5. 17:26


iPhone 4.2

iPad 4.2
Font Families: 40Font Families: 57
Academy Engraved LET
 AcademyEngravedLetPlain
American Typewriter
AmericanTypewriterAmericanTypewriter
AmericanTypewriter-BoldAmericanTypewriter-Bold
Apple Color Emoji
AppleColorEmojiAppleColorEmoji
AppleGothic
AppleGothicAppleGothic
Arial
ArialMTArialMT
Arial-ItalicMTArial-ItalicMT
Arial-BoldMTArial-BoldMT
Arial-BoldItalicMTArial-BoldItalicMT
Arial Hebrew
ArialHebrewArialHebrew
ArialHebrew-BoldArialHebrew-Bold
Arial Rounded MT Bold
ArialRoundedMTBoldArialRoundedMTBold
Bangla Sangam MN
BanglaSangamMNBanglaSangamMN
BanglaSangamMN-BoldBanglaSangamMN-Bold
Baskerville
BaskervilleBaskerville
Baskerville-ItalicBaskerville-Italic
Baskerville-BoldBaskerville-Bold
Baskerville-BoldItalicBaskerville-BoldItalic
Bodoni 72
 BodoniSvtyTwoITCTT-Book
 BodoniSvtyTwoITCTT-BookIta
 BodoniSvtyTwoITCTT-Bold
Bodoni 72 Oldstyle
 BodoniSvtyTwoOSITCTT-Book
 BodoniSvtyTwoOSITCTT-BookIt
 BodoniSvtyTwoOSITCTT-Bold
Bodoni 72 Smallcaps
 BodoniSvtyTwoSCITCTT-Book
Bodoni Ornaments
 BodoniOrnamentsITCTT
Bradley Hand
 BradleyHandITCTT-Bold
Chalkboard SE
ChalkboardSE-RegularChalkboardSE-Regular
ChalkboardSE-BoldChalkboardSE-Bold
Chalkduster
 Chalkduster
Cochin
CochinCochin
Cochin-ItalicCochin-Italic
Cochin-BoldCochin-Bold
Cochin-BoldItalicCochin-BoldItalic
Copperplate
 Copperplate
 Copperplate-Bold
Courier
CourierCourier
Courier-ObliqueCourier-Oblique
Courier-BoldCourier-Bold
Courier-BoldObliqueCourier-BoldOblique
Courier New
CourierNewPSMTCourierNewPSMT
CourierNewPS-ItalicMTCourierNewPS-ItalicMT
CourierNewPS-BoldMTCourierNewPS-BoldMT
CourierNewPS-BoldItalicMTCourierNewPS-BoldItalicMT
DB LCD Temp
DBLCDTempBlackDBLCDTempBlack
Devanagari Sangam MN
DevanagariSangamMNDevanagariSangamMN
DevanagariSangamMN-BoldDevanagariSangamMN-Bold
Didot
 Didot
 Didot-Italic
 Didot-Bold
Futura
Futura-MediumFutura-Medium
Futura-MediumItalicFutura-MediumItalic
Futura-CondensedExtraBoldFutura-CondensedExtraBold
Geeza Pro
GeezaProGeezaPro
GeezaPro-BoldGeezaPro-Bold
Georgia
GeorgiaGeorgia
Georgia-ItalicGeorgia-Italic
Georgia-BoldGeorgia-Bold
Georgia-BoldItalicGeorgia-BoldItalic
Gill Sans
 GillSans
 GillSans-Italic
 GillSans-Bold
 GillSans-BoldItalic
Gujarati Sangam MN
GujaratiSangamMNGujaratiSangamMN
GujaratiSangamMN-BoldGujaratiSangamMN-Bold
Gurmukhi MN
GurmukhiMNGurmukhiMN
GurmukhiMN-BoldGurmukhiMN-Bold
Heiti J
STHeitiJ-LightSTHeitiJ-Light
STHeitiJ-MediumSTHeitiJ-Medium
Heiti K
STHeitiK-LightSTHeitiK-Light
STHeitiK-MediumSTHeitiK-Medium
Heiti SC
STHeitiSC-LightSTHeitiSC-Light
STHeitiSC-MediumSTHeitiSC-Medium
Heiti TC
STHeitiTC-LightSTHeitiTC-Light
STHeitiTC-MediumSTHeitiTC-Medium
Helvetica
HelveticaHelvetica
Helvetica-ObliqueHelvetica-Oblique
Helvetica-BoldHelvetica-Bold
Helvetica-BoldObliqueHelvetica-BoldOblique
Helvetica Neue
HelveticaNeueHelveticaNeue
HelveticaNeue-ItalicHelveticaNeue-Italic
HelveticaNeue-BoldHelveticaNeue-Bold
HelveticaNeue-BoldItalicHelveticaNeue-BoldItalic
Hiragino Kaku Gothic ProN
HiraKakuProN-W3HiraKakuProN-W3
HiraKakuProN-W6HiraKakuProN-W6
Hiragino Min ProN
 HiraMinProN-W3
 HiraMinProN-W6
Hoefler Text
 HoeflerText-Regular
 HoeflerText-Italic
 HoeflerText-Black
 HoeflerText-BlackItalic
Kailasa
KailasaKailasa
Kailasa-BoldKailasa-Bold
Kannada Sangam MN
KannadaSangamMNKannadaSangamMN
KannadaSangamMN-BoldKannadaSangamMN-Bold
Malayalam Sangam MN
MalayalamSangamMNMalayalamSangamMN
MalayalamSangamMN-BoldMalayalamSangamMN-Bold
Marker Felt
MarkerFelt-ThinMarkerFelt-Thin
MarkerFelt-WideMarkerFelt-Wide
Optima
 Optima-Regular
 Optima-Italic
 Optima-Bold
 Optima-BoldItalic
Oriya Sangam MN
OriyaSangamMNOriyaSangamMN
OriyaSangamMN-BoldOriyaSangamMN-Bold
Palatino
Palatino-RomanPalatino-Roman
Palatino-ItalicPalatino-Italic
Palatino-BoldPalatino-Bold
Palatino-BoldItalicPalatino-BoldItalic
Papyrus
 Papyrus
Party LET
 PartyLetPlain
Sinhala Sangam MN
SinhalaSangamMNSinhalaSangamMN
SinhalaSangamMN-BoldSinhalaSangamMN-Bold
Snell Roundhand
SnellRoundhandSnellRoundhand
SnellRoundhand-BoldSnellRoundhand-Bold
Tamil Sangam MN
TamilSangamMNTamilSangamMN
TamilSangamMN-BoldTamilSangamMN-Bold
Telugu Sangam MN
TeluguSangamMNTeluguSangamMN
TeluguSangamMN-BoldTeluguSangamMN-Bold
Thonburi
 Thonburi
 Thonburi-Bold
Times New Roman
TimesNewRomanPSMTTimesNewRomanPSMT
TimesNewRomanPS-ItalicMTTimesNewRomanPS-ItalicMT
TimesNewRomanPS-BoldMTTimesNewRomanPS-BoldMT
TimesNewRomanPS-BoldItalicMTTimesNewRomanPS-BoldItalicMT
Trebuchet MS
TrebuchetMSTrebuchetMS
TrebuchetMS-ItalicTrebuchetMS-Italic
TrebuchetMS-BoldTrebuchetMS-Bold
Trebuchet-BoldItalicTrebuchet-BoldItalic
Verdana
VerdanaVerdana
Verdana-ItalicVerdana-Italic
Verdana-BoldVerdana-Bold
Verdana-BoldItalicVerdana-BoldItalic
Zapf Dingbats
 ZapfDingbatsITC
Zapfino
ZapfinoZapfino

출처 : http://www.michaelcritz.com/2010/04/02/fonts-for-ipad-iphone/

    

설정

트랙백

댓글

iOS icon 파일명 및 크기

Programming/Objective-C 2012. 9. 5. 00:39

이미지(px)

파일명

사용처

요구상태기타
512x512iTunesArtworkAd Hoc iTunes

선택적 필요

파일은 png 형식이어야 함, 그러나 이름에서는 .png 확장 제외
57x57Icon.png아이폰/아이팟 터치 앱스토어와 홈 화면필요<없음>
114x114Icon@2x.pngiPhone 4 고해상 홈 화면선택적 필요

< 없음>

72x72Icon-72.png

iPad 호환 홈 화면

선택적 필요

< 없음>

29x29Icon-Small.png

Spotlight와 설정

선택적 필요<없음>
50x50Icon-Small-50.pngiPad 호환 Spotlight설정된 bundle이 있다면 필요, 아니면 선택적 필요<없음>
58x58Icon-Small@2x.png

Spotlight와 iPhone 4 고해상도

설정된 bundle이 있다면 필요, 아니면 선택적 필요<없음>

스샷: 320 x 480 (1~4장)


Splash:

Default.png : 320 x 480

Default@2x.png : 640 x 960

+

iPhone5 해상도 지원 이미지 : 640 x 1136


    

설정

트랙백

댓글

Release build에서 NSLog 무효화

Programming/Objective-C 2012. 8. 31. 15:14

prefix header에 아래 코드 삽입.

Release 빌드할 때 무효화 자동 처리.


#ifndef __OPTIMIZE__

#define NSLog(...) NSLog(__VA_ARGS__)

#else

#define NSLog(...) {}

#endif

    

설정

트랙백

댓글

NSDate to String

Programming/Objective-C 2012. 7. 23. 19:45

PatternResult (in a particular locale)
yyyy.MM.dd G 'at' HH:mm:ss zzz1996.07.10 AD at 15:08:56 PDT
EEE, MMM d, ''yyWed, July 10, '96
h:mm a12:08 PM
hh 'o''clock' a, zzzz12 o'clock PM, Pacific Daylight Time
K:mm a, z0:00 PM, PST
yyyyy.MMMM.dd GGG hh:mm aaa01996.July.10 AD 12:08 PM




-(void) date1{
      // output : 7/23/12
      NSDate *today = [NSDate dateWithTimeIntervalSinceNow:0];
      NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
      [dateFormat setDateStyle:NSDateFormatterShortStyle];
      NSString *dateString = [dateFormat stringFromDate:today];
      NSLog(@"date1 : %@", dateString);
}

-(void) date2{
      // output : 07/23/2012 07:36PM
      NSDate *today = [NSDate date];
      NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
      [dateFormat setDateFormat:@"MM/dd/yyyy hh:mma"];
      NSString *dateString = [dateFormat stringFromDate:today];
      NSLog(@"date2 : %@", dateString);
      
}
-(void) date3{
      // output : Monday July 23, 2012
      NSDate *today = [NSDate date];
      NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
      [dateFormat setDateFormat:@"EEEE MMMM d, YYYY"];
      NSString *dateString = [dateFormat stringFromDate:today];
      NSLog(@"date3 : %@", dateString);
}

-(void) date4{
      // output : 7:38 PM, GMT+09:00
      NSDate *today = [NSDate date];
      NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
      [dateFormat setDateFormat:@"h:mm a, zzz"];
      NSString *dateString = [dateFormat stringFromDate:today];
      NSLog(@"date4 : %@", dateString);
}

-(void) date5{
      // output : 월요일 7월 23, 2012
      NSDate *date = [NSDate date];
      NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
      [dateFormat setDateFormat:@"EEEE MMMM d, YYYY"];
      [dateFormat setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"ko_kr"]];
      NSString *dateString = [dateFormat stringFromDate:date];
      NSLog(@"date5 : %@", dateString);
}


    

설정

트랙백

댓글

becomeFirstResponder, resignFirstResponder 애니메이션 duration time 설정

Programming/Objective-C 2012. 7. 22. 04:48

UITextField 를 터치하면 나타나거나 사라지는 keypad의 애니메이션 속도를 조정하기 위해서는 아래와 같은 방법을 사용할 수 있다.



[UIView animateWithDuration:0.3 animations:^{
                  [inputText becomeFirstResponder];
            }];

[UIView animateWithDuration:0.3 animations:^{
                  [inputText resignFirstResponder];
            }];


애니메이션 종료 시점에 처리해야하는 부분이 있으면 간단하게 아래와 같이 처리할 수도 있다. (delegate 메소드를 사용하는 것을 권장.)


[UIView animateWithDuration:0.3 animations:^{
                  [inputText becomeFirstResponder];      
            } completion:^(BOOL finished) {
                  // 애니메이션 종료후 처리
            }];



애니메이션 없이 바로 나타나게 하려면 UIView animate duration time을 0으로 설정하면 된다. 



    

설정

트랙백

댓글

iOS의 SQLite-FMDB 사용

Programming/Objective-C 2012. 4. 27. 10:29

iOS에서 데이터를 저장하는 방식 중에는 크게 번들 plist 파일에 저장하는 방법과 SQLite로 db방식으로 저장하는 방법이 있다. plist는 파일 형식으로 저장하는 방법, SQLite는 db 형식으로 저장하는 방법으로 이해할 수 있는데 SQLite를 사용하기 위해서는 db를 쓰고 읽는 기능을 하기 위해서는 적지 않은 코드를 작성해야하는 번거로움이 있다. 이를 해결하고자 FMDB라는 Wrapper library를 GitHub에서 배포하고 있다. 정리 차원에서 일본 블로거 포스트 내용을 번역하여 정리해 본다.


1. FMDB 란?

FMDB는 SQLite를 iOS의 Objective-C에서 사용하기 편하도록 Wrapper Library를 GitHub에서 공개하고 있다. 

https://github.com/ccgus/fmdb


인터페이스는 JDBC 또는 ADO.NET에 가깝다. 따라서, 이것을 사용한 적이 있으면 원활하게 이해할 수 있을 것이다.



2. FMDB 준비

먼저 FMDB를 이용하고 싶은 프로젝트에 SQLite 라이브러리를 추가한다. 

1. Xcode 왼쪽 창의 탐색창에서 프로젝트를 선택 

2. 오른쪽 창에 PROJECT와 TARGET이 표시되며 TARGET을 선택

3. 오른쪽 창, 상단 탭에서 Build Phases 선택

4. 빌드에 대한 설정 항목이 표시되는데 그 중에서 Link Binary With Libraries를 선택

5. Link Binary With Libraries 왼쪽 아래에 +를 클릭

6. Choose framework and libraries add :라는 메시지가 표시되고

7. 검색을 통해서 프레임워크와 라이브러리 목록에서 libsqlite3.0.dylib 선택 

8. 대화 상자 오른쪽 아래 Add 버튼을 클릭


그 다음으로 FMDB 소스를 프로젝트에 추가한다.

1. FMDB 프로젝트 페이지에서 ZIP 파일을 내려 받는다.

2. 압축을 풀고 src 폴더에서 fmdb.m 파일을 제외하고 프로젝트에 import

3. 



3. 데이터베이스 만들기 및 open / close

데이터베이스를 제공하는 경우 미리 만든 데이터베이스 파일을 프로젝트 리소스에 통합하고 그 것을 복사하거나 SQLite를 생성해야한다. iOS 애플리케이션 작업 영역에 app.db라는 데이터베이스 파일을 생성하는 경우는 다음과 같이 처리한다.


NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *dir = [paths objectAtIndex : 0]; FMDatabase *db = [FMDatabase databaseWithPath:[dir stringByAppendingPathComponent:@"app.db"]]; NSLog(@"%@", db); [db open]; [db close];

databaseWithPath 메소드에 파일의 경로를 지정하여 파일이 기존에 존재하면 참조하고 없을 경우에는 새로 만들고 연결된 FMDatabase 인스턴스를 반환한다.


데이터베이스 작업을 시작하면 이 인스턴스에 대한 open 메소드를 호출한다. 종료는 close 메소드, close를 호출하면 데이터베이스를 닫고 변경된 내용을 파일에 저장한다. 


4. CREATE

테이블 생성은 다음과 같다.


NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *dir = [paths objectAtIndex : 0];
FMDatabase *db = [FMDatabase databaseWithPath:[dir stringByAppendingPathComponent:@"app.db"]];
NSString *sql = @"CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT);";
[db open];
[db executeUpdate:sql];
[db close];


FMDatabase - executeUpdate 메소드에 CREATE 문장을 지정하여 작업을 수행한다. SQLite는 IF NOT EXISTS에 대응하고, 이 것을 붙이면 테이블이 존재하지 않을 때만 생성 해주기 때문에 편리하다.


5. INSERT

행을 추가하는 것은 다음과 같다.


NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *dir = [paths objectAtIndex : 0];
FMDatabase *db = [FMDatabase databaseWithPath:[dir stringByAppendingPathComponent:@"app.db"]];
NSString *sql = @"INSERT INTO users (name) VALUES (?)";

[db open];
[db executeUpdate:sql, @"이름" ];
[db close];


executeUpdate는 Prepared Statement에 대응하고 있다. SQL 문장에서 ?를 작성한 매개변수 부분에 가변 인자를 할당한다.



6. DELETE

행 삭제는 다음과 같다.


NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *dir = [paths objectAtIndex : 0];
FMDatabase *db = [FMDatabase databaseWithPath:[dir stringByAppendingPathComponent:@"app.db"]];

NSString *sql = @"DELETE FROM users WHERE id =?";
[db open];
[db executeUpdate:sql, [NSNumber numberWithInteger:14]];
[db close];


7. SELECT

데이터 선택은 다음과 같다.


NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *dir = [paths objectAtIndex : 0];
FMDatabase *db = [FMDatabase databaseWithPath:[dir stringByAppendingPathComponent:@"app.db"]];

NSString *sql = @"SELECT id, name FROM users;";
[db open];

FMResultSet *results = [db executeQuery:sql];
NSMutableArray *users = [[NSMutableArray alloc] initWithCapacity:0];

while ([results next])
{
      User *user = [[User alloc] init];
      user.userId = [results intForColumnIndex:0];
      user.name = [results stringForColumnIndex:2];
      [users addObject:user];
}

[db close];


FMDatabase - executeQuery 처리 결과를 FMResultSet 인스턴스로 반환한다.

FMResultSet - next를 호출하여 가져온 행을 순차적으로 불러온다. 행이 있는 경우 YES, 끝이면 NO를 반환하므로 이 메소드를 호출하는 while 조건문을 통해서 모든 행이 열거된다.



8. 트랜잭션

SQLite는 암묵적으로 트랜잭션 제어를 한다. 평상시에는 문제가 없지만 대량의 INSERT를 실행하면 그때마다 BEGIN ~ COMMIT이 실행되기 때문에 속도가 크게 저하된다. 이러한 작업을 수행하려면 대상 구간을 명시적으로 트랜잭션 처리하는 것이 좋다.


FMDB가 명시적으로 트랜잭션 FMDatabase - beginTransaction 메서드에서 시작되고 commit으로 종료된다. 오류 등으로 처리 전의 상태로 되돌리려면 rollback 메소드를 실행한다.


NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *dir = [paths objectAtIndex : 0];
FMDatabase *db = [FMDatabase databaseWithPath:[dir stringByAppendingPathComponent:@"app.db"]];

NSString *sql = @"SELECT id, name FROM users;";
[db open];

FMResultSet *results = [db executeQuery:sql];
NSMutableArray *users = [NSMutableArray array];

while ([results next])
{
      User *user = [[User alloc] init];
      user.userId = [results intForColumnIndex:0];
      user.name = [results stringForColumnIndex:2];
      NSLog(@"%@", user.name);
      [users addObject:user];
}

[db close];


// Transaction
sql = @"INSERT INTO users (name) VALUES (?)";

[db open];
[db beginTransaction];

BOOL isSucceeded = YES;
for( User* user in users )
{
      NSLog(@"name:%@", user.name);
      if( ![db executeUpdate:sql, user.name] )
      {
isSucceeded = NO;
break;
      }
}

if( isSucceeded )
{
      [db commit];
}
else
{
      [db rollback];
}

[db close];

명시적 트랜잭션을 실행하는 동안에는 데이터베이스 전체가 잠긴다. 따라서 구간 내에서 새로운 데이터베이스를 open하면 응답 없음으로 간주한다.



9. 형식

FMDB를 이용하여 데이터를 취득, 설정하는 형식에 대한 정리.


데이터를 취득하기 위해서는 FMDatabase - executeQuery의 결과로 반환된 FMResultSet 인스턴스 메소드를 사용한다. 메소드 이름은 typeForColumn 또는 typeForColumnIndex 다. 각 컬럼 이름과 위치 인덱스 값을 얻을 수 있다.


예를 들어, stringForColumn에 "name"을 지정했다면 name 문자열(TEXT)의 컬럼을 가져온다. intForColumnIndex:4를 지정하면 SELECT 문을 검색할 4 번째 정수(INTEGER)를 얻을 수 있다. 대표적인 데이터 형식 대응은 아래와 같다.


SQLite

Objective-C 

검색 메소드 

 TEXT

NSString 

stringForColumn, stringForColumnIndex 

INTEGER 

int 

intForColumn, intForColumnIndex 

BOOL 

BOOL 

boolForColumn, boolForColumnIndex 

REAL 

double 

doubleForColumn, doubleForColumnIndex 

DATETIME (INTEGER, REAL, TEXT)

NSDate 

dateForColumn, dateForColumnIndex 

BLOB 

NSData 

dataForColumn, dataForColumnIndex 


FMDatabase - executeQuery로 지정하는 매개변수는 런타임에 유형검사를 한다. TEXT면 NSString, INTEGER면 NSNumber로 대응하며 잘 못된 형식을 지정하면 런타임 에러를 발생시킨다.


데이터 취득의 경우,  FMResultSet이 Objective-C의 변환을 담당하지만 설정시에 변환은 직접 해야한다. 변환 대응은 아래와 같다.


 SQLite

Objective-C 

변환 방법 

 TEXT

NSString 

변환 불필요 

INTEGER 

NSInteger 

NSNumber - numberWithInt 

BOOL 

BOOL 

NSNumber - numberWithBool 

REAL 

double 

NSNumber - numberWithDouble 

DATETIME (INTEGER, REAL, TEXT) 

NSDate 

변환 불필요 

BLOB 

NSData 

변환 불필요. 


SQLite의 날짜 형식은 다음과 같이 정의되어 있다. (Datatypes In SQLite Version 3 에서 인용).


TEXT as ISO8601 strings (“YYYY-MM-DD HH:MM:SS.SSS”).

REAL as Julian day numbers, the number of days since noon in Greenwich on November 24, 4714 B.C. according to the proleptic Gregorian calendar.

INTEGER as Unix Time, the number of seconds since 1970-01-01 00:00:00 UTC.


DATETIME은 고유의 형식이 있는 것은 아니고 TEXT, REAL, INTEGER를 사용한다. SQLite 클라이언트는 TEXT 밖에 허용하지 않으며 System.Data.SQLite과 같이 연결 문자열 DATETIME 형식의 해석 방법을 선택할 수 있다. 

FMDB의 경우 내부적으로 Unix Timestamp에서 운용하고 있으므로 형식을 정의할 때 INTEGER로 하면 좋다. 



10. Lita(데이터베이스 관리 프로그램)

FMDB를 이용한 데이터베이스 작업을 했을 때 그 결과가 어떻게 반영되었는지 확인하고 싶을 때가 있다. 또한 애플리케이션에서 사용하는 SQL 문을 검토하기 위해 실제 데이터베이스에 대해 SQL을 실행하고 싶은 경우도 많다. 이럴 때 사용하면 유용한 AIR 애플리케이션이 바로 Lita 다.


Lita – SQLite Administration Tool


iPhone 시뮬레이터를 통해 SQLite 데이터베이스를 생성한 경우, Mac의 다음 위치에 파일이 생성된다.

/Users/사용자이름/Library/Application Support/iPhone Simulator/5.0/Applications/애플리케이션 ID/Documents


만들어진 파일을 Lita에서 열고 애플리케이션이 데이터베이스 작업을 행한 후, Lita에서 갱신하면 저장된 데이터의 변경을 쉽게 할 수 있다. 





    

설정

트랙백

댓글

iOS XML parser NSXMLParser[2]

Programming/Objective-C 2012. 4. 21. 19:11

이전 포스트에서는 NSXMLParser를 사용할 때 호출되는 기본적인 델리게이트 메소드들을 살펴보았다. 이번에는 각 element및 attribute 값들을 객체를 이용하여 저장해 본다. 저장 방식은 iOS5 programming cookbook 샘플소스를 수정해서 사용했다.


먼저 사용할 xml 데이터는 아래와 같은 형식이다.

<?xml version="1.0" encoding="UTF-8"?>
<root>
      <person id="1">
            <firstName>Anthony</firstName>
            <lastName>Robbins</lastName>
            <age>51</age>
      </person>

      <person id="2">
            <firstName>Richard</firstName>
            <lastName>Branson</lastName>
            <age>61</age>
      </person>
</root>


아래는 개별 element들을 저장하기 위한 클래스다. XML 트리형식의 구조로 상위 element 데이터에 접근할 수 있도록 parent라는 자신과 같은 객체 형식의 참조 속성을 포함하고 있다. 단순히 참조하는 형태이기 때문에 weak로 생성한다.


XMLElement.h

#import <Foundation/Foundation.h>

@interface XMLElement : NSObject

@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSString *text;
@property (nonatomic, strong) NSDictionary *attributes;
@property (nonatomic, strong) NSMutableArray *subElements;
@property (nonatomic, weak) XMLElement *parent;

@end


XMLElement.m

#import "XMLElement.h"

@implementation XMLElement

@synthesize name;
@synthesize text;
@synthesize attributes;
@synthesize subElements;
@synthesize parent;

- (NSMutableArray *) subElements{
  if (subElements == nil){
    subElements = [[NSMutableArray alloc] init];
  }
  return subElements;
}

@end


subElements 최초 참조시 객체가 생성되지 않았을 때는 생성해서 return하도록 처리되어 있다. 

아래는 메인 델리게이트 클래스다.


#import <UIKit/UIKit.h>

@class XMLElement;

@interface Parsing_XML_with_NSXMLParserAppDelegate
           : UIResponder <UIApplicationDelegate, NSXMLParserDelegate>

@property (nonatomic, strong) UIWindow *window;
@property (nonatomic, strong) NSXMLParser *xmlParser;
@property (nonatomic, strong) XMLElement *rootElement;
@property (nonatomic, strong) XMLElement *currentElementPointer;

@end



#import "Parsing_XML_with_NSXMLParserAppDelegate.h"
#import "XMLElement.h"

@implementation Parsing_XML_with_NSXMLParserAppDelegate

@synthesize window = _window;
@synthesize xmlParser;
@synthesize rootElement;
@synthesize currentElementPointer;

- (void)parserDidStartDocument:(NSXMLParser *)parser{
      self.rootElement = nil;
      self.currentElementPointer = nil;
}

- (void)parserDidEndDocument:(NSXMLParser *)parser{
      self.currentElementPointer = nil;
}

- (void)        parser:(NSXMLParser *)parser 
       didStartElement:(NSString *)elementName 
          namespaceURI:(NSString *)namespaceURI
         qualifiedName:(NSString *)qName
            attributes:(NSDictionary *)attributeDict{
  
      if (self.rootElement == nil){
            /* 루트 element가 생성되지 않았을 때 */
            
            self.rootElement = [[XMLElement alloc] init];
            self.currentElementPointer = self.rootElement;
      } else {
            /* 루트 element가 있을 때 새로운 XMLElement 객체를 만들고 현재 참조 element를 parent로
             새로 만들어진 element를 현재 참조 element로 연결 */
            
            XMLElement *newElement = [[XMLElement alloc] init];
            newElement.parent = self.currentElementPointer;
            [self.currentElementPointer.subElements addObject:newElement];
            self.currentElementPointer = newElement;
      }

      self.currentElementPointer.name = elementName;
      self.currentElementPointer.attributes = attributeDict;
  
}

- (void)        parser:(NSXMLParser *)parser
         didEndElement:(NSString *)elementName
          namespaceURI:(NSString *)namespaceURI
         qualifiedName:(NSString *)qName{
      
      /* element를 빠져 나올 때 현재 XMLElement에서 parent로 참조하고 있는 element를
       현재 참조로 대입 */
      self.currentElementPointer = self.currentElementPointer.parent;
  
}

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
  
      self.currentElementPointer.text = string;

}

- (BOOL)            application:(UIApplication *)application 
  didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{

      NSString *xmlFilePath = [[NSBundle mainBundle] pathForResource:@"MyXML"
                                                          ofType:@"xml"];

      NSData *xml = [[NSData alloc] initWithContentsOfFile:xmlFilePath];

      self.xmlParser = [[NSXMLParser alloc] initWithData:xml];
      
      self.xmlParser.delegate = self;

      if ([self.xmlParser parse]){
            NSLog(@"The XML is parsed.");
            XMLElement *element;
            XMLElement *subElement;
            for(element in self.rootElement.subElements){
                  NSLog(@"person ID : %@", [element.attributes objectForKey:@"id"]);
                  for(subElement in element.subElements){
                        NSLog(@"\t%@ : %@", subElement.name, subElement.text);
                  }
            }
      } else{
            NSLog(@"Failed to parse the XML");
      }
  
      self.window = [[UIWindow alloc] initWithFrame:
                 [[UIScreen mainScreen] bounds]];

      self.window.backgroundColor = [UIColor whiteColor];
      [self.window makeKeyAndVisible];
      return YES;
}
@end


위 코드에서 가장 핵심이 되는 부분은 아래 부분이다. 새로은 element를 생성한 후, 생성된 element객체 parent를 현재 참조하고 있는 element로 설정한다. 이는 현재 element 에서 자식 element가 새롭게 생성되어 연결되는 과정을 보여준다. 새로은 element를 addObject로 추가하고 마지막으로는 새롭게 생성된 element를 현재 element에 대입하여 새롭게 생성된 element가 현재 element가 되도록 처리해 준다. 

XMLElement *newElement = [[XMLElement alloc] init]; newElement.parent = self.currentElementPointer; [self.currentElementPointer.subElements addObject:newElement]; self.currentElementPointer = newElement;


다음은 출력된 결과다.


The XML is parsed.
person ID : 1
	firstName : Anthony
	lastName : Robbins
	age : 51
person ID : 2
	firstName : Richard
	lastName : Branson
	age : 61












    

설정

트랙백

댓글

iOS XML parser NSXMLParser[1]

Programming/Objective-C 2012. 4. 20. 00:43

iOS에서 사용할 수 있는 XML 파서가 꽤 많다. 대체적으로 메모리 사용량이나 속도면에서 TBXML을 많이 사용하는 듯 하다. 무료도 제공되는 라이브러리가 성능면에서는 좋은 것 같은데... 문제는 iOS의 빠른 업데이트를 따라가려면 좀 시간이 걸린다는 것... TBXML에서는 아직 xCode에서 업데이트 된 ARC(Automatic Reference Counting)를 지원하지 않는 것 같아서 일단 기본 제공되는 NSXMLParser 사용법 부터 정리해 보기로 한다.


NSXMLParser도 delegate 메소드를 통해서 element의 값들에 접근할 수 있다. iOS를 공부하면서 가장 이해하기 어려웠던 부분이 바로 delegate 메소드 호출방식이었다. 전혀 속도면에서 도움이 될 것 같지 않은 방법을 채택하고 있는지라 초기에는 왜 이렇게 해야만하는지에 대한 의문에 사로잡혀 있다보니 한 없이 복잡하고 어려웠다. 아무튼 공부는 이해가 안되면 그냥 정신줄을 놓고 외워서 받아들이는 편이 정신 건강에 좋다.


본론으로 들어가서... 본인도 틈틈히 개인적으로 공부하는 차원에서 정리하고 있기 때문에 내용에서 오류가 발견될 수도 있다. 오류가 있으면 알려주시면 좋겠다. 


NSXMLParser는 parse 메소드에 메시지를 전달하면 delegate로 연결된 객체에서 처리해 놓은 일련의 메소드들을 호출하면서 파싱을 진행한다. 아래는 NSXMLParser가 파싱을 하면서 실행되는 메소드들의 순서다.


NSXMLParser 객체 parse 메소드 호출 후,

1. parserDidStartDocument

2. didStartElement

3. foundCharacters

4. didEndElement

5. parserDidEndDocument


이 과정에서 오류가 발견되면 parseErrorOccurred 메소드가 호출된다.


아래는 샘플로 활용할 xml 내용이다.


<?xml version="1.0" encoding="UTF-8"?> <catalog> <book id="bk101"> <author>Gambardella, Matthew</author> <title>XML Developer's Guide</title> <genre>Computer</genre> <price>44.95</price> <publish_date>2000-10-01</publish_date> <description>An in-depth look at creating applications with XML.</description> </book> </catalog>


일단 번들에 있는 xml 데이터를 불러와서 파싱을 시작한다.

NSString *path = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"sample.xml"];
    NSData *data = [NSData dataWithContentsOfFile:path];
    
    self.xmlParser = [[NSXMLParser alloc] initWithData:data];
    self.xmlParser.delegate = self;
    if([self.xmlParser parse]){
        NSLog(@"The XML is Parsed.");
        
    }else {
        NSLog(@"Failed to parse the XML");
    }

[self.xmlParser parse] 메소드를 호출하여 파싱을 시작하면 처음으로 호출되는 메소드는 parserDidStartDocument 

-(void) parserDidStartDocument:(NSXMLParser *)parser{
      NSLog(@"parserDidStartDocument");
      // tab 처리를 위한 전역 변수
      tabString = [NSMutableString new];
}

그리고 순차적으로 element들을 탐색하기 시작한다. 각 element에 접근할 때마다 아래 메소드가 실행된다.


-(void) parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName 
                                          namespaceURI:(NSString *)namespaceURI 
                                          qualifiedName:(NSString *)qName 
                                          attributes:(NSDictionary *)attributeDict{
      tabString = [[tabString stringByAppendingString:@"\t"] mutableCopy];
      NSLog(@"%@", [tabString stringByAppendingString:elementName]);
      
}

elementName가 각 element명이다. 여기서는 NSLog 쓸 때 tab을 하나씩 추가하여 들여쓰기를 한다.  해당 노드의 attribute들은 NSDictionary형으로 attributeDict을 참조해서 사용하면 된다.


그 다음으로 실행되는 것이 아래 메소드다.


-(void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
      NSLog(@"%@", [tabString stringByAppendingString:string]);
}

didStartElement 메소드에서 접근한 element의 value 값을 확인할 수 있다. 그 다음으로는 element의 값까지 접근했으니 element가 닫히는 부분이다. 여기서는 일단 elementName을 출력하고 tabString의 끝에 있는 '\t'을 하나씩 제거하여 들여쓰기를 전 단계로 되돌린다.


-(void) parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName 
                                          namespaceURI:(NSString *)namespaceURI 
                                          qualifiedName:(NSString *)qName{
      NSLog(@"%@", [tabString stringByAppendingString:elementName]);
      tabString = [[tabString substringToIndex:[tabString length]-1] mutableCopy];
}

 

이렇게 뎁스 안쪽으로 탐색을 하여 값을 참조하고 밖으로 나오면서 최종 root element를 닫으면 아래 메소드가 실행되고 parsing을 종료하게 된다. 


-(void) parserDidEndDocument:(NSXMLParser *)parser{
      NSLog(@"parserDidEndDocument");
      tabString = nil;
}


output 결과 >

parserDidStartDocument
	catalog
	
   
		book
		
      
			author
			Gambardella, Matthew
			author
		
      
			title
			XML Developer's Guide
			title
		
      
			genre
			Computer
			genre
		
      
			price
			44.95
			price
		
      
			publish_date
			2000-10-01
			publish_date
		
      
			description
			An in-depth look at creating applications 
      with XML.
			description
		
   
		book
	

	catalog
parserDidEndDocument
The XML is Parsed.


element value가 없는 것들도 메소드는 실행되기 때문에 줄 내림이 생기는 것을 알 수 있다. 

이제 해야할 일은 각 element와 attribute에 접근하여 값을 확인할 수 있으니 이것을 나름의 데이터에 저장해서 활용하면 된다. 다음 포스트에서는 참조된 데이터를 저장하여 사용하는 부분을 추가하도록 하겠다.



    

설정

트랙백

댓글

형식이 있는 문자열이 사용할 수 있는 토큰

Programming/Objective-C 2012. 4. 10. 13:20

Objective-C에서 형식이 있는 문자열이 사용할 수 있는 토큰

심벌표시
 %@ id
 %d, %D, %ilong
 %u, %Uunsigned long
 %hishort
 %huunsigned short
 %qilong long
 %quunsigned long
 %x, %X16진법으로 표시한 unsigned long
 %o, %O8진법으로 표시한 unsigned long
 %f, %e, %E, %g, %Gdouble
 %cASCII 문자인 unsigned char
 %C유니코드 문자인 unichar
 %schar* (ASCII 문자들. NULL로 끝을 나타내는 C 문자열)
 %Sunichar* (유니코드 문자들. NULL로 끝을 나타내는 C 문자열)
 %pvoid* (0x로 시작하는 16진법으로 표시한 주소)
 %%% 문자



    

설정

트랙백

댓글

UIColor 매크로(RGB, RGBA, HTML)

Programming/Objective-C 2012. 4. 4. 17:49


#define RGB(r, g, b) [UIColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:1]

#define RGBA(r, g, b, a) [UIColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:a]

#define HTML(rgb) RGB((double)(rgb >> 16 & 0xff), (double)(rgb >> 8 & 0xff), (double)(rgb & 0xff))

//////////////////////////////////////////////////////////////////////

// Code without the macro

  msgLabel.textColor = [UIColor colorWithRed:255/255.0 green:251/255.0 blue:204/255.0 alpha:1];

// Or like this...

  msgLabel.textColor = [UIColor colorWithRed:1.0 green:.98 blue:.8 alpha:1];

 

// Code with macro

      msgLabel.textColor = RGB(255, 251, 204);

[label setTextColor:HTML(0xff0000)];


참고 : http://iphonedevelopertips.com/cocoa/uicolor-macros.html









    

설정

트랙백

댓글