アプリ内課金アイテムの情報(SKProduct)を取得する


価格情報はiTunesConnectから取得するのが推奨された方法です。
なので、課金アイテム情報([SKProduct](https://developer.apple.com/library/prerelease/ios/documentation/StoreKit/Reference/SKProduct_Reference/index.html))を取得するProductManagerなるクラスを作りました。
(最近、AppStore価格変更されましたし、ちょっとは役に立つかなと・・)

※実際の課金処理は[こちらの記事](http://qiita.com/sora/items/e65ed31493a2d9b8f1dd)で紹介してます。

``` Swift:ViewController.swift　(使用例)
class ViewController: UIViewController {
    
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        //プロダクト情報取得
        ProductManager.request(productIdentifiers: productIdentifiers,
                               completion: {[weak self] (products: [SKProduct], error: Error?) -> Void in
                                guard error == nil else { return }
                                
                                for product in products {
                                    //価格を抽出
                                    let priceString = product.localizedPrice ?? "--"
                                    /*
                                     TODO: UI更新
                                     
                                     
                                     
                                     
                                     
                                     */
                                }
                                
        })
}
```

※Swift2.0でwarning出たので、、修正しました。。

```Swift:ProductManager.swift
import Foundation
import StoreKit


/// 価格情報取得エラー
public enum ProductManagerError: Error {
    case emptyProductIdentifiers
    case noValidProducts
    case notMatchProductIdentifier
    case skError(messaga: String)
    case unkown
    
    public var localizedDescription: String {
        switch self {
        case .emptyProductIdentifiers:
            return "プロダクトIDが指定されていません。"
        case .noValidProducts:
            return "有効なプロダクトを取得できませんでした。"
        case .notMatchProductIdentifier:
            return "指定したプロダクトIDと取得したプロダクトIDが一致していません。"
        case .skError(let message):
            return message
        default:
            return "不明なエラー"
        }
    }
}


/// 価格情報を取得するためのクラス
final public class ProductManager: NSObject {
    ///保持用
    static private var managers: Set<ProductManager> = Set()
    
    ///完了通知
    public typealias Completion = ([SKProduct], Error?) -> Void
    
    ///完了通知
    public typealias CompletionForSingle = (SKProduct?, Error?) -> Void
    
    ///完了通知用
    private var completion: Completion

    ///価格問い合わせ用オブジェクト(保持用)
    private var productRequest: SKProductsRequest?
    
    ///初期化
    private init(completion: @escaping Completion) {
        self.completion = completion
    }
    
    /// 課金アイテム情報を取得(複数)
    ///
    /// - Parameters:
    ///   - productIdentifiers: プロダクトID配列
    ///   - completion: 課金アイテム情報取得完了時の処理
    class func request(productIdentifiers: [String], completion: @escaping Completion) {
        guard !productIdentifiers.isEmpty else {
            completion([], ProductManagerError.emptyProductIdentifiers)
            return
        }
        
        let productManager = ProductManager(completion: completion)
        let productRequest = SKProductsRequest(productIdentifiers: Set(productIdentifiers))
        productRequest.delegate = productManager
        productRequest.start()
        productManager.productRequest = productRequest
        managers.insert(productManager)
    }
    
    /// 課金アイテム情報を取得(1つ)
    ///
    /// - Parameters:
    ///   - productIdentifier: プロダクトID
    ///   - completion: 課金アイテム情報取得完了時の処理
    class func request(productIdentifier: String, completion: @escaping CompletionForSingle) {
        ProductManager.request(productIdentifiers: [productIdentifier]) { (products, error) in
            guard error == nil else {
                completion(nil, error)
                return
            }
            
            guard let product = products.first else {
                completion(nil, ProductManagerError.noValidProducts)
                return
            }
            
            guard product.productIdentifier == productIdentifier else {
                completion(nil, ProductManagerError.notMatchProductIdentifier)
                return
            }
            
            completion(product, nil)
        }
    }    
}

// MARK: - SKProducts Request Delegate
extension ProductManager: SKProductsRequestDelegate{
    public func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
        let error = !response.products.isEmpty ? nil : ProductManagerError.noValidProducts
        completion(response.products, error)
    }
    
    public func request(_ request: SKRequest, didFailWithError error: Error) {
        completion([], ProductManagerError.skError(messaga: error.localizedDescription))
        ProductManager.managers.remove(self)
    }
    
    public func requestDidFinish(_ request: SKRequest) {
        ProductManager.managers.remove(self)
    }
}


// MARK: - Utility
public extension SKProduct {
    /// 価格
    var localizedPrice: String? {
        let numberFormatter = NumberFormatter()
        numberFormatter.formatterBehavior = .behavior10_4
        numberFormatter.numberStyle = .currency
        numberFormatter.locale = priceLocale
        return numberFormatter.string(from: price)
    }
}
```

ちなみに、[Appleのサンプルコード](https://developer.apple.com/library/ios/samplecode/sc1991/Introduction/Intro.html)(StoreManger)では、

・シングルトン
・取得完了通知はNSNotificationCenter
・SKProductを保持

という実装になってます。(どれもかぶってない・・・)
価格情報を静かに更新するような場合は、NSNotificationCenterでいいかなぁとも思います。が、
価格情報今から読み込みます！読み込み完了しました！的なことをしたい場合は、ブロックスがいいかなぁとも思います。
用途と好みに応じて使い分ければいいかなと＾＾；



※Objective-Cで書いてたのをSwiftで書き直したのですが、、
間違ってたら、、、優しくご指導いただければ幸いです。。