invalid Toolchain. New apps and app updates must be built with the public (GM) versions of Xcode 6 or later, and iOS 8 SDK or later. Don't submit apps built with beta software.

앱 개발을 완료하고 앱 검수를 위해 앱을 업로드한다. 그다음 "심사를 위해 제출"을 누를 때 위와 같은 오류가 뜬다면 나의 경우 아래와 같이 해결했다.

원문 : http://stackoverflow.com/questions/32174954/submitting-app-from-building-in-xcode-6-4/32233429#32233429

Apps that you submit should be developed using the latest version of Xcode from the Mac App Store and should be built for publicly available versions of iOS, OS X, and watchOS — except when GM seeds are available. Now Mac App Store's Xcode is 6.4 and OS X Yosemite is Build 14F27. If you user xcode 6.4 on OS X El Capitan, you should follow the steps:

  1. Using Xcode, then archive your project
  2. Open organizer, find your .xcarchive file find .xcarchive file
  3. Right click the xcarchive file, choose [Show package Contents]
  4. Find Products/Applications/XXX.app/Info.plist
  5. then change [BuildMachineOSBuild] value to 14F27, just like this: example
  6. Now, you can go to Xcode->organizer, then 【Submit to App Store】




WRITTEN BY
tucan.dev
개인 iOS 개발, tucan9389

,

이번에 두번째 계산기를 개발하다가 만들게된 콤마찍기 알고리즘(?)입니다. 구상하기는 생각보다 복잡했고 시간이 많이 걸렸는데 짜는데 시간은 얼마 안걸렸네요.. (허망) 
아무튼 짠다고 고생했는데 혼자 썩히기엔 아까워서 공유합니다.

사용법

  • 기본적으로 convertCommaFormula: 를 사용하면 문자열을 반환해줍니다.
  • 추가적으로 convertCommaFormula:location: 을 사용하면 현재 커서 위치를 파라미터로 넣고 현재 커서 위치까지 반환(NSDictionary)해 줍니다. 
저는 convertCommaFormula:location: 메서드를 구현해서 사용했었고 편의를 위해 convertCommaFormula: 를 뽑아보았습니다.

NSString *text = @"482193423+1284+41-(327123.4+2)";
NSString *resultText = [NSString convertCommaFormula:text];
NSLog(@"result : %@", resultText); 
// result : 482,193,423+1,284+41-(327,123.4+2)


코드

NSString+ConvertComma.h

#define COMMA_INDEX 3

@interface NSString (ConvertComma)

+ (NSString *) convertCommaFormula:(NSString *)formula ;
+ (NSDictionary *) convertCommaFormula:(NSString *)formula cursorlocation:(NSInteger)location ;

@end


NSString+ConvertComma.m

@implementation NSString (ConvertComma)


+ (NSString *) convertCommaFormula:(NSString *)formula {
    return [NSString convertCommaFormula:formula cursorlocation:0][@"text"];
}

+ (NSDictionary *) convertCommaFormula:(NSString *)formula cursorlocation:(NSInteger)location {
    
    NSString *beforeCursorText = [formula substringWithRange:NSMakeRange(0, location)];
    NSString *removedCommaBeforeCursorText = [beforeCursorText stringByReplacingOccurrencesOfString:@"," withString:@""];
    NSString *removedText = [formula stringByReplacingOccurrencesOfString:@"," withString:@""];
    
    NSMutableString *mBeforeText = [NSMutableString stringWithString:removedCommaBeforeCursorText];
    NSMutableString *mOriginText = [NSMutableString stringWithString:removedText];
    // 12|74
    
    if (![LayoutInfo shared].isNotComma) {
        NSInteger numbercount = 0;
        for (NSInteger i=mOriginText.length-1; i>=0; i--) {
            char c = [mOriginText characterAtIndex:i];
            if ('0'<=c && c<='9') {
                numbercount++;
            } else if (c=='.') {
                numbercount = 0;
                // 다시 되돌아가면서 콤마 지우기
                for (NSInteger j=i; j<mOriginText.length; j++) {
                    char cc = [mOriginText characterAtIndex:j];
                    if (cc==',') {
                        [mOriginText replaceCharactersInRange:NSMakeRange(j, 1) withString:@""];
                        if (j<mBeforeText.length) {
                            [mBeforeText replaceCharactersInRange:NSMakeRange(j, 1) withString:@""];
                        }
                        j--;
                    } else if ('0'<=cc && cc<='9') {
                        continue;
                    } else {
                        if (i==j) continue;
                        break;
                    }
                }
            } else {
                numbercount = 0;
            }
            
            
            if (numbercount==COMMA_INDEX+1) {
                numbercount-=COMMA_INDEX;
                // i+1번째에 ,추가
                
                [mOriginText replaceCharactersInRange:NSMakeRange(i+1, 0) withString:@","];
                if (i+1<mBeforeText.length) {
                    [mBeforeText replaceCharactersInRange:NSMakeRange(i+1, 0) withString:@","];
                }
            }
        }
    }
    
    return @{@"text":mOriginText, @"cursorindex":@(mBeforeText.length)};
}


@end




WRITTEN BY
tucan.dev
개인 iOS 개발, tucan9389

,
사실 5분은 오버고..
암튼 엄청 빠르게 서버 푸시 시스템을 구축할 수 있다는 뜻입니다. 아래 링크에 들어가서 따라하시면 됩니다.


위 링크에서 필요한 내용이나, 추가할만한 내용을 뽑아봤습니다. 



nodejs 서버사이드 iOS 푸시

iOS용부터 설명하겠다. 링크 여기 강좌에 들어가면 정말 쉽게 푸시 구현하는 방법을 설명해 놓았다. 3분안에 푸시를 쏘아볼 수 있다! 내가 위 링크의 포스팅보다는 잘 쓸 자신이 없으므로 나는 배포하는 부분을 좀 이야기하겠다. 테스트만 해볼 분들은 링크만 참고하면 되지만 배포할때는 아래 코드로 수정해야한다.

1) 푸시 개발용, 푸시 배포용 
// Developer 
var options = { 
    gateway : "gateway.sandbox.push.apple.com", 
    cert: './keys2/cert_production.pem',
    key: './keys2/key_production.pem',
    production: false
};

// AppStore 배포, Adhoc 배포
var options = { 
    gateway : "gateway.push.apple.com",//"gateway.sandbox.push.apple.com", 
    cert: './keys2/cert_production.pem',
    key: './keys2/key_production.pem',
    production: true
};

바뀐 부분은 두 라인인데, gateway부분과 production부분이다. gateway 값을 바꿔주고 productiontrue로 수정해야한다. 이렇게하면 AppStore배포나 Adhoc배포에서 푸시가 날아간다. 그러니 저렇게 바꿔서 테스트를 해보고 싶으면 Adhoc으로 배포하여 푸시테스트를 해보면 된다. (필자는 Developer, Adhoc, AppStore Distribute 상황에서 모두 테스트해보았고 그 결과를 말하는 것이다.) 

2) 푸시 여러개 한번에 보내기
위에서 소개한 강좌에 들어가면 푸시를 한번에 하나밖에 보내지 못한다. 아래 소스는 푸시 날리는 부분에 푸시아이디값을 Array로 만들어 한꺼번에 여러개 날릴 수 있게 해준다. 

var myDeviceArray = [ ]
for (var i=0; i<results.length; i++) {
     var token = results[i]._id;//'앞에서 Xcode로 build 하면서 획득한 아이폰 디바이스 토큰을 입력한다.'
     var myDevice = new apn.Device(token);
     myDeviceArray.push(myDevice);
}
try {
     apnConnection.pushNotification(note, myDeviceArray);
} catch (e) {
     console.log("apn exception : " + e);
}


apnConnection.pushNotification(note, myDeviceArray);
이 부분이 푸시를 실제 날리는 부분이고, 애플에게 푸시를 쏘아달라고 요청하게된다.

여기서 원래 myDeviceArray가 아닌 myDevice라는 객체를 넣었었는데, 필자는 여기에 myDevice의 배열을 넣었다. 이렇게하면 한번에 20개, 30개씩 푸시를 날릴 수 있다. 




nodejs 서버사이드 android 푸시

안드로이드는 iOS보다 휠씬 간단하게 구현이 가능하다. 안드로이드는 인증서 이런게 없기때문에 그냥 코드 구현을 하고 키값만 넣어주면 된다. 
그런데 자료를 찾다보니 앞서 소개한 iOS푸시구현 블로그와 동일한 저자가 안드로이드푸시 저자가 동일하고, 정리가 정말 잘되있다.. 링크를 따라가면  구현할 수 있을 것이다. 

여기서는 링크에서 서버쪽 코드만 빼내서 설명하겠다.

우리가 사용할 nodejs모듈은 https://github.com/ToothlessGear/node-gcm이다. node파일이 위치한 디렉토리에 들어가서 npm install node-gcm 명령을 치면 된다.

상단에 node-gcm를 불러오고

var gcm = require('node-gcm');


// or with object values
var message = new gcm.Message({
     collapseKey: 'demo',
     delayWhileIdle: true,
     timeToLive: 3,
     data: {
          lecture_id:"notice",
          title:"제목입니다",
          desc: "설명입니다",
          param1: '첫번째파람',
          param2: '두번째파람'
     }
});

var server_access_key = '/*안드로이드 개발자가 넘겨준 서버키*/';
var sender = new gcm.Sender(server_access_key);
var registrationIds = [ ];     // 여기에 pushid 문자열을 넣는다.

registrationIds = ['/*안드로이드 단말기에서 나온 푸시 아이디*/'];

/*
for (var i=0; i<push_ids.length; i++) {
     registrationIds.push(push_ids[i]);
}
*/

// 푸시를 날린다!
sender.send(message, registrationIds, 4, function (err, result) {
     // 여기서 푸시 성공 및 실패한 결과를 준다. 재귀로 다시 푸시를 날려볼 수도 있다.
     console.log(result); 
});
이상 서버사이드 아이폰, 안드로이드 푸시 알림이였습니다. 


WRITTEN BY
tucan.dev
개인 iOS 개발, tucan9389

,
이번 포스팅에서는 iOS에서 Objective-C로 푸시를 구현하는 방법에 대해 설명하겠다. 그렇지만 단순히 푸시를 받는것이 다가 아닌 푸시를 받았을 때 다음 이벤트 처리도 앱에서 어떻게 처리할지에대한 고민도 함께할 것이다. 이 부분은 애플에서 제공하는 방법이 생각보다 복잡하게 되있는것 같으므로 포스팅에 남기기로 하였다.

먼저 푸시를 받는 방법에 대해 설명하..  검색해보니 바로 원하는 링크가 안나와서 푸시 받는법부터 설명해보겠다.



1. 푸시 아이디를 얻어내는법

푸시 아이디를 얻고자 하는 시점에서 다음을 호출하면 된다. 
// 현재 푸시가 On인지 Off인지 알아내는 함수
BOOL pushEnable = NO;
if ([[UIApplication sharedApplication] respondsToSelector:@selector(isRegisteredForRemoteNotifications)]) {
    pushEnable = [[UIApplication sharedApplication] isRegisteredForRemoteNotifications];
} else {
    UIRemoteNotificationType types = [[UIApplication sharedApplication] enabledRemoteNotificationTypes];
    pushEnable = types & UIRemoteNotificationTypeAlert;
}

// 푸시 아이디를 달라고 폰에다가 요청하는 함수
UIApplication *application = [UIApplication sharedApplication];
if ([application respondsToSelector:@selector(isRegisteredForRemoteNotifications)]) {
    NSLog(@"upper ios8");
    // iOS 8 Notifications
    [application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]];
    [application registerForRemoteNotifications];
} else {
    NSLog(@"down ios8");
    // iOS < 8 Notifications
    [application registerForRemoteNotificationTypes:
     (UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound)];
}


// AppDelegate.m 파일에서 아래 함수를 추가한다. 아래 함수는 푸시 아이디를 받아내는 함수이고, 푸시아이디는 아래 함수를 통해서만 받을 수 있다.
// AppDelegate.m
- (void)application:(UIApplication *)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
     NSString* newToken = [[[NSString stringWithFormat:@"%@",deviceToken]
                           stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]] stringByReplacingOccurrencesOfString:@" " withString:@""];
     NSLog(@"DeviceToken : %@", newToken );
}


위 코드를 보면 이것저것 복잡해보인다. 여기서 중요한 라인만 설명을 하자면

UIApplication *application = [UIApplication sharedApplication];
[application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]];
[application registerForRemoteNotifications];
위 세 라인이다.
1. 앱 싱글톤 델리게이트를 받아와서 2. 푸시받을 타입을 정한다. 그리고 3. 실제로 푸시 아이디를 달라고 요청한다.
registerForRemoteNotifications가 호출되는 순간 appdelegate.m에 
첫째, 앱을 설치하고 처음으로 registerForRemoteNotifications를 호출하는 상황이면 사용자에게 푸시를 허용할지 말지에 대한 경고창이 뜬다. 여기서 허용을 하면 AppDelegate.m에 구현된application:didRegisterForRemoteNotificationsWithDeviceToken: 함수가 호출되면서 deviceToken파라미터에 푸시아이디가 담기게 된다. 반대로 사용자가 푸시 허용을 하지 않는다면 application:didRegisterForRemoteNotificationsWithDeviceToken: 함수는 호출되지 않는다. 그게 다다. 사용자가 푸시알림 허용 안했다고해서 뭐 어떻게 더 바로 할 수 있는게 없다. 푸시알림 무한 유도를 막기위함의 애플의 생각이 아닐까 싶은데, 이 부분때문에 고민을 좀 많이했다. 그래서 준비한게 BOOL pushEnable;이고 푸시가 현재 허용되있는지 아닌지 대강 판단해준다. 푸시가 꼭 필요한 어플일 경우는 pushEnable를 받아내서 확인할 수 있다. (하지만 이것도 사용자가 한번도 푸시 설정을 안했는지, 실제 푸시 거부를 한건지 알 방법은 없다. 단지 현재 푸시 설정 On/Off 여부만 알 수 있다.)
둘째, 앱을 설치하고 푸시 허용을 해놓은 상태이며(최초 registerForRemoteNotifications를 호출 해본 상태), registerForRemoteNotifications를 호출했다면 AppDelegate.m에 application:didRegisterForRemoteNotificationsWithDeviceToken:함수가 실행되면서 푸시아이디를 받아올 수 있다. 푸시 아이디를 서버에 등록할 때 사실상 푸시 아이디는 바뀔 수가 있다. 그렇기에 나같은 경우는 푸시아이디와 디바이스아이디를 함께 서버에 보내서 디바이스 아이디 기준으로 푸시 아이디를 저장한다. 아무튼 두번째의 경우는 무조건 푸시아이디를 받아올 수 있는 상황이다.

세번째, 앱을 설치하고 푸시 허용을 해제해놓은 상태이며(마찬가지로 최초 registerForRemoteNotifications를 호출 해본 상태), registerForRemoteNotifications를 호출했다면 아무일도 일어나지 않는다. 그렇기때문에 이 경우는 미리 pushEnable를 받아내서 푸시가 가능한지 아닌지를 판단해놓을 필요가 있다. pushEnableNO이면 푸시 허용 Off인 상태인거다. 그러면 푸시 설정을 해라고 경고를 띄우면 된다. 


2. 서버에서 푸시를 쏘았을때 폰에서 받은 위 호출되는 함수
서버에서 푸시를 쏘자마자 알아내는 방법은 하나밖에 없다. -> 앱이 실행되는 중일때이다.
앱이 만약 꺼저있거나, 백그라운드에서 돌고있으면 푸시가 왔는지 안왔는지 앱에서는 모른다. 앱이 다시 포그라운드로 왔을때 푸시가 왔으면 ‘푸시받는함수'가 호출된다. 하지만 웃긴것이, 앱이 백그라운드에도 없고 메모리에 올라가있지 않을때 푸시가 왔을때, 바탕화면에서 푸시를 받아서 푸시를 눌러 앱에 들어가면 ‘푸시받는함수’가 호출되지 않는다. 이 예외적인 부분을 처리하는 방법에 대해 설명하겠다.
 
// AppDelegate.m
// 앱이 런칭되서 메모리에 올라갈때 실행되는 함수이다.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    UILocalNotification *notificationUserInfo = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
    if (notificationUserInfo) {
//        NSLog(@"app recieved notification from remote%@",notification);
        NSMutableDictionary *mNotificationUserInfo = [NSMutableDictionary dictionaryWithDictionary:(id)notificationUserInfo];
        mNotificationUserInfo[@"appLaunch"] = @"yes";
        [self application:application didReceiveRemoteNotification:mNotificationUserInfo];
    }else{
//        NSLog(@"app did not recieve notification");
    }
    return YES;
}

여기서
// AppDelegate.m
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {

    [PushController shared].userInfo = userInfo;

    if (userInfo[@"appLaunch"]) {
        [PushController shared].waitingViewDidLoad = YES;
    } else {
        if (application.applicationState==UIApplicationStateActive) {
            [[PushController shared] pushInActiveStatus];
        } else {
            // if (application.applicationState==UIApplicationStateInactive) {
            [[PushController shared] presentPushPostTableViewController];
        }
    }
}
위의 PushController는 일단 신경쓰지 말고 푸시를 받았을 때 라이프 사이클만 신경쓰자. 
저기서 특이한 점은 
if (userInfo[@"appLaunch”])
를 이용하여 첫째 상황인지 먼저 구분하고
if (application.applicationState==UIApplicationStateActive) ;
를 이용하여 둘째상황과 셋째상황을 구분했다는 점이다.

첫째 상황에서는 아직 ViewController가 생성이 되기 전의 라이프사이클이다. 그렇기때문에 ViewControllerviewDidLoad를 호출하기 전까지 푸시정보를 잠시 가지고 있어야한다. 
둘째 상황에서는 application.applicationState==UIApplicationStateInactive에 해당하는 상태인데, 현재 앱이 백그라운드에서 포그라운드로 가고 있다는 뜻으로 사용자가 앱을 켜놓고 바탕화면에서 푸시를 눌렀을 때를 말한다. 이때는 바로 푸시 행위를 진행하면 된다.
셋째상황은 application.applicationState==UIApplicationStateActive의 상황으로써 앱이 이미 포그라운드에서 돌고 있었다는 뜻이고 이때는 자동으로 푸시 기능을 실행시켜버리는게 아니라 상단에 팝업창이 떠서 푸시가 온것처럼 만들어주면 사용자가 인지하기 쉬울 것이다. (안드로이드는 기본으로 제공되고, 아이폰은 카톡에서 볼 수 있는 기능이다) 상단에서 팝업이 잠시 내려왔을때 팝업을 누르면 푸시 기능을 수행하게 만들었는데, 이 기능은 PushController에 있다. PushController는 UI작업을 좀 했기 때문에 라인이 좀 되서 첨부해두겠다.

아무튼 위와같이 처리하면 푸시를 받는 일을 놓히지 않고 처리할 수 있다. 
 



WRITTEN BY
tucan.dev
개인 iOS 개발, tucan9389

,

공지알림서비스 for PNU CSE


가격
무료

첫 버전 출시
2015년 6월

설명
부산대학교 정보컴퓨터공학부를 위해 만들어진 공지 알림 서비스입니다. 이 프로젝트는 부산대에 소속되지 않고 오로지 부산대 정컴인의 편의를 위해 개발된 무료 서비스입니다.

특징
- 약 15명 정도의 서로 다른 교수님 홈페이지가 지원됨
- 약 50여개의 서로 다른 URL을 모아서 보여줌
- 약 300개의 글을 모아 잘 정리되어 이 앱에서 보여줌
- 학사의 공지 또한 지원
- 푸시 알림 지원
- 안드로이드, 아이폰, 아이패드 지원

할말
본 서비스는 학과에서 만난 학생들과 함께 한 강의에서 만들어진 팀 프로젝트로써 2015년 1학기에 강의가 끝남과 동시에 서비스를 오픈하였습니다. 그리고 팀원들 모두 큰 흥미를 느끼고 프로젝트를 없애지 않고 이어서 개발하기로 결정하였고, 힘이 닫는대까지는 서비스가 운영, 유자, 보수 될 것입니다. 안드로이드 개발자 , 웹 개발자 팀원에게는 항상 감사한 마음입니다. 앞으로 꾸준히 서비스가 유지될 수 있도록 많은 유저들의 서비스 이용이 있으면 좋겠습니다.


스크린샷






다운로드 링크
Play Store : https://play.google.com/store/apps/details?id=com.pnucse.csenotice.Hugang
App Store : https://itunes.apple.com/app/id1000141391

소개페이지 : http://14.49.37.33:8003/DSTWebManager

피드백 : canapio.dst@gmail.com




WRITTEN BY
tucan.dev
개인 iOS 개발, tucan9389

,
1. 구글 드라이브 web host 폴더 만들기
1) 구글 드라이브 가입하기
2) 구글 드라이브 컴퓨터에 설치하기
3) 배포용 폴더 만들기 (본인의 경우 Google 드라이브>2015 학교>수업>안드로이드실험>프로젝트>ios_APP의 경로로 설정했다)
4) 배포용 폴더를 웹호스팅 공개로 설정


빨간색 네모박스에 있는 키값을 꼭 복사해놓는다. (내 폴더 키는 소중하니까 모자이크처리)
맨 마지막에 URL 만들때 필요하다!

다음으로 고급을 누른다.

변경을 누른다.

'웹에 공개’를 누르고 저장을 누르면 웹 호스팅 폴더가 완성됬다!

5) Finder에서 확인해보면 폴더 모양이 공유된거처럼 생겼다.



2. Adhoc용으로 ipa파일 만들기
1) AdHoc용 Provisioning Profiles을 만들고 더운받아서 실행한다.
-> 아마 Xcode가 실행될 것이다.

2) Product>Archive하기 전에 세팅을 한다.
-> Adhoc으로 Archive(필드와 비슷한 것)해야하기 때문에 세팅을 한다.
- Project>General에서 Team설정 : None에서 자신의 계정으로 설정
- Project>Build Setting에서 Code Signing>Code Signing identity를 iPhone Distribution:XXXXX로 설정
- Project>Build Setting에서 Code Signing>Provisioning Profile을 1번에서 만든 AdHoc Provisioning Profile로 설정

3) Product>Archive를 실행


주의사항 : 시뮬레이터가 선택되있으면 Archive가 비활성되있다. 위 스샷처럼 iOS Device를 선택해주자.

4) 빌드가 끝나면 새로운 창이 뜨면서 어떤식으로 배포할 것인지 정하는 프로세스가 나올것이다. export를 하여 Adhoc을 고르고 구글 드라이브와 연동되있는 폴더에 ipa파일을 생성하면 된다.

3. BetaBuilder를 이용하여 URL만들기

1) choose IPA…버튼을 눌러서 .ipa파일을 가져온다.

2) 하단에 Full Web Deloyment Path를 입력한다. 
-> 구글 드라이브의 경우 https://googledrive.com/host/XXXXXXXXXXXXX
XXXXXXXXXXX부분에는 1.4번 구글드라이브 폴더에서 가져온 ipa파일이 들어있는 해당 디렉토리의 키값이다.


3) Generate Depolyment Files… 버튼을 눌러서 .ipa파일이 들어있는 폴더에 Generate 시킨다.


4. 링크 배포하기
UDID를 등록한 핸드폰(혹은 패드)에서 위 링크를 타고 들어가면 html이 열릴 것이다.
'Tap Here to install’을 누르면 다운이 시작될 것이다.

아래 스샷은 맥에서 연 사진이지만, 단말기의 브라우저에서 열어야한다.



WRITTEN BY
tucan.dev
개인 iOS 개발, tucan9389

,

한글 시계


가격
$0.99

첫 버전 출시
2014년 10월

다운로드 수
약 2,000건 다운 (무료+유료 2014.11 기준)

설명
한글날 출시된 “한글 시계” 앱은 텍스트를 한글만을 이용하여 만들었습니다. 어색하지 않는 UI 배치를 하려고 많이 고심했습니다. 

특징
- 앱스토어 최초 한글 시계
- 거부감이 들지 않는 UI 배치
- 편안한 애니메이션 효과
- 여러 설정 기능들


스크린샷










다운로드 링크 : https://itunes.apple.com/us/id923856886
피드백 : yapprj@gmail.com






WRITTEN BY
tucan.dev
개인 iOS 개발, tucan9389

,

Guitar Kit+


가격
$1.99

첫 버전 출시
2014년 1월

다운로드 수
약 60,000건 다운 (무료+유료 2014.11 기준)

설명
기본에 충실한 iOS용 계산기 어플입니다. 아이폰 아이패드 둘다 지원을 하며, 기본 계산기 기능 + 여러 추가 기능을 합쳐서 만들었습니다. 

특징
- 전문적인 앱임에도 불구하고 아름다운 UI를 지원
- 약 4000개의 코드 디비
- 타이핑을 이용한 코드 검색
- 자판을 터치하여 코드 검색
- 자주 쓰는 코드 저장
- 메트로놈 기능
- 초보를 위한 28개 기본 코드 연습 기능



소개 영상


스크린샷






다운로드 링크 : https://itunes.apple.com/app/id791551793
피드백 : guitar.chord.plus@gmail.com





WRITTEN BY
tucan.dev
개인 iOS 개발, tucan9389

,


쉬운 계산기


가격
무료

첫 버전 출시
2013년 4월

다운로드 수
약 40,000건 다운 (2014.11 기준)

유저 활성 현황
하루 약 4,000명이 앱을 사용하고 있으며 아무 홍보 없이 매일 50건이상씩 다운로드가 일어나고 있음

설명
기본에 충실한 iOS용 계산기 어플입니다. 아이폰 아이패드 둘다 지원을 하며, 기본 계산기 기능 + 여러 추가 기능을 합쳐서 만들었습니다. 

특징
- 쉬운 화면 인터페이스
- 직관적인 터치 애니메이션
- 다항 연산
- 00버튼
- %(백분율) 연산
- 딜리트(한글자씩 지우기) 기능
- 내보내기, 불러오기 기능
- 앱이 꺼져도 계산 기록 보족
- 아이폰, 아이패드 지원


스크린샷






다운로드 링크 : https://itunes.apple.com/app/id626808258
피드백 : easi.calculator@gmail.com




WRITTEN BY
tucan.dev
개인 iOS 개발, tucan9389

,