Showing posts with label iOS. Show all posts
Showing posts with label iOS. Show all posts

Sunday, November 11, 2018

iOS CocoaPods에서 Admob SDK 7.9.1 버전 사용하기

Admob을 Google SDK페이지에서 시킨대로 사용하다 보면 최신버전은 7.9.1인데 7.8.1이 연동되어 다음과 같은 에러메세지가 나는 경우가 있다.

You are currently using version 7.8.1 of the SDK. Please consider updating your SDK to the most recent SDK version to get the latest features and bug fixes. The latest SDK can be downloaded from http://goo.gl/iGzfsP. A full list of release notes is available at https://developers.google.com/admob/ios/rel-notes.

이렇게 되는 원인은 다음과 같다. Podfile에서 다음과 같이 되어 있는 경우이다.
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '7.0'
target 'project' do
  use_frameworks!
  pod 'Firebase'
  pod 'Firebase/Core'
  pod 'Firebase/AdMob'
  pod 'Fabric'
  pod 'Crashlytics'
  pod 'Google/Analytics'
end

이것을 다음과 같이 바꾸자. Firebase/AdMob을 제거하고 수동으로 Google-Mobile-Ads-SDK를 집어 넣자.
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '7.0'
target 'nextdoor' do
  use_frameworks!
  pod 'Firebase'
  pod 'Firebase/Core'
#  pod 'Firebase/AdMob'
  pod 'Fabric'
  pod 'Crashlytics'
  pod 'Google/Analytics'
  pod 'Google-Mobile-Ads-SDK'
end

그러면 최신버전 7.9.1의 Admob SDK가 연동된다.
Firebase/AdMob이 구형 7.8.1을 참조해서 문제가 생긴 것이다.

Saturday, April 29, 2017

iOS ARC 코딩 규칙

다음은 ARC를 적용하기 위한 코딩 상의 규칙이다.

1. 명시적으로 dealloc을 호출해서는 안된다.
2. retain, release, retainCount, autorelease를 명시적으로 호출하거나 오버라이드 할 수 없다.
3. dealloc 메소드에 대해 ARC의 적용을 받지 않는 인스턴스 변수를 해제하기 위해 오버라이드를 할 수는 있다. 단 이 때에도 [super dealloc]을 명시적으로 호출해서는 안된다.
4. CFRetain, CFRelease는 앞서 설명했듯이 사용할 수 있다. 코어 파운데이션 타입은 ARC에서 제외되므로 수동으로 관리해야 한다.
5. C구조체 내에 Objective-C 객체 포인터를 저장할 수 없다. 구조체보다는 Objective-C 객체를 쓸 것을 추천한다.
6. id 와 void *간의 캐주얼 캐스팅을 쓸 수 없다. 이들 간의 캐스팅에서는 컴파일러가 객체의 라이프사이클을 파악할 수 있도록 추가적인 지시어 (__reatin 등)를 써서 관계를 명시해야 한다.
7. NSAutoreleasePool 객체는 더 이상 쓸 수 없다. @autoreleasepool{ } 블럭을 사용한다.
8. NSZone을 쓸 수 없다. 어차피 ARC가 아니어도 최신 런타임은 이를 무시해버린다.
9. 접근자명에 new를 붙일 수 없다. @property NSString *newTitle;은 컴파일 오류를 일으킨다.
10. 단, getter 명을 바꾸면 쓸 수는 있다. @property (getter=theNewTitle) NSString *newTitle;은 동작한다.

변수 지정자
프로퍼티 지정자처럼 변수 지정자가 추가되었다.
__strong__weak
__unsafe_unretained__autoreleasing
__strong은 디폴트 값이다. 이 변수에 할당한 객체는 강한 참조를 하게 된다.
weak는 약한 참조만을 갖도록 한다. 변수가 가리키는 객체가 파괴되면 (객체는 강한참조의 수가 0일 때 자동으로 파괴된다) nil로 변경된다.

unsafe_unretained 는 강한 참조처럼 객체를 유지하지 않지만 약참조처럼 nil로 변경되지 않는다. CF객체나 C포인터등을 가리킬 때 사용한다.
__autoreleasing은 자동 해제될 객체를 담는 변수이다. 함수의 인자로 넘겨지는 변수는 모두 이 타입을 사용한다.

strong = retain
weak = objective c
assign = c

iOS Swift 요약 정리

init? 실패할수도 있는(nil을 리턴할수 있는) 생성자
A ?? B 3항 연산자로 unwrapping이 가능하면 unwrapping 실패하면 B를 리턴

Optional - nil을 가질수 있음. 그리고 할당하지 않으면 nil임
? - 언래핑 해야함
!  - 암묵적 언래핑이 되어 있어서 !를 붙여서 언래핑 할필요 없음

Casting
as NSString  - 다운캐스팅
as! NSString - 업캐스팅. 실패시 에러
as? NSString - 옵셔널캐스팅. 실패시 false 리턴. if절에서 체크

if let, var 언래핑 where

Guard
일반적으로 에러처리해서 함수에서 리턴하는 명령들이다
guard let number = value where value < 10 else {
    // false시 처리할 문장
    return
}

do while => repeat while

Switch
0..49가능 where 가능
fallthrough break없이 다음 case까지 진행가능

String formatting
print(“\(userName)”)

Function
func funcName(var1:int, var2: int) -> Float
{
}

Class static method
class ClassName
{
    class func funcName
}

함수에 변수 포인터 전달
inout

생성자/소멸자
init()
deinit()

계산된 속성
var balance: Float {
    get {
        return _balance
    }
    set(newBalance) {
        _balance = newBalance
    }
}


접근자
open, public
internal (default)
fileprivate
private


do catch
try func() - exception을 throw하는 경우에는 이렇게 호출해야함
try! func() - throw하는 함수에 대해서 에러처리를 받지 않고 호출함

enum ZZZException : ErrorType {
case XXX
case YYY
}

defer - 함수 종료시에 무조건 호출되는 함수를 지정

이 예제에서 class타입은 override할 수 있지만, static타입은 컴파일 에러를 발생시키네요.
static타입 선언은 class final선언과 같다고 볼 수 있습니다.

클로저
let multiply = {(val1: Int, val2: Int) -> Int in
    return val1*val2;
}

Tuesday, April 25, 2017

iOS 오픈소스 라이브러리 모음

SDWebImage - 이미지 다운로더 및 캐쉬 라이브러리
https://github.com/rs/SDWebImage

Toast - 안드로이드 Toast 기능 라이브러리
https://github.com/scalessec/Toast

JSONKit - 써보니 그냥 편하고 빠른 JSON Parser
https://github.com/johnezang/JSONKit

AFNetworking - Network Framework
https://github.com/AFNetworking/AFNetworking

iOS ARC와 GC의 차이

가비지 컬렉션 방식은, 메모리 관리를 가비지 컬렉터라는 것이 프로그램 실행중에 동적으로 감시하고 있다가, 더이상 사용할 필요가 없다고 여겨지는 것을 메모리에서 삭제해 주는 것입니다. 즉, 실행타임에서 메모리 관리를 하는 것입니다.


그와는 달리, ARC는 프로그램이 실행되고 있는 상태에서 감시하는 것이 아니라, 코드를 빌드할 때에(컴파일할 때) 컴파일러가 프로그래머 대신에 release 코드를 적절한 위치에 넣어주는 것입니다.


이건 아주 중요한 장점인데, 가비지 컬렉션이라는 것이(대표적으로 Java나 .NET에서 사용됩니다.) 항상 메모리를 차지하고 감시해야기 때문에 프로그램 자체 외에 메모리 사용량이 더 늘어날 수 밖에 없으며, 지속적인 감시를 위해 CPU를 일부 사용할 수 밖에 없는데 비해, ARC는 어차피 수동으로 개발자가 넣을 코드를 컴파일러가 넣어주는 것이기 때문에, 전혀 그런 오버헤드가 필요 없다는 것입니다.

iOS GCD와 NSOperationQueue의 차이

GCD는 동시에 실행하려는 작업 단위를 대표 할 수있는 경량의 방법이다. 해당 작업 단위는 개발자가 직접 스케줄하지 않고 시스템이 스케줄 관리를 해준다. 블럭들 사이에서 의존성을 부여하는 것은 쉽지 않은 일이며, 작업 취소 혹은 일시정시 같은 일을 하기 위해서는 각 개발자가 개인별로 추가해야한다.

NSOperation과 NSOperationQueue는 GCD에 비해 추가적인 기능을 제공하며 여러 operation에 의존성을 부여할 수도 있다. 뿐만 아니라 재사용도 가능하며 취소 혹은 일시정지와 같은 기능도 가능하다. NSOperation은 KVO 기술을 완벽하게 사용할 수 있다. 그래서 NSOperation이 실행되기 시작하면 NSNotificationCenter를 통해 상태 변화에 대한 노티를 받을 수 있다. 

iOS 백그라운드 비동기 작업 방법 (3 가지)

1. PerformSelectorInXXX
self performSelectorInBackground:@selector(myMethod:) withObject:
self performSelectorInMainThread:@selector(updateUI:) withObject:

-(void)updateUI:(NSDictionary *)param{
}

2. NSOperationQueue
_queue = [[NSOperationQueue alloc] init];

//시간이 오래 걸리는 작업을 만들어 백그라운드 큐에 돌려줍시다.
    NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
        [self heavyOperation];
        //오래 걸리는 작업의 결과로 UI업데이트를 진행시켜줍니다.
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            [self updateUI:nil];
        }];
    }];
    [_queue addOperation:operation];

3. GCD
// 여기서부터 비동기 코드 시작.
    // dispatch_async 함수는 내부블럭의 코드 실행에 영향을 받지 않고 바로 실행이 끝난다.
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{
        // 작업이 오래 걸리는 API를 백그라운드 스레드에서 실행한다.
        BOOL res = [self heavyOperation];
        dispatch_async(dispatch_get_main_queue(), ^{
            // 이 블럭은 메인스레드(UI)에서 실행된다.
            if (res) {
                [self updateUI:nil];
            }else {
                [self alertFail];
            }
        });
    });

dispatch_one : 한번만
dispatch_after : 일정 시간 후에

Thursday, December 15, 2016

iOS Collection element of type 'double' is not an Objective-C object

http://stackoverflow.com/questions/22758048/collection-element-of-type-double-is-not-an-objective-c-object

위의 에러메세지는 컬렉션에는 double 타입이 들어가지 못한다는 에러 메세지이다.
그래서 NSNumber로 박싱을 해야 한다.

박싱은 간단하게 @(doubleTypeVariable) 또는 [NSNumber numberWithDouble:doubleTypeVariable] 이렇게 할수 있다.

Tuesday, July 5, 2016

iOS WWDC 2016 요약 정리


watchOS3
백그라운드 로딩속도 향상
글자인식
긴급상황+위치정보 전송
휠체어 운동량
미키마우스 인터페이스 변경


iOS10
손으로 들었을때 자동으로 켜짐
홈버튼 눌러서 잠금해제로 변경
잠금상태에서 3D터치로 메세지전송,사진촬영등 가능
제어센터 좌우 슬라이드
시리 써드파티 개방
사진 촬영장소, 얼굴인식 분류 가능
스마트홈앱
전화 스팸여부 확인, 써드파티 VoIP와 연결
메세지 업그레이드 - 손글씨, 전체화면, 이모티콘 변환
아이메세지 특수효과

Friday, July 1, 2016

iOS Swift 키보드 show/hide 시 UI 위치 보정 처리

    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)
        
        registerForKeyboardNotifications()
    }
    
    override func viewWillDisappear(animated: Bool) {
        super.viewWillDisappear(animated)
        
        unregisterForKeyboardNotifications()
    }

    //MARK: Keyboard events
    func registerForKeyboardNotifications() {
        NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(keyboardWillShow), name: UIKeyboardWillShowNotification, object: nil)
        NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(keyboardWillHide), name: UIKeyboardWillHideNotification, object: nil)
    }

    func unregisterForKeyboardNotifications() {
        NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillShowNotification, object: nil)
        NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillHideNotification, object: nil)
    }
    
    func keyboardWillShow(note: NSNotification) {
        // UI 키보드 위로 올려준다
        print("keyboardWillShow")

        let s = note.userInfo![UIKeyboardFrameEndUserInfoKey]
        let rect = s!.CGRectValue()
        
        // 입력창 위치를 올려줌
        var frame = textField.frame
        frame.origin.y -= rect.height
        textField.frame = frame

        let keyboardFrameEnd = view!.convertRect(rect, toView: nil)
        view.frame = CGRectMake(0, 0, keyboardFrameEnd.size.width, keyboardFrameEnd.origin.y)
        view.layoutIfNeeded()
    }
    
    func keyboardWillHide(note: NSNotification) {
        // UI 원위치 한다
        print("keyboardWillHide")

        let s = note.userInfo![UIKeyboardFrameBeginUserInfoKey]
        let rect = s!.CGRectValue()

        var frame = textField.frame
        frame.origin.y += rect.height
        textField.frame = frame
        
        frame = view.frame
        view.frame = CGRectMake(frame.origin.x, frame.origin.y, frame.width, frame.height + rect.height)
        view.layoutIfNeeded()
    }

Monday, June 27, 2016

iOS Swift NSThread 생성하기

selector의 구문이 바뀌었다.
self 클래스에 runLoop()가 있어야 한다.

            let thread = NSThread(target:self, selector:#selector(runLoop), object: nil)
            thread.start()
  
func runLoop() {
        while(true) {
            print("runLoop")
        }
    }

Thursday, May 26, 2016

iOS 리젝 사유 17.1 Apps cannot transmit data...

변경된 사항이 거의 없는데도 불구하고 아래의 사유로 iOS 앱이 리젝 되었다.
이 문제의 원인은 사용자 정보를 서버로 전송하는데 유저의 동의를 구하지 않았고, 개인정보 취급방침을 지정하지 않았다는 것이다.
예전에 스토어 심사를 할때는 유저의 동의를 구하는 절차는 구현하였는데, 개인정보 취급방침이 없었던게 문제이다.
이번에 웹브라우저로 링크를 걸어서 개인정보 취급방침을 띄우는 것을 구현하고 재심사를 신청했다.

17.1 - Apps cannot transmit data about a user without obtaining the user's prior permission and providing the user with access to information about how and where the data will be used

17.1 Details

We noticed that your app does not obtain the user’s consent prior to uploading users’ scores to a global leaderboard.

To collect personal data with your app, you must make it clear to the user that their personal data will be uploaded to your server.

Next Steps

Please revise your app to include a privacy policy URL in the App Information page on iTunesConnect and ensure that the URL you provide directs users to your privacy policy.

Monday, May 16, 2016

iOS PHP 인앱 구매 영수증 서버 검증

//iOS -(void)serverVerfication:(SKPaymentTransaction*)transaction andRestore:(BOOL)isRestore
{
    NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
    NSString *user_id = [userDefault objectForKey:@"uid"];
    NSString *item_id = transaction.payment.productIdentifier;
   
    // 추가된 order_id(구글과 맞추기 위해서 용어를 변경하였다.)
    NSString *order_id;
    int restore = 0;
    if(isRestore == YES) {
        order_id = transaction.originalTransaction.transactionIdentifier;
        restore = 1;
    }
    else {
        order_id = transaction.transactionIdentifier;
    }

    // Load the receipt from the app bundle.
    NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
    NSData *receipt = [NSData dataWithContentsOfURL:receiptURL];
   
    // Create the JSON object that describes the request
    NSError *error;
    NSDictionary *requestContents = @{
                                      @"receipt-data": [receipt base64EncodedStringWithOptions:0],
                                      @"platform": @"ios",
                                      @"item_id": item_id,
                                      @"order_id": order_id,
                                      @"user_id": user_id,
                                      @"restore": [NSNumber numberWithInt:restore],
                                      @"sandbox": [NSNumber numberWithInt:0]
                                      };
    NSData *requestData = [NSJSONSerialization dataWithJSONObject:requestContents
                                                          options:0
                                                            error:&error];
   
    // Create a POST request with the receipt data.
//    NSURL *storeURL = [NSURL URLWithString:@"https://sandbox.itunes.apple.com/verifyReceipt"];


    //검증하려고 하는 자체 서버
    NSURL *storeURL = [NSURL URLWithString:[URLManager getPaymentLog]];
    NSMutableURLRequest *storeRequest = [NSMutableURLRequest requestWithURL:storeURL];
    [storeRequest setHTTPMethod:@"POST"];
    [storeRequest setHTTPBody:requestData];
   
    // Make a connection to the iTunes Store on a background queue.
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [NSURLConnection sendAsynchronousRequest:storeRequest queue:queue
                           completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
                               if (connectionError) {
                                   /* ... Handle error ... */
                               } else {
                                   NSError *error;
                                   NSString *jsonData = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];

                                   NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:[jsonData dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingAllowFragments error:&error];
                                   if (!jsonResponse) { /* ... Handle error ...*/
                                   }
                                   /* ... Send a response back to the device ... */
                                   NSNumber *result = [jsonResponse objectForKey:@"result"];
                                   if(result == nil) {
                                       NSLog(@"구매 인증 실패");
                                       handleSendBuyEvent(-1, 0);// M_E_ERROR = -1
                                   }
                                   else {
                                       if([result longValue] == 0) {
                                           NSLog(@"구매 인증 성공");
                                           [self provideContent: transaction.payment.productIdentifier];
                                       }
                                       else {
                                           NSLog(@"구매 인증 실패 %d", [result longValue]);
                                           handleSendBuyEvent(-1, 0);// M_E_ERROR = -1
                                       }
                                   }
                               }
                           }];
   
}

 
//PHP
//JSON데이터를 $_POST에 넣어준다
$postdata = file_get_contents("php://input");
$_POST = json_decode($postdata, true);

function verify($sandbox, $receipt) {
    // Environment sandbox인지 체크
    // 영수증을 애플 서버에 보내자
    $endpoint = "";
   
    // 샌드박스일 경우
    if($sandbox == 1) {
         $endpoint = 'https://sandbox.itunes.apple.com/verifyReceipt';     
    }
    else {// 실결제일 경우
        $endpoint = 'https://buy.itunes.apple.com/verifyReceipt';
    }
    error_log("endpoint=$endpoint");

    $postData = json_encode(array('receipt-data' => $receipt)); 
   
    // curl로 요청하자
    $ch = curl_init($endpoint); 
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION , true); 
    curl_setopt($ch, CURLOPT_POST, true); 
    curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
   
    $response = curl_exec($ch); 
    $errno    = curl_errno($ch); 
    $errmsg   = curl_error($ch); 
    curl_close($ch);  

    error_log("response=$response");
    if($errno)
        error_log("errno=$errno");
    if($errmsg)
        error_log("errmsg=$errmsg");
    error_log("response ok");
   
    $data = json_decode($response);
    error_log("data=".print_r($data, true));
   
    if(!is_object($data)) {
        return false;
    }
   
    if(!isset($data->status) || $data->status != 0) {
        return false;
    }
    return true;
}

    // 2. 애플 영수증 검증
    // 실결제를 먼저하고, 샌드박스 결제를 검증해야 한다.
    // 왜냐하면 애플 심사과정에서는 샌드박스로 테스트를 하기 때문이다.
    if(strlen($receipt) > 0) {
        if(!verify(0, $receipt)) {// 실결제 테스트
            if(!verify(1, $receipt)) {// 샌드박스 테스트
                // 실결제, 샌드박스 둘다 실패하면 에러다
                $ret = array("result" => 5, "error" => "receipt error");
                echo json_encode($ret);
                exit;
            }
        }
    }
    // 문제없이 exit가 안되었다면 검증이 성공한 것이다.

Sunday, April 24, 2016

iOS 디바이스 모델 정보 얻기

iOS 최신 버전에서는 [[UIDevice currentDevice]model]을 하여도 "iPhone"이라는 문자열 밖에 얻을 수 없다.
따라서 아래와 같이 구현하면 최신 iOS에서도 디바이스 모델 정보를 얻어올 수 있다.
다만 시뮬레이터에서 실행하면 x86_64가 얻어진다.(64비트 아이폰의 경우)

#import <sys/utsname.h> // import it in your header or implementation file.

NSString* deviceName()
{
    struct utsname systemInfo;
    uname(&systemInfo);

    return [NSString stringWithCString:systemInfo.machine
                              encoding:NSUTF8StringEncoding];
}

@"i386"      on 32-bit Simulator
@"x86_64"    on 64-bit Simulator
@"iPod1,1"   on iPod Touch
@"iPod2,1"   on iPod Touch Second Generation
@"iPod3,1"   on iPod Touch Third Generation
@"iPod4,1"   on iPod Touch Fourth Generation
@"iPod7,1"   on iPod Touch 6th Generation
@"iPhone1,1" on iPhone
@"iPhone1,2" on iPhone 3G
@"iPhone2,1" on iPhone 3GS
@"iPad1,1"   on iPad
@"iPad2,1"   on iPad 2
@"iPad3,1"   on 3rd Generation iPad
@"iPhone3,1" on iPhone 4 (GSM)
@"iPhone3,3" on iPhone 4 (CDMA/Verizon/Sprint)
@"iPhone4,1" on iPhone 4S
@"iPhone5,1" on iPhone 5 (model A1428, AT&T/Canada)
@"iPhone5,2" on iPhone 5 (model A1429, everything else)
@"iPad3,4" on 4th Generation iPad
@"iPad2,5" on iPad Mini
@"iPhone5,3" on iPhone 5c (model A1456, A1532 | GSM)
@"iPhone5,4" on iPhone 5c (model A1507, A1516, A1526 (China), A1529 | Global)
@"iPhone6,1" on iPhone 5s (model A1433, A1533 | GSM)
@"iPhone6,2" on iPhone 5s (model A1457, A1518, A1528 (China), A1530 | Global)
@"iPad4,1" on 5th Generation iPad (iPad Air) - Wifi
@"iPad4,2" on 5th Generation iPad (iPad Air) - Cellular
@"iPad4,4" on 2nd Generation iPad Mini - Wifi
@"iPad4,5" on 2nd Generation iPad Mini - Cellular
@"iPad4,7" on 3rd Generation iPad Mini - Wifi (model A1599)
@"iPhone7,1" on iPhone 6 Plus
@"iPhone7,2" on iPhone 6
@"iPhone8,1" on iPhone 6S
@"iPhone8,2" on iPhone 6S Plus
@"iPhone8,4" on iPhone SE

Monday, February 29, 2016

iOS 앱 버전 정보 불러오기

NSString *version = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];

Monday, December 21, 2015

iOS 디바이스 UUID 획득하기

//처음에 UUID를 KeyChain에서 불러오는데 nil이라면 UUID를 생성해서 KeyChain에 저장한다.
//저장 후에 다시 함수를 호출 하면 저장된 값을 리턴한다.
NSString* getUUID()
{
    // initialize keychaing item for saving UUID.
    KeychainItemWrapper *wrapper = [[KeychainItemWrapper alloc] initWithIdentifier:@"UUID" accessGroup:nil];
   
    NSString *uuid = [wrapper objectForKey:(__bridge id)(kSecAttrAccount)];
   
    if( uuid == nil || uuid.length == 0)
    {
        // if there is not UUID in keychain, make UUID and save it.
        CFUUIDRef uuidRef = CFUUIDCreate(NULL);
        CFStringRef uuidStringRef = CFUUIDCreateString(NULL, uuidRef);
        CFRelease(uuidRef);
        uuid = [NSString stringWithString:(__bridge NSString *) uuidStringRef];
        CFRelease(uuidStringRef);
       
        // save UUID in keychain
        [wrapper setObject:uuid forKey:(__bridge id)(kSecAttrAccount)];
    }
   
    return uuid;
}

Monday, December 14, 2015

iOS 리뷰 유도를 위해서 앱스토어로 이동하기

#define URL_APPSTORE @"itms-apps://itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?id=xxxxxxxx&onlyLatestVersion=true&pageNumber=0&sortOrdering=1&type=Purple+Software"

            // 앱스토어로 이동
            [[UIApplication sharedApplication] openURL:[NSURL URLWithString:URL_APPSTORE]];

Monday, November 9, 2015

iOS Please verify that your device’s clock is properly set, and that your signing certificate is not expired.

Please verify that your device’s clock is properly set, and that your signing certificate is not expired. (0xE8008018).

Xcode -> Preferences -> Accounts -> View Details -> Download All

Thursday, October 22, 2015

iOS 개발자 인증서 내보내기

iOS 개발자 인증서를 여러 맥북에서 공유하기 위해서 내보내기를 해보았다.

1. 먼저 key파일을 내보내야 하므로 '키체인 접근'을 실행시키자.
여기서 내보낼 키를 Development, Distribution 두개를 선택하고 '2개 항목 보내기'를 선택하여 .pem 파일로 저장한다.

2. 두번째는 개발자 프로필을 내보내야 하므로 Xcode를 실행시켜서 Preference에서 Accounts를 선택하여 내보낼 Apple ID를 선택하고 하단에 있는 설정모양 아이콘을 눌러서 Export Developer Accounts를 선택하여 내보내자.


이 두가지 파일을 다른 맥북에 옮겨서 두개를 더블 클릭하면 동일하게 다른 맥북에서도 iOS 개발을 할수 있다.



iOS -ObjC 옵션에서 SDL 2.0 라이브러리가 duplicate가 날때

iOS 프로젝트에서 SDL 2.0 라이브러리를 사용할때가 있다. 그런데 여기서 SDL_main을 사용하지 않고 라이브러리로만 사용할때 -ObjC를 사용하게 되면 duplicate symbol for architecture x86_64라는 에러메세지가 뜬다.

-ObjC는 duplicate를 허용하지 않는 옵션이기 때문에 메인 프로젝트에 UIApplication을 띄우는 main함수가 있고 그리고 SDL 라이브러리 안에도 있기 때문이다.

따라서 다음과 같이 SDL_uikitappdelegate.m에서 main을 주석처리하면 해결된다.