GameCenter Matchmaking 을 위한 Checklist

@required

1. 현재 user 인증
2. MatchMaking User Interface
3. Handle invitation from other user

@optional
1. Programmatcially Match find 코드 삽입
2. Advanced Match Making 코드 삽입 

 
UIKIT_EXTERN NSString *NSStringFromCGPoint(CGPoint point);
UIKIT_EXTERN NSString *NSStringFromCGSize(CGSize size);
UIKIT_EXTERN NSString *NSStringFromCGRect(CGRect rect);
UIKIT_EXTERN NSString *NSStringFromCGAffineTransform(CGAffineTransform transform);
UIKIT_EXTERN NSString *NSStringFromUIEdgeInsets(UIEdgeInsets insets);
UIKIT_EXTERN NSString *NSStringFromUIOffset(UIOffset offset);

NSStringFromCGRect를 아십니까? 

NSLog로 CGPoint, CGRect를 찍어봐야 할일이 은근 많은데 보통 보면 아래와 같은 코드를 사용합니다.


 
    NSLog(@"%f,%f,%f,%f,",rect.origin.x,rect.origin.y,rect.size.width,rect.size.height);

하지만 NSStringFromCGRect를 활용하면 %@ 한방에 찍을 수가 있습니다.
나온 결과는 아래와 같습니다.

2011-11-24 15:29:40.397 App[9981:f803] -[AppView layoutSubviews],33th - currenctRect = {{14, 20}, {768, 1024}}




XML을 파싱할 일이 생겨서,  이래저래 만져보다가 작업한 내용을 정리 합니다 :)

1. XML Parsing

Android 에서 제공하는 XmlResourceParser 를 사용하기로 했습니다.

XMLResourceParser는 다른 Java XML Parser들에 비해 성능이 좋은게 특징입니다.
이유인 즉은, 다른 Android XML 파일들 처럼, pre compiled 해서 birnary에 들어 가서 처리 하기 때문입니다.
아래는 간단하게 XML 을 읽어 들이는 코드 입니다. 
public Problem parseXML(XmlResourceParser xmlParser)
			throws Exception {

		Problem problem = new Problem();
		String name = null;

		while (xmlParser.getEventType() != XmlPullParser.END_DOCUMENT) {

			if (xmlParser.getEventType() == XmlPullParser.START_TAG) {
				name = xmlParser.getName();
			} else if (xmlParser.getEventType() == XmlPullParser.TEXT) {
				if (name == null) {
					Log.d("ProblemDAO-parseXML", "name is null ? error ??");

				}
			} else if (xmlParser.getEventType() == XmlPullParser.END_TAG) {
				name = null;
			}
			xmlParser.next();
		}

		return problem;
	}

2. XML to Pojo



a. 첫 번째 시도 - if 지옥

자 읽어 들인 XML을 Java Plaiin Object 로 매핑할 차리 인데요.

제일 처음 작업한게 아래와 같은 코드 입니다.
if(name.equalsIgnoreCase("title") == true) {
}
위와 같은 코드의 문제점은 if/elseif/elseif 지옥이 나타난다는것이죠. :(
그리고,  xml 형식등의 변경이 있을때, pojo/xml 이외에 추가로 관리할 곳이 늘어 난다는 점입니다.

b. 두 번째 시도 - Reflection

그래서 생각해낸것이 Java의 Reflection 입니다.

xml element 이름을 pojo 의 setter와 연결시키도록 아래와 같이 코드를 작성하였습니다..

               char[] stringArray = name.toCharArray();
		stringArray[0] = Character.toUpperCase(stringArray[0]);
		String methodName = "set"+ new String(stringArray);	
                try {
			Method m = Problem.class.getMethod(methodName, String.class);
			m.invoke(problem, text);
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
				Method m = Problem.class.getMethod(methodName,Integer.class);
				m.invoke(problem,Integer.valueOf(text));
		}

조금 신경 쓴부분이 바로 Setter의 자료형 부분입니다.
m.invoke 가 없는 메소드 인 경우에, NoSouchMethodException을 발생시키기 때문에 이를 캐치하여,
다른 형으로 시도 해보도록 코드를 작성하였습니다.

3. 더 해볼것

추후에, JacksonMapper 등에서 어떻게 json 을 pojo화 시키는지 좀 스터디해보고 좀 더  좋은 방법이 있나 생각해봐야겠습니다 :)

facebook sdk 의 github 소개페이지


최근에 github 에 프로젝트를 하나 올렸는데,
제 프로젝트는 소개페이지가 허접한 TXT 파일인데, facebook 의 이렇게 wiki 처럼 포맷팅이된 것임을 확인했습니다 ;ㅂ;

이게 뭐야 하고 찾아보니, markdown 이라는 문법을 github에서 지원하는것이더군요.

마크다운 문법 페이지 링크

 그런데, 이건 로컬에서 작성하고, 서버에 올려서 계속 확인하자니, 여간 귀찮은게 아니더군요 ;ㅂ;

그래서 로컬에서 사용할 수 있는 Reader를 찾아보니, 역시나 있습니다 ^
Readown , MarkdownLive 라는 프로젝트가 있었고, 결론적으로 MarkdownLive를 계속 사용하기로 하여,
MarkdownLive를 소개합니다.

프로젝트 페이지 바로 가기 (https://github.com/rentzsch/markdownlive) 

 
위와 같이, 우측에 Preview, 좌측에 원본 파일이 나타나서, 에디팅하기 쉽게 되어 있습니다 ^^

github 를 사용하는 개발자라면, must have item 이 아닌가 생각합니다 :) 




제가 최근에 진행한 프로젝트에 작성한 소스코드중 재사용할 만한것들을 골라서, github에 올려보려고 합니다.

이걸 해보는 이유

1. git에 대한 사용법 공부
2. 제대로된 Open Source Project 운영에 대한 첫걸음

SimpleImagePickerhttps://github.com/Geekboard/SimpleImagePicker )

오늘 공개할 Project는 Simple ImagePicker 입니다.

UIImagePickerController를 사용할때, 카메라가 없는 장비(iPad1, and old iPod Touches) 를 서포트 하기 위해서,
SourceType을 체크해주어야합니다.

SourceType을 사용하는곳마다 해야하니, 여간 귀찮은게 아니더군요 ;ㅂ;
그래서 만들게 되었고 이렇게 공유합니다 ^^
 


Simulator에서도 스크린샷을 저장할 수 있는지 아셨나요? ;ㅂ;

단축키로도 쉽게 저장하 실수 있습니다.



iOS5 에서, UIPickerView가 UITableViewDateSource Protocol을 Adopting 한다는것외에 
따로 문서화로 달라진것은 없습니다.  

그런데, frame.size.width값을 이제 받아 들이게 되었습니다.
UIPickerView *pickerView = [[UIPickerView alloc] initWithFrame:CGRectMake(0, 0, 160, 100)];
    [pickerView setDataSource:self];
    [pickerView setDelegate:self];
    
    [self.view addSubview:pickerView];
    [pickerView release];

위의 코드는 pickerView의 width를 160으로 설정한 예입니다.
iOS4를 비롯한 모든 예전 버전에는 width 값은 무시되고 항상 320으로 설정되었습니다.

iOS 4.3 Simulator

 
그런데, 같은 코드를 iOS5 에서 실행 시키면.

iOS5 Simlurator

 
 위와 같이 width 속성이 먹는것을 알 수 있습니다.

어차피 적용안되는거,, height 값만 신경쓰고, width는 대강 작성했던 코드가 문제를 일으켰습니다. ;ㅂ;
여튼 width가 적용이 안되서 좀 답답한것도 있었는데, 이제 되는군요 :)

내년쯤.. iOS4를 더이상 Follow-up 하지 않을 정도 되면 사용해볼수 있겠군요 :)

 

1. @RequestMapping 에 java.util.Date 받기.

http://linkedjava.blogspot.com/2011/06/spring-controller-with-date-object.html

2. JacksonMappper 에서 java.util.Date Serialize 하기

http://linkedjava.blogspot.com/2011/06/spring-controller-with-date-object.html
 



  
Accounts.framework

Accounts Framework  은 Twitter 계정을 관리 하는 Framework 이며, 목적은 아래의 3가지라고 볼수 있습니다.
 

1. Account 접근
2. Account 추가
3. Account 유효성 체크


Accounts Framework의 Class 들

ACAcccountStore
ACAccount 
ACAccountType
ACAccountCredential


Account 목록 가져오기.
 
Default로 account를 접근하는것이 금지되어있기 때문에, 권한을 요청해야 합니다.
  ACAccountStore *store = [[ACAccountStore alloc] init]; 
    ACAccountType *twitterType = [store accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
    [store requestAccessToAccountsWithType:twitterType
                     withCompletionHandler:^(BOOL granted, NSError *error) {
                         if(granted){
                            //accessgranted !
                             NSLog(@"granted = %d",granted);
                             
                             NSArray *accounts = [store accountsWithAccountType:twitterType];
                             NSLog(@"accounts = %@", accounts);
                         }
                         }];
    [store release];

 
권한을 요청하면, 위와 같은 권한 요청 팝업이 나오게 되며,  사용자가 허락하거나 거절 할 수 있습니다.
한번 허락을 했다고 해서,  계속 허락되는것은 아니며,
사용자는 Settings -> Twitter 에서 허락 여부를 On/Off 할 수 있습니다.

 
Allow 한 후에,  위의 코드를 다시 실행 시켜 보면,

2011-10-14 16:34:07.600 TwitterTest[3364:12103] accounts = (

    "type:com.apple.twitter\nidentifier: 527A4FE0-FBAA-40A1-A7C8-C6FA2EEE9698\naccountDescription: @liketaehoon\nusername: liketaehoon\nobjectID: x-coredata://0102DFCE-5B1C-438B-B14B-D9BB471DD885/Account/p2\nenabledDataclasses: {(\n)}\nproperties: {\n    \"user_id\" = 48573923;\n}\nparentAccount: (null)\nowningBundleID:com.apple.Preferences"

)

 
이렇게 계정 정보가 읽어 집니다.

Twitter 계정 추가.

계정을 추가 하는 방법도 상당히 쉽습니다. 
ACAccount *account = [[ACAccount alloc] initWithAccountType:twitterType];
ACAccountCredential *oauthCredential = [[ACAccountCredential alloc] initWithOAuthToken:token
tokenSecret:secret]; [accountStore saveAccount:account withCompletionHandler:^(BOOL success,
account.credential = oauthCredential; NSError *error) {
if (success) {
//account validated and saved
} else {
//save failed, handle your errors! !}
}];
[oauthCredential release];
[account release];

OAuthToken을 얻어 오는것은 해당포스트의 내용을 벗어나는 일이라 설명하지 않겠습니다.
ACAccount  Object 생성,
OAuthToken을 이용해, ACAccountCredential Object를 만들어, ACAcount 에 넣어주고,
ACAccountStore를 통해 save 하면됩니다.

ACAccountStore단에서 validating도 해주게 됩니다.

나의 App에서 Twitter 계정을 로그인하게 해주는 기능을 제공해야 할까는 고민해봐야 겠지만,
상당히 쉽게 사용할 수 가 있습니다.


Accounts.framework 에 대한 짧은 생각.

Accounts.framework이 Twitter뿐아니라,
Facebook 이나 기타 서비스들을 품을 수 있게 설계된 framework인것으로 판단되어,
앞으로의 방향이 기대되는 framework이라고도 볼수 있겠네요 :)

 


Twitter 가 iOS 에 Integrate 되었습니다.
"드디어, MGTwitterEngine 과 같은 Library에서 벗어 날수 있겠군요! "
라고 생각하시면 큰일 납니다 ;ㅂ;  ( 위에 Optional 로 Link 되어 있는 이유를 아시겠죠? ^^ )

아직 iOS5 보전보다 iOS4 버전이 많을테니, 하위 호환성을 생각해줘야 하기 때문에 여전히 버릴수가 없습니다.
어쨌든 들어 왔습니다 !
그래서 제가 개발하고 있는 것들에 Twitter Framework 을 붙혀 볼까 합니다. 

TWTweetComposeViewController

MFMailComposeViewController와 비슷한 형태의 ViewController 라고 생각하시면 됩니다. 
한번 띄워 볼까요? ^^ 

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

    TWTweetComposeViewController *viewController = [[TWTweetComposeViewController alloc] init];
    self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
    self.window.rootViewController = viewController;
    [viewController release];
    [self.window makeKeyAndVisible];
    return YES;
}

위와 같이 Code 를 작성하고, 결과가 나오기를 바랬는데 ;ㅂ;

*** Terminating app due to uncaught exception 'TWUnsupportedPresentationException', reason: 'TWTweetComposeViewController cannot currently be used within a popover or as a non-modal view controller'

*** First throw call stack:

 
위와 같은 에러가 나옵니다.  
말인 즉슨 modal view controller 가 아니면 사용할수가 없다는 군요 ;ㅂ;

그래서 코드는 아래로 옮겨 주고, present modal 로 보여줬습니다.
- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    
    TWTweetComposeViewController *composeViewController = [[TWTweetComposeViewController alloc] init];
    [self presentModalViewController:composeViewController animated:YES];
    [composeViewController release];
}


위와 같이 수정하고 나니,


 위와 같이 이쁜 디자인으로 잘 나오는군요.

// Sets the initial text to be tweeted. Returns NO if the specified text will
// not fit within the character space currently available, or if the sheet
// has already been presented to the user.
- (BOOL)setInitialText:(NSString *)text;

// Adds an image to the tweet. Returns NO if the additional image will not fit
// within the character space currently available, or if the sheet has already
// been presented to the user.
- (BOOL)addImage:(UIImage *)image;

// Removes all images from the tweet. Returns NO and does not perform an operation
// if the sheet has already been presented to the user. 
- (BOOL)removeAllImages;

// Adds a URL to the tweet. Returns NO if the additional URL will not fit
// within the character space currently available, or if the sheet has already
// been presented to the user.
- (BOOL)addURL:(NSURL *)url;

// Removes all URLs from the tweet. Returns NO and does not perform an operation
// if the sheet has already been presented to the user.
- (BOOL)removeAllURLs;

// Specify a block to be called when the user is finished. This block is not guaranteed
// to be called on any particular thread.
@property (nonatomic, copy) TWTweetComposeViewControllerCompletionHandler completionHandler;

그럼 TWTweetComposeViewController 의 header파일을 살짝 살펴 볼까요?

setInitialText:
addImage: 
addUrl:
등이 보이는군요.

여기서 재밌는게, addImage:/addUrl:등이 BOOL 을 return 한다는것입니다.
Tweet이 140 자 까지만 허용하기 떄문에, img 혹은 url 을 추가할때마다, url character가 추가 되기 때문에,
더이상 추가할 수 없을때 "NO"를 return 할것 같습니다.
 while([composeViewController addImage:[UIImage imageNamed:@"circle"]] == YES) {
        NSLog(@"abcdefg");
    }
  이런식으로 코딩을 했을때, "abcdefg"가 한번만 호출되는것으로 봐서, 중복체크도 해주는 모양입니다.
그리고, image를 넣어준 후에 viewController를 노출 시키면, 이미지 Thumnail 과 남은 글자수가 140이 아닌 119로 변경됨을 알수 있습니다.

 

그럼, Twitter 계정정보는 어떻게 접근할까요?
아니, Twitter 계정이 로그인안되어 있으면, TWTweetComposeViewController 를 띄워봐야 헛일 아닙니까?

우선, 사용자 입장에서 Twitter 계정설정은 "아이폰 설정"에서 할 수 있습니다.

 
트위터 앱이 설치 되어 있지 않아도, Twitter 계정을 로그인 할 수 있습니다.

 
심지어 여러개의 Account를 설정할 수 있으며,
Twitter App이 설치되어 있는지 여부도 알려줍니다. ( 아이폰에서 확인 )
해당 스크린샷들은 모두 iOS5 Simulator 에서 캡쳐 한것으로, Simulator에서도 다 테스트 가능합니다.

다시 본론으로 들어 가서, 로그인이 되어 있지 않다면, ViewController가 어떻게 동작할까요?

 
트위터계정이 없다고, Settings 로 이동할것인지, 취소 할것인지 묻습니다. 취소하게되면, Modal 로 띄워진,
ViewController가  Dismiss 됩니다.


이건 좀 구리지 않나요? ViewController를 띄우기 전에 미리 알수 있으면 좋을것 같다는 생각이 드네요 ^^
 
UIKIT_CLASS_AVAILABLE(5_0) @interface TWTweetComposeViewController : UIViewController {
}

// Returns if Twitter is accessible and at least one account has been setup.
+ (BOOL)canSendTweet;

TWTweetComposeViewController의 + method 로 canSendTweet이 있습니다.
트위터 계정이 없다면, NO, 있다면 YES를 얻을 수 있습니다.

그럼,,현재 로그인된 Twitter 정보는 얻을 수 없을까?
아니, 난 Tweet 작성말고, TimeLine을 얻어 오고 싶은데, 이런것들은 어떻게 할수 있을까?

글이 길어 진것 같아 :)
이 질문들은 다음 블로깅에서 답해보도록 하겠습니다 ^^ 

+ Recent posts