KakaoDev Talk.

Dev./iOS Dev.

Written by CenoX on 2014. 12. 15. 05:02

질문에 관한 내용은 카카오스토리 API이고, 플랫폼은 iOS SDK 8.1, 언어는 Swift입니다.

Swift가 나오고, 애플이 Swift와 Objective-C를 둘 다 사용할 수 있도록 해준 덕분에 현재 MyApp-Bridging-Header.h 파일에 


//  Use this file to import your target's public headers that you would like to expose to Swift.


#import <KakaoOpenSDK/KakaoOpenSDK.h>

이렇게 올려놓고 쓰고 ViewController.swift 파일에서 호출해서 쓰고있습니다.

그럼.. 염치 불구하고 질문 올리겠습니다..

아래에 있는 자료가 원래 KakaoDevelopers_ (https://developers.kakao.com/) 에 업로드 되어있는 Objective-C로 작성 된 기존 KakaoStorySampleLoginViewController.m 파일입니다.


* Copyright 2014 Daum Kakao Corp.


* Redistribution and modification in source or binary forms are not permitted without specific prior written permission.


* Licensed under the Apache License, Version 2.0 (the "License");

* you may not use this file except in compliance with the License.

* You may obtain a copy of the License at


*    http://www.apache.org/licenses/LICENSE-2.0


* Unless required by applicable law or agreed to in writing, software

* distributed under the License is distributed on an "AS IS" BASIS,


* See the License for the specific language governing permissions and

* limitations under the License.


#import "KakaoStorySampleAppDelegate.h"

#import "KakaoStorySampleLoginViewController.h"

#import <KakaoOpenSDK/KakaoOpenSDK.h>

@implementation KakaoStorySampleLoginViewController

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {

    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];

    if (self) {

        self.title = @"Login";


    return self;


- (void)loadView {

    [super loadView];

    // logo display

    int marginBottom = 25;

    UIImage *kakaoLogoImage = [KOImages kakaoLogo];

    UIImageView *kakaoLogoImageView = [[UIImageView alloc] initWithImage:kakaoLogoImage];

    kakaoLogoImageView.frame = CGRectMake((self.view.frame.size.width - kakaoLogoImage.size.width) / 2, (self.view.frame.size.height - kakaoLogoImage.size.width - marginBottom) / 2, kakaoLogoImage.size.width, kakaoLogoImage.size.height);

    kakaoLogoImageView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin;

    [self.view addSubview:kakaoLogoImageView];

    // button display

    UIButton *kakaoAccountConnectButton = [self createKakaoAccountConnectButton];

    [self.view addSubview:kakaoAccountConnectButton];


- (UIButton *)createKakaoAccountConnectButton {

    // button position

    int xMargin = 30;

    int marginBottom = 25;

    CGFloat btnWidth = self.view.frame.size.width - xMargin * 2;

    int btnHeight = 42;

    UIButton *btnKakaoLogin

            = [[KOLoginButton alloc] initWithFrame:CGRectMake(xMargin, self.view.frame.size.height - btnHeight - marginBottom, btnWidth, btnHeight)];

    btnKakaoLogin.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleWidth;

    // action

    [btnKakaoLogin addTarget:self action:@selector(invokeLoginWithTarget:) forControlEvents:UIControlEventTouchUpInside];

    return btnKakaoLogin;


- (void)viewDidLoad {

    [super viewDidLoad];

    // Do any additional setup after loading the view from its nib.


- (void)viewDidUnload {

    [super viewDidUnload];

    // Release any retained subviews of the main view.

    // e.g. self.myOutlet = nil;


- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {

    if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {

        return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);

    } else {

        return YES;



#pragma mark - actions

- (IBAction)invokeLoginWithTarget:(id)sender {

    [[KOSession sharedSession] close];

    [[KOSession sharedSession] openWithCompletionHandler:^(NSError *error) {

        if ([[KOSession sharedSession] isOpen]) {

            // login success.

            NSLog(@"login success.");

            [[[UIApplication sharedApplication] delegate] performSelector:@selector(showMainView)];

        } else {

            UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"에러"





            [alertView show];




- (IBAction)invokeCancelLoginWithTarget:(id)sender {

    [[KOSession sharedSession] close];


- (IBAction)invokeToggleStatusBar:(id)sender {

    [[UIApplication sharedApplication] setStatusBarHidden:![UIApplication sharedApplication].statusBarHidden withAnimation:YES];



그리고 아래가 지금 제가 만들고 있는 Swift 파일입니다.


//  ViewController.swift

//  ManyPost


//  Created by CenoX on 2014. 12. 15..

//  Copyright (c) 2014 CenoX. All rights reserved.


import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {


        // Do any additional setup after loading the view, typically from a nib.



    override func didReceiveMemoryWarning() {


        // Dispose of any resources that can be recreated.




    /*========Starting Make View for Kakao API =======*/



    override func loadView() {



        var marginBottom: CGFloat

        marginBottom = 25


        var kakaoLogoImage: UIImage!

        kakaoLogoImage = KOImages.kakaoLogo()


        var kakaoLogoImageView: UIImageView!

        kakaoLogoImageView.image = kakaoLogoImage


        kakaoLogoImageView.frame = CGRectMake((self.view.frame.size.width - kakaoLogoImage.size.width) / 2, (self.view.frame.size.height - kakaoLogoImage.size.width - marginBottom) / 2, kakaoLogoImage.size.width, kakaoLogoImage.size.height)


        kakaoLogoImageView.autoresizingMask = UIViewAutoresizing.FlexibleLeftMargin | UIViewAutoresizing.FlexibleRightMargin | UIViewAutoresizing.FlexibleTopMargin | UIViewAutoresizing.FlexibleBottomMargin




        var kakaoAccountConnectButton: UIButton!

        kakaoAccountConnectButton.addTarget(self, action: "createKakaoAccountConnectButton", forControlEvents: .TouchUpInside)


        /*Break Point*/

        Original Source (Objective-C)



        UIButton *kakaoAccountConnectButton = [self createKakaoAccountConnectButton];





    func createKakaoAccountConnectButton()

        //Action for kakaoAccountConnectButton


        var xMargin:CGFloat = 30

        var marginBottom: CGFloat = 25

        var btnWidth: CGFloat = self.view.frame.size.width - xMargin * 2

        var btnHeight: CGFloat = 42


        var btnKakaoLogin: UIButton!

        btnKakaoLogin.frame = CGRectMake(xMargin, self.view.frame.size.height - btnHeight - marginBottom, btnWidth, btnHeight)

        btnKakaoLogin.autoresizingMask = UIViewAutoresizing.FlexibleTopMargin | UIViewAutoresizing.FlexibleBottomMargin


        btnKakaoLogin.addTarget(self, action: "invokeLoginWithTarget", forControlEvents: .TouchUpInside)




    /*============#pragma mark - actions===========*/



    @IBAction func invokeLoginWithTarget() {

        var errors: NSError


        KOSession.sharedSession().openWithCompletionHandler(errors) {

            if (KOSession.sharedSession().isOpen()) {

                println("Login Success")


            } else {

                var alert = UIAlertController(title: "Error", message: errors.localizedDescription, preferredStyle: .Alert)





이렇게 적었습니다만 강조하고 싶은 부분은 두부분 입니다.

일단 첫번째

var errors: NSError

KOSession.sharedSession().openWithCompletionHandler(errors) {

기존 소스코드는

[[KOSession sharedSession] openWithCompletionHandler:^(NSError *error) {

라고 처리가 되어 있습니다만, Swift상에서 *Handler:^(NSError *error) 부분을 어떻게 처리해야 할 지 모르겠습니다..

직접 선언 된 헤더파일을 살펴보면


 기기의 로그인 수행 가능한 카카오 앱에 로그인 요청을 전달한다.

 @param completionHandler 요청 완료시 실행될 block. 오류 처리와 로그인 완료 작업을 수행한다.


- (void)openWithCompletionHandler:(KOSessionCompletionHandler)completionHandler;

라고 적혀 있고, 스위프트에서 구문을 호출 할 때 *CompletionHandler를 어떻게 처리해야할 지에 대한 설명도 간략히 나와있습니다만

아무리 고심해도 이 부분을 어떻게 처리해야 할지 모르겠습니다..





기존 소스코드를 보면

[[[UIApplication sharedApplication] delegate] performSelector:@selector(showMainView)];

이렇게 처리가 되어 있습니다만

NSObject안에 속해있는 -performSelector: 구문은 swift에서는 사용할 수 없습니다..ㅠ ( http://stackoverflow.com/questions/24158427/alternative-to-performselector-in-swift )

사용 가능하지 않다는 말에, 자포자기 하며 사용할 만한 소스코드를 찾아서 적은 코드가 바로 저


입니다만, 작동 할 것 같진 않아보입니다.

[Samsung Tools] ODIN 3.07


Written by CenoX on 2014. 9. 11. 19:39

[Samsung Tools] ODIN 3.07



이번엔 자료를 하나 올리려 합니다


이름은 ‘Odin(오딘) v3.07’ 인데요



갤럭시 류 휴대폰의 펌웨어 다운로드 도구

정도로 생각하면 되겠네요


자료찾기도 힘들고 블로그에 올려놓고 필요할때마다 들어오면 편하겠다 싶어서 올려둡니다 ㅋㅋ





1.    압축을 푼다

2.    Odin3.exe를 실행시킨다

3.    삼성류 폰을 다운로드모드로 진입시켜 연결한다

4.    다운로드 받은 펌웨어를 넣고 Start를 누른다



뭐가 들어가는지는 알수없지만

필요한경우 PIT 파일이나 Bootloader, PDA, Phone, CSC, UMS 다 넣을수도 있어요

보통의 소프트웨어 업그레이드나 다운그레이드, 복구같은경우는 펌웨어 구해서 PDA에 넣고 돌리기만 해도 충분히 가능합니다만


기저대역이 날라가거나 그런경우는 CSC같은거 찾아서 올려줘야해요

, 자료는 다들 알아서 구하시겠죠?

iOS 학교정보 파싱 소스코드

Dev./iOS Dev.

Written by CenoX on 2014. 9. 8. 20:21

iOS 학교정보 파싱 소스코드

안녕하세요 민족 대명절 추석이라는데 정말 신명나게 할짓이 없는 CenoX 입니다

청소년 개발자시라면 분명 자신의 '학교앱' 이라는 것에 대해 관심을 가지실텐데요, 

'자기 학교는 어플리케이션도 개발할 수 있는 멋진 학교다' 라던가

'내가 학교 어플리케이션을 만들면 학교에서 유명해지겠지' 라던가

뭐 다양한 이유가 있을거라 생각합니다

 주변에 보면 김급식이나 장급식 같은 여러 급식 정보 어플리케이션을 볼 수 있는데  그 어플리케이션들은 어떻게 당신이 학교정보만 입력했는데도 학교정보를 찾아오는지.. 궁금하지 않나요?

전 그부분이 궁금해서 이 '학교앱' 이라는 걸 개발해보기 시작했습니다

본문에 앞서 Wier의 김규한 팀장님께 감사의 말씀드립니다

많은도움 주셨습니다

일단 차례차례 목표를 잡아보죠


학교정보를 로딩 후 저장해야하고,

그 학교정보를 토대로 급식정보 불러온다.

이게 대략적인 어플리케이션의 핵심 기능이 되겠죠?

더 이상의 기능을 넣는것은 개발자들 마음대로 하는거니까요 :)

[소스 설명]

소스가 돌아가는 방식은 꽤나 간단합니다

Picker View에서 자신이 다니는 학교의 교육청을 불러온 후, 자신의 학교를 검색하면

교육청의 주소에서 해당값의 학교를 찾아 반환되는 값을 파싱, 필요한 정보만 걸러내는 방식입니다

*왜 굳이 교육청을 구분해야 하느냐에 대해서

학교이름에는 중복이 되는학교가 있는데다가, 각 교육청마다 사이트가 다릅니다

(서울특별시교육청주소: http://hes.sen.go.kr/, 경기도교육청주소: http://hes.sje.go.kr/)

PickerView의 row들은 education.plist에서 불러옵니다

그럼, 필요하다고 생각되는 정보는 3개

NSString *namResult;

NSString *schResult;

NSString *codResult;

먼저 말씀드리자면 namResult는 학교의 이름값을 반환하고,

schResult는 학교의 위치, 마지막으로 codResult는 학교 고유코드입니다

또 하나의 NSString변수 eachURL은 PickerView가 움직일때마다 실행되는

-(void)pickerView: didSelectRow: inComponent 메서드에서 각 교육청에 해당되는 URL이 변수값으로 설정됩니다

검색어들이 문자열조합을 거쳐서 나갈때는

stringByAddingEscapesUsingEncoding: 메소드를 거쳐 UTF8로 인코딩 되어 값을 내보내고, 반환받은 값은 파싱과정을 거쳐 걸러진 후, 인코딩 되지 않은 상태로 저장됩니다 (인코딩이 되지 않았더라도, 자체적인 코드 스칼라를 통해 출력시 한글로 정상출력됩니다)

모든 값들을 불러오고, 정상적으로 수행하였다면, schoolInfo.plist 라는 파일을 메인 Bundle에서 가져와 Document 폴더에 복사하고, 거기에 각 변수들을 저장합니다


시뮬레이터에선 메인번들에 schoolInfo.plist가 없어도 정상작동 하지만 기기에서는 오류가 발생하며 튕깁니다. 되도록이면 Supporting Files 그룹 안에 schoolInfo.plist 파일을 생성해주시기 바랍니다 안하면 copy에러나요 ㅇㅇ...

*추가해야할 아울렛


UIButton *2ea





파싱이라고 하긴 좀 뭐한감이 있지만, 뭐 그대로 적어봅니다

왜 파싱이라고 하긴 좀 뭐한감이 있냐면,

parser를 쓰지않아서 파싱이라 하기 뭐하다..랄까 뭐 파싱이라면 파싱이겠지요

소스코드를 보면 중간에 

NSString *nam1 = [NSString stringWithFormat:nam0[2]];

NSArray *nam2 = [nam1 componentsSeparatedByString:@"\""];

namResult = [NSString stringWithFormat:nam2[2]];

NSString *sch1 = [NSString stringWithFormat:sch0[2]];

NSArray *sch2 = [sch1 componentsSeparatedByString:@"\""];

schResult = [NSString stringWithFormat:sch2[2]];

NSString *cod1 = [NSString stringWithFormat:cod0[2]];

NSArray *cod2 = [cod1 componentsSeparatedByString:@"\""];

codResult = [NSString stringWithFormat:cod2[2]];

이런 구문을 중간에 찾으실 수 있을텐데요

간단히 설명드리자면 엄청 간단합니다

위에서 eachURL와 searchID를 조합하여 만든 searchResult.

이 searchResult를 인터넷으로 접속하여보면, 구분하지 않고선 의미없는 텍스트들이 나열이됩니다

그 텍스트들을 NSData로 데이터화하여 plist에 [(NSData *) writeToFile:(NSString *) acomically:(BOOL *)]; 을 통해 저장시키고

아까 정의해두었던 nam, sch, cod의 Result값을 찾아내기 위해 먼저 NSArray를 생성합니다 단, 배열안에 들어가는 원소를 '분리' 하도록 했는데요, 그게 componentsSeparatedByString:(NSString *)에 들어간 각각의 값들입니다

(이부분부터는 김규한 위어팀장님으로 부터 알게된 사실입니다, 감사합니다)

그렇게 때어낸 각각의 배열 3개를 잘 들여다보면 배열의 3번째 원소에

ex) nam0(2) //배열의 원소는 0부터 센다

필요한값이 앞으로 빠진걸 볼 수 있죠

하지만 아까와같이 우리에게 필요한 값은 들어있지만, 좀 더 범위가 작은, 그리고 알아보기 쉬운 배열로 바뀝니다

하지만 우린 '필요한 값' 만 걸러내어야하죠

그래서 또 자르기 위해 배열을 생성하고 원소를 "로 구분하여 분리시킵니다

그리고나서 또 3번째 원소를 살펴보면 우리가 원하는 '값' 만이 존재합니다

그래서 각각의 result를 2번째로 생성한 Array의 3번째 원소로 접근시켜주면 우리가 원하는 값만을 뜯어낼 수 있는거죠

namResult, schResult, codResult 모두 같은 방법으로 분리됩니다


각각의 IBOutlet들은 용도에 맞게 정의하였으며, 필요하면 바꾸시고, 그냥 쓰셔도됩니다.

IBAction에서의 메소드를 줄이고자, 왠만하면 void구문안에 구현하고 [self (void구문명) ] 식으로 호출할 수 있게 해놓았구요

정보를 plist로 저장하고 이용한 이유는 plist는 어느 뷰에서나 코드만 있으면 접근가능하고, objectForKey만 있으면 사용이 매우 간편하기 때문에 이용했습니다

주석처리 나름 한다곤 했지만 소스정리를 완벽하게 하지 못했을 수도 있으니 궁금한게 있으시면 언제든지 코멘트 날려주세요

개발자를 배려한다면 소스제공자는 써주시는게..ㅎ

[수정사항] - 2014.09.25

기존 소스인

NSString *nam1 = [NSString stringWithFormat:nam0[2]];

    NSLog(@"nam1 is %@", nam1);

    NSArray *nam2 = [nam1 componentsSeparatedByString:@"\""];

    NSLog(@"nam2 is %@", nam2);

    namResult = [NSString stringWithFormat:nam2[2]];

    NSLog(@"namResult is %@", namResult);


[NSString stringWithFormat:nam0[2]] <- 구문에서 Format string is not a string literral (potentially insecure) 경고를 없애기 위하여

//    NSString *nam1 = [NSString stringWithFormat:nam0[2]];

    NSString *nam1 = nam0[2];

    NSLog(@"nam1 is %@", nam1);

    NSArray *nam2 = [nam1 componentsSeparatedByString:@"\""];

    NSLog(@"nam2 is %@", nam2);

//    namResult = [NSString stringWithFormat:nam2[2]];

    namResult = nam2[2];

    NSLog(@"namResult is %@", namResult);

로 교체함.



사진도 올립니다

소스코드 정상작동하는 것 확인했습니다

'Dev. > iOS Dev.' 카테고리의 다른 글

iOS에 내장된 Framework를 이용하여, 트위터에 글 포스팅하는 소스코드.  (0) 2015.02.13
kakaoDev Talk. -절망  (0) 2014.12.16
KakaoDev Talk -삽질  (0) 2014.12.16
KakaoDev Talk.  (0) 2014.12.15