iOS Firebase Push 사용하기

iOS 2019. 4. 26. 17:58

iOS 로 앱을 만들고, 푸쉬를 보내도 아무런 문제가 없습니다. 하지만, firebase와 통합을 하면, firebase를 통해 안드로이드 iOS까지 모두 문자를 보내 관리할 수 있습니다. 더구나, 유니티로 푸쉬를 보내려면, Firebase로 관리하면 깔끔 하므로, 그렇게 하는것이 좋습니다.

 

[선행작업]

프로비저닝 프로파일 만들기 (푸쉬 인증서 설명도 같이 있음) : https://nicgoon.tistory.com/202 .

 

[공식 문서]

iOS에서 Firebase 클라우드 메시징 클라이언트 앱 설정 : https://firebase.google.com/docs/cloud-messaging/ios/client?authuser=0 .
iOS 앱에서 메시지 수신 : https://firebase.google.com/docs/cloud-messaging/ios/receive?authuser=0 .

 

[주요링크]

파이어 베이스 : http://firebase.google.com/ .

 

 

 

1. xcode를 프로젝트 생성.

프로젝트를 생성하기전 푸쉬 관련 인증서 및 프로비저닝 파일을 모두 만들어 두시기 바랍니다.

먼저 싱글뷰 타입으로 프로젝트를 생성하기 바랍니다.

 

2. Firebase 프로젝트 생성.

파이어 베이스 첫 화면에서 콘솔로 이동 버튼을 눌러 콘솔로 이동합니다.

프로젝트 추가를 눌러 프로젝트를 생성합니다

프로젝트 추가 창이 뜨면, 적당한 값들을 입력하고, 프로젝트 만들기 버튼을 눌러 주도록 합니다.

그럼 프로젝트가 추가 되고 해당 프로젝트가 선택된 상태의 프로젝트 화면이 표시 됩니다.

 

3. 앱생성 및 Firebase SDK 추가.

아래의 프로젝트가 선택된 화면 상태에서, iOS 추가 버튼을 눌러 해당 프로젝트에 iOS앱을 추가하도록 합니다.

앱등록 항목이 뜨면, xcode를 만들때 입력한 iOS와 일치하는 번들 ID를 입력하도록 합니다. 그리고, 앱 등록 버튼을 눌러 주도록 합니다.

그럼 구성파일 다운로드 파일이 나오 데, 파일을 다운로드하고, 설명대로 파일을 추가하도록 합니다. 그리고, 다음 버튼을 누르면 됩니다.

Firebase SDK를 설치하기 위해서는 CocoaPods 파일이 설치되어 있어야 합니다. 코코아 Pods는 단 한줄의 명령으로 설치가 가능하며, Xcode가 설치되어 있어야 아래와 같이 설치할 수 있는 것으로 보입니다.

$ sudo gem install cocoapods

xcode로 만든 프로젝트 폴더로 이동합니다. 그리고, Podfile을 생성합니다. (이미 생성되어 있다면 할 필요 없습니다.) 주의할점은 앞에 sudo를 붙여 권한을 얻으려고 하면 오동작 합니다.

pod init

Podfile을 열고 다음 코드를 추가합니다. 

pod 'Firebase/Core'

그런데 처음 열면 어디에 넣어야할 지 모르실 수 있습니다. 하지만, 애플애들이 이런거 놔둘 애들이 아닙니다. 아래와 같이 코드가 되어 있을 것 입니다. #Pods for firebasepush 주석 아래 넣어 두시기 바랍니다.

파일을 저장하고 다음 명령어를 실행하세요. (권한을 얻기 위해 앞에 sudo를 붙이면 오동작합니다.) 정상적으로 동작을 한다면, 알아서, firebase sdk가 설치됩니다. 앞서 프레임 워크 포드가 설치되는데, 이게 시간을 좀 많이 잡아 먹습니다. (이미 설치되어 있다면 설치하지 않을 수도 있습니다.) 이후 파이어 베이스 SDK가 정상적으로 설치가 완료 됩니다.

pod install

설치가 완료되면, 프레임 워크 라이브러리가 자동으로 설정됩니다. 저는 pod를 처음 사용해 보아, 프레임워크를 추가하려고 갔는데, 자동으로 떡 들어와 있었습니다.

여기서 가장 중요한 것은, 아래 페이지에서, [앱에 사용할 .xcworkspace 파일이 생성됩니다. 향후 애플리케이션의 모든 개발 작업에 이 파일을 사용하세요] 라는 문구 입니다. 그대로 하지 않으면 초기화 코드를 추가할 때, import Firebase 구문을 추가하면, Could not build Objective-C module 'Firebase' 라는 오류를 만납니다. xcode를 종료하고, 프로젝트 폴더에서 새로 생긴 [프로젝트명.xcworkspace] 파일을 더블클릭해 프로젝트를 다시 열여 줍니다. 그리고 아래 이미지 처럼 다음 버튼을 눌러 주도록 합니다.

그럼 파이어 베이스 초기화 코드를 추가하는 코드가 표시 되는데, 추가할 것은 프레임워크 추가 구문과 초기화 메소드 추가 호출 부분뿐입니다. 단, 2줄. 그대로 추가하고, 다음 버튼을 눌러 주도록 합니다. (여기서 오류가 난다면, xcode를 xcworkspace 확장자를 가진 파일을 더블클릭해 실행하지 않아서 일 경우가 많습니다.)

다음을 누르면, 앱이 통신을 했는지 확인하려고 계속 대기 화면이 호출됩니다. 기기와 맥북을 연결후 한 번 실행해 줍니다.

이 화면에서 오래 동안 확인을 못하면, 이전을 눌러 준뒤 다시 다음을 눌러 5번 항목으로와 빌드를 하면, 아래와 같은 화면을 볼 수 있습니다. (몇 번 앱을 빌드하고, 실행하고를 반복하면됩니다. - 한 두번 정도 입니다.). 콘솔로 이동 버튼을 눌러 콘솔로 이동하도록 합니다.

 

4. firebase messaging SDK 추가하기

여기서 부터는 iOS에서 Firebse 클라우드 메시징 클라이언트 앱 설정 ( 링크 ) 이라는 공식 문서를 보고 작성하였습니다. 앞으로 공식문서라 함은 iOS에서 Firebase 클라우드 메시징 클라이언트 앱 설정 문서를 가리킵니다.

지금까지 우리는 Firebase 의 기본을 추가했습니다. 이제는 Cloud Messaging 관련 SDK를 추가하도록 하겠습니다. 공식 문서의 앞 페이지 부분은 이미 구현을 하였고, firebase messaging SDK 추가합니다. 문서 내용을 보시면 알겠지만 앞서 파이어 베이스 SDK를 구성할 때 함께 했어도 문제는 없었을 것이지만, 문서가 나눠져 있으므로, 문서대로 하도록 하겠습니다.

앞서 수정했던, podfile 파일을 열어 다음과 같이 Messaging 을 추가하면됩니다. Core에 Messaging 파일을 추가하도록 합니다.

pod 'Firebase/Core'
pod 'Firebase/Messaging'

그리고, 콘솔을 열어 프로젝트 폴더로 이동한 뒤 다음과 같이 입력해 줍니다. ( 앞에서도 이야기 했지만, sudo 를 통해 권한을 획득하려 하면 오류가 납니다.)

$ pod install

그리고, 파인더에서, 프로젝트명.scworkspace 파일을 더블 클릭해, xcode를 실행하도록 합니다.

 

5. PushKit.framework 등록.

iOS자체 푸쉬는 PushKit 프레임워크를 사용합니다. 이 것만으로 모두, 푸쉬가 구현 가능하며, 많은 분들이 관리상의 이유로 파이어 베이스만 으로, android iOS를 구성합니다. 파이어 베이스 또한 단독으로 푸쉬를 구성한 것은 아니며, PushKit. 프레임 워크를 이용해 푸쉬 기능을 구현했습니다.

그래서 우리 프로젝트에도 Pushkit을 넣어 주어야 정상적으로 푸쉬 기능을 사용할 수 있습니다. 

프로젝트를 선택하고, Capabilities 탭에서, Push를 활성화 합니다.

그리고 Genral 탭을 눌러 프레임 워크를 포함해 주도록 합니다.

 

6. APN 인증키 업로드.

메시지를 보내려면,  APN 인증키와 메시지등을 함께 구글 Apple Push Notification Server로 요청을 해야 합니다. 이 때 메시지와 함께 키를 보내야 하는 데, 이 때사용하는 것이 APN 키 입니다. APN키외에도 인증서를 이용해 메시지를 요청하는 방법이 있지만, Firebase에서는 APN 인증키를 통해 하는 것을 권장하고 있습니다. 저는 2가지 방식을 다 사용해 봤는데, 여러모로 APN 인증키를 사용하는 방법이 편합니다. 

개발자 사이트로 이동해 Certificates, Identifiers & Profile 페이지로 가 Keys All 메뉴를 선택합니다. 해당 페이지가 나오면, + 버튼을 눌러 APN 키 만들기를 시작합니다.

아래의 예시를 참고해 페이지를 작성하고, Continue 버튼을 눌러 주도록 합니다.

다음 페이지는 APNs 관련 설명 이며 키가 만들어 진다는 내용 입니다. Confirm 을 눌러 생성하도록 합니다.

생성하면, 아래와 같은 화면이 나옵니다. Download를 눌러 키를 다운로드 하고, Done 키를 눌러 종료 하도록 합니다.

여기서 다운로드한 파일을 그대로, 파이어 베이스 프로젝트에 셋팅해 주면됩니다. 파이어 베이스 프로젝트 콘솔로가 프로젝트 페이지의 톱니바퀴 버튼을 눌러 뜬 메뉴에서 프로젝트 설정을 선택합니다.

여기서 클라우드 메시징 탭을 선택하고, APN 인증키 업로드 버튼을 눌러 주도록 합니다.

여기서 인증키 업로드창이 뜨는 데, 앞서 내용을 확인해 적절한 값들을 넣어 주고 업로드 버튼을 눌러 키를 업로드합니다.

 

7. 원격 알림등록.

공식 문서에서는 적절한 시점에 원격 알림을 등록하라고 되어 있습니다. 예제에서는 AkppDelegate.swift 파일의 func application 속에 등록하고 있습니다. 소스를 그대로 복사해 해당 위치에 추가합니다.

        if #available(iOS 10.0, *) {
            // For iOS 10 display notification (sent via APNS)
            UNUserNotificationCenter.current().delegate = self
            
            let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
            UNUserNotificationCenter.current().requestAuthorization(
                options: authOptions,
                completionHandler: {_, _ in })
        } else {
            let settings: UIUserNotificationSettings =
                UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
            application.registerUserNotificationSettings(settings)
        }
        
        application.registerForRemoteNotifications()

그리고 파이어 베이스 메시징을 프레임워크를 사용할 것임을 코드에 선언합니다. (파이어 베이스 문서에는 잘못되어 있을 수 있습니다. 저는 이 내용이 빠져 있더군요)

import FirebaseMessaging

여기까지 했다면, UNUserNotificationCenter.current().delegate = self 이 부분에 오류가 났을 것 입니다. 해당 델리게이트를 포함해 필요메소드를 정의해 주면 오류가 사라집니다. AppDelegate.swift 파일 끝에, ( AppDelegate 클래스 밖 )에 아래 소스를 복사합니다.

// [START ios_10_message_handling]
@available(iOS 10, *)
extension AppDelegate : UNUserNotificationCenterDelegate {
    
    // Receive displayed notifications for iOS 10 devices.
    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                willPresent notification: UNNotification,
                                withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        let userInfo = notification.request.content.userInfo
        
        // With swizzling disabled you must let Messaging know about the message, for Analytics
        // Messaging.messaging().appDidReceiveMessage(userInfo)
        // Print message ID.
        if let messageID = userInfo[gcmMessageIDKey] {
            print("Message ID: \(messageID)")
        }
        
        // Print full message.
        print(userInfo)
        
        // Change this to your preferred presentation option
        completionHandler([])
    }
    
    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                didReceive response: UNNotificationResponse,
                                withCompletionHandler completionHandler: @escaping () -> Void) {
        let userInfo = response.notification.request.content.userInfo
        // Print message ID.
        if let messageID = userInfo[gcmMessageIDKey] {
            print("Message ID: \(messageID)")
        }
        
        // Print full message.
        print(userInfo)
        
        completionHandler()
    }
}
// [END ios_10_message_handling]

 

그러면, gcmMessageIDKey 값이 없어 오류가 날 것 입니다. AppDelegate 클래스의 멤버로, let gcmMessageIDKey = "gcm.message_id" 을 추가하면됩니다.

 

8. 메시지 대리자 설정.

앱이 시작되는 부분에 추가하면되며, 예제에는 AppDelegate.swift 파일에 application 메소드 속, FirebaseApp.configure() 메소드 아래에 포함되어 있습니다.

Messaging.messaging().delegate = self

이렇게 추가하면 MessagingDelegate 델리게이트를 상속하고 정의 해 주면됩니다. AppDelegate.swift 파일 속 AppDelegate 클래스 밖(문서 끝)에 아래의 코드를 추가해 주도록 합니다. 이 부분은 토큰을 받고 메시지를 받는 부분 입니다.

extension AppDelegate : MessagingDelegate {
  // [START refresh_token]
  func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) {
    print("Firebase registration token: \(fcmToken)")
    
    let dataDict:[String: String] = ["token": fcmToken]
    NotificationCenter.default.post(name: Notification.Name("FCMToken"), object: nil, userInfo: dataDict)
    // TODO: If necessary send token to application server.
    // Note: This callback is fired at each app startup and whenever a new token is generated.
  }
  // [END refresh_token]
  // [START ios_10_data_message]
  // Receive data messages on iOS 10+ directly from FCM (bypassing APNs) when the app is in the foreground.
  // To enable direct data messages, you can set Messaging.messaging().shouldEstablishDirectChannel to true.
  func messaging(_ messaging: Messaging, didReceive remoteMessage: MessagingRemoteMessage) {
    print("Received data message: \(remoteMessage.appData)")
  }
  // [END ios_10_data_message]
}

 

9. 메시지 받는 부분 추가.

여기서 부터는 iOS 앱에서 메시지 수신 이라는 문서 ( 링크 ) 를 참고 합니다. 

소스는 AppDelegate.swift에 메소드 2개를 추가하는 것 뿐 입니다.

func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
  // If you are receiving a notification message while your app is in the background,
  // this callback will not be fired till the user taps on the notification launching the application.
  // TODO: Handle data of notification

  // With swizzling disabled you must let Messaging know about the message, for Analytics
  // Messaging.messaging().appDidReceiveMessage(userInfo)

  // Print message ID.
  if let messageID = userInfo[gcmMessageIDKey] {
    print("Message ID: \(messageID)")
  }

  // Print full message.
  print(userInfo)
}

func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any],
                 fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
  // If you are receiving a notification message while your app is in the background,
  // this callback will not be fired till the user taps on the notification launching the application.
  // TODO: Handle data of notification

  // With swizzling disabled you must let Messaging know about the message, for Analytics
  // Messaging.messaging().appDidReceiveMessage(userInfo)

  // Print message ID.
  if let messageID = userInfo[gcmMessageIDKey] {
    print("Message ID: \(messageID)")
  }

  // Print full message.
  print(userInfo)

  completionHandler(UIBackgroundFetchResult.newData)
}

 

10. 메시지 보내기.

앱이 꺼져 있을 때는 아이폰의 헤드업 알림으로 표시가 되고, 켜저 있을 때는 내부 메시지 받는 부분에서 처리가됩니다.

파이어 베이스 콘솔에서, Cloud Messaging 버튼을 눌러, 클라우드 메시지 페이지를 열어 주도록 합니다.

그리고, 뜬 알림작성 페이지에, 적당한 문구를 입력하고 다음을 눌러 줍니다.

2번째 페이지가 뜨면, 우리가 만든 앱을 선택해 주고, 검토 버튼을 눌러 줍니다.

검토 화면이 나오면, 게시 버튼을 눌러 메시지를 보냅니다.

 

메시지가 잘 도착했나요? 저는 잘 도착했습니다. 아이폰은 안드로이드와 달리, 푸쉬를 받지 않도록 해 두면, 앱이 켜진 상태에서도 푸쉬 메시지를 받지 못합니다.

'iOS' 카테고리의 다른 글

iOS in-app purchase  (7) 2019.04.14
Push Notification (iOS, client)  (0) 2019.04.13
애플 앱을 기기에서, 실행하기 위한 방법  (0) 2019.04.12
웹 뷰 추가 및 웹통신 (UIWebView 이용)  (1) 2019.04.05
Posted by 창업닉군
,

iOS in-app purchase

iOS 2019. 4. 14. 20:17

iOS 결제 모듈을 사용하려면, Provisioning Profile이 있어야 합니다.

 

선행작업.

프로비저닝 프로파일 및 푸쉬 인증서 생성 : https://nicgoon.tistory.com/202 .

 

공식 문서.

StoreKit : https://developer.apple.com/documentation/storekit .
In-App Purchase : https://developer.apple.com/documentation/storekit/in-app_purchase .

 

참고 문서.

참고 1 : https://medium.com/@bestiosdevelope/implement-in-app-purchase-iap-in-ios-applications-swift-4d1649509599 .
참고 2 : https://devmjun.github.io/archive/purchase-1 . (자세히 설명되어 있음).
참고 영상 2 : https://www.youtube.com/watch?v=o3hJ0rY1NNw .

 

참고 사이트.

Itunes Connect : https://itunesconnect.apple.com/ .
Apple ID : https://appleid.apple.com/

 

1. 테스터 등록.

테스터로 사용할 계정을 위해 메일을 만들고 애플 아이디에 접속해 계정을 생성할 필요가 없습니다. 애플 아이디에 메일을 그냥 입력하면 되며, 심지어는 실제 사용되고 있는 애플 아이디를 넣는 다면, 이미 사용중 이라는 오류가 발생합니다.

먼저 아이툰즈 컨넥트에 접속합니다. 사용자 및 액세스를 클릭 합니다.

사용자 및 액세스 화면이 나오면, Sandbox > 테스터를 선택합니다. 그리고 나온 화면에 + 키를 눌러 주도록 합니다.

그럼 아래와 같이 테스터를 초대 하는 화면이 나올 텐데, 적당히 입력하고 초대 버튼을 눌러 줍니다. 주의 할 점은 실제 사용중인 애플 아이디를 넣으면 오류가 납니다. 자신이 사용하지 않고, 다른 사람도 사용중이 않을 메일 계정을 넣어 테스터 계정으로 사용하도록 합니다.

2. 앱 등록.

테스트 앱을 등록해 주도록 합니다. 저는 번들 아이디가  com.tistory.yeslife.inapp 인 inapp 이라는 앱을 등록할 것 입니다.

먼저 애플 스토어 컨넥트 에서 나의 앱을 선택합니다.

나의 앱 페이지가 나타나면, + 를 눌러 앱을 추가하도록 합니다.

신규앱을 등록하는 폼이 나타나면 아래와 같이 입력해 줍니다. 이름은 앱스토어 내에서 고유해야 합니다. 이미 사용중인 경우, 오류가 납니다. SKU는 앱의 상점을 위한 아이디인데, 적당히 고유한 값을 주면됩니다. 다 입력했으면 생성 버튼을 눌러 주도록 합니다. 

앱 이 생성되고, 앱이 표시 되면, 메뉴의 [앱 내 추가 기능]을 선택해 주도록 합니다.

그럼 앱내 구입 항목을 선택할 수 있는데 여기서 + 버튼 눌러 소모성 상품을 추가하도록 합니다.

앱 내 생성하려는 구입 선택항 목에서, 소모품을 선택하고 생성 버튼을 눌러 주도록 합니다.

그 다음 상품을 넣는 창에서 각각 적당한 값을 넣으면 되지만, 제품 ID는 각 상품마다 고유한 값을 잘 넣어 줘야 합니다.

그럼 등록되었지만, [메타 데이터 누락됨] 이라고 나올 수 있는데, 이 것은 우리가 심사를 위한 데이터를 넣지 않아 생기는 일이고, 테스트를 위한 것은 여기까지 입력하면됩니다.

저는 아래와 같이 3개의 소모성 아이템을 구매 했습니다.

 

앱은 현재 메타 데이터가 누락되어 있는데, 메타데이터를 설정된 뒤, 다음과 같이 앱내 구입으로 선택되어야 합니다.

 

3. 결제 옵저버.

swift에서 옵저버는 멤버 프로퍼티의 값이 변할 때, 알려주는 일종의 콜백 입니다. 결제가 시작될 때, 거절 될때등, 결제 상태를 확인해 이를 처리할 옵저버가 필요합니다.

옵저버는 SKPaymentTransactionObserver 프로토콜(c#의 인터페이스)을 상속해 구현해야 합니다. 코드는 대략 아래와 같습니다.


class StoreObserver: NSObject, SKPaymentTransactionObserver {
    
    
    // 멤버 변수들.
    var purchased = [SKPaymentTransaction]();
    
    
    
    
    override init() {
        super.init()
        
        // 생성자를 위한 초기화 메소드.
        
    }
    
    func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
        
        
        
        for transaction in transactions
        {
            
            switch transaction.transactionState
            {
                
            // 결제가 진행 중인 경우.
            case .purchasing :
                print("결제가 진행되고 있습니다.");
                break;
                
            // 결제 창을 띄우는 데 실패했습니다.
            case .deferred:
                print("아이폰의 잠기는 등의 이유로 결제 창을 띄우지 못했습니다.");
                SKPaymentQueue.default().finishTransaction( transaction );
                break;
                
            // 결제를 성공한 경우.
            case .purchased:
                print("결제를 성공하였습니다.");
                handlePurchased( transaction );
                SKPaymentQueue.default().finishTransaction( transaction );
                break;
                
            // 결제를 실패한 경우.
            case .failed:
                print("결제를 실패하였습니다.");
                SKPaymentQueue.default().finishTransaction( transaction );
                break;
                
            // 결제 검증을 하였습니다.
            case .restored:
                print("상품 검증을 하였습니다.");
                SKPaymentQueue.default().finishTransaction( transaction );
                break;
                
            default :
                print("알수 없는 오류를 만났습니다.")
                SKPaymentQueue.default().finishTransaction( transaction );
                break;
                
            }
            
        }
        
        
        
    }
    
    
    func handlePurchased( _ transaction : SKPaymentTransaction ) {
        
        purchased.append(transaction)


        SKPaymentQueue.default().restoreCompletedTransactions();

        
        print( "영수증 주소 : \(Bundle.main.appStoreReceiptURL)" );
        
        
        let receiptData = NSData( contentsOf: Bundle.main.appStoreReceiptURL! );
        print(receiptData)
        
        let receiptString = receiptData!.base64EncodedString(options: NSData.Base64EncodingOptions());
        
        
        print ( "구매 성공 트랜젝션 아이디 : \(transaction.transactionIdentifier!)" );
        print ( "상품 아이디 : \(transaction.payment.productIdentifier)" );
        print ( "구매 영수증 : \(receiptString)" );
     
        // 결제를 마무리 하도록 합니다.
        SKPaymentQueue.default().finishTransaction( transaction );


        
    }
    
    
}

대게의 코드는 공식 문서를 통해 쉽게 알 수 있지만 중요한 부분은 영수증을 받는 부분 입니다. 이 부분은 크게 언급이 공식 문서에 없어, 애를 먹었는데, Bundle.main.appStoreReceiptURL 값에 영수증 파일명이 들어 있습니다. 이것을 NSData를 이용해 값을 가지고 오고, Base64  인코딩을 해 사용하면됩니다.

결제를 할 때마다 transaction 이 생성되는 데, 실패하던, 걸절하던, 꼭 finishTransaction 처리를 해 줘야 합니다. 그렇지 않으면, 껐다 키면 다시 트랜젝션값들이 계속 추가 됩니다. 이 것은 앱이 비정상적으로 종료되었을 때도, 처리를 하기 위함으로 보입니다.

 

4. 상품 리스트 가지고 오기 및 구매 처리.

결제는 대체로 뷰에서 일어 나므로, viewController에서 처리하는 것이 좋습니다.

처음 상품 리스트를 가지고 오는 부분이 있습니다. 이때, 위에서 등록한 상품의 ID들을 적어 주면됩니다. 코드는 아래와 같습니다.

import UIKit
import StoreKit

class ViewController: UIViewController, SKProductsRequestDelegate {
    
    
    // 멤버 변수들.
    let iapObserver = StoreObserver()
    var productRequest : SKProductsRequest?
    
    // 입력 받은 상풉 정보들을 저장하는 변수들.
    var validProductArray = [SKProduct]();
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
        // 페이먼트 관련 설정을 해 주도록 합니다.
        SKPaymentQueue.default().add(iapObserver)
        
        // 상품 정보를 가지고 오는 리퀘스트를 설정하도록 합니다.
        let pIDs = Set(["gold500","gold100"]);
        productRequest = SKProductsRequest( productIdentifiers: pIDs );
        productRequest!.delegate = self;
        
        // 앱스토어 상품 정보를 요청합니다.
        productRequest!.start();
        
        
    }

    
    // 구매 진행 가능 여부를 확인합니다.
    @IBAction func checkPaymentAble(_ sender: Any) {
        
        if( SKPaymentQueue.canMakePayments() )
        {
            print("결제 요청이 가능합니다.");
        }else
        {
            print("결제 요청이 불가능합니다.");
        }
        
    }
    
    @IBAction func tryPaymentQueue(_ sender: Any) {
        
        print( "골드 100개 결제 할거에요." )
        
        let payment = SKMutablePayment( product: validProductArray[0] );
        
        SKPaymentQueue.default().add(payment)
        
        print( "변화가 있나요?" )
        
    }
    
    
    // 상품 정보를 받은 후 처리 메소드 입니다.
    func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
        
        
        print("상품갯수 \(response.products.count)")
        print("확인하지 못한 상품 갯수 \(response.invalidProductIdentifiers.count)")
        
        
        for product in response.products
        {
            print("이름:\(product.localizedTitle)\n가격:\(product.price)")
            validProductArray.append(product)
        }
        
        
        
        
        
    }
    
    
}

 

이 것만으로 결제는 코드는 끝 입니다. 실제 결제를 해 볼때는 아이폰에서, 애플 아이디를 로그아웃하는 것이 좋습니다.

 

5. 검증 서버. (php)

원래 node js만 구현하려고 했으나, 잘 되지 않아 php로 먼저 결제 서버를 구현하였고, 그것을 바탕으로 node.js를 구현하였습니다.

애플 결제는 서버로 검증하는 것이 100배 쉽습니다. 그냥 POST 로 영수증을 Json형태로 보내면 끝 입니다. 받은 값에, 결제 날자 구매 수량 등이 있으므로, 이 것을 비교해 앱의 정보를 확인하면 되겠습니다. 아래는 샌드박스 주소로 되어 있습니다. 실제 결제 주소는 https://developer.apple.com/library/archive/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html#//apple_ref/doc/uid/TP40010573-CH104-SW2 에서 확인할 수 있습니다.

                // 영수증.
                $receipt = "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";
                $addr = "https://sandbox.itunes.apple.com/verifyReceipt";
                $host = "sandbox.itunes.apple.com";
                $path = "/verifyReceipt";

                // 보낼 json 데이터를 만들어 주도록 합니다.
                $bodyJson = '{ "receipt-data" : "' . $receipt . '" }';


                // 요청을 위한 주소를 만들어 주도록 합니다.
                $endline = "\r\n";
                $req = "";


                // 주소 및 타입 관련 헤더를 추가합니다.
                $req = "POST {$path} HTTP/1.1" . $endline
                . "Host: {$host}" . $endline
                . "Content-Type: application/json" . $endline
                ;

                // 데이터를 보내기 위한 기본 헤더를 추가합니다.
                $req .= "Content-Length: " . strlen($bodyJson) . $endline
                . "Connection: Close" . $endline
                ;

                // 헤더의 끝을 표시하는 빈 문자열을 설정합니다.
                $req .= $endline;
                
                // 보낼 내용을 추가합니다.
                $req .= $bodyJson;
                ;




                // 영수증 서버에 접속을 시도합니다.
                $fsock = @fsockopen( "ssl://{$host}", 443 );

                // 영수증 서버에 접속을 실패한 경우 처리 입니다.
                if( !$fsock )
                {
                    echo "구글 서버에 접속 실패하였습니다.!";
                    exit;
                }
                
                // 데이터를 보내도록 합니다.
                fwrite( $fsock, $req );


                // 데이터 받기를 위해 필요한 값들을 선언합니다.
                $headPassed = false;
                $resDatas = "";


                // 데이터 받기가 완료될 때 까지 대기하면 데이터를 받아 출력합니다.
                while( !feof($fsock) )
                {

                    // 한 줄 라인을 가지고 옵니다.
                    $line = fgets($fsock, 128);

                    // 아직 헤더는 아니지만, 헤더의 끝을 만난 경우, 헤더가 끝났음을 마킹하고, 종료합니다.
                    if( $line == "\r\n" && !$headPassed )
                    {
                        $headPassed = true;
                        continue;
                    }

                    // 헤더가 아닌 경우만, 값을 출력하도록 합니다.
                    if( $headPassed )
                    {
                        $resDatas .= $line;
                    }

                }

                // 연결을 닫아 주도록 합니다.
                fclose( $fsock );



                // JSON 데이터를 표시하도록 합니다.
                echo "받은 데이터 : " . $resDatas;


                echo "보낸 데이터 :" . $req;

 

6. 검증서버 (node.js)

알고리즘 및 구현은 php와 동일하므로, 설명을 생략하겠습니다.

const request_json = require("request-json");



// 인앱 서버 주소.
const serverAddr = "https://sandbox.itunes.apple.com/verifyReceipt";
// const serverAddr = "https://buy.itunes.apple.com/verifyReceipt";

// 구매 영수증 주소.
const receipt = "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";



const host = "https://sandbox.itunes.apple.com";
const path = "/verifyReceipt";



const receiptJson = {
    'receipt-data': receipt
};



var client = request_json.createClient( host );
client.post( path, receiptJson, function( err, res, body ){

    if( err )
    {
        console.error( err );
        return;
    }

    console.log( JSON.stringify( body ) );

});

 

7. Restore

앱을 지우거나 했을 때, 기존의 결제 내역을 복구하는 기능입니다. 저 같은 경우 서버에서, 결제 검증을 하고, 데이터를 가지고 있을 것 이므로, 필요하지 않아 구현하지 않았습니다.

 

 

 

Posted by 창업닉군
,

Push Notification (일명 : 푸쉬)는 서버에서 사용자에게 일방적으로 메시지를 보내는 기능 입니다. 주로, 운영자가 특정 소식을 전할 때 사용됩니다. 사인할 프로비저닝 파일이 필요하며, App id에는 Push를 사용할 것으로 설정이되어 있어야 합니다. (앱 아이디 수정도 가능합니다.)

 

선행작업.

프로비저닝 프로파일 만들기 (푸쉬 인증서 설명도 같이 있음) : https://nicgoon.tistory.com/202 (숙지하셔야 합니다.)

 

공식문서

유저 노티피케이션 : https://developer.apple.com/documentation/usernotifications ( 이 문서에 스텝이 있습니다.).
푸쉬 킷 : https://developer.apple.com/documentation/pushkit ( 함께 사용되지만, 참고만 있습니다. )

 

참고 문서

예제1: https://faith-developer.tistory.com/153 (끝부분만 보시면됩니다.).
예제2: http://monibu1548.github.io/2018/05/29/push-cert/ ( 역시 끝 부부만 보시면됩니다.)

 

1. 준비.

Xcode 프로젝트를 생성합니다. 위의 [프로비저닝 프로파일 만들기], 를 참고해, 프로젝트를 듭니다. 저는 [com.tistory.yeslife.hello] 라는 번들 아이디로 프로젝트를 만들었습니다. 이 페이지에서 참고한 기준 문서는 유저 노티피케이션 입니다. 아래 Topics 부분을 보면, 순서로 나열된 것이 있습니다.

 

2. 프레임 워크 추가.

프로젝트 > Build Phases 탭을 차례로 선택하고, Link Binary With Libraries를 확장 시킵니다.

+ 버튼을 눌러, UserNotifications.framework 와 PushKit.framework를 추가해 줍니다.

 

3. Capabilities 탭을 선택해 푸쉬를 활성화 해 줍니다.

프로젝트 > Capabilities 탭을 차례로 선택해 줍니다. 그리고, 페이지를 스크롤해 Push Notifications를 활성화 해 줍니다. (Provisioning Profile이 서정되어 있지 않다면, 활성화 할 수 없습니다.).

 

4. 사용자에게 알림을 사용할 것인지 묻기.

UserNotifications 문서 토픽에 보면, 첫 스텝으로 Asking Permission to Use Notifications 문서 ( 링크 ) 가 있습니다. 그 문서를 참고해 사용할 것인지 묻는 코드를 작성하였습니다.

 

1) 앱 델리 게이트 부분에 사용자 물음 추가. (Application 메소드 속에 넣으면 좋습니다.).

// 유저 노티피 케이션 센터를 가지고 옵니다.
let center = UNUserNotificationCenter.current()

// 사용 여부를 묻습니다.
center.requestAuthorization(options: [.alert, .sound])
{ (granted, error) in

    // 승인 받았는지 여부를 여기서 확인할 수 있습니다.
    guard granted else {
        print( "유저가 푸쉬 알림을 허락하지 않았습니다." )
        return
    }

    print( "유저가 푸쉬 알림을 허락하였습니다." )

}

 

5. 장치를 푸쉬 서버에 등록.

유저가 푸쉬를 허락한 경우, 장치를 APNs에 등록하고, 등록된 키 값을 받아 오는 부분 입니다. 공식 문서는 Registering Your App with APNs 부분을 확인하시면 되겠습니다. 

1) 위에서 푸쉬 알림 허락한 부분에 등록을 요청하는 코드를 등록 합니다.

주의 할 점은, 응답이 왔을 때 메인 쓰레드 (UI)와 동기화 처리가 필요하므로, DispatchQueue.main.async 메소드 속에서 호출해야 합니다.

위에서 작성했던 코드가 아래와 같이 됩니다.

let notificationCenter = UNUserNotificationCenter.current()
        
        notificationCenter.getNotificationSettings
        { (settings) in
            
            // Do not schedule notifications if not authorized.
            let center = UNUserNotificationCenter.current()
            
            // Request permission to display alerts and play sounds.
            center.requestAuthorization(options: [.alert, .sound])
            { (granted, error) in
                
                
                // 유저가 푸쉬를 사용하지 않겠다고 한 경우, 여기서 처리합니다.
                guard granted else {
                    print("유저가 푸쉬 알림을 승인하지 않았습니다.")
                    return
                }
                
                
                // 푸쉬를 처리한 경우, 여기서 처리 합니다.
                print("유저가 푸쉬를 승인하였습니다.")
                
                
                // 메인 쓰레드 UI와 Thread 동기화 처리 될 수 있도록, DispatchQueue.main.async 메소드로 감싸 줍니다.
                DispatchQueue.main.async {
                    
                    // APNs 에 스마트폰을 등록하는 메소드 입니다. (네트워크)
                    UIApplication.shared.registerForRemoteNotifications()
                    
                }
                
                
            }
        }

 

2) APNs로 부터 응답처리.

2개의 메소드 모두 AppDelegate 클래스에 추가하도록 합니다.

// APNs 서버에 등록한 경우 표시.
func application(_ application: UIApplication,
                 didRegisterForRemoteNotificationsWithDeviceToken
    deviceToken: Data) {

    // 토큰 값을 가지고 옵니다.
    let deviceTokenString = deviceToken.reduce("", {$0 + String(format: "%02X", $1)})

    // 콘솔에 토큰 값을 표시해 줍니다.
    print("APNs device token: \(deviceTokenString)")


}



// APNs 서버에 등록하지 못한 경우, 오류를 표시.
func application(_ application: UIApplication,
                 didFailToRegisterForRemoteNotificationsWithError
    error: Error) {

    // Try again later.


}

 

6 클라이언트 검증해 보기.

일단 클라이언트 부분의 푸쉬 부분은 끝났지만, 이게 잘 동작하는 지 확인할 필요가 있습니다. 서버까지 구축 후 푸쉬를 보내어 보았는데, 안되면, 과연 서버 문제인지, 클라이언트 문제인지 파악하기 힘들 수 있습니다. 그래서 다른 사람들이 구축해 놓은 서버 프로그램으로 푸쉬가 잘되는지 확인할 수 있습니다. 우리는 http://apns-gcm.bryantan.info 라는 사이트를 이용할 것 입니다.

당연한 이야기 일 수 있습니다만, 앱이 종료된 상태여야 푸쉬가 옵니다. 앱이 켜진 상태에서는 푸쉬를 보내도 푸쉬가 오지 않습니다.!!

그리고, 이 것을 테스트 하기 위해서는 푸쉬 인증서가 필요하며, 위의 인증서 설명 링크를 통해 인증서를 만들 도록 하시기 바랍니다.

먼저 pans-gcm ( http://apns-gcm.bryantan.info ) 에 접속 합니다. 아래와 같은 화면이 나오면, 각 값을 입력해 줍니다.

Device Token에는 기기를 APNs에 등록하고, 받아 왔던 토큰, Message는 보낼 메시지를 넣어주세요. Pem File은 인증서로 만들었던, 파일을 넣어주세요. Environment는 Sandbox를 선택해 줍니다. 그리고 Submit 버튼을 누르면, 메시지가 보내어 집니다.

 

원래 계획은 이곳에 서버 제작도 넣으려고 했으나, 따로 분리하는 것이 좋겠다 생각되어, 클라이언트 제작 내용만 남깁니다. 이 후 서버 제작 부분도 페이지를 만들도록 하겠습니다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

'iOS' 카테고리의 다른 글

iOS Firebase Push 사용하기  (3) 2019.04.26
iOS in-app purchase  (7) 2019.04.14
애플 앱을 기기에서, 실행하기 위한 방법  (0) 2019.04.12
웹 뷰 추가 및 웹통신 (UIWebView 이용)  (1) 2019.04.05
Posted by 창업닉군
,

오랜만에 애플을 개발할 일이 있어, 애플 계정을 사용하게 되었는데 한글로 잘 번역이 되어 있어 매우 놀랐습니다. 뭐 사용법을 모두 아는 터라, 필요는 없었지만, 미처 알지 못한 부분에 대해서 확인을 할 수 있어 유익했습니다.

원래 이 글을 남기기전, 영어 문서로 되어 잇을 것 같아 디테일하게 글을 쓰려고 했지만, 문서가 한글로되어 있어서, 그냥 개요정도만을 표시할 것 같습니다.

맥의 설명서를 보려면, 애플 개발자 프로그램을 활성한 유료 개발자만 가능합니다.

 

1. Developer ID 인증서 생성하기.

앱을 개발할수 있는 개발 ID를 생성하는 과정이며, 자신의 맥에서 생성한, CSR 파일을 애플 개발자 사이트에 등록함으로써 생성합니다. 즉 등록한 기기에서만 앱을 개발할 수 있습니다.

 

1) 키체인 접근에서 CSR (Certificate Singing Rquest) 을 생성하기.

하단 독에서 라운처 패드를 열어 줍니다.

기타 아이콘들이 들어 있는 폴더를 선택해 줍니다.

그리고 호출 된 창에서, 케체인을 선택합니다.

키체인 창이 뜨면, 상단 드롭다운 메뉴에서, 키체인 접근 > 인증서 지원 > 인증 기관에서 인증서 요청을 선택합니다.

인증 지원 창이 뜨면, 먼저 디스크에 저장됨을 선택하고, 사용자 이메일 주소일반 이름을 입력하고 계속을 눌러 줍니다.

그러면, 파일 저장위치가 선택되고 CSR 파일이 만들어지게 됩니다.

 

2) CSR 업로드.

애플 개발자 사이트 ( Developer.apple.com)에서, Account로 들어와 왼쪽 메뉴에서 [Certificates, IDs & Profiles] 를 선택해 줍니다.

화면이 바뀌면, Development항목을 선택하고, 오른쪽 화면 상단의 + 버튼을 눌러 줍니다.

어떤 인증서 타입이 필요하냐고 묻는 창이 뜨면, iOS App Development를 선택하고 스크롤를 쭉 내려, 

CSR 생성 화면 입니다. 우리는 이미 앞서 생성했으므로, 컨티뉴를 선택해 다음으로 넘어 갑니다.

다음 창이 뜨면, CSR 파일(앞서 생성했던)을 업로드하고, 다음 버튼이 활성화 되면, 눌러 다음으로 넘어 갑니다.

그러면 아래와 같이 개발 인증서가 완성됩니다. 언제든지 다운로드 받을 수 있습니다. 다운로드를 눌러 파일을 다운로드 하도록 합니다.

아래와 같이 인증서가 다운로드 되었을 것 입니다. (파일 이름은 적당히 바꿔도 됩니다.). 이 것을 더블 클릭해 키체인에 등록해야 합니다.

 

2. 앱 아이디 등록.

앱 아이디는 어떤 앱을 자신이 만들것인지 등록하는 과정입니다. 모든 앱은 기기에 테스트하거나, 애플스토어에 등록하거나 할 때, 이 아이디가 필요합니다. 앱 아이디는 2종류가 있습니다. 명시적인 앱아이디와 와일드 카드 앱 아이디가 있습니다. 와일드 카드는 테스트등 앱의 번들 아이디가 수시로 바뀌는 환경에서 좋습니다. 명시적인 앱 아이디는 앱을 배포할 때 사용하는 것이 좋습니다.

1) 앱아이디 항목 선택.

왼쪽 메뉴에서 App IDs를 선택하고, 오른쪽 상단의 + 버튼을 눌러 줍니다.

 

2) 앱 아이디 등록.

App ID Description 은 앱에 대한 설명인데, 우리가 인식하기 좋은 값들을 넣어 줍니다.

App ID Suffix 항목에서는 명시적 아이디( Explicit App ID )를 선택하고 번들 아이디를 넣어 줍니다. 우리가 앞으로 생성할 프로젝트의 번들 아이디와 일치해야 합니다.

App Service  항목은 이 앱이 사용할 서비스를 선택할 수 있습니다. 당연한 이야기지만, 여기서 선택하지 않은 서비스는 앱에서구현할 수 없습니다. Game Center와 In-App Purchase는 이미 자동으로 선택이되어 있습니다. 여기서 푸쉬 정도만 추가로 선택하고, Continue를 눌러 다음으로 넘어 갑니다.

 

3) 앱 아이디 확인 및 등록.

그럼 아래와 같이 입력 사항을 확인하는 창이 듭니다. 별 이상이 없다면, 아래 Register 버튼을 눌러 줍니다.

그럼 앱 아이디 등록이 완료 됩니다.

 

3. 장치 등록.

위에서 앱 등록하는 과정이 어떤 앱을 만들것인지 등록하는 과정이 였다면, 장치는 어떤 장치에서 사용할것인지를 등록하는 과정입니다. 개발 단계외 출시전 테스트를 위한 애드 훅을 배포할 때는 이 장치 등록이 필요합니다.

 

1) IPHONE UDID 확인하기.

일단 아이튠즈를 실행 합니다. (아이폰을 꽂으면 자동을 실행되었던 것 같은데, 일단 켜두는 것이 좋을 것 같습니다.)

아이폰을 맥에 연결합니다. 그러면, IPhone 정보 연결 허용을 묻는 창이 뜨는 데 계속을 눌러 줍니다. 

그리고 아래 창이 뜨면, 아이폰을 확인(잠겨 있다면, 잠겨 있는 것을 풀어 줍니다.), 이 컴퓨터를 신뢰하시겠습니까? 라는 다이얼로그가 떠 있다면, 신뢰 버튼을 눌러 줍니다. (암호가 있다면, 암호도 눌러 주도록 합니다.).

그럼 아래 이미지와 같이 기기가 추가 되고, 기기를 선택해 줍니다.

그럼 창이 뜨고, 아래 부분을 확인할 수 있을 것 입니다. 일련 번호 부분을 몇 번 누르다 보면,  UDID를 확인할 수 있습니다.

아래와 같이 UDID가 표시되면 마우스 오른쪽 버튼을 눌러 복사하기를 선택하기를 눌러 주면, UDID가 복사 상태가 됩니다. 메모장에 붙여 넣거나 해 둡니다.

 

2) 장치 추가

아래와 같이 Devices > All을 선택하고, 나타난 화면 오른쪽 상단의  +. 버튼을 눌러 줍니다.

장치 정보를 입력하는 창이 뜨면, 이름에 작당한 값을 넣고, UDID는 조금전 확인했던 값을 넣어 주도록 합니다. 그리고, 다음 버튼을 눌러 줍니다.

마지막으로 장치를 검토 및 등록 창이 나오면, 문제가 없다면, 등록 버튼을 눌러 등록을 완료해 주도록 합니다.

 

4. 개발용 프로비전닝 프로파일 만들기.

마지막으로 개발용 프로비저닝 프로파일을 만들어 줍니다. 이 파일을 이용해 앱을 사인해 실제 기기에서 앱을 실행해 볼수 있습니다. 개발용 프로비저닝 프로파일을 선택해 창이 뜨면, + 버튼을 눌러 생성 페이지를 불러 주도록 합니다.

여기서 우리는 iOS용을 만들것 이므로, iOS App Development를 선택해 주도록 합니다. 당연한 이야기지만, 배포할 때는 Distbution 섹션의 App Store 를 선택하면 됩니다.

앱 아이디를 선택하는 창이 뜨면, 앞서 만들었던 앱 아이디를 선택해 주면됩니다.

앞서 만들었던 개발 인증서를 선택해 줍니다. 여러개 선택해도 되지만 우리는 하나만 만들었으므로, 하나만 표시됩니다. (이 인증서가 등록된 기기에서만 이 프로비저닝 프로파일을 이용해 사인을 할 수 있습니다.).

기기 등록창이 나타나면, 앞서 등록했던 기기를 선택해 주면 됩니다. (여러개 선택해 줄 수 있습니다.).

마지막으로 프로 비저닝 파일의 이름을 입력해 주는 창입니다. 적당히 입력하고 컨티뉴를 선택해 줍니다.

그럼 프로비저닝 프로파일이 생성완료되고, 다운로드 와 설치를 하면됩니다.

 

5. Push Notification 인증서 만들기. (옵션).

1) 인증서 생성.

이 작업은 하지 않아도 앱을 생성하거나 하는데 문제는 없습니다. 하지만, Push Notification은 거의 모든 앱에 사용되는 기능이 므로, 꼭 만들어 두기 바랍니다. 

인증서 관리 페이지에서, 좌측메뉴 개발자 인증서를 선택하고, + 버튼을 눌러, 인증서를 추가하도록 합니다. (여기서는 개발용으로 만들지만, 푸쉬는 Production으로 만들어도, 개발과 배포에서 모두 사용이 가능합니다.)

개발용 Push Notification 파일을 선택하고, Continue 버튼을 눌러 주도록 합니다.

앱 선택 페이지에서 적당한 앱을 선택하도록 합니다.

다음 CSR File 업로드 설명 페이지가 나오는 데, 앞서 개발 인증서를 만들 때 사용하였으므로, 그 파일을 이용할 것 이므로, 그냥 Continue를 눌러 다음으로 넘어 갑니다.

CSR File 에서 파일을 앞서 만들었던 CSR File을 선택하고 Continue 파일을 눌러 주도록 합니다.

다운로드 페이지가 나오면, 인증서를 다운로드하고, 다운로드된 파일을 적당한 이름으로 바꿔 줍니다.

다운로드된 인증서를 더블 클릭하면, 인증서가 등록되는 데 다음과 같은 창이 뜰 것 입니다. 키체인을 로그인 으로 두고 추가 버튼을 눌러 줍니다.

 

2) APNs 파일 생성

등록했던 인증서에서 인증서와 키의 파일을 빼내는 작업이 필요합니다.

먼저, APs 인증서를 내보내기 해 p12 파일을 만들어 주도록 합니다. 키 인증서를 열고 키체인은 로그인, 카테고리는 인증서를 각각 선택해 줍니다.

개발용 푸쉬 인증서를 마우스 오른쪽 버튼으로 선택해 나온 메뉴에서, 내보내기를 선택합니다.

저장 창이 뜨면 적당한 이름을 입력하고 저장을 눌러 주도록 합니다.

그리고 다음 암호 입력창이 뜨는데, 적당한 값으로 입력합니다. 확인을 누르면, 맥 암호를 물을 텐데 입력 하면, 인증서에 대한 p12 파일이 만들이 집니다.

이 번에는 인증서의 키를 내 보내 도록 하겠습니다. (키 입니다. 인증서를 한 번더 선택하지 않도록 합니다.). APs 인증서의 앞쪽의 삼각형 눌러 키가 나열 되도록 합니다.

키가 나열 되면, 앞서 했던 작업과 같이 키를 마우스 오른쪽 버튼으로 선택하고 내보내기를 선택 합니다.

입력 창이 뜨면 적당한 파일 이름을 선택하고 저장 버튼을 눌러 주도록 합니다.

패스워드를 입력하고, 확인 버튼을 눌러 줍니다. 그리고 나온 맥 암호 입력 창에 암호를 입력하면, 키에 대한 p12 파일이 만들어 집니다.

자 이렇게 2개의 인증서가 만들어 지면 성공 입니다.

터미널을 열고 인증서를 pem 파일로 만들어 줍니다. (패스워드를 입력하라고 하는데, 내보내기 할 때 입력했던 패스워드를 입력해 주면됩니다.), 틀려도 파일은 생성되었지만, 과연 사용할 수 있을 런지는 모르겠습니다.

openssl pkcs12 -clcerts -nokeys -out [생성될 파일명] -in [p12 확장자 파일명]

저는 아래와 같이 입력했습니다.

openssl pkcs12 -clcerts -nokeys -out yeslife_ap_cer.pem -in yeslife_ap_cer.p12

 

이번에는 키를 pem 파일로 만들겠지만, 명령이 약간 틀립니다. 주의해서 입력해 주세요.

openssl pkcs12 -nocerts -out [생성될 파일명] -in [p12 파일]

저는 아래와 같이 입력했습니다. 그리고, 내보내기 할 때, 입력했던, 비밀 번호를 입력하면, Pem 패스워드를 새로 만들어 줍니다.

openssl pkcs12 -clcerts -out yeslife_ap_key.pem -in yeslife_ap_key.p12

 

이번에는 만들어진 key의 파일을 암호를 해재 해 줍니다. key의 pem 파일을 만들며 새로 입력한 패스워드를 입력해 주도록 합니다.

openssl rsa -in [키의 pem 파일] -out [새로 만들어질 암호가 해재된 pem 파일]

저는 아래와 같이 입력했습니다.

openssl rsa -in yeslife_ap_key.pem -out yeslife_ap_key.unencrypted.pem

 

이제 인증서의 pem 파일과 암호가 해제된 키 파일을 하나로 연결해 APNs에서 사용하는 인증서를 만들어 주도록 합니다.

cat [푸쉬 인증서 pem 파일] [압호가 해제된 푸쉬 키 pem 파일] > [만들어질 파일명]

저는 아래와 같이 입력했습니다.

cat yeslife_ap_cer.pem yeslife_ap_key.unencrypted.pem > yeslife_apns.pem

 

 

6. xcode를 통해 앱 생성하기.

프로젝트를 생성할 때 아래와 같이, Bundle Identifier 를 등록했던 앱 아이디와 맞춰 줍니다.

프로젝트가 생성되면, 스토리 보드에서 다음과 같이, HELLO를 입력합니다. (원한다면, 이 상황에서 시뮬레이터를 통해 앱을 실행해 봐도 상관이 없습니다.)

아이폰을 맥에 꽂아 줍니다. 그리고 빌드 타겟을 실제 기기로 선택해 줍니다.

그리고, 프로젝트를 선택해 빌드 셋팅 탭을 눌러 메뉴들이 표시되게 합니다.

그리고, 디버그 사이닝을 Manual로 바꿔 줍니다.

그리고 제너럴 탭으로 가, Provisioning Profile을 Import로 두고, 앞서 만들고 다운 받았던, Provisioning 파일을 선택합니다.

그리고, Singing Cervificate 가 None으로 되어 있을 수 있는데, 앞서 생성한 개발 인증서가 등록되지 않아서 일 수 있습니다. 앞서 다운로드 받았던 인증서를 더블 클릭해 주도록 합니다. (문제가 없다면, 넘어 가면됩니다.).

그리고, 런 버튼을 눌러 주면, 장치에서 잘 실행됩니다.

마약 로그인 암호를 계속 묻는 다면, 화면 상단의 풀다운 메뉴에서 Xcode > Preference 에 Account에 계정을 들록하지 않아서 일수 있습니다. 인증서 생성시 입력했던 이메일을 입력해 주세요.

그리고, 앱 Failed with exit code 1 오류가 계속 난다면, 키체인의 인증서를 두번 눌러 뜬창에서, 인증서를 눌러, 신뢰를 표시한뒤, 모두 항상 신뢰를 눌러 줍니다.

 

이렇게 하고 키체인에서, 상단 풀다운 메뉴의 파일 > 모든 키체인 잠금을 해야 패스워드를 다시 물어 본다.

그리고, 이번에는, 패스워드를 물어 볼때, 첫 번째는 입력하고 확인, 두번째 패스워드 뜰때는 항상 허용을 눌러 주도록 하자.

 

>> 트러블 슈팅 1. 기기가 잠겨 있는 동안에는 개발을 사용할 수 없습니다.

맥에 아이폰을 연결할 때, 뭔가를 하고 있어서, 신뢰 버튼을 눌러주지 못한 경우, 발생. 아이폰의 설정 > 일반 > 재설정 > 위치 및 개인 정보 보호 재설정을 누른 뒤 다시 선을 뺐다ㅗ 꽂으면, 신뢰하시겠습니까? 라고 뜨고, 신뢰를 누르면 된다.
https://babysunmoon.tistory.com/entry/Xcode-빌드-오류-Development-cannot-be-enabled-while-your-device-is-locked (설명)

 

 

 

 

 

'iOS' 카테고리의 다른 글

iOS Firebase Push 사용하기  (3) 2019.04.26
iOS in-app purchase  (7) 2019.04.14
Push Notification (iOS, client)  (0) 2019.04.13
웹 뷰 추가 및 웹통신 (UIWebView 이용)  (1) 2019.04.05
Posted by 창업닉군
,

이 시점은 UIWebView가 deprecated 되는 상황이다. 하지만, 이 리뷰를 남기는 것은, 개발자 결제를 하지 않고, 개발을 하려고하니, WebKit   프레임 워크를 사용할 수 없었던 것 같다. 프로젝트 일정은 다가오고, 개발자 계정은 클라이언트님과 일정이 맞지 않아 개발을 먼저 하려고 하니, 사용해야 했다. 다분히 내 메모 용임을 밝혀 둔다.

 

.. 참고 자료.

https://usinuniverse.github.io/2018/06/18/uiwebview.html

 

1. 웹뷰 추가.

사실 webview는 코드가 네 줄에, 컨트롤에 웹뷰를 만들고, 아웃렛만 연결하면 거의 끝난다.

class ViewController: UIViewController {    

    @IBOutlet var wv: UIWebView!

    override func viewDidLoad() {

        super.viewDidLoad()

        // Do any additional setup after loading the view.

        

        let url = URL(string:"http://naver.com")

        let req = URLRequest( url: url! )

        wv.loadRequest(req)        

    }

}

 

2. 웹뷰에서 자바스크립트 호출하기.

이 것은 매우 간단하다. 아래 코드를 버튼이나 필요한 시점에 넣으면 실행이 된다.

self.wv.stringByEvaluatingJavaScript(from: "alert('ok?');");

 

3. javascript에서 웹앱(switf 코드 호출하기).

스키마를 사용해서, 앱에 데이터를 전달하는 수단으로 스키마를 사용하고 있다. 스키마는 앱간의 데이터 전송이나, 앱을 동작시키는 기능까지 하는 방법이다. 따라서 OS레벨까지 동작할 가능성이 크다. 응답도 느리고, 호출순서와 응답순서가 다를 수 있다. 호출을 순차적으로 하나씩 해야 한다.

   1) info.plist 에 스키마 등록
      아래와 같이 URL 스키마를 등록하도록 하자. (해당 스키마로 앱이 켜지며 호출된다.).

 

   2) 스키마를 받아 처리하는 코드.

AppDelegate 파일에 아래와 같은 메소드를 추가합니다. 이미 같은 application 메소드가 정의되어 있지만, iOS는 같은 이름이라도, 응답에 사용되는 파라메타가 틀리면 다른 메소드로 인식하기 때문에, 긴장하지 않아도 된다.

    func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {        

        if url.host == "doSomething" {

            print("do something")

        }

        return true

    }

 

 

3) html에서 스키마를 불러 주는 메소드.

document.location = "testwebapp://doSomething";

 

* 매우 간단하다.

 

 

 

 

 

 

 

'iOS' 카테고리의 다른 글

iOS Firebase Push 사용하기  (3) 2019.04.26
iOS in-app purchase  (7) 2019.04.14
Push Notification (iOS, client)  (0) 2019.04.13
애플 앱을 기기에서, 실행하기 위한 방법  (0) 2019.04.12
Posted by 창업닉군
,