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

Programming/Objective-C 2013.05.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로 얼마나 클릭이 이루어졌는지도 그래프로 확인이 가능하다.

저작자 표시 비영리 변경 금지
신고
    

설정

트랙백

댓글

Release build에서 NSLog 무효화

Programming/Objective-C 2012.08.31 15:14

prefix header에 아래 코드 삽입.

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


#ifndef __OPTIMIZE__

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

#else

#define NSLog(...) {}

#endif

저작자 표시 비영리 변경 금지
신고
    

설정

트랙백

댓글

NSDate to String

Programming/Objective-C 2012.07.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.07.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.04.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.04.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.04.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.04.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.04.04 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









저작자 표시 비영리 변경 금지
신고
    

설정

트랙백

댓글