価格情報はiTunesConnectから取得するのが推奨された方法です。
なので、課金アイテム情報(SKProduct)を取得するProductManagerなるクラスを作りました。
(最近、AppStore価格変更されましたし、ちょっとは役に立つかなと・・)
※実際の課金処理はこちらの記事で紹介してます。
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出たので、、修正しました。。
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のサンプルコード(StoreManger)では、
・シングルトン
・取得完了通知はNSNotificationCenter
・SKProductを保持
という実装になってます。(どれもかぶってない・・・)
価格情報を静かに更新するような場合は、NSNotificationCenterでいいかなぁとも思います。が、
価格情報今から読み込みます!読み込み完了しました!的なことをしたい場合は、ブロックスがいいかなぁとも思います。
用途と好みに応じて使い分ければいいかなと^^;
※Objective-Cで書いてたのをSwiftで書き直したのですが、、
間違ってたら、、、優しくご指導いただければ幸いです。。