From 20b4f6c4ed3b5f35e80d1233c61194ae1cb99a34 Mon Sep 17 00:00:00 2001 From: Kevin VanGelder Date: Thu, 12 Feb 2015 14:06:53 -0800 Subject: [PATCH 1/3] implemented product class, updated docs, other minor changes --- .travis.yml | 1 - README.md | 114 ++++++++++++++++++++++++++++++++++++--- lib/ProMotion-iap.rb | 1 + lib/ProMotion/iap.rb | 8 +-- lib/ProMotion/product.rb | 27 ++++++++++ spec/iap_product_spec.rb | 35 ++++++++++++ 6 files changed, 173 insertions(+), 13 deletions(-) create mode 100644 lib/ProMotion/product.rb create mode 100644 spec/iap_product_spec.rb diff --git a/.travis.yml b/.travis.yml index 8a63149..2a25986 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,5 +7,4 @@ gemfile: - Gemfile script: - bundle install - - bundle exec rake clean - bundle exec rake spec diff --git a/README.md b/README.md index 1c176c3..5e5b129 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,10 @@ # ProMotion-iap +[![Gem Version](https://badge.fury.io/rb/ProMotion-iap.svg)](http://badge.fury.io/rb/ProMotion-iap) +[![Build Status](https://travis-ci.org/clearsightstudio/ProMotion-iap.svg)](https://travis-ci.org/clearsightstudio/ProMotion-iap) + ProMotion-iap is in-app purchase notification support for the -popular RubyMotion gem [ProMotion](https://github.com/clearsightstudio/ProMotion). + popular RubyMotion gem [ProMotion](https://github.com/clearsightstudio/ProMotion). ## Installation @@ -11,7 +14,79 @@ gem 'ProMotion-iap' ## Usage -### AppDelegate +### IAP::Product Class + +The `Product` class is an abstraction layer that provides a simpler interface when working with a single IAP product. + If you are dealing with multiple products you will want to use the IAP Module directly (documented below). + +```ruby +class PurchaseScreen < PM::Screen + + def on_load + + product = PM::IAP::Product.new("productid") + + product.retrieve do |product, error| + # product looks something like the following + { + product_id: "productid1", + title: "title", + description: "description", + price: , + formatted_price: "$0.99", + price_locale: , + downloadable: false, + download_content_lengths: , # TODO: ? + download_content_version: , # TODO: ? + product: + } + end + + product.purchase do |status, transaction| + case status + when :in_progress + # Usually do nothing, maybe a spinner + when :deferred + # Waiting on a prompt to the user + when :purchased + # Notify the user, update any affected UI elements + when :canceled + # They just canceled, no big deal. + when :error + # Failed to purchase + transaction.error.localizedDescription # => error message + end + end + + product.restore do |status, products| + if status == :restored + # Update your UI, notify the user + end + end + + end +end + +``` + +#### Product.new(product_id) + +Stores the product_id for use in the class methods. + +#### retrieve(&callback) + +Retrieves the product. + +#### purchase(&callback) + +Begins a purchase of the product. + +#### restore(&callback) + +Begins a restoration of the previously purchased product. + + +### IAP Module Include `PM::IAP` to add some in-app purchase methods to a screen, app delegate, or other class. @@ -22,7 +97,7 @@ class PurchaseScreen < PM::Screen def on_load - retrieve_iaps([ "productid1", "productid2" ]) do |products, error| + retrieve_iaps[ "productid1", "productid2" ] do |products, error| # products looks something like the following [{ product_id: "productid1", @@ -54,7 +129,7 @@ class PurchaseScreen < PM::Screen end end - restore_iaps do |status, products| + restore_iaps "productid" do |status, products| if status == :restored # Update your UI, notify the user end @@ -65,19 +140,42 @@ class PurchaseScreen < PM::Screen end ``` -#### purchase_iap(*product_ids, &callback) +#### retrieve_iaps(*product_ids, &callback) + +Retrieves in-app purchase products in an array of mapped hashes. The callback method should accept `products` and `error`. + + +#### purchase_iaps(*product_ids, &callback) + +Prompts the user to login to their Apple ID and complete the purchase. The callback method should accept `status` and `transaction`. + The callback method will be called several times with the various statuses in the process. If more than one `product_id` is provided + the callback method will be called several times per product with the applicable transaction. + + +#### restore_iaps(*product_ids, &callback) + +Restores a previously purchased IAP to the user (for example if they have upgraded their device). This relies on the Apple ID the user + enters at the prompt. Unfortunately, if there is no purchase to restore for the signed-in account, no error message is generated and + will fail silently. + -TODO -```ruby -``` Find the Product ID here: ![product id](http://clrsight.co/jh/2015-02-11-d8xw6.png?+) +## Authors +| Contribuor | Twitter | +| Jamon Holmgren | [@jamonholmgren](http://twitter.com/jamonholmgren) | +| Kevin VanGelder | [@kevinvangelder](http://twitter.com/kevinvangelder) | + +## Inspired By +- [Helu](https://github.com/ivanacostarubio/helu) +- [Mark Rickert's Code Example](https://github.com/OTGApps/TheShowCloser/blob/master/app/helpers/iap_helper.rb) + ## Contributing diff --git a/lib/ProMotion-iap.rb b/lib/ProMotion-iap.rb index 49fac20..d167c51 100644 --- a/lib/ProMotion-iap.rb +++ b/lib/ProMotion-iap.rb @@ -7,5 +7,6 @@ Motion::Project::App.setup do |app| lib_dir_path = File.dirname(File.expand_path(__FILE__)) app.files << File.join(lib_dir_path, "ProMotion/iap.rb") + app.files << File.join(lib_dir_path, "ProMotion/product.rb") app.frameworks << "StoreKit" end diff --git a/lib/ProMotion/iap.rb b/lib/ProMotion/iap.rb index aa2f96e..fd1bcfb 100644 --- a/lib/ProMotion/iap.rb +++ b/lib/ProMotion/iap.rb @@ -2,9 +2,9 @@ module ProMotion module IAP attr_accessor :completion_handlers - def purchase_iaps(product_id, &callback) + def purchase_iaps(*product_ids, &callback) iap_setup - retrieve_iaps product_id do |products| + retrieve_iaps product_ids do |products| products.each do |product| self.completion_handlers["purchase-#{product[:product_id]}"] = callback payment = SKPayment.paymentWithProduct(product[:product]) @@ -14,9 +14,9 @@ def purchase_iaps(product_id, &callback) end alias purchase_iap purchase_iaps - def restore_iaps(product_id, &callback) + def restore_iaps(*product_ids, &callback) iap_setup - retrieve_iaps product_id do |products| + retrieve_iaps product_ids do |products| products.each do |product| self.completion_handlers["restore-#{product[:product_id]}"] = callback SKPaymentQueue.defaultQueue.restoreCompletedTransactions diff --git a/lib/ProMotion/product.rb b/lib/ProMotion/product.rb new file mode 100644 index 0000000..32536fa --- /dev/null +++ b/lib/ProMotion/product.rb @@ -0,0 +1,27 @@ +module ProMotion + class IAP::Product + include PM::IAP + + attr_reader :product_id + + def initialize(product_id) + @product_id = product_id + end + + def retrieve(&callback) + retrieve_iaps(product_id) do |products, error| + callback.call products.first, error + end + end + + def purchase(&callback) + purchase_iaps(product_id, &callback) + end + + def restore(&callback) + restore_iaps(product_id) do |status, products| + callback.call status, products.find{|p| p[:product_id] == product_id } + end + end + end +end \ No newline at end of file diff --git a/spec/iap_product_spec.rb b/spec/iap_product_spec.rb new file mode 100644 index 0000000..5401e70 --- /dev/null +++ b/spec/iap_product_spec.rb @@ -0,0 +1,35 @@ +describe PM::IAP::Product do + + + it "#retrieve" do + subject = PM::IAP::Product.new("retrieveid") + subject.mock!(:retrieve_iaps) do |product_ids, &callback| + product_ids.should.include "retrieveid" + end + subject.retrieve do |products, error| + end + + end + + it "#purchase" do + subject = PM::IAP::Product.new("purchaseid") + subject.mock!(:purchase_iaps) do |product_ids, &callback| + product_ids.should.include "purchaseid" + end + subject.purchase do |status, transaction| + end + end + + it "#restore" do + subject = PM::IAP::Product.new("restoreid") + subject.mock!(:restore_iaps) do |product_ids, &callback| + product_ids.should.include "restoreid" + callback.call(:restored, [{product_id: "restoreid2"}, {product_id: "restoreid"}, {product_id: "restoreid4"}]) + end + subject.restore do |status, product| + status.should == :restored + product.should == { product_id: "restoreid" } + end + end + +end \ No newline at end of file From db7d8871f061003cfb59bba45bfbbca15e7e3180 Mon Sep 17 00:00:00 2001 From: Kevin VanGelder Date: Thu, 12 Feb 2015 14:15:59 -0800 Subject: [PATCH 2/3] readme tweaks --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 5e5b129..ba362cd 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ gem 'ProMotion-iap' ## Usage -### IAP::Product Class +### PM::IAP::Product Class The `Product` class is an abstraction layer that provides a simpler interface when working with a single IAP product. If you are dealing with multiple products you will want to use the IAP Module directly (documented below). @@ -58,7 +58,7 @@ class PurchaseScreen < PM::Screen end end - product.restore do |status, products| + product.restore do |status, product| if status == :restored # Update your UI, notify the user end @@ -71,7 +71,7 @@ end #### Product.new(product_id) -Stores the product_id for use in the class methods. +Stores the product_id for use in the instance methods. #### retrieve(&callback) @@ -97,7 +97,7 @@ class PurchaseScreen < PM::Screen def on_load - retrieve_iaps[ "productid1", "productid2" ] do |products, error| + retrieve_iaps [ "productid1", "productid2" ] do |products, error| # products looks something like the following [{ product_id: "productid1", @@ -140,19 +140,19 @@ class PurchaseScreen < PM::Screen end ``` -#### retrieve_iaps(*product_ids, &callback) +#### retrieve_iaps(`*`product_ids, &callback) Retrieves in-app purchase products in an array of mapped hashes. The callback method should accept `products` and `error`. -#### purchase_iaps(*product_ids, &callback) +#### purchase_iaps(`*`product_ids, &callback) Prompts the user to login to their Apple ID and complete the purchase. The callback method should accept `status` and `transaction`. The callback method will be called several times with the various statuses in the process. If more than one `product_id` is provided the callback method will be called several times per product with the applicable transaction. -#### restore_iaps(*product_ids, &callback) +#### restore_iaps(`*`product_ids, &callback) Restores a previously purchased IAP to the user (for example if they have upgraded their device). This relies on the Apple ID the user enters at the prompt. Unfortunately, if there is no purchase to restore for the signed-in account, no error message is generated and @@ -170,7 +170,7 @@ Find the Product ID here: ## Authors | Contribuor | Twitter | | Jamon Holmgren | [@jamonholmgren](http://twitter.com/jamonholmgren) | -| Kevin VanGelder | [@kevinvangelder](http://twitter.com/kevinvangelder) | +| Kevin VanGelder | [@kevinvangelder](http://twitter.com/kevin_vangelder) | ## Inspired By - [Helu](https://github.com/ivanacostarubio/helu) From 92af44f6772e9cfa054a2ef6e8dc508120fbf3c6 Mon Sep 17 00:00:00 2001 From: Kevin VanGelder Date: Thu, 12 Feb 2015 14:16:26 -0800 Subject: [PATCH 3/3] typo in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ba362cd..c4c6d1b 100644 --- a/README.md +++ b/README.md @@ -168,7 +168,7 @@ Find the Product ID here: ## Authors -| Contribuor | Twitter | +| Contributor | Twitter | | Jamon Holmgren | [@jamonholmgren](http://twitter.com/jamonholmgren) | | Kevin VanGelder | [@kevinvangelder](http://twitter.com/kevin_vangelder) |