From 32b4f86efa598d9b6f4caa1af67eac5b504994dc Mon Sep 17 00:00:00 2001 From: Veronique Date: Thu, 21 May 2020 22:32:32 +0200 Subject: [PATCH] Add documentation with Jazzy --- .jazzy.yaml | 52 + Documentation/API documentation.md | 20 + Documentation/Getting Started.md | 10 + Documentation/Getting Started/Installation.md | 14 + .../Getting Started/Micro Example.md | 90 + .../Images/InAppPurchaseLib.png | Bin .../Images/ScreenshotInstallation.png | Bin Documentation/License.md | 21 + Documentation/Usage.md | 8 + Documentation/Usage/Analytics.md | 42 + .../Displaying products with purchases.md | 59 + Documentation/Usage/Displaying products.md | 36 + .../Usage/Displaying subscriptions.md | 36 + Documentation/Usage/Errors.md | 20 + Documentation/Usage/Handling purchases.md | 93 + Documentation/Usage/Initialization.md | 46 + Documentation/Usage/Purchasing.md | 65 + Documentation/Usage/Refreshing.md | 13 + Documentation/Usage/Restoring purchases.md | 25 + Documentation/Usage/Server integration.md | 21 + README.md | 555 +----- .../InAppPurchaseLib/Common/IAPCallback.swift | 13 + .../InAppPurchaseLib/Common/IAPError.swift | 32 +- Sources/InAppPurchaseLib/InAppPurchase.swift | 12 +- .../InAppPurchaseLib/InAppPurchaseLib.swift | 17 +- .../InAppPurchaseLib/Product/IAPProduct.swift | 1 - docs/API documentation.html | 680 +++++++ .../Classes/DefaultPurchaseDelegate.html | 171 +- .../Documents => }/Classes/InAppPurchase.html | 194 +- docs/{jazzy => }/Enums/IAPErrorCode.html | 298 +-- docs/Enums/IAPPeriodFormat.html | 247 +++ docs/{jazzy => }/Enums/IAPProductType.html | 169 +- .../Enums/IAPPurchaseResultState.html | 179 +- docs/Enums/IAPRefreshResultState.html | 274 +++ docs/{jazzy => }/Extensions/SKProduct.html | 169 +- docs/Getting Started.html | 221 +++ docs/Protocols/IAPErrorProtocol.html | 220 +++ docs/Protocols/IAPPurchaseDelegate.html | 244 +++ .../Protocols/InAppPurchaseLib.html | 193 +- docs/Structs/IAPError.html | 252 +++ .../Documents => }/Structs/IAPProduct.html | 169 +- .../Structs/IAPPurchaseResult.html | 171 +- .../Structs/IAPRefreshResult.html | 171 +- docs/Usage.html | 296 +++ docs/analytics.html | 221 +++ docs/{jazzy => }/badge.svg | 6 +- docs/{jazzy => }/css/highlight.css | 0 docs/css/jazzy.css | 395 ++++ docs/displaying-products-with-purchases.html | 231 +++ docs/displaying-products.html | 213 +++ docs/displaying-subscriptions.html | 214 +++ .../Contents/Info.plist | 0 .../Documents/API documentation.html | 680 +++++++ .../Classes/DefaultPurchaseDelegate.html | 171 +- .../Documents}/Classes/InAppPurchase.html | 194 +- .../Documents/Enums/IAPErrorCode.html | 298 +-- .../Documents/Enums/IAPPeriodFormat.html | 247 +++ .../Documents/Enums/IAPProductType.html | 169 +- .../Enums/IAPPurchaseResultState.html | 179 +- .../Enums/IAPRefreshResultState.html | 274 +++ .../Documents/Extensions/SKProduct.html | 169 +- .../Resources/Documents/Getting Started.html | 221 +++ .../Documents/Protocols/IAPErrorProtocol.html | 220 +++ .../Protocols/IAPPurchaseDelegate.html | 244 +++ .../Documents/Protocols/InAppPurchaseLib.html | 193 +- .../Resources/Documents/Structs/IAPError.html | 252 +++ .../Documents}/Structs/IAPProduct.html | 169 +- .../Documents}/Structs/IAPPurchaseResult.html | 171 +- .../Documents}/Structs/IAPRefreshResult.html | 171 +- .../Contents/Resources/Documents/Usage.html | 296 +++ .../Resources/Documents/analytics.html | 221 +++ .../Resources/Documents/css/highlight.css | 0 .../Resources/Documents/css/jazzy.css | 395 ++++ .../displaying-products-with-purchases.html | 231 +++ .../Documents/displaying-products.html | 213 +++ .../Documents/displaying-subscriptions.html | 214 +++ .../Contents/Resources/Documents/errors.html | 206 ++ .../Documents/handling-purchases.html | 271 +++ .../Resources/Documents/img/carat.png | Bin .../Contents/Resources/Documents/img/dash.png | Bin .../Contents/Resources/Documents/img/gh.png | Bin .../Resources/Documents/img/spinner.gif | Bin 0 -> 1849 bytes .../Contents/Resources/Documents/index.html | 325 ++++ .../Resources/Documents/initialization.html | 227 +++ .../Resources/Documents/installation.html | 194 ++ .../Contents/Resources/Documents/js/jazzy.js | 0 .../Resources/Documents/js/jazzy.search.js | 70 + .../Resources/Documents/js/jquery.min.js | 0 .../Resources/Documents/js/lunr.min.js | 1 + .../Documents/js/typeahead.jquery.js | 1674 +++++++++++++++++ .../Resources/Documents/micro-example.html | 262 +++ .../Resources/Documents/purchasing.html | 245 +++ .../Resources/Documents/refreshing.html | 189 ++ .../Documents/restoring-purchases.html | 201 ++ .../Contents/Resources/Documents/search.json | 1 + .../Documents/server-integration.html | 198 ++ .../Contents/Resources/docSet.dsidx | Bin 0 -> 45056 bytes docs/docsets/InAppPurchaseLib.tgz | Bin 0 -> 108828 bytes docs/errors.html | 206 ++ docs/handling-purchases.html | 271 +++ docs/{jazzy => }/img/carat.png | Bin docs/{jazzy => }/img/dash.png | Bin docs/{jazzy => }/img/gh.png | Bin docs/img/spinner.gif | Bin 0 -> 1849 bytes docs/index.html | 325 ++++ docs/initialization.html | 227 +++ docs/installation.html | 194 ++ docs/jazzy/Classes.html | 205 -- docs/jazzy/Enums.html | 276 --- docs/jazzy/Enums/IAPPeriodFormat.html | 196 -- docs/jazzy/Enums/IAPRefreshResultState.html | 223 --- docs/jazzy/Extensions.html | 163 -- docs/jazzy/Protocols.html | 233 --- docs/jazzy/Protocols/IAPErrorProtocol.html | 169 -- docs/jazzy/Protocols/IAPPurchaseDelegate.html | 193 -- docs/jazzy/Structs.html | 248 --- docs/jazzy/Structs/IAPError.html | 196 -- docs/jazzy/Typealiases.html | 190 -- docs/jazzy/css/jazzy.css | 374 ---- .../Contents/Resources/Documents/Classes.html | 205 -- .../Contents/Resources/Documents/Enums.html | 276 --- .../Documents/Enums/IAPPeriodFormat.html | 196 -- .../Enums/IAPRefreshResultState.html | 223 --- .../Resources/Documents/Extensions.html | 163 -- .../Resources/Documents/Protocols.html | 233 --- .../Documents/Protocols/IAPErrorProtocol.html | 169 -- .../Protocols/IAPPurchaseDelegate.html | 193 -- .../Contents/Resources/Documents/Structs.html | 248 --- .../Resources/Documents/Structs/IAPError.html | 196 -- .../Resources/Documents/Typealiases.html | 190 -- .../Resources/Documents/css/jazzy.css | 374 ---- .../Contents/Resources/Documents/index.html | 699 ------- .../Contents/Resources/Documents/search.json | 1 - .../Contents/Resources/docSet.dsidx | Bin 45056 -> 0 bytes docs/jazzy/docsets/InAppPurchaseLib.tgz | Bin 71508 -> 0 bytes docs/jazzy/index.html | 699 ------- docs/jazzy/search.json | 1 - docs/jazzy/undocumented.json | 348 ---- docs/{jazzy => }/js/jazzy.js | 0 docs/js/jazzy.search.js | 70 + docs/{jazzy => }/js/jquery.min.js | 0 docs/js/lunr.min.js | 1 + docs/js/typeahead.jquery.js | 1674 +++++++++++++++++ docs/micro-example.html | 262 +++ docs/purchasing.html | 245 +++ docs/refreshing.html | 189 ++ docs/restoring-purchases.html | 201 ++ docs/search.json | 1 + docs/server-integration.html | 198 ++ .../DefaultPurchaseDelegate/index.html | 151 -- docs/swift-doc/IAPError/index.html | 119 -- docs/swift-doc/IAPErrorCode/index.html | 119 -- docs/swift-doc/IAPErrorProtocol/index.html | 129 -- docs/swift-doc/IAPPeriodFormat/index.html | 71 - docs/swift-doc/IAPProduct/index.html | 130 -- docs/swift-doc/IAPProductType/index.html | 83 - docs/swift-doc/IAPPurchaseCallback/index.html | 51 - docs/swift-doc/IAPPurchaseDelegate/index.html | 147 -- docs/swift-doc/IAPPurchaseResult/index.html | 83 - .../IAPPurchaseResultState/index.html | 83 - docs/swift-doc/IAPRefreshCallback/index.html | 51 - docs/swift-doc/IAPRefreshResult/index.html | 83 - .../IAPRefreshResultState/index.html | 77 - docs/swift-doc/InAppPurchase/index.html | 592 ------ docs/swift-doc/InAppPurchaseLib/index.html | 578 ------ docs/swift-doc/all.css | 1 - docs/swift-doc/index.html | 204 -- docs/undocumented.json | 166 ++ 168 files changed, 18941 insertions(+), 11676 deletions(-) create mode 100644 .jazzy.yaml create mode 100644 Documentation/API documentation.md create mode 100644 Documentation/Getting Started.md create mode 100644 Documentation/Getting Started/Installation.md create mode 100644 Documentation/Getting Started/Micro Example.md rename InAppPurchaseLib.png => Documentation/Images/InAppPurchaseLib.png (100%) rename ScreenshotInstallation.png => Documentation/Images/ScreenshotInstallation.png (100%) create mode 100644 Documentation/License.md create mode 100644 Documentation/Usage.md create mode 100644 Documentation/Usage/Analytics.md create mode 100644 Documentation/Usage/Displaying products with purchases.md create mode 100644 Documentation/Usage/Displaying products.md create mode 100644 Documentation/Usage/Displaying subscriptions.md create mode 100644 Documentation/Usage/Errors.md create mode 100644 Documentation/Usage/Handling purchases.md create mode 100644 Documentation/Usage/Initialization.md create mode 100644 Documentation/Usage/Purchasing.md create mode 100644 Documentation/Usage/Refreshing.md create mode 100644 Documentation/Usage/Restoring purchases.md create mode 100644 Documentation/Usage/Server integration.md create mode 100644 docs/API documentation.html rename docs/{jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents => }/Classes/DefaultPurchaseDelegate.html (50%) rename docs/{jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents => }/Classes/InAppPurchase.html (87%) rename docs/{jazzy => }/Enums/IAPErrorCode.html (62%) create mode 100644 docs/Enums/IAPPeriodFormat.html rename docs/{jazzy => }/Enums/IAPProductType.html (55%) rename docs/{jazzy => }/Enums/IAPPurchaseResultState.html (53%) create mode 100644 docs/Enums/IAPRefreshResultState.html rename docs/{jazzy => }/Extensions/SKProduct.html (68%) create mode 100644 docs/Getting Started.html create mode 100644 docs/Protocols/IAPErrorProtocol.html create mode 100644 docs/Protocols/IAPPurchaseDelegate.html rename docs/{jazzy => }/Protocols/InAppPurchaseLib.html (86%) create mode 100644 docs/Structs/IAPError.html rename docs/{jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents => }/Structs/IAPProduct.html (58%) rename docs/{jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents => }/Structs/IAPPurchaseResult.html (58%) rename docs/{jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents => }/Structs/IAPRefreshResult.html (57%) create mode 100644 docs/Usage.html create mode 100644 docs/analytics.html rename docs/{jazzy => }/badge.svg (93%) rename docs/{jazzy => }/css/highlight.css (100%) create mode 100644 docs/css/jazzy.css create mode 100644 docs/displaying-products-with-purchases.html create mode 100644 docs/displaying-products.html create mode 100644 docs/displaying-subscriptions.html rename docs/{jazzy => }/docsets/InAppPurchaseLib.docset/Contents/Info.plist (100%) create mode 100644 docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/API documentation.html rename docs/{jazzy => docsets/InAppPurchaseLib.docset/Contents/Resources/Documents}/Classes/DefaultPurchaseDelegate.html (50%) rename docs/{jazzy => docsets/InAppPurchaseLib.docset/Contents/Resources/Documents}/Classes/InAppPurchase.html (87%) rename docs/{jazzy => }/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Enums/IAPErrorCode.html (62%) create mode 100644 docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Enums/IAPPeriodFormat.html rename docs/{jazzy => }/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Enums/IAPProductType.html (55%) rename docs/{jazzy => }/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Enums/IAPPurchaseResultState.html (53%) create mode 100644 docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Enums/IAPRefreshResultState.html rename docs/{jazzy => }/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Extensions/SKProduct.html (68%) create mode 100644 docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Getting Started.html create mode 100644 docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Protocols/IAPErrorProtocol.html create mode 100644 docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Protocols/IAPPurchaseDelegate.html rename docs/{jazzy => }/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Protocols/InAppPurchaseLib.html (86%) create mode 100644 docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Structs/IAPError.html rename docs/{jazzy => docsets/InAppPurchaseLib.docset/Contents/Resources/Documents}/Structs/IAPProduct.html (58%) rename docs/{jazzy => docsets/InAppPurchaseLib.docset/Contents/Resources/Documents}/Structs/IAPPurchaseResult.html (58%) rename docs/{jazzy => docsets/InAppPurchaseLib.docset/Contents/Resources/Documents}/Structs/IAPRefreshResult.html (57%) create mode 100644 docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Usage.html create mode 100644 docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/analytics.html rename docs/{jazzy => }/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/css/highlight.css (100%) create mode 100644 docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/css/jazzy.css create mode 100644 docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/displaying-products-with-purchases.html create mode 100644 docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/displaying-products.html create mode 100644 docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/displaying-subscriptions.html create mode 100644 docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/errors.html create mode 100644 docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/handling-purchases.html rename docs/{jazzy => }/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/img/carat.png (100%) rename docs/{jazzy => }/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/img/dash.png (100%) rename docs/{jazzy => }/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/img/gh.png (100%) create mode 100644 docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/img/spinner.gif create mode 100644 docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/index.html create mode 100644 docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/initialization.html create mode 100644 docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/installation.html rename docs/{jazzy => }/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/js/jazzy.js (100%) create mode 100644 docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/js/jazzy.search.js rename docs/{jazzy => }/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/js/jquery.min.js (100%) create mode 100644 docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/js/lunr.min.js create mode 100644 docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/js/typeahead.jquery.js create mode 100644 docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/micro-example.html create mode 100644 docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/purchasing.html create mode 100644 docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/refreshing.html create mode 100644 docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/restoring-purchases.html create mode 100644 docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/search.json create mode 100644 docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/server-integration.html create mode 100644 docs/docsets/InAppPurchaseLib.docset/Contents/Resources/docSet.dsidx create mode 100644 docs/docsets/InAppPurchaseLib.tgz create mode 100644 docs/errors.html create mode 100644 docs/handling-purchases.html rename docs/{jazzy => }/img/carat.png (100%) rename docs/{jazzy => }/img/dash.png (100%) rename docs/{jazzy => }/img/gh.png (100%) create mode 100644 docs/img/spinner.gif create mode 100644 docs/index.html create mode 100644 docs/initialization.html create mode 100644 docs/installation.html delete mode 100644 docs/jazzy/Classes.html delete mode 100644 docs/jazzy/Enums.html delete mode 100644 docs/jazzy/Enums/IAPPeriodFormat.html delete mode 100644 docs/jazzy/Enums/IAPRefreshResultState.html delete mode 100644 docs/jazzy/Extensions.html delete mode 100644 docs/jazzy/Protocols.html delete mode 100644 docs/jazzy/Protocols/IAPErrorProtocol.html delete mode 100644 docs/jazzy/Protocols/IAPPurchaseDelegate.html delete mode 100644 docs/jazzy/Structs.html delete mode 100644 docs/jazzy/Structs/IAPError.html delete mode 100644 docs/jazzy/Typealiases.html delete mode 100644 docs/jazzy/css/jazzy.css delete mode 100644 docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Classes.html delete mode 100644 docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Enums.html delete mode 100644 docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Enums/IAPPeriodFormat.html delete mode 100644 docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Enums/IAPRefreshResultState.html delete mode 100644 docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Extensions.html delete mode 100644 docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Protocols.html delete mode 100644 docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Protocols/IAPErrorProtocol.html delete mode 100644 docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Protocols/IAPPurchaseDelegate.html delete mode 100644 docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Structs.html delete mode 100644 docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Structs/IAPError.html delete mode 100644 docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Typealiases.html delete mode 100644 docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/css/jazzy.css delete mode 100644 docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/index.html delete mode 100644 docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/search.json delete mode 100644 docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/docSet.dsidx delete mode 100644 docs/jazzy/docsets/InAppPurchaseLib.tgz delete mode 100644 docs/jazzy/index.html delete mode 100644 docs/jazzy/search.json delete mode 100644 docs/jazzy/undocumented.json rename docs/{jazzy => }/js/jazzy.js (100%) create mode 100644 docs/js/jazzy.search.js rename docs/{jazzy => }/js/jquery.min.js (100%) create mode 100644 docs/js/lunr.min.js create mode 100644 docs/js/typeahead.jquery.js create mode 100644 docs/micro-example.html create mode 100644 docs/purchasing.html create mode 100644 docs/refreshing.html create mode 100644 docs/restoring-purchases.html create mode 100644 docs/search.json create mode 100644 docs/server-integration.html delete mode 100755 docs/swift-doc/DefaultPurchaseDelegate/index.html delete mode 100755 docs/swift-doc/IAPError/index.html delete mode 100755 docs/swift-doc/IAPErrorCode/index.html delete mode 100755 docs/swift-doc/IAPErrorProtocol/index.html delete mode 100755 docs/swift-doc/IAPPeriodFormat/index.html delete mode 100755 docs/swift-doc/IAPProduct/index.html delete mode 100755 docs/swift-doc/IAPProductType/index.html delete mode 100755 docs/swift-doc/IAPPurchaseCallback/index.html delete mode 100755 docs/swift-doc/IAPPurchaseDelegate/index.html delete mode 100755 docs/swift-doc/IAPPurchaseResult/index.html delete mode 100755 docs/swift-doc/IAPPurchaseResultState/index.html delete mode 100755 docs/swift-doc/IAPRefreshCallback/index.html delete mode 100755 docs/swift-doc/IAPRefreshResult/index.html delete mode 100755 docs/swift-doc/IAPRefreshResultState/index.html delete mode 100755 docs/swift-doc/InAppPurchase/index.html delete mode 100755 docs/swift-doc/InAppPurchaseLib/index.html delete mode 100755 docs/swift-doc/all.css delete mode 100755 docs/swift-doc/index.html create mode 100644 docs/undocumented.json diff --git a/.jazzy.yaml b/.jazzy.yaml new file mode 100644 index 0000000..c63591e --- /dev/null +++ b/.jazzy.yaml @@ -0,0 +1,52 @@ +# `jazzy --help config` + +output: docs +clean: true +author: Iridescent +author_url: https://iridescent.dev +module: InAppPurchaseLib +title: InAppPurchaseLib documentation +copyright: "Copyright © 2020 [Iridescent](https://iridescent.dev)." +documentation: Documentation/*/*.md +abstract: Documentation/*.md +github_url: https://github.com/iridescent-dev/iap-swift-lib +hide_documentation_coverage: false +custom_categories: + - name: "Getting Started" + children: + - "Installation" + - "Micro Example" + - "License" + - name: "Usage" + children: + - "Initialization" + - "Displaying products" + - "Displaying subscriptions" + - "Refreshing" + - "Purchasing" + - "Handling purchases" + - "Restoring purchases" + - "Displaying products with purchases" + - "Errors" + - "Analytics" + - "Server integration" + - name: "API documentation" + children: + - InAppPurchase + - InAppPurchaseLib + - DefaultPurchaseDelegate + - IAPPurchaseDelegate + - IAPProduct + - IAPProductType + - SKProduct + - IAPPeriodFormat + - IAPPurchaseCallback + - IAPRefreshCallback + - IAPPurchaseResult + - IAPRefreshResult + - IAPPurchaseResultState + - IAPRefreshResultState + - IAPError + - IAPErrorCode + - IAPErrorProtocol +theme: fullwidth diff --git a/Documentation/API documentation.md b/Documentation/API documentation.md new file mode 100644 index 0000000..3614965 --- /dev/null +++ b/Documentation/API documentation.md @@ -0,0 +1,20 @@ +# Classes and Protocols + +The most important class is `InAppPurchase`. All the functions you need are defined in this class. + +If you have *consumable* and/or *non-renewing subscription* products in your application, you must have a class that adopts the `IAPPurchaseDelegate` protocol. + +# Products +* Input: the library requires an array of `IAPProduct` when it is initialized. + +* Output: the library will returns [SKProduct](https://developer.apple.com/documentation/storekit/skproduct) extended with helpful methods. See the [`SKProduct` extension](Extensions/SKProduct.html). + +# Callbacks +`refresh()`, `purchase()` and `restorePurchases()` are asynchronous functions. You must provide a callback that will allow you to perform actions depending on the result. + +* For `refresh()` and `restorePurchases()` functions, the result will be `IAPRefreshResult`. + +* For `purchase()` function, the result will be `IAPPurchaseResult`. + +# Errors +When calling `refresh()`, `purchase()` or `restorePurchases()`, the callback can return an `IAPError` if the state is `failed`. Look at `IAPErrorCode` to see the list of error codes you can receive. diff --git a/Documentation/Getting Started.md b/Documentation/Getting Started.md new file mode 100644 index 0000000..5f64fb2 --- /dev/null +++ b/Documentation/Getting Started.md @@ -0,0 +1,10 @@ +If you haven't already, I highly recommend your read the *Overview* and *Preparing* section of Apple's [In-App Purchase official documentation](https://developer.apple.com/in-app-purchase). + +## Requirements +* Configure your App and Xcode to support In-App Purchases. + * [AppStore Connect Setup](https://help.apple.com/app-store-connect/#/devb57be10e7) +* Create and configure your [Fovea.Billing](https://billing.fovea.cc/?ref=iap-swift-lib) project account: + * Set your bundle ID + * The iOS Shared Secret (or shared key) is to be retrieved from [AppStoreConnect](https://appstoreconnect.apple.com/) + * The iOS Subscription Status URL (only if you want subscriptions) + diff --git a/Documentation/Getting Started/Installation.md b/Documentation/Getting Started/Installation.md new file mode 100644 index 0000000..c14c162 --- /dev/null +++ b/Documentation/Getting Started/Installation.md @@ -0,0 +1,14 @@ +# Installation +

+ +

+ +* Select your project in Xcode +* Go to the section *Swift Package* +* Click on *(+) Add Package Dependency* +* Copy the Git URL: *https://github.com/iridescent-dev/iap-swift-lib.git* +* Click on *Next* > *Next* +* Make sure your project is selected in *Add to target* +* Click on *Finish* + +*Note:* You have to `import InAppPurchaseLib` wherever you use the library. diff --git a/Documentation/Getting Started/Micro Example.md b/Documentation/Getting Started/Micro Example.md new file mode 100644 index 0000000..2491fe3 --- /dev/null +++ b/Documentation/Getting Started/Micro Example.md @@ -0,0 +1,90 @@ +# Micro Example + + +```swift +/** AppDelegate.swift */ +import UIKit +import InAppPurchaseLib + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + // Initialize the library + InAppPurchase.initialize( + iapProducts: [ + IAPProduct(productIdentifier: "my_product", productType: .nonConsumable) + ], + validatorUrlString: "https://validator.fovea.cc/v1/validate?appName=demo&apiKey=12345678" + ) + return true + } + + func applicationWillTerminate(_ application: UIApplication) { + // Clean + InAppPurchase.stop() + } +} +``` + +```swift +/** ViewController.swift */ +import UIKit +import StoreKit +import InAppPurchaseLib + +class ViewController: UIViewController { + private var loaderView = LoaderView() + @IBOutlet weak var statusLabel: UILabel! + @IBOutlet weak var productTitleLabel: UILabel! + @IBOutlet weak var productDescriptionLabel: UILabel! + @IBOutlet weak var purchaseButton: UIButton! + @IBOutlet weak var restorePurchasesButton: UIButton! + + override func viewDidLoad() { + super.viewDidLoad() + // Add action for purchases and restore butons. + purchaseButton.addTarget(self, action: #selector(self.purchase), for: .touchUpInside) + restorePurchasesButton.addTarget(self, action: #selector(self.restorePurchases), for: .touchUpInside) + } + + override func viewWillAppear(_ animated: Bool) { + self.refreshView() + InAppPurchase.refresh(callback: { _ in + self.refreshView() + }) + } + + func refreshView() { + guard let product: SKProduct = InAppPurchase.getProductBy(identifier: "my_product") else { + self.productTitleLabel.text = "Product unavailable" + return + } + // Display product information. + productTitleLabel.text = product.localizedTitle + productDescriptionLabel.text = product.localizedDescription + purchaseButton.setTitle(product.localizedPrice, for: .normal) + + // Disable the button if the product has already been purchased. + if InAppPurchase.hasActivePurchase(for: "my_product") { + statusLabel.text = "OWNED" + purchaseButton.isPointerInteractionEnabled = false + } + } + + @IBAction func purchase(_ sender: Any) { + self.loaderView.show() + InAppPurchase.purchase( + productIdentifier: "my_product", + callback: { result in + self.loaderView.hide() + }) + } + + @IBAction func restorePurchases(_ sender: Any) { + self.loaderView.show() + InAppPurchase.restorePurchases(callback: { result in + self.loaderView.hide() + }) + } +} +``` diff --git a/InAppPurchaseLib.png b/Documentation/Images/InAppPurchaseLib.png similarity index 100% rename from InAppPurchaseLib.png rename to Documentation/Images/InAppPurchaseLib.png diff --git a/ScreenshotInstallation.png b/Documentation/Images/ScreenshotInstallation.png similarity index 100% rename from ScreenshotInstallation.png rename to Documentation/Images/ScreenshotInstallation.png diff --git a/Documentation/License.md b/Documentation/License.md new file mode 100644 index 0000000..ce44199 --- /dev/null +++ b/Documentation/License.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Iridescent + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Documentation/Usage.md b/Documentation/Usage.md new file mode 100644 index 0000000..e96c58e --- /dev/null +++ b/Documentation/Usage.md @@ -0,0 +1,8 @@ +The process of implementing in-app purchases involves several steps: +1. Displaying the list of purchasable products +2. Initiating a purchase +3. Delivering and finalizing a purchase +4. Checking the current ownership of non-consumables and subscriptions +5. Implementing the Restore Purchases button + +*Note:* You have to `import InAppPurchaseLib` wherever you use the library. diff --git a/Documentation/Usage/Analytics.md b/Documentation/Usage/Analytics.md new file mode 100644 index 0000000..946ebd1 --- /dev/null +++ b/Documentation/Usage/Analytics.md @@ -0,0 +1,42 @@ +# Analytics +Tracking the purchase flow is a common things in apps. Especially as it's core to your revenue model. + +We can track 5 events, which step in the purchase pipeline a user reached. +1. `purchase initiated` +2. `purchase cancelled` +3. `purchase failed` +4. `purchase deferred` +5. `purchase succeeded` + +Here's a quick example showing how to implement this correctly. + +``` swift +func makePurchase() { + Analytics.trackEvent("purchase initiated") + InAppPurchase.purchase( + productIdentifier: "my_product_id", + callback: { result in + switch result.state { + case .purchased: + // Reminder: We are not processing the purchase here, only updating your UI. + // That's why we do not send an event to analytics. + case .failed: + Analytics.trackEvent("purchase failed") + case .deferred: + Analytics.trackEvent("purchase deferred") + case .cancelled: + Analytics.trackEvent("purchase cancelled") + } + }) +} + +// IAPPurchaseDelegate implementation +func productPurchased(productIdentifier: String) { + Analytics.trackEvent("purchase succeeded") + InAppPurchase.finishTransactions(for: productIdentifier) +} +``` + +The important part to remember is that a purchase can occur outside your app (or be approved when the app is not running), that's why tracking `purchase succeeded` has to be part of the `productPurchased` delegate function. + +Refer to the [Consumables](handling-purchases.html#consumables) section to learn more about the `productPurchased` function. diff --git a/Documentation/Usage/Displaying products with purchases.md b/Documentation/Usage/Displaying products with purchases.md new file mode 100644 index 0000000..0ec900c --- /dev/null +++ b/Documentation/Usage/Displaying products with purchases.md @@ -0,0 +1,59 @@ +# Displaying products with purchases +In your store screen, where you present your products titles and prices with a purchase button, there are some cases to handle that we skipped. Owned products and deferred purchases. + +### Owned products +Non-consumables and active auto-renewing subscriptions cannot be purchased again. You should adjust your UI to reflect that state. Refer to `InAppPurchase.hasActivePurchase()` to and to the example later in this section. + +### Deferred purchases +Apple's **Ask to Buy** feature lets parents approve any purchases initiated by children, including in-app purchases. + +With **Ask to Buy** enabled, when a child requests to make a purchase, the app is notified that the purchase is awaiting the parent’s approval in the purchase callback: + +``` swift +InAppPurchase.purchase( + productIdentifier: productIdentifier, + callback: { result in + switch result.state { + case .deferred: + // Pending parent approval + } +}) +``` + +In the _deferred_ case, the child has been notified by StoreKit that the parents have to approve the purchase. He might then close the app and come back later. You don't have much to do, but to display in your UI that there is a purchase waiting for parental approval in your views. + +We will use the `hasDeferredTransaction` method: + +``` swift +InAppPurchase.hasDeferredTransaction(for productIdentifier: String) -> Bool +``` + +### Example +Here's an example that covers what has been discussed above. We will update our example `refreshView` function from before: + +``` swift +@objc func refreshView() { + guard let product: SKProduct = InAppPurchase.getProductBy(identifier: "my_product_id") else { + self.titleLabel.text = "Product unavailable" + return + } + self.titleLabel.text = product.localizedTitle + // ... + + // "Ask to Buy" deferred purchase waiting for parent's approval + if InAppPurchase.hasDeferredTransaction(for: "my_product_id") { + self.statusLabel.text = "Waiting for Approval..." + self.purchaseButton.isPointerInteractionEnabled = false + } + // "Owned" product + else if InAppPurchase.hasActivePurchase(for: "my_product_id") { + self.statusLabel.text = "OWNED" + self.purchaseButton.isPointerInteractionEnabled = false + } + else { + self.purchaseButton.isPointerInteractionEnabled = true + } +} +``` + +When a product is owned or has a deferred purchase, we make sure the purchase button is grayed out. We also use a status label to display some details. Of course, you are free to design your UI as you see fit. diff --git a/Documentation/Usage/Displaying products.md b/Documentation/Usage/Displaying products.md new file mode 100644 index 0000000..83c71b0 --- /dev/null +++ b/Documentation/Usage/Displaying products.md @@ -0,0 +1,36 @@ +# Displaying products +Let's start with the simplest case: you have a single product. + +You can retrieve all information about this product using the function `InAppPurchase.getProductBy(identifier: "my_product_id")`. This returns an [SKProduct](https://developer.apple.com/documentation/storekit/skproduct) extended with helpful methods. + +Those are the most important: + - `productIdentifier: String` - The string that identifies the product to the Apple AppStore. + - `localizedTitle: String` - The name of the product, in the language of the device, as retrieved from the AppStore. + - `localizedDescription: String` - A description of the product, in the language of the device, as retrieved from the AppStore. + - `localizedPrice: String` - The cost of the product in the local currency (_read-only property added by this library_). + +*Example*: + +You can add a function similar to this to your view. + +``` swift +@objc func refreshView() { + guard let product: SKProduct = InAppPurchase.getProductBy(identifier: "my_product_id") else { + self.titleLabel.text = "Product unavailable" + return + } + self.titleLabel.text = product.localizedTitle + self.descriptionLabel.text = product.localizedDescription + self.priceLabel.text = product.localizedPrice +} +``` + +This example assumes `self.titleLabel` is a UILabel, etc. + +Make sure to call this function when the view appears on screen, for instance by calling it from [`viewWillAppear`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621510-viewwillappear). + +``` swift +override func viewWillAppear(_ animated: Bool) { + self.refreshView() +} +``` diff --git a/Documentation/Usage/Displaying subscriptions.md b/Documentation/Usage/Displaying subscriptions.md new file mode 100644 index 0000000..0094c2e --- /dev/null +++ b/Documentation/Usage/Displaying subscriptions.md @@ -0,0 +1,36 @@ +# Displaying subscriptions +For subscription products, you also have some data about subscription periods and introductory offers. + + - `func hasIntroductoryPriceEligible() -> Bool` - The product has an introductory price the user is eligible to. + - `localizedSubscriptionPeriod: String?` - The period of the subscription. + - `localizedIntroductoryPrice: String?` - The cost of the introductory offer if available in the local currency. + - `localizedIntroductoryPeriod: String?` - The subscription period of the introductory offer. + - `localizedIntroductoryDuration: String?` - The duration of the introductory offer. + +**Example** + +``` swift +@objc func refreshView() { + guard let product: SKProduct = InAppPurchase.getProductBy(identifier: "my_product_id") else { + self.titleLabel.text = "Product unavailable" + return + } + self.titleLabel.text = product.localizedTitle + self.descriptionLabel.text = product.localizedDescription + + // Format price text. Example: "0,99€ / month for 3 months (then 3,99 € / month)" + var priceText = "\(product.localizedPrice) / \(product.localizedSubscriptionPeriod!)" + if product.hasIntroductoryPriceEligible() { + if product.introductoryPrice!.numberOfPeriods == 1 { + priceText = "\(product.localizedIntroductoryPrice!) for \(product.localizedIntroductoryDuration!)" + + " (then \(priceText))" + } else { + priceText = "\(product.localizedIntroductoryPrice!) / \(product.localizedIntroductoryPeriod!)" + + " for \(product.localizedIntroductoryDuration!) (then \(priceText))" + } + } + self.priceLabel.text = priceText +} +``` + +*Note:* You have to `import StoreKit` wherever you use `SKProduct`. diff --git a/Documentation/Usage/Errors.md b/Documentation/Usage/Errors.md new file mode 100644 index 0000000..a428e48 --- /dev/null +++ b/Documentation/Usage/Errors.md @@ -0,0 +1,20 @@ +# Errors + +When calling `refresh()`, `purchase()` or `restorePurchases()`, the callback can return an `IAPError` if the state is `failed`. +Here is the list of `IAPErrorCode` you can receive: + +* Errors returned by `refresh()`, `purchase()` or `restorePurchases()` + - `libraryNotInitialized` - You must call the `initialize` fuction before using the library. + - `bundleIdentifierInvalid` - The Bundle Identifier is invalid. + - `validatorUrlInvalid` - The Validator URL String is invalid. + - `refreshReceiptFailed` - Failed to refresh the App Store receipt. + - `validateReceiptFailed` - Failed to validate the App Store receipt with Fovea. + - `readReceiptFailed` - Failed to read the receipt validation. + +* Errors returned by `refresh()` + - `refreshProductsFailed` - Failed to refresh products from the App Store. + +* Errors returned by `purchase()` + - `productNotFound` - The product was not found on the App Store and cannot be purchased. + - `cannotMakePurchase` - The user is not allowed to authorize payments. + - `alreadyPurchasing` - A purchase is already in progress. diff --git a/Documentation/Usage/Handling purchases.md b/Documentation/Usage/Handling purchases.md new file mode 100644 index 0000000..06febe8 --- /dev/null +++ b/Documentation/Usage/Handling purchases.md @@ -0,0 +1,93 @@ +# Handling purchases +Finally, the magic happened: a user purchased one of your products! Let's see how we handle the different types of products. + +- [Non-Consumables](#non-consumables) +- [Auto-Renewable Subscriptions](#auto-renewable-subscriptions) +- [Consumables](#consumables) +- [Non-Renewing Subscriptions](#non-renewing-subscriptions) + + +## Non-Consumables +Wherever your app needs to know if a non-consumable product has been purchased, use `InAppPurchase.hasActivePurchase(for: +productIdentifier)`. This will return true if the user currently owns the product. + +**Note:** The last known state for the user's purchases is stored as [UserDefaults](https://developer.apple.com/documentation/foundation/userdefaults). As such, their status is always available to your app, even when offline. + +If you have a server that needs to know about the purchase. You should rely on Fovea's webhook instead of doing anything in here. We will see that later in the [Server integration](server-integration.html) section. + + +## Auto-Renewable Subscriptions +As with non-consumables, you will use `InAppPurchase.hasActivePurchase(for: productIdentifier)` to check if the user is an active subscriber to a given product. + +You might also like to call refresh regularly, for example when entering your main view. When appropriate, the library will refresh the receipt to detect subscription renewals or expiry. + +As we've seend in the [Refreshing](refreshing.html) section: + +``` swift +override func viewWillAppear(_ animated: Bool) { + self.refreshView() + InAppPurchase.refresh(callback: { _ in + self.refreshView() + }) +} +``` + +**Note:** Don't be reluctant to call `refresh()` often. Internally, the library ensures heavy operation are only performed if necessary: for example when a subscription just expired. So in 99% of cases this call will result in no-operations. + + +## Consumables +If the purchased products in a **consumable**, your app is responsible for delivering the purchase then acknowlege that you've done so. Delivering generally consists in increasing a counter for some sort of virtual currency. + +Your app can be notified of a purchase at any time. So the library asks you to provide an **IAPPurchaseDelegate** from initialization. + +In `InAppPurchase.initialize()`, we can pass an **IAPPurchaseDelegate** instance. This object implements the **productPurchased(productIdentifier:)** function, which is called whenever a purchase is approved. + +Here's a example implementation: + +``` swift +class AppDelegate: UIResponder, UIApplicationDelegate, IAPPurchaseDelegate { + ... + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + InAppPurchase.initialize( + iapProducts: [...], + iapPurchaseDelegate: self, // ADDED: iapPurchaseDelegate + validatorUrlString: "https://validator.fovea.cc/v1/validate?appName=demo&apiKey=12345678") + } + + // IAPPurchaseDelegate implementation + func productPurchased(productIdentifier: String) { + // TODO + } +} +``` + +It's also important to know that when a purchase is approved, money isn't yet to reach your bank account. You have to acknowledge delivery of the (virtual) item to finalize the transaction. That is why we have to call `InAppPurchase.finishTransactions(for: productIdentifier)` as soon as we delivered the product. + +**Example** + +Let's define a class that adopts the **IAPPurchaseDelegate** protocol, it can very well be your application delegate. + +``` swift +func productPurchased(productIdentifier: String) { + switch productIdenfier { + case "10_silver": + addSilver(10) + case "100_silver": + addSilver(100) + } + InAppPurchase.finishTransactions(for: productIdentifier) + Analytics.trackEvent("purchase succeeded", productIdentifier) +} +``` + +Here, we implement our own unlocking logic and call `InAppPurchase.finishTransactions()` afterward (assuming `addSilver` is synchronous). + +*Note:* `productPurchased` is called when a purchase has been confirmed by Fovea's receipt validator. If you have a server, he probably already has been notified of this purchase using the webhook. + +**Reminder**: Keep in mind that purchase notifications might occur even if you never called the `InAppPurchase.purchase()` function: purchases can be made from another device or the AppStore, they can be approved by parents when the app isn't running, purchase flows can be interupted, etc. The pattern above ensures your app is always ready to handle purchase events. + + +## Non-Renewing Subscriptions +For non-renewing subscriptions, delivering consists in increasing the amount of time a user can access a given feature. Apple doesn't manage the length and expiry of non-renewing subscriptions: you have to do this yourself, as for consumables. + +Basically, everything is identical to consumables. diff --git a/Documentation/Usage/Initialization.md b/Documentation/Usage/Initialization.md new file mode 100644 index 0000000..18f2e54 --- /dev/null +++ b/Documentation/Usage/Initialization.md @@ -0,0 +1,46 @@ +# Initialization +Before everything else the library must be initialized. This has to happen as soon as possible. A good way is to call the `InAppPurchase.initialize()` method when the application did finish launching. In the background, this will load your products and refresh the status of purchases and subscriptions. + +`InAppPurchase.initialize()` requires the following arguments: +* `iapProducts` - An array of `IAPProduct` +* `validatorUrlString` - The validator url retrieved from [Fovea](https://billing.fovea.cc/?ref=iap-swift-lib) + +Each `IAPProduct` contains the following fields: +* `productIdentifier` - The product unique identifier +* `productType` - The `IAPProductType` (*consumable*, *nonConsumable*, *nonRenewingSubscription* or *autoRenewableSubscription*) + +*Example:* + +A good place is generally in your application delegate's `didFinishLaunchingWithOptions` function, like below: + +``` swift +import InAppPurchaseLib + +class AppDelegate: UIResponder, UIApplicationDelegate, IAPPurchaseDelegate { + ... + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + InAppPurchase.initialize( + iapProducts: [ + IAPProduct(productIdentifier: "monthly_plan", productType: .autoRenewableSubscription), + IAPProduct(productIdentifier: "yearly_plan", productType: .autoRenewableSubscription), + IAPProduct(productIdentifier: "disable_ads", productType: .nonConsumable) + ], + validatorUrlString: "https://validator.fovea.cc/v1/validate?appName=demo&apiKey=12345678") + } + + func productPurchased(productIdentifier: String) { + // ... process purchase (we'll see that later) + } +} +``` + +You should also call the `stop` method when the application will terminate, for proper cleanup. +``` swift + func applicationWillTerminate(_ application: UIApplication) { + InAppPurchase.stop() + } +``` + +For more advanced use cases, in particular when you have implemented user login, you'll have to make some adjustments. We'll learn more about this in the [Server integration](server-integration.html) section. + +*Tip:* If initialization was successful, you should see a new receipt validation event in [Fovea's Dashboard](https://billing-dashboard.fovea.cc/events). diff --git a/Documentation/Usage/Purchasing.md b/Documentation/Usage/Purchasing.md new file mode 100644 index 0000000..aede98d --- /dev/null +++ b/Documentation/Usage/Purchasing.md @@ -0,0 +1,65 @@ +# Purchasing +The purchase process is generally a little bit more involving than most people would expect. Why is it not just: purchase → on success unlock the feature? + +Several reasons: +- In-app purchases can be initiated outside the app +- In-app purchases can be deferred, pending parental approval +- Apple wants to be sure you delivered the product before charging the user + +That is why the process looks like so: +- being ready to handle purchase events from app startup +- finalizing transactions when product delivery is complete +- sending purchase request, for which successful doesn't always mean complete + +### Initiating a purchase +To initiate a purchase, use the `InAppPurchase.purchase()` function. It takes the `productIdentifier` and a `callback` function, called when the purchase has been processed. + +**Important**: Do not process the purchase here, we'll handle that later! + +From this callback, you can for example unlock the UI by hiding your loading indicator and display a message to the user. + +*Example:* + +``` swift +self.loaderView.show() +InAppPurchase.purchase( + productIdentifier: "my_product_id", + callback: { _ in + self.loaderView.hide() +}) +``` + +This simple example locks the UI with a loader when the purchase is in progress. We'll see later how the purchase has to be processed by your applicaiton. + +The callback also gives more information about the outcome of the purchase, you might want to use it to update your UI as well. Note that some events are useful for analytics. So here's a more complete example. + +``` swift +self.loaderView.show() +InAppPurchase.purchase( + productIdentifier: "my_product_id", + callback: { result in + self.loaderView.hide() + + switch result.state { + case .purchased: + // Product successfully purchased + // Reminder: Do not process the purchase here, only update your UI. + // that's why we do not send data to analytics. + openThankYouScreen() + case .failed: + // Purchase failed + // - Human formated reason can be found in result.localizedDescription + // - More details in either result.skError or result.iapError + showError(result.localizedDescription) + case .deferred: + // The purchase is deferred, waiting for the parent's approval + openWaitingParentApprovalScreen() + case .cancelled: + // The user canceled the request, generally only useful for analytics. + } +}) +``` + +If the purchase fails, result will contain either `.skError`, a [`SKError`](https://developer.apple.com/documentation/storekit/skerror/code) from StoreKit, or `.iapError`, an [`IAPError`](errors.html). + +*Tip:* After a successful purchase, you should see a new transaction in [Fovea's dashboard](https://billing-dashboard.fovea.cc/transactions). diff --git a/Documentation/Usage/Refreshing.md b/Documentation/Usage/Refreshing.md new file mode 100644 index 0000000..ff76a77 --- /dev/null +++ b/Documentation/Usage/Refreshing.md @@ -0,0 +1,13 @@ +# Refreshing +Data might change or not be yet available when your "product" view is presented. In order to properly handle those cases, you should refresh your view after refreshing in-app products metadata. You want to be sure you're displaying up-to-date information. + +To achieve this, call `InAppPurchase.refresh()` when your view is presented. + +``` swift +override func viewWillAppear(_ animated: Bool) { + self.refreshView() + InAppPurchase.refresh(callback: { _ in + self.refreshView() + }) +} +``` diff --git a/Documentation/Usage/Restoring purchases.md b/Documentation/Usage/Restoring purchases.md new file mode 100644 index 0000000..2eab331 --- /dev/null +++ b/Documentation/Usage/Restoring purchases.md @@ -0,0 +1,25 @@ +# Restoring purchases +Except if you only sell consumable products, Apple requires that you provide a "Restore Purchases" button to your users. In general, it is found in your application settings. + +Call this method when this button is pressed. + +``` swift +@IBAction func restorePurchases(_ sender: Any) { + self.loaderView.show() + InAppPurchase.restorePurchases(callback: { result in + self.loaderView.hide() + switch result.state { + case .succeeded: + if result.addedPurchases > 0 { + print("Restore purchases successful.") + } else { + print("No purchase to restore.") + } + case .failed: + print("Restore purchases failed.") + } + }) +} +``` + +The `callback` method is called once the operation is complete. You can use it to unlock the UI, by hiding your loader for example, and display the adapted message to the user. diff --git a/Documentation/Usage/Server integration.md b/Documentation/Usage/Server integration.md new file mode 100644 index 0000000..ed35b28 --- /dev/null +++ b/Documentation/Usage/Server integration.md @@ -0,0 +1,21 @@ +# Server integration +In more advanced use cases, you have a server component. Users are logged in and you'll like to unlock the content for this user on your server. The safest approach is to setup a [Webhook on Fovea](https://billing.fovea.cc/documentation/webhook/?ref=iap-swift-lib). You'll receive notifications from Fovea that transaction have been processed and/or subscriptions updated. + +The information sent from Fovea has been verified from Apple's server, which makes it way more trustable than information sent from your app itself. + +To take advantage of this, you have to inform the library of your application username. This `applicationUsername` can be provided as a parameter of the `InAppPurchase.initialize` method and updated later by changing the associated property. + +*Example:* +``` swift +InAppPurchase.initialize( + iapProducts: [...], + validatorUrlString: "..."), + applicationUsername: UserSession.getUserId()) + +// later ... +InAppPurchase.applicationUsername = UserSession.getUserId() +``` + +If a user account is mandatory in your app, you will want to delay calls to `InAppPurchase.initialize()` to when your user's session is ready. + +Do not hesitate to [contact Fovea](mailto:support@fovea.cc) for help. diff --git a/README.md b/README.md index ee9697f..31b7d43 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,8 @@

- +

-> An easy-to-use library for In-App Purchases, using Fovea.Billing for receipts validation. - -- [Features](#features) -- [Getting Started](#getting-started) - - [Requirements](#requirements) - - [Installation](#installation) -- [Usage](#usage) - - [Initialization](#initialization) - - [Displaying products](#displaying-products) - - [Displaying subscriptions](#displaying-subscriptions) - - [Refreshing](#refreshing) - - [Purchasing](#purchasing) - - [Making a purchase](#making-a-purchase) - - [Processing purchases](#processing-purchases) - - [Restoring purchases](#restoring-purchases) - - [Purchased products](#purchased-products) - - [Purchases information](#purchases-information) - - [Errors](#errors) -- [Server integration](#server-integration) -- [Xcode Demo Project](#xcode-demo-project) -- [References](#references) -- [Troubleshooting](#troubleshooting) -- [License](#license) +> An easy-to-use Swift library for In-App Purchases, using Fovea.Billing for receipts validation. # Features @@ -39,21 +17,9 @@ * ✅ Server integration with a Webhook # Getting Started -If you haven't already, I highly recommend your read the *Overview* and *Preparing* section of Apple's [In-App Purchase official documentation](https://developer.apple.com/in-app-purchase) - -## Requirements -* Configure your App and Xcode to support In-App Purchases. - * [AppStore Connect Setup](https://help.apple.com/app-store-connect/#/devb57be10e7) -* Create and configure your [Fovea.Billing](https://billing.fovea.cc/?ref=iap-swift-lib) project account: - * Set your bundle ID - * The iOS Shared Secret (or shared key) is to be retrieved from [AppStoreConnect](https://appstoreconnect.apple.com/) - * The iOS Subscription Status URL (only if you want subscriptions) +If you haven't already, I highly recommend your read the *Overview* and *Preparing* section of Apple's [In-App Purchase official documentation](https://developer.apple.com/in-app-purchase). ## Installation -

- -

- * Select your project in Xcode * Go to the section *Swift Package* * Click on *(+) Add Package Dependency* @@ -64,492 +30,117 @@ If you haven't already, I highly recommend your read the *Overview* and *Prepari *Note:* You have to `import InAppPurchaseLib` wherever you use the library. +## Micro Example -# Usage - -The process of implementing in-app purchases involves several steps: -1. Displaying the list of purchasable products -2. Initiating a purchase -3. Delivering and finalizing a purchase -4. Checking the current ownership of non-consumables and subscriptions -5. Implementing the Restore Purchases button - -## Initialization -Before everything else the library must be initialized. This has to happen as soon as possible. A good way is to call the `InAppPurchase.initialize()` method when the application did finish launching. In the background, this will load your products and refresh the status of purchases and subscriptions. - -`InAppPurchase.initialize()` accepts the following arguments: -* `iapProducts` - An array of **IAPProduct** (REQUIRED) -* `validatorUrlString` - The validator url retrieved from [Fovea](https://billing.fovea.cc/?ref=iap-swift-lib) (REQUIRED) -* `applicationUsername` - The user name, if your app implements user login (optional) - -Each **IAPProduct** contains the following fields: -* `productIdentifier` - The product unique identifier -* `productType` - The **IAPProductType** (`consumable`, `nonConsumable`, `nonRenewingSubscription` or `autoRenewableSubscription`) - -*Example:* - -A good place is generally in your application delegate's `didFinishLaunchingWithOptions` function, like below: - -``` swift +```swift +/** AppDelegate.swift */ +import UIKit import InAppPurchaseLib -class AppDelegate: UIResponder, UIApplicationDelegate, IAPPurchaseDelegate { - ... +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + // Initialize the library InAppPurchase.initialize( iapProducts: [ - IAPProduct(productIdentifier: "monthly_plan", productType: .autoRenewableSubscription), - IAPProduct(productIdentifier: "yearly_plan", productType: .autoRenewableSubscription), - IAPProduct(productIdentifier: "disable_ads", productType: .nonConsumable) + IAPProduct(productIdentifier: "my_product", productType: .nonConsumable) ], - validatorUrlString: "https://validator.fovea.cc/v1/validate?appName=demo&apiKey=12345678") + validatorUrlString: "https://validator.fovea.cc/v1/validate?appName=demo&apiKey=12345678" + ) + return true } - func productPurchased(productIdentifier: String) { - // ... process purchase (we'll see that later) - } -} -``` - -You should also call the `stop` method when the application will terminate, for proper cleanup. -``` swift func applicationWillTerminate(_ application: UIApplication) { + // clean InAppPurchase.stop() } -``` - -For more advanced use cases, in particular when you have implemented user login, you'll have to make some adjustments. We'll learn more about this in the [Server integration](#server-integration) section. - -*Tip:* If initialization was successful, you should see a new receipt validation event in [Fovea's Dashboard](https://billing-dashboard.fovea.cc/events). - -## Displaying products -Let's start with the simplest case: you have a single product. - -You can retrieve all information about this product using the function `InAppPurchase.getProductBy(identifier: "my_product_id")`. This returns an [SKProduct](https://developer.apple.com/documentation/storekit/skproduct) extended with helpful methods. - -Those are the most important: - - `productIdentifier: String` - The string that identifies the product to the Apple AppStore. - - `localizedTitle: String` - The name of the product, in the language of the device, as retrieved from the AppStore. - - `localizedDescription: String` - A description of the product, in the language of the device, as retrieved from the AppStore. - - `localizedPrice: String` - The cost of the product in the local currency (_read-only property added by this library_). - -*Example*: - -You can add a function similar to this to your view. - -``` swift -@objc func refreshView() { - guard let product: SKProduct = InAppPurchase.getProductBy(identifier: "my_product_id") else { - self.titleLabel.text = "Product unavailable" - return - } - self.titleLabel.text = product.localizedTitle - self.descriptionLabel.text = product.localizedDescription - self.priceLabel.text = product.localizedPrice -} -``` - -This example assumes `self.titleLabel` is a UILabel, etc. - -Make sure to call this function when the view appears on screen, for instance by calling it from [`viewWillAppear`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621510-viewwillappear). - -``` swift -override func viewWillAppear(_ animated: Bool) { - self.refreshView() } ``` -## Displaying subscriptions -For subscription products, you also have some data about subscription periods and introductory offers. - - - `func hasIntroductoryPriceEligible() -> Bool` - The product has an introductory price the user is eligible to. - - `localizedSubscriptionPeriod: String?` - The period of the subscription. - - `localizedIntroductoryPrice: String?` - The cost of the introductory offer if available in the local currency. - - `localizedIntroductoryPeriod: String?` - The subscription period of the introductory offer. - - `localizedIntroductoryDuration: String?` - The duration of the introductory offer. - -**Example** - -``` swift -@objc func refreshView() { - guard let product: SKProduct = InAppPurchase.getProductBy(identifier: "my_product_id") else { - self.titleLabel.text = "Product unavailable" - return - } - self.titleLabel.text = product.localizedTitle - self.descriptionLabel.text = product.localizedDescription - - // Format price text. Example: "0,99€ / month for 3 months (then 3,99 € / month)" - var priceText = "\(product.localizedPrice) / \(product.localizedSubscriptionPeriod!)" - if product.hasIntroductoryPriceEligible() { - if product.introductoryPrice!.numberOfPeriods == 1 { - priceText = "\(product.localizedIntroductoryPrice!) for \(product.localizedIntroductoryDuration!)" + - " (then \(priceText))" - } else { - priceText = "\(product.localizedIntroductoryPrice!) / \(product.localizedIntroductoryPeriod!)" + - " for \(product.localizedIntroductoryDuration!) (then \(priceText))" - } - } - self.priceLabel.text = priceText -} -``` - -*Note:* You have to `import StoreKit` wherever you use `SKProduct`. - -## Refreshing -Data might change or not be yet available when your "product" view is presented. In order to properly handle those cases, you should refresh your view after refreshing in-app products metadata. You want to be sure you're displaying up-to-date information. - -To achieve this, call `InAppPurchase.refresh()` when your view is presented. - -``` swift -override func viewWillAppear(_ animated: Bool) { - self.refreshView() - InAppPurchase.refresh(callback: { _ in - self.refreshView() - }) -} -``` - -## Purchasing -The purchase process is generally a little bit more involving than most people would expect. Why is it not just: purchase → on success unlock the feature? - -Several reasons: -- In-app purchases can be initiated outside the app -- In-app purchases can be deferred, pending parental approval -- Apple wants to be sure you delivered the product before charging the user - -That is why the process looks like so: -- being ready to handle purchase events from app startup -- finalizing transactions when product delivery is complete -- sending purchase request, for which successful doesn't always mean complete - -### Initiating a purchase -To initiate a purchase, use the `InAppPurchase.purchase()` function. It takes the `productIdentifier` and a `callback` function, called when the purchase has been processed. - -**Important**: Do not process the purchase here, we'll handle that later! - -From this callback, you can for example unlock the UI by hiding your loading indicator and display a message to the user. - -*Example:* - -``` swift -self.loaderView.show() -InAppPurchase.purchase( - productIdentifier: "my_product_id", - callback: { _ in - self.loaderView.hide() -}) -``` - -This simple example locks the UI with a loader when the purchase is in progress. We'll see later how the purchase has to be processed by your applicaiton. - -The callback also gives more information about the outcome of the purchase, you might want to use it to update your UI as well. Note that some events are useful for analytics. So here's a more complete example. - -``` swift -self.loaderView.show() -InAppPurchase.purchase( - productIdentifier: "my_product_id", - callback: { result in - self.loaderView.hide() +```swift +/** ViewController.swift */ +import UIKit +import StoreKit +import InAppPurchaseLib - switch result.state { - case .purchased: - // Product successfully purchased - // Reminder: Do not process the purchase here, only update your UI. - // that's why we do not send data to analytics. - openThankYouScreen() - case .failed: - // Purchase failed - // - Human formated reason can be found in result.localizedDescription - // - More details in either result.skError or result.iapError - showError(result.localizedDescription) - case .deferred: - // The purchase is deferred, waiting for the parent's approval - openWaitingParentApprovalScreen() - case .cancelled: - // The user canceled the request, generally only useful for analytics. +class ViewController: UIViewController { + private var loaderView = LoaderView() + @IBOutlet weak var statusLabel: UILabel! + @IBOutlet weak var productTitleLabel: UILabel! + @IBOutlet weak var productDescriptionLabel: UILabel! + @IBOutlet weak var purchaseButton: UIButton! + @IBOutlet weak var restorePurchasesButton: UIButton! + + override func viewDidLoad() { + super.viewDidLoad() + // Add action for purchases and restore butons. + purchaseButton.addTarget(self, action: #selector(self.purchase), for: .touchUpInside) + restorePurchasesButton.addTarget(self, action: #selector(self.restorePurchases), for: .touchUpInside) } -}) -``` - -If the purchase fails, result will contain either `.skError`, a [`SKError`](https://developer.apple.com/documentation/storekit/skerror/code) from StoreKit, or `.iapError`, an [`IAPError`](#errors). - -*Tip:* After a successful purchase, you should see a new transaction in [Fovea's dashboard](https://billing-dashboard.fovea.cc/transactions). - -## Handling purchases -Finally, the magic happened: a user purchased one of your products! Let's see how we handle the different types of products. -### Non-Consumables -Wherever your app needs to know if a non-consumable product has been purchased, use `InAppPurchase.hasActivePurchase(for: -productIdentifier)`. This will return true if the user currently owns the product. - -**Note:** The last known state for the user's purchases is stored as [UserDefaults](https://developer.apple.com/documentation/foundation/userdefaults). As such, their status is always available to your app, even when offline. - -If you have a server that needs to know about the purchase. You should rely on Fovea's webhook instead of doing anything in here. We will see that later in the [Server integration](#server-integration) section. - -### Auto-Renewable Subscriptions -As with non-consumables, you will use `InAppPurchase.hasActivePurchase(for: productIdentifier)` to check if the user is an active subscriber to a given product. - -You might also like to call refresh regularly, for example when entering your main view. When appropriate, the library will refresh the receipt to detect subscription renewals or expiry. - -As we've seend in the [Refreshing](#refreshing) section: - -``` swift -override func viewWillAppear(_ animated: Bool) { - self.refreshView() - InAppPurchase.refresh(callback: { _ in + override func viewWillAppear(_ animated: Bool) { + self.refreshView() + InAppPurchase.refresh(callback: { _ in self.refreshView() - }) -} -``` - -**Note:** Don't be reluctant to call `refresh()` often. Internally, the library ensures heavy operation are only performed if necessary: for example when a subscription just expired. So in 99% of cases this call will result in no-operations. - -### Consumables -If the purchased products in a **consumable**, your app is responsible for delivering the purchase then acknowlege that you've done so. Delivering generally consists in increasing a counter for some sort of virtual currency. - -Your app can be notified of a purchase at any time. So the library asks you to provide an **IAPPurchaseDelegate** from initialization. - -In `InAppPurchase.initialize()`, we can pass an **IAPPurchaseDelegate** instance. This object implements the **productPurchased(productIdentifier:)** function, which is called whenever a purchase is approved. - -Here's a example implementation: - -``` swift -class AppDelegate: UIResponder, UIApplicationDelegate, IAPPurchaseDelegate { - ... - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - InAppPurchase.initialize( - iapProducts: [...], - iapPurchaseDelegate: self, // ADDED: iapPurchaseDelegate - validatorUrlString: "https://validator.fovea.cc/v1/validate?appName=demo&apiKey=12345678") + }) } - // IAPPurchaseDelegate implementation - func productPurchased(productIdentifier: String) { - // TODO + func refreshView() { + guard let product: SKProduct = InAppPurchase.getProductBy(identifier: "my_product") else { + self.productTitleLabel.text = "Product unavailable" + return + } + // Display product information. + productTitleLabel.text = product.localizedTitle + productDescriptionLabel.text = product.localizedDescription + purchaseButton.setTitle(product.localizedPrice, for: .normal) + + // Disable the button if the product has already been purchased. + if InAppPurchase.hasActivePurchase(for: "my_product") { + statusLabel.text = "OWNED" + purchaseButton.isPointerInteractionEnabled = false + } } -} -``` - -It's also important to know that when a purchase is approved, money isn't yet to reach your bank account. You have to acknowledge delivery of the (virtual) item to finalize the transaction. That is why we have to call `InAppPurchase.finishTransactions(for: productIdentifier)` as soon as we delivered the product. - -**Example** -Let's define a class that adopts the **IAPPurchaseDelegate** protocol, it can very well be your application delegate. - -``` swift -func productPurchased(productIdentifier: String) { - switch productIdenfier { - case "10_silver": - addSilver(10) - case "100_silver": - addSilver(100) + @IBAction func purchase(_ sender: Any) { + self.loaderView.show() + InAppPurchase.purchase( + productIdentifier: "my_product", + callback: { result in + self.loaderView.hide() + }) } - InAppPurchase.finishTransactions(for: productIdentifier) - Analytics.trackEvent("purchase succeeded", productIdentifier) -} -``` - -Here, we implement our own unlocking logic and call `InAppPurchase.finishTransactions()` afterward (assuming `addSilver` is synchronous). -*Note:* `productPurchased` is called when a purchase has been confirmed by Fovea's receipt validator. If you have a server, he probably already has been notified of this purchase using the webhook. - -**Reminder**: Keep in mind that purchase notifications might occur even if you never called the `InAppPurchase.purchase()` function: purchases can be made from another device or the AppStore, they can be approved by parents when the app isn't running, purchase flows can be interupted, etc. The pattern above ensures your app is always ready to handle purchase events. - -### Non-Renewing Subscriptions -For non-renewing subscriptions, delivering consists in increasing the amount of time a user can access a given feature. Apple doesn't manage the length and expiry of non-renewing subscriptions: you have to do this yourself, as for consumables. - -Basically, everything is identical to consumables. - -## Restoring purchases -Except if you only sell consumable products, Apple requires that you provide a "Restore Purchases" button to your users. In general, it is found in your application settings. - -Call this method when this button is pressed. - -``` swift -@IBAction func restorePurchases(_ sender: Any) { - self.loaderView.show() - InAppPurchase.restorePurchases(callback: { result in + @IBAction func restorePurchases(_ sender: Any) { + self.loaderView.show() + InAppPurchase.restorePurchases(callback: { result in self.loaderView.hide() - switch result.state { - case .succeeded: - if result.addedPurchases > 0 { - print("Restore purchases successful.") - } else { - print("No purchase to restore.") - } - case .failed: - print("Restore purchases failed.") - } - }) -} -``` - -The `callback` method is called once the operation is complete. You can use it to unlock the UI, by hiding your loader for example, and display the adapted message to the user. - - -## Displaying products with purchases -In your store screen, where you present your products titles and prices with a purchase button, there are some cases to handle that we skipped. Owned products and deferred purchases. - -### Owned products -Non-consumables and active auto-renewing subscriptions cannot be purchased again. You should adjust your UI to reflect that state. Refer to `InAppPurchase.hasActivePurchase()` to and to the example later in this section. - -### Deferred purchases -Apple's **Ask to Buy** feature lets parents approve any purchases initiated by children, including in-app purchases. - -With **Ask to Buy** enabled, when a child requests to make a purchase, the app is notified that the purchase is awaiting the parent’s approval in the purchase callback: - -``` swift -InAppPurchase.purchase( - productIdentifier: productIdentifier, - callback: { result in - switch result.state { - case .deferred: - // Pending parent approval - } -}) -``` - -In the _deferred_ case, the child has been notified by StoreKit that the parents have to approve the purchase. He might then close the app and come back later. You don't have much to do, but to display in your UI that there is a purchase waiting for parental approval in your views. - -We will use the `hasDeferredTransaction` method: - -``` swift -InAppPurchase.hasDeferredTransaction(for productIdentifier: String) -> Bool -``` - -### Example -Here's an example that covers what has been discussed above. We will update our example `refreshView` function from before: - -``` swift -@objc func refreshView() { - guard let product: SKProduct = InAppPurchase.getProductBy(identifier: "my_product_id") else { - self.titleLabel.text = "Product unavailable" - return - } - self.titleLabel.text = product.localizedTitle - // ... - - // "Ask to Buy" deferred purchase waiting for parent's approval - if InAppPurchase.hasDeferredTransaction(for: "my_product_id") { - self.statusLabel.text = "Waiting for Approval..." - self.purchaseButton.isPointerInteractionEnabled = false - } - // "Owned" product - else if InAppPurchase.hasActivePurchase(for: "my_product_id") { - self.statusLabel.text = "OWNED" - self.purchaseButton.isPointerInteractionEnabled = false - } - else { - self.purchaseButton.isPointerInteractionEnabled = true + }) } } ``` -When a product is owned or has a deferred purchase, we make sure the purchase button is grayed out. We also use a status label to display some details. Of course, you are free to design your UI as you see fit. +# Documentation +- [Getting Started](https://iridescent-dev.github.io/iap-swift-lib/Getting%20Started.html) +- [Usage](https://iridescent-dev.github.io/iap-swift-lib/Usage.html) +- [API documentation](https://iridescent-dev.github.io/iap-swift-lib/API%20documentation.html) -## Errors - -When calling `refresh()`, `purchase()` or `restorePurchases()`, the callback can return an `IAPError` if the state is `failed`. -Here is the list of `IAPErrorCode` you can receive: - -* Errors returned by `refresh()`, `purchase()` or `restorePurchases()` - - `libraryNotInitialized` - You must call the `initialize` fuction before using the library. - - `bundleIdentifierInvalid` - The Bundle Identifier is invalid. - - `validatorUrlInvalid` - The Validator URL String is invalid. - - `refreshReceiptFailed` - Failed to refresh the App Store receipt. - - `validateReceiptFailed` - Failed to validate the App Store receipt with Fovea. - - `readReceiptFailed` - Failed to read the receipt validation. - -* Errors returned by `refresh()` - - `refreshProductsFailed` - Failed to refresh products from the App Store. - -* Errors returned by `purchase()` - - `productNotFound` - The product was not found on the App Store and cannot be purchased. - - `cannotMakePurchase` - The user is not allowed to authorize payments. - - `alreadyPurchasing` - A purchase is already in progress. - -## Analytics -Tracking the purchase flow is a common things in apps. Especially as it's core to your revenue model. - -We can track 5 events, which step in the purchase pipeline a user reached. -1. `purchase initiated` -2. `purchase cancelled` -3. `purchase failed` -4. `purchase deferred` -5. `purchase succeeded` - -Here's a quick example showing how to implement this correctly. - -``` swift -func makePurchase() { - Analytics.trackEvent("purchase initiated") - InAppPurchase.purchase( - productIdentifier: "my_product_id", - callback: { result in - switch result.state { - case .purchased: - // Reminder: We are not processing the purchase here, only updating your UI. - // That's why we do not send an event to analytics. - case .failed: - Analytics.trackEvent("purchase failed") - case .deferred: - Analytics.trackEvent("purchase deferred") - case .cancelled: - Analytics.trackEvent("purchase cancelled") - } - }) -} - -// IAPPurchaseDelegate implementation -func productPurchased(productIdentifier: String) { - Analytics.trackEvent("purchase succeeded") - InAppPurchase.finishTransactions(for: productIdentifier) -} -``` - -The important part to remember is that a purchase can occur outside your app (or be approved when the app is not running), that's why tracking `purchase succeeded` has to be part of the `productPurchased` delegate function. - -Refer to the [Consumables](#consumables) section to learn more about the `productPurchased` function. - -## Server integration -In more advanced use cases, you have a server component. Users are logged in and you'll like to unlock the content for this user on your server. The safest approach is to setup a [Webhook on Fovea](https://billing.fovea.cc/documentation/webhook/?ref=iap-swift-lib). You'll receive notifications from Fovea that transaction have been processed and/or subscriptions updated. - -The information sent from Fovea has been verified from Apple's server, which makes it way more trustable than information sent from your app itself. - -To take advantage of this, you have to inform the library of your application username. This `applicationUsername` can be provided as a parameter of the `InAppPurchase.initialize` method and updated later by changing the associated property. - -*Example:* -``` swift -InAppPurchase.initialize( - iapProducts: [...], - validatorUrlString: "..."), - applicationUsername: UserSession.getUserId()) - -// later ... -InAppPurchase.applicationUsername = UserSession.getUserId() -``` - -If a user account is mandatory in your app, you will want to delay calls to `InAppPurchase.initialize()` to when your user's session is ready. - -Do not hesitate to [contact Fovea](mailto:support@fovea.cc) for help. +See also: +- [In-App Purchase official documentation](https://developer.apple.com/in-app-purchase) +- [StoreKit Documentation](https://developer.apple.com/documentation/storekit/in-app_purchase) # Xcode Demo Project Do not hesitate to check the demo project available on here: [iap-swift-lib-demo](https://github.com/iridescent-dev/iap-swift-lib-demo). -# References -- [API documentation](https://billing.fovea.cc/iap-swift-lib/api) -- [StoreKit Documentation](https://developer.apple.com/documentation/storekit/in-app_purchase) - # Coding -Generate the documentation, using [this fork](https://github.com/johankool/swift-doc/tree/access-level-option) of swift-doc (on `--minimum-access-level` is part of the main distrib). - +Generate the documentation, using [Jazzy](https://github.com/realm/jazzy) by running the following command: ``` -swift-doc generate sources --module-name InAppPurchase --format html --output docs --minimum-access-level public --base-url /iap-swift-lib/ +jazzy ``` # Troubleshooting Common issues are covered here: https://github.com/iridescent-dev/iap-swift-lib/wiki/Troubleshooting + # License InAppPurchaseLib is open-sourced library licensed under the MIT License. See [LICENSE](LICENSE) for details. diff --git a/Sources/InAppPurchaseLib/Common/IAPCallback.swift b/Sources/InAppPurchaseLib/Common/IAPCallback.swift index ac65678..0e3309b 100644 --- a/Sources/InAppPurchaseLib/Common/IAPCallback.swift +++ b/Sources/InAppPurchaseLib/Common/IAPCallback.swift @@ -10,6 +10,8 @@ import StoreKit public typealias IAPPurchaseCallback = (IAPPurchaseResult) -> Void + +/// The result returned in the `purchase()` callback. public struct IAPPurchaseResult { public internal(set) var state: IAPPurchaseResultState public internal(set) var iapError: IAPError? = nil @@ -22,15 +24,22 @@ public struct IAPPurchaseResult { } } +/// The list of the different states of the `IAPPurchaseResult`. public enum IAPPurchaseResultState { + /// The purchase was successful. case purchased + /// Puchase failed. case failed + /// The purchase was cancelled by the user. case cancelled + /// The purchase is deferred. case deferred } public typealias IAPRefreshCallback = (IAPRefreshResult) -> Void + +/// The result returned in the `refresh()` or `restorePurchases()` callback. public struct IAPRefreshResult { public internal(set) var state: IAPRefreshResultState public internal(set) var iapError: IAPError? = nil @@ -38,8 +47,12 @@ public struct IAPRefreshResult { public internal(set) var updatedPurchases: Int = 0 } +/// The list of the different states of the `IAPRefreshResult`. public enum IAPRefreshResultState { + /// Refresh was successful. case succeeded + /// Refresh failed. case failed + /// Refresh has been skipped because it is not necessary. case skipped } diff --git a/Sources/InAppPurchaseLib/Common/IAPError.swift b/Sources/InAppPurchaseLib/Common/IAPError.swift index 748f9ec..b659701 100644 --- a/Sources/InAppPurchaseLib/Common/IAPError.swift +++ b/Sources/InAppPurchaseLib/Common/IAPError.swift @@ -12,23 +12,49 @@ public protocol IAPErrorProtocol: LocalizedError { var code: IAPErrorCode { get } } +/// The list of error codes that can be returned by the library. public enum IAPErrorCode { + /* MARK: - Errors returned by `refresh()`, `purchase()` or `restorePurchases()` */ + /// You must call the `initialize` fuction before using the library. case libraryNotInitialized - case productNotFound - case cannotMakePurchase - case alreadyPurchasing + /// The Bundle Identifier is invalid. case bundleIdentifierInvalid + + /// The Validator URL String is invalid. case validatorUrlInvalid + + /// Failed to refresh the App Store receipt. case refreshReceiptFailed + + /// Failed to validate the App Store receipt with Fovea. case validateReceiptFailed + + /// Failed to read the receipt validation. case readReceiptFailed + /* MARK: - Errors returned by `refresh()` */ + /// Failed to refresh products from the App Store. case refreshProductsFailed + + /* MARK: - Errors returned by `purchase()` */ + /// The product was not found on the App Store and cannot be purchased. + case productNotFound + + /// The user is not allowed to authorize payments. + case cannotMakePurchase + + /// A purchase is already in progress. + case alreadyPurchasing } +/// When calling `refresh()`, `purchase()` or `restorePurchases()`, the callback can return an `IAPError` if the state is `failed`. public struct IAPError: IAPErrorProtocol { + /// The error code. + /// - See also: `IAPErrorCode`. public var code: IAPErrorCode + + /// The error description. public var localizedDescription: String { switch code { case .libraryNotInitialized: diff --git a/Sources/InAppPurchaseLib/InAppPurchase.swift b/Sources/InAppPurchaseLib/InAppPurchase.swift index 5eb946f..caa0efe 100644 --- a/Sources/InAppPurchaseLib/InAppPurchase.swift +++ b/Sources/InAppPurchaseLib/InAppPurchase.swift @@ -8,7 +8,7 @@ import Foundation import StoreKit - +/// public class InAppPurchase: NSObject, InAppPurchaseLib { // InAppPurchaseLib version number. internal static let versionNumber = "1.0.2" @@ -58,7 +58,7 @@ public class InAppPurchase: NSObject, InAppPurchaseLib { /// Refresh Product list and user Receipt. /// - Parameter callback: The function that will be called after processing. - /// - See also:`IAPRefreshCallback` and `IAPRefreshResult`. + /// - See also: `IAPRefreshResult` public static func refresh(callback: @escaping IAPRefreshCallback) { if !initialized { callback(IAPRefreshResult(state: .failed, iapError: IAPError(code: .libraryNotInitialized))) @@ -124,7 +124,7 @@ public class InAppPurchase: NSObject, InAppPurchaseLib { /* MARK: - Products information */ /// Gets all products retrieved from the App Store /// - Returns: An array of products. - /// - See also: `SKProduct`. + /// - See also: `SKProduct` public static func getProducts() -> Array { return IAPProductService.shared.getProducts() } @@ -132,7 +132,7 @@ public class InAppPurchase: NSObject, InAppPurchaseLib { /// Gets the product by its identifier from the list of products retrieved from the App Store. /// - Parameter identifier: The identifier of the product. /// - Returns: The product if it was retrieved from the App Store. - /// - See also: `SKProduct`. + /// - See also: `SKProduct` public static func getProductBy(identifier: String) -> SKProduct? { return IAPProductService.shared.getProductBy(identifier: identifier) } @@ -150,7 +150,7 @@ public class InAppPurchase: NSObject, InAppPurchaseLib { /// - productIdentifier: The identifier of the product to purchase. /// - quantity: The quantity to purchase (default value = 1). /// - callback: The function that will be called after processing. - /// - See also:`IAPPurchaseCallback` and `IAPPurchaseResult`. + /// - See also: `IAPPurchaseResult` public static func purchase(productIdentifier: String, quantity: Int, callback: @escaping IAPPurchaseCallback) { if !initialized { callback(IAPPurchaseResult(state: .failed, iapError: IAPError(code: .libraryNotInitialized))) @@ -166,7 +166,7 @@ public class InAppPurchase: NSObject, InAppPurchaseLib { /// Restore purchased products. /// - Parameter callback: The function that will be called after processing. - /// - See also:`IAPRefreshCallback` and `IAPRefreshResult`. + /// - See also: `IAPRefreshResult` public static func restorePurchases(callback: @escaping IAPRefreshCallback) { if !initialized { callback(IAPRefreshResult(state: .failed, iapError: IAPError(code: .libraryNotInitialized))) diff --git a/Sources/InAppPurchaseLib/InAppPurchaseLib.swift b/Sources/InAppPurchaseLib/InAppPurchaseLib.swift index 8e746ab..f1bdbf2 100644 --- a/Sources/InAppPurchaseLib/InAppPurchaseLib.swift +++ b/Sources/InAppPurchaseLib/InAppPurchaseLib.swift @@ -9,7 +9,7 @@ import Foundation import StoreKit -/// The protocol that `InAppPurchase`` adopts. +/// The protocol that `InAppPurchase` adopts. public protocol InAppPurchaseLib { /// The array of `IAPProduct`. static var iapProducts: Array { get } @@ -33,20 +33,20 @@ public protocol InAppPurchaseLib { /// Refresh Product list and user Receipt. /// - Parameter callback: The function that will be called after processing. - /// - See also:`IAPRefreshCallback` and `IAPRefreshResult`. + /// - See also:`IAPRefreshResult` static func refresh(callback: @escaping IAPRefreshCallback) -> Void /* MARK: - Products information */ /// Gets all products retrieved from the App Store /// - Returns: An array of products. - /// - See also: `SKProduct`. + /// - See also: `SKProduct` static func getProducts() -> Array /// Gets the product by its identifier from the list of products retrieved from the App Store. /// - Parameter identifier: The identifier of the product. /// - Returns: The product if it was retrieved from the App Store. - /// - See also: `SKProduct`. + /// - See also: `SKProduct` static func getProductBy(identifier: String) -> SKProduct? @@ -60,12 +60,12 @@ public protocol InAppPurchaseLib { /// - productIdentifier: The identifier of the product to purchase. /// - quantity: The quantity to purchase (default value = 1). /// - callback: The function that will be called after processing. - /// - See also:`IAPPurchaseCallback` and `IAPPurchaseResult`. + /// - See also:`IAPPurchaseResult` static func purchase(productIdentifier: String, quantity: Int, callback: @escaping IAPPurchaseCallback) -> Void /// Restore purchased products. /// - Parameter callback: The function that will be called after processing. - /// - See also:`IAPRefreshCallback` and `IAPRefreshResult`. + /// - See also:`IAPRefreshResult` static func restorePurchases(callback: @escaping IAPRefreshCallback) -> Void /// Finish all transactions for the product. @@ -116,8 +116,7 @@ public extension InAppPurchaseLib { } -/* MARK: - The protocol that you must adopt. */ -/// The protocol that you must adopt if you have `consumable` and/or `nonRenewingSubscription` products. +/// The protocol that you must adopt if you have *consumable* and/or *non-renewing subscription* products. public protocol IAPPurchaseDelegate { /// Called when a product is newly purchased, updated or restored. /// - Parameter productIdentifier: The identifier of the product. @@ -127,7 +126,7 @@ public protocol IAPPurchaseDelegate { } -/// The default implementation of `IAPPurchaseDelegate` if no other is provided. +/// The default implementation of `IAPPurchaseDelegate` if no other is provided. It is enough if you only have *non-consumable* and/or *auto-renewable subscription* products. public class DefaultPurchaseDelegate: IAPPurchaseDelegate { public init(){} diff --git a/Sources/InAppPurchaseLib/Product/IAPProduct.swift b/Sources/InAppPurchaseLib/Product/IAPProduct.swift index 74e919e..115a87e 100644 --- a/Sources/InAppPurchaseLib/Product/IAPProduct.swift +++ b/Sources/InAppPurchaseLib/Product/IAPProduct.swift @@ -7,7 +7,6 @@ import Foundation - public struct IAPProduct { /// The identifier of the product. diff --git a/docs/API documentation.html b/docs/API documentation.html new file mode 100644 index 0000000..70cd86c --- /dev/null +++ b/docs/API documentation.html @@ -0,0 +1,680 @@ + + + + API documentation Reference + + + + + + + + + + + + + + + + +
+

+ + InAppPurchaseLib documentation + + (77% documented) +

+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ +
+ + + +
+ +
+ +
+
+

API documentation

+

Classes and Protocols

+ +

The most important class is InAppPurchase. All the functions you need are defined in this class.

+ +

If you have consumable and/or non-renewing subscription products in your application, you must have a class that adopts the IAPPurchaseDelegate protocol.

+

Products

+ + +

Callbacks

+ +

refresh(), purchase() and restorePurchases() are asynchronous functions. You must provide a callback that will allow you to perform actions depending on the result.

+ + +

Errors

+ +

When calling refresh(), purchase() or restorePurchases(), the callback can return an IAPError if the state is failed. Look at IAPErrorCode to see the list of error codes you can receive.

+ +
+
+ +
+
+
+
    +
  • +
    + + + + InAppPurchase + +
    +
    +
    +
    +
    +
    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public class InAppPurchase : NSObject, InAppPurchaseLib
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + InAppPurchaseLib + +
    +
    +
    +
    +
    +
    +

    The protocol that InAppPurchase adopts.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public protocol InAppPurchaseLib
    + +
    +
    +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    The default implementation of IAPPurchaseDelegate if no other is provided. It is enough if you only have non-consumable and/or auto-renewable subscription products.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public class DefaultPurchaseDelegate : IAPPurchaseDelegate
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + IAPPurchaseDelegate + +
    +
    +
    +
    +
    +
    +

    The protocol that you must adopt if you have consumable and/or non-renewing subscription products.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public protocol IAPPurchaseDelegate
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + IAPProduct + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct IAPProduct
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + IAPProductType + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public enum IAPProductType
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + SKProduct + +
    +
    +
    +
    +
    +
    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    extension SKProduct
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + IAPPeriodFormat + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public enum IAPPeriodFormat
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + IAPPurchaseCallback + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public typealias IAPPurchaseCallback = (IAPPurchaseResult) -> Void
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + IAPRefreshCallback + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public typealias IAPRefreshCallback = (IAPRefreshResult) -> Void
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + IAPPurchaseResult + +
    +
    +
    +
    +
    +
    +

    The result returned in the purchase() callback.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct IAPPurchaseResult
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + IAPRefreshResult + +
    +
    +
    +
    +
    +
    +

    The result returned in the refresh() or restorePurchases() callback.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct IAPRefreshResult
    + +
    +
    +
    +
    +
  • +
  • + +
    +
    +
    +
    +
    +

    The list of the different states of the IAPPurchaseResult.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public enum IAPPurchaseResultState
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + IAPRefreshResultState + +
    +
    +
    +
    +
    +
    +

    The list of the different states of the IAPRefreshResult.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public enum IAPRefreshResultState
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + IAPError + +
    +
    +
    +
    +
    +
    +

    When calling refresh(), purchase() or restorePurchases(), the callback can return an IAPError if the state is failed.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public struct IAPError : IAPErrorProtocol
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + IAPErrorCode + +
    +
    +
    +
    +
    +
    +

    The list of error codes that can be returned by the library.

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public enum IAPErrorCode
    + +
    +
    +
    +
    +
  • +
  • +
    + + + + IAPErrorProtocol + +
    +
    +
    +
    +
    +
    +

    Undocumented

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public protocol IAPErrorProtocol : LocalizedError
    + +
    +
    +
    +
    +
  • +
+
+
+
+ +
+
+ + + + diff --git a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Classes/DefaultPurchaseDelegate.html b/docs/Classes/DefaultPurchaseDelegate.html similarity index 50% rename from docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Classes/DefaultPurchaseDelegate.html rename to docs/Classes/DefaultPurchaseDelegate.html index 3a8424b..5e4025c 100644 --- a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Classes/DefaultPurchaseDelegate.html +++ b/docs/Classes/DefaultPurchaseDelegate.html @@ -4,116 +4,163 @@ DefaultPurchaseDelegate Class Reference - + + + + + + -
-
-

InAppPurchaseLib (52% documented)

-

View on GitHub

-
-
-
-
+

+ + InAppPurchaseLib documentation + + (77% documented)

-
+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ + + + + + + +
+
-
    +
    • @@ -202,14 +252,15 @@

      Parameters

-
- - + + diff --git a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Classes/InAppPurchase.html b/docs/Classes/InAppPurchase.html similarity index 87% rename from docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Classes/InAppPurchase.html rename to docs/Classes/InAppPurchase.html index b43f50e..269267c 100644 --- a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Classes/InAppPurchase.html +++ b/docs/Classes/InAppPurchase.html @@ -4,116 +4,163 @@ InAppPurchase Class Reference - + + + + + + -
-
-

InAppPurchaseLib (52% documented)

-

View on GitHub

-
-
-
-
+

+ + InAppPurchaseLib documentation + + (77% documented)

-
+ +

+

+ +
+

+ +

+ + + View on GitHub + +

+ + + + +
-
-
    +
    • @@ -446,7 +495,7 @@

      Products information

      Gets all products retrieved from the App Store

      See

      - See also: SKProduct. + See also: SKProduct
      @@ -482,7 +531,7 @@

      Return Value

      Gets the product by its identifier from the list of products retrieved from the App Store.

      See

      - See also: SKProduct. + See also: SKProduct
      @@ -533,7 +582,7 @@

      Purchasing and Restoring

      -
        +
        • @@ -581,7 +630,7 @@

          Return Value

          Request a Payment from the App Store.

          See

          - See also:IAPPurchaseCallback and IAPPurchaseResult. + See also: IAPPurchaseResult
          @@ -590,7 +639,7 @@

          Return Value

          Declaration

          Swift

          -
          public static func purchase(productIdentifier: String, quantity: Int, callback: @escaping IAPPurchaseCallback)
          +
          public static func purchase(productIdentifier: String, quantity: Int, callback: @escaping IAPPurchaseCallback)
          @@ -656,7 +705,7 @@

          Parameters

          Restore purchased products.

          See

          - See also:IAPRefreshCallback and IAPRefreshResult. + See also: IAPRefreshResult
          @@ -665,7 +714,7 @@

          Parameters

          Declaration

          Swift

          -
          public static func restorePurchases(callback: @escaping IAPRefreshCallback)
          +
          public static func restorePurchases(callback: @escaping IAPRefreshCallback)
          @@ -799,7 +848,7 @@

          Purchases information

          -
            +
            • @@ -1014,14 +1063,15 @@

              Return Value

            - - - + + diff --git a/docs/jazzy/Enums/IAPErrorCode.html b/docs/Enums/IAPErrorCode.html similarity index 62% rename from docs/jazzy/Enums/IAPErrorCode.html rename to docs/Enums/IAPErrorCode.html index b78db89..16f2456 100644 --- a/docs/jazzy/Enums/IAPErrorCode.html +++ b/docs/Enums/IAPErrorCode.html @@ -4,116 +4,163 @@ IAPErrorCode Enumeration Reference - + + + + + + -
            -
            -

            InAppPurchaseLib (52% documented)

            -

            View on GitHub

            -
            -
            -
            -
            +

            + + InAppPurchaseLib documentation + + (77% documented)

            -
            + +

            +

            + +
            +

            + +

            + + + View on GitHub + +

            + + + + + + + +
            +
            -
              +
              + + +
              + +

              Errors returned by refresh(), purchase() or restorePurchases()

              +

              +
              +
              +
              • @@ -140,7 +199,7 @@

                IAPErrorCode

                -

                Undocumented

                +

                You must call the initialize fuction before using the library.

                @@ -157,9 +216,9 @@

                Declaration

              • @@ -167,14 +226,14 @@

                Declaration

                -

                Undocumented

                +

                The Bundle Identifier is invalid.

                Declaration

                Swift

                -
                case productNotFound
                +
                case bundleIdentifierInvalid
                @@ -184,9 +243,9 @@

                Declaration

              • @@ -194,14 +253,14 @@

                Declaration

                -

                Undocumented

                +

                The Validator URL String is invalid.

                Declaration

                Swift

                -
                case cannotMakePurchase
                +
                case validatorUrlInvalid
                @@ -211,9 +270,9 @@

                Declaration

              • @@ -221,14 +280,14 @@

                Declaration

                -

                Undocumented

                +

                Failed to refresh the App Store receipt.

                Declaration

                Swift

                -
                case alreadyPurchasing
                +
                case refreshReceiptFailed
                @@ -238,9 +297,9 @@

                Declaration

              • @@ -248,14 +307,14 @@

                Declaration

                -

                Undocumented

                +

                Failed to validate the App Store receipt with Fovea.

                Declaration

                Swift

                -
                case bundleIdentifierInvalid
                +
                case validateReceiptFailed
                @@ -265,9 +324,9 @@

                Declaration

              • @@ -275,26 +334,39 @@

                Declaration

                -

                Undocumented

                +

                Failed to read the receipt validation.

                Declaration

                Swift

                -
                case validatorUrlInvalid
                +
                case readReceiptFailed
              • +
              +
            +
            +
            + + +
            + +

            Errors returned by refresh()

            +

            +
            +
            +
            • @@ -302,26 +374,39 @@

              Declaration

              -

              Undocumented

              +

              Failed to refresh products from the App Store.

              Declaration

              Swift

              -
              case refreshReceiptFailed
              +
              case refreshProductsFailed
            • +
            +
            +
            +
            + + +
            + +

            Errors returned by purchase()

            +

            +
            +
            +
            • @@ -329,14 +414,14 @@

              Declaration

              -

              Undocumented

              +

              The product was not found on the App Store and cannot be purchased.

              Declaration

              Swift

              -
              case validateReceiptFailed
              +
              case productNotFound
              @@ -346,9 +431,9 @@

              Declaration

            • @@ -356,14 +441,14 @@

              Declaration

              -

              Undocumented

              +

              The user is not allowed to authorize payments.

              Declaration

              Swift

              -
              case readReceiptFailed
              +
              case cannotMakePurchase
              @@ -373,9 +458,9 @@

              Declaration

            • @@ -383,14 +468,14 @@

              Declaration

              -

              Undocumented

              +

              A purchase is already in progress.

              Declaration

              Swift

              -
              case refreshProductsFailed
              +
              case alreadyPurchasing
              @@ -399,14 +484,15 @@

              Declaration

            -
            - - + + diff --git a/docs/Enums/IAPPeriodFormat.html b/docs/Enums/IAPPeriodFormat.html new file mode 100644 index 0000000..d2e9a5f --- /dev/null +++ b/docs/Enums/IAPPeriodFormat.html @@ -0,0 +1,247 @@ + + + + IAPPeriodFormat Enumeration Reference + + + + + + + + + + + + + + + + +
            +

            + + InAppPurchaseLib documentation + + (77% documented) +

            + +

            +

            + +
            +

            + +

            + + + View on GitHub + +

            + +
            + + + +
            + +
            + +
            +
            +

            IAPPeriodFormat

            +
            +
            +
            public enum IAPPeriodFormat
            + +
            +
            +

            Undocumented

            + +
            +
            + +
            +
            +
            +
              +
            • +
              + + + + short + +
              +
              +
              +
              +
              +
              +

              Undocumented

              + +
              +
              +

              Declaration

              +
              +

              Swift

              +
              case short
              + +
              +
              +
              +
              +
            • +
            • +
              + + + + long + +
              +
              +
              +
              +
              +
              +

              Undocumented

              + +
              +
              +

              Declaration

              +
              +

              Swift

              +
              case long
              + +
              +
              +
              +
              +
            • +
            +
            +
            +
            + +
            +
            + + + + diff --git a/docs/jazzy/Enums/IAPProductType.html b/docs/Enums/IAPProductType.html similarity index 55% rename from docs/jazzy/Enums/IAPProductType.html rename to docs/Enums/IAPProductType.html index 5f397d9..645973d 100644 --- a/docs/jazzy/Enums/IAPProductType.html +++ b/docs/Enums/IAPProductType.html @@ -4,116 +4,163 @@ IAPProductType Enumeration Reference - + + + + + + -
            -
            -

            InAppPurchaseLib (52% documented)

            -

            View on GitHub

            -
            -
            -
            -
            +

            + + InAppPurchaseLib documentation + + (77% documented)

            -
            + +

            +

            + +
            +

            + +

            + + + View on GitHub + +

            + + + + + + + +
            +
            -
              +
              • @@ -237,14 +287,15 @@

                Declaration

            -
            - - + + diff --git a/docs/jazzy/Enums/IAPPurchaseResultState.html b/docs/Enums/IAPPurchaseResultState.html similarity index 53% rename from docs/jazzy/Enums/IAPPurchaseResultState.html rename to docs/Enums/IAPPurchaseResultState.html index ade15de..42485c4 100644 --- a/docs/jazzy/Enums/IAPPurchaseResultState.html +++ b/docs/Enums/IAPPurchaseResultState.html @@ -4,116 +4,163 @@ IAPPurchaseResultState Enumeration Reference - + + + + + + -
            -
            -

            InAppPurchaseLib (52% documented)

            -

            View on GitHub

            -
            -
            -
            -
            +

            + + InAppPurchaseLib documentation + + (77% documented)

            -
            + +

            +

            + +
            +

            + +

            + + + View on GitHub + +

            + + + + + + + +
            +
            -
              +
              • @@ -140,7 +190,7 @@

                IAPPurchaseResultState

                -

                Undocumented

                +

                The purchase was successful.

                @@ -167,7 +217,7 @@

                Declaration

                -

                Undocumented

                +

                Puchase failed.

                @@ -194,7 +244,7 @@

                Declaration

                -

                Undocumented

                +

                The purchase was cancelled by the user.

                @@ -221,7 +271,7 @@

                Declaration

                -

                Undocumented

                +

                The purchase is deferred.

                @@ -237,14 +287,15 @@

                Declaration

            -
            - - + + diff --git a/docs/Enums/IAPRefreshResultState.html b/docs/Enums/IAPRefreshResultState.html new file mode 100644 index 0000000..f9151e4 --- /dev/null +++ b/docs/Enums/IAPRefreshResultState.html @@ -0,0 +1,274 @@ + + + + IAPRefreshResultState Enumeration Reference + + + + + + + + + + + + + + + + +
            +

            + + InAppPurchaseLib documentation + + (77% documented) +

            + +

            +

            + +
            +

            + +

            + + + View on GitHub + +

            + +
            + + + +
            + +
            + +
            +
            +

            IAPRefreshResultState

            +
            +
            +
            public enum IAPRefreshResultState
            + +
            +
            +

            The list of the different states of the IAPRefreshResult.

            + +
            +
            + +
            +
            +
            +
              +
            • +
              + + + + succeeded + +
              +
              +
              +
              +
              +
              +

              Refresh was successful.

              + +
              +
              +

              Declaration

              +
              +

              Swift

              +
              case succeeded
              + +
              +
              +
              +
              +
            • +
            • +
              + + + + failed + +
              +
              +
              +
              +
              +
              +

              Refresh failed.

              + +
              +
              +

              Declaration

              +
              +

              Swift

              +
              case failed
              + +
              +
              +
              +
              +
            • +
            • +
              + + + + skipped + +
              +
              +
              +
              +
              +
              +

              Refresh has been skipped because it is not necessary.

              + +
              +
              +

              Declaration

              +
              +

              Swift

              +
              case skipped
              + +
              +
              +
              +
              +
            • +
            +
            +
            +
            + +
            +
            + + + + diff --git a/docs/jazzy/Extensions/SKProduct.html b/docs/Extensions/SKProduct.html similarity index 68% rename from docs/jazzy/Extensions/SKProduct.html rename to docs/Extensions/SKProduct.html index 19d937b..8fbe59d 100644 --- a/docs/jazzy/Extensions/SKProduct.html +++ b/docs/Extensions/SKProduct.html @@ -4,116 +4,163 @@ SKProduct Extension Reference - + + + + + + -
            -
            -

            InAppPurchaseLib (52% documented)

            -

            View on GitHub

            -
            -
            -
            -
            +

            + + InAppPurchaseLib documentation + + (77% documented)

            -
            + +

            +

            + +
            +

            + +

            + + + View on GitHub + +

            + + + + + + + +
            +
            -
              +
              • @@ -317,14 +367,15 @@

                Declaration

            -
            - - + + diff --git a/docs/Getting Started.html b/docs/Getting Started.html new file mode 100644 index 0000000..717182d --- /dev/null +++ b/docs/Getting Started.html @@ -0,0 +1,221 @@ + + + + Getting Started Reference + + + + + + + + + + + + + + + + +
            +

            + + InAppPurchaseLib documentation + + (77% documented) +

            + +

            +

            + +
            +

            + +

            + + + View on GitHub + +

            + +
            + + + +
            + +
            + +
            +
            +

            Getting Started

            +

            If you haven’t already, I highly recommend your read the Overview and Preparing section of Apple’s In-App Purchase official documentation.

            +

            Requirements

            + +
              +
            • Configure your App and Xcode to support In-App Purchases. + +
            • +
            • Create and configure your Fovea.Billing project account: + +
                +
              • Set your bundle ID
              • +
              • The iOS Shared Secret (or shared key) is to be retrieved from AppStoreConnect
              • +
              • The iOS Subscription Status URL (only if you want subscriptions)
              • +
            • +
            + +
            +
            + +
            +
            +
            + +
            +
            +
            + +
            +
            + + + + diff --git a/docs/Protocols/IAPErrorProtocol.html b/docs/Protocols/IAPErrorProtocol.html new file mode 100644 index 0000000..8c3d780 --- /dev/null +++ b/docs/Protocols/IAPErrorProtocol.html @@ -0,0 +1,220 @@ + + + + IAPErrorProtocol Protocol Reference + + + + + + + + + + + + + + + + +
            +

            + + InAppPurchaseLib documentation + + (77% documented) +

            + +

            +

            + +
            +

            + +

            + + + View on GitHub + +

            + +
            + + + +
            + +
            + +
            +
            +

            IAPErrorProtocol

            +
            +
            +
            public protocol IAPErrorProtocol : LocalizedError
            + +
            +
            +

            Undocumented

            + +
            +
            + +
            +
            +
            +
              +
            • +
              + + + + code + +
              +
              +
              +
              +
              +
              +

              Undocumented

              + +
              +
              +

              Declaration

              +
              +

              Swift

              +
              var code: IAPErrorCode { get }
              + +
              +
              +
              +
              +
            • +
            +
            +
            +
            + +
            +
            + + + + diff --git a/docs/Protocols/IAPPurchaseDelegate.html b/docs/Protocols/IAPPurchaseDelegate.html new file mode 100644 index 0000000..34c6641 --- /dev/null +++ b/docs/Protocols/IAPPurchaseDelegate.html @@ -0,0 +1,244 @@ + + + + IAPPurchaseDelegate Protocol Reference + + + + + + + + + + + + + + + + +
            +

            + + InAppPurchaseLib documentation + + (77% documented) +

            + +

            +

            + +
            +

            + +

            + + + View on GitHub + +

            + +
            + + + +
            + +
            + +
            +
            +

            IAPPurchaseDelegate

            +
            +
            +
            public protocol IAPPurchaseDelegate
            + +
            +
            +

            The protocol that you must adopt if you have consumable and/or non-renewing subscription products.

            + +
            +
            + +
            +
            +
            +
              +
            • + +
              +
              +
              +
              +
              +

              Called when a product is newly purchased, updated or restored.

              +
              +

              Important

              +

              You have to acknowledge delivery of the (virtual) item to finalize the transaction. Then you have to call InAppPurchase.finishTransactions(for: productIdentifier)as soon as you have delivered the product.

              + +
              + +
              +
              +

              Declaration

              +
              +

              Swift

              +
              func productPurchased(productIdentifier: String)
              + +
              +
              +
              +

              Parameters

              + + + + + + + +
              + + productIdentifier + + +
              +

              The identifier of the product.

              +
              +
              +
              +
              +
              +
            • +
            +
            +
            +
            + +
            +
            + + + + diff --git a/docs/jazzy/Protocols/InAppPurchaseLib.html b/docs/Protocols/InAppPurchaseLib.html similarity index 86% rename from docs/jazzy/Protocols/InAppPurchaseLib.html rename to docs/Protocols/InAppPurchaseLib.html index 63ceafc..cdb81d0 100644 --- a/docs/jazzy/Protocols/InAppPurchaseLib.html +++ b/docs/Protocols/InAppPurchaseLib.html @@ -4,116 +4,163 @@ InAppPurchaseLib Protocol Reference - + + + + + + -
            -
            -

            InAppPurchaseLib (52% documented)

            -

            View on GitHub

            -
            -
            -
            -
            +

            + + InAppPurchaseLib documentation + + (77% documented)

            -
            + +

            +

            + +
            +

            + +

            + + + View on GitHub + +

            + + + + + + + +
            +
            -
            -
              +
              • @@ -431,7 +481,7 @@

                Products information

                Gets all products retrieved from the App Store

                See

                - See also: SKProduct. + See also: SKProduct
                @@ -467,7 +517,7 @@

                Return Value

                Gets the product by its identifier from the list of products retrieved from the App Store.

                See

                - See also: SKProduct. + See also: SKProduct
                @@ -518,7 +568,7 @@

                Purchasing and Restoring

                -
            - - + + diff --git a/docs/Structs/IAPError.html b/docs/Structs/IAPError.html new file mode 100644 index 0000000..3ce0582 --- /dev/null +++ b/docs/Structs/IAPError.html @@ -0,0 +1,252 @@ + + + + IAPError Structure Reference + + + + + + + + + + + + + + + + +
            +

            + + InAppPurchaseLib documentation + + (77% documented) +

            + +

            +

            + +
            +

            + +

            + + + View on GitHub + +

            + +
            + + + +
            + +
            + +
            +
            +

            IAPError

            +
            +
            +
            public struct IAPError : IAPErrorProtocol
            + +
            +
            +

            When calling refresh(), purchase() or restorePurchases(), the callback can return an IAPError if the state is failed.

            + +
            +
            + +
            +
            +
            +
              +
            • +
              + + + + code + +
              +
              +
              +
              +
              +
              +

              The error code.

              +
              +

              See

              + See also: IAPErrorCode. + +
              + +
              +
              +

              Declaration

              +
              +

              Swift

              +
              public var code: IAPErrorCode
              + +
              +
              +
              +
              +
            • +
            • +
              + + + + localizedDescription + +
              +
              +
              +
              +
              +
              +

              The error description.

              + +
              +
              +

              Declaration

              +
              +

              Swift

              +
              public var localizedDescription: String { get }
              + +
              +
              +
              +
              +
            • +
            +
            +
            +
            + +
            +
            + + + + diff --git a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Structs/IAPProduct.html b/docs/Structs/IAPProduct.html similarity index 58% rename from docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Structs/IAPProduct.html rename to docs/Structs/IAPProduct.html index 889974f..f52efcb 100644 --- a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Structs/IAPProduct.html +++ b/docs/Structs/IAPProduct.html @@ -4,116 +4,163 @@ IAPProduct Structure Reference - + + + + + + -
            -
            -

            InAppPurchaseLib (52% documented)

            -

            View on GitHub

            -
            -
            -
            -
            +

            + + InAppPurchaseLib documentation + + (77% documented)

            -
            + +

            +

            + +
            +

            + +

            + + + View on GitHub + +

            + + + + + + + +
            +
            -
              +
              • @@ -251,14 +301,15 @@

                Parameters

            -
            - - + + diff --git a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Structs/IAPPurchaseResult.html b/docs/Structs/IAPPurchaseResult.html similarity index 58% rename from docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Structs/IAPPurchaseResult.html rename to docs/Structs/IAPPurchaseResult.html index 6bc3b5d..d17b2a6 100644 --- a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Structs/IAPPurchaseResult.html +++ b/docs/Structs/IAPPurchaseResult.html @@ -4,116 +4,163 @@ IAPPurchaseResult Structure Reference - + + + + + + -
            -
            -

            InAppPurchaseLib (52% documented)

            -

            View on GitHub

            -
            -
            -
            -
            +

            + + InAppPurchaseLib documentation + + (77% documented)

            -
            + +

            +

            + +
            +

            + +

            + + + View on GitHub + +

            + + + + + + + +
            +
            -
              +
              • @@ -237,14 +287,15 @@

                Declaration

            -
            - - + + diff --git a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Structs/IAPRefreshResult.html b/docs/Structs/IAPRefreshResult.html similarity index 57% rename from docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Structs/IAPRefreshResult.html rename to docs/Structs/IAPRefreshResult.html index 8d70385..670d0f2 100644 --- a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Structs/IAPRefreshResult.html +++ b/docs/Structs/IAPRefreshResult.html @@ -4,116 +4,163 @@ IAPRefreshResult Structure Reference - + + + + + + -
            -
            -

            InAppPurchaseLib (52% documented)

            -

            View on GitHub

            -
            -
            -
            -
            +

            + + InAppPurchaseLib documentation + + (77% documented)

            -
            + +

            +

            + +
            +

            + +

            + + + View on GitHub + +

            + + + + + + + +
            +
            -
              +
              • @@ -237,14 +287,15 @@

                Declaration

            -
            - - + + diff --git a/docs/Usage.html b/docs/Usage.html new file mode 100644 index 0000000..535a067 --- /dev/null +++ b/docs/Usage.html @@ -0,0 +1,296 @@ + + + + Usage Reference + + + + + + + + + + + + + + + + +
            +

            + + InAppPurchaseLib documentation + + (77% documented) +

            + +

            +

            + +
            +

            + +

            + + + View on GitHub + +

            + +
            + + + +
            + +
            + +
            +
            +

            Usage

            +

            The process of implementing in-app purchases involves several steps:

            + +
              +
            1. Displaying the list of purchasable products
            2. +
            3. Initiating a purchase
            4. +
            5. Delivering and finalizing a purchase
            6. +
            7. Checking the current ownership of non-consumables and subscriptions
            8. +
            9. Implementing the Restore Purchases button
            10. +
            + +

            Note: You have to import InAppPurchaseLib wherever you use the library.

            + +
            +
            + +
            +
            +
            + +
            +
            +
            + +
            +
            + + + + diff --git a/docs/analytics.html b/docs/analytics.html new file mode 100644 index 0000000..697340d --- /dev/null +++ b/docs/analytics.html @@ -0,0 +1,221 @@ + + + + Analytics Reference + + + + + + + + + + + + + + + +
            +

            + + InAppPurchaseLib documentation + + (77% documented) +

            + +

            +

            + +
            +

            + +

            + + + View on GitHub + +

            + +
            + + + +
            + +
            + +
            +
            + +

            Analytics

            + +

            Tracking the purchase flow is a common things in apps. Especially as it’s core to your revenue model.

            + +

            We can track 5 events, which step in the purchase pipeline a user reached.

            + +
              +
            1. purchase initiated
            2. +
            3. purchase cancelled
            4. +
            5. purchase failed
            6. +
            7. purchase deferred
            8. +
            9. purchase succeeded
            10. +
            + +

            Here’s a quick example showing how to implement this correctly.

            +
            func makePurchase() {
            +  Analytics.trackEvent("purchase initiated")
            +  InAppPurchase.purchase(
            +    productIdentifier: "my_product_id",
            +    callback: { result in
            +      switch result.state {
            +      case .purchased:
            +        // Reminder: We are not processing the purchase here, only updating your UI.
            +        //           That's why we do not send an event to analytics.
            +      case .failed:
            +        Analytics.trackEvent("purchase failed")
            +      case .deferred:
            +        Analytics.trackEvent("purchase deferred")
            +      case .cancelled:
            +        Analytics.trackEvent("purchase cancelled")
            +    }
            +  })
            +}
            +
            +// IAPPurchaseDelegate implementation
            +func productPurchased(productIdentifier: String) {
            +  Analytics.trackEvent("purchase succeeded")
            +  InAppPurchase.finishTransactions(for: productIdentifier)
            +}
            +
            + +

            The important part to remember is that a purchase can occur outside your app (or be approved when the app is not running), that’s why tracking purchase succeeded has to be part of the productPurchased delegate function.

            + +

            Refer to the Consumables section to learn more about the productPurchased function.

            + +
            +
            + + +
            +
            + + + + diff --git a/docs/jazzy/badge.svg b/docs/badge.svg similarity index 93% rename from docs/jazzy/badge.svg rename to docs/badge.svg index 81f3c9b..f6985a8 100644 --- a/docs/jazzy/badge.svg +++ b/docs/badge.svg @@ -8,7 +8,7 @@ - + @@ -19,10 +19,10 @@ documentation - 52% + 77% - 52% + 77% diff --git a/docs/jazzy/css/highlight.css b/docs/css/highlight.css similarity index 100% rename from docs/jazzy/css/highlight.css rename to docs/css/highlight.css diff --git a/docs/css/jazzy.css b/docs/css/jazzy.css new file mode 100644 index 0000000..ff59f5f --- /dev/null +++ b/docs/css/jazzy.css @@ -0,0 +1,395 @@ +*, *:before, *:after { + box-sizing: inherit; } + +body { + margin: 0; + background: #fff; + color: #333; + font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; + letter-spacing: .2px; + -webkit-font-smoothing: antialiased; + box-sizing: border-box; } + +h1 { + font-size: 2rem; + font-weight: 700; + margin: 1.275em 0 0.6em; } + +h2 { + font-size: 1.75rem; + font-weight: 700; + margin: 1.275em 0 0.3em; } + +h3 { + font-size: 1.5rem; + font-weight: 700; + margin: 1em 0 0.3em; } + +h4 { + font-size: 1.25rem; + font-weight: 700; + margin: 1.275em 0 0.85em; } + +h5 { + font-size: 1rem; + font-weight: 700; + margin: 1.275em 0 0.85em; } + +h6 { + font-size: 1rem; + font-weight: 700; + margin: 1.275em 0 0.85em; + color: #777; } + +p { + margin: 0 0 1em; } + +ul, ol { + padding: 0 0 0 2em; + margin: 0 0 0.85em; } + +blockquote { + margin: 0 0 0.85em; + padding: 0 15px; + color: #858585; + border-left: 4px solid #e5e5e5; } + +img { + max-width: 100%; } + +a { + color: #4183c4; + text-decoration: none; } + a:hover, a:focus { + outline: 0; + text-decoration: underline; } + a.discouraged { + text-decoration: line-through; } + a.discouraged:hover, a.discouraged:focus { + text-decoration: underline line-through; } + +table { + background: #fff; + width: 100%; + border-collapse: collapse; + border-spacing: 0; + overflow: auto; + margin: 0 0 0.85em; } + +tr:nth-child(2n) { + background-color: #fbfbfb; } + +th, td { + padding: 6px 13px; + border: 1px solid #ddd; } + +pre { + margin: 0 0 1.275em; + padding: .85em 1em; + overflow: auto; + background: #f7f7f7; + font-size: .85em; + font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } + +code { + font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } + +.item-container p > code, .item-container li > code, .top-matter p > code, .top-matter li > code { + background: #f7f7f7; + padding: .2em; } + .item-container p > code:before, .item-container p > code:after, .item-container li > code:before, .item-container li > code:after, .top-matter p > code:before, .top-matter p > code:after, .top-matter li > code:before, .top-matter li > code:after { + letter-spacing: -.2em; + content: "\00a0"; } + +pre code { + padding: 0; + white-space: pre; } + +.content-wrapper { + display: flex; + flex-direction: column; } + @media (min-width: 768px) { + .content-wrapper { + flex-direction: row; } } +.header { + display: flex; + padding: 8px; + font-size: 0.875em; + background: #444; + color: #999; } + +.header-col { + margin: 0; + padding: 0 8px; } + +.header-col--primary { + flex: 1; } + +.header-link { + color: #fff; } + +.header-icon { + padding-right: 6px; + vertical-align: -4px; + height: 16px; } + +.breadcrumbs { + font-size: 0.875em; + padding: 8px 16px; + margin: 0; + background: #fbfbfb; + border-bottom: 1px solid #ddd; } + +.carat { + height: 10px; + margin: 0 5px; } + +.navigation { + order: 2; } + @media (min-width: 768px) { + .navigation { + order: 1; + width: 25%; + max-width: 300px; + padding-bottom: 64px; + overflow: hidden; + word-wrap: normal; + background: #fbfbfb; + border-right: 1px solid #ddd; } } +.nav-groups { + list-style-type: none; + padding-left: 0; } + +.nav-group-name { + border-bottom: 1px solid #ddd; + padding: 8px 0 8px 16px; } + +.nav-group-name-link { + color: #333; } + +.nav-group-tasks { + margin: 8px 0; + padding: 0 0 0 8px; } + +.nav-group-task { + font-size: 1em; + list-style-type: none; + white-space: nowrap; } + +.nav-group-task-link { + color: #808080; } + +.main-content { + order: 1; } + @media (min-width: 768px) { + .main-content { + order: 2; + flex: 1; + padding-bottom: 60px; } } +.section { + padding: 0 32px; + border-bottom: 1px solid #ddd; } + +.section-content { + max-width: 834px; + margin: 0 auto; + padding: 16px 0; } + +.section-name { + color: #666; + display: block; } + .section-name p { + margin-bottom: inherit; } + +.declaration .highlight { + overflow-x: initial; + padding: 8px 0; + margin: 0; + background-color: transparent; + border: none; } + +.task-group-section { + border-top: 1px solid #ddd; } + +.task-group { + padding-top: 0px; } + +.task-name-container a[name]:before { + content: ""; + display: block; } + +.section-name-container { + position: relative; } + .section-name-container .section-name-link { + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + margin-bottom: 0; } + .section-name-container .section-name { + position: relative; + pointer-events: none; + z-index: 1; } + .section-name-container .section-name a { + pointer-events: auto; } + +.item-container { + padding: 0; } + +.item { + padding-top: 8px; + width: 100%; + list-style-type: none; } + .item a[name]:before { + content: ""; + display: block; } + .item .token, .item .direct-link { + display: inline-block; + text-indent: -20px; + padding-left: 3px; + margin-left: 20px; + font-size: 1rem; } + .item .declaration-note { + font-size: .85em; + color: #808080; + font-style: italic; } + +.pointer-container { + border-bottom: 1px solid #ddd; + left: -23px; + padding-bottom: 13px; + position: relative; + width: 110%; } + +.pointer { + left: 21px; + top: 7px; + display: block; + position: absolute; + width: 12px; + height: 12px; + border-left: 1px solid #ddd; + border-top: 1px solid #ddd; + background: #fff; + transform: rotate(45deg); } + +.height-container { + display: none; + position: relative; + width: 100%; + overflow: hidden; } + .height-container .section { + background: #fff; + border: 1px solid #ddd; + border-top-width: 0; + padding-top: 10px; + padding-bottom: 5px; + padding: 8px 16px; } + +.aside, .language { + padding: 6px 12px; + margin: 12px 0; + border-left: 5px solid #dddddd; + overflow-y: hidden; } + .aside .aside-title, .language .aside-title { + font-size: 9px; + letter-spacing: 2px; + text-transform: uppercase; + padding-bottom: 0; + margin: 0; + color: #aaa; + -webkit-user-select: none; } + .aside p:last-child, .language p:last-child { + margin-bottom: 0; } + +.language { + border-left: 5px solid #cde9f4; } + .language .aside-title { + color: #4183c4; } + +.aside-warning, .aside-deprecated, .aside-unavailable { + border-left: 5px solid #ff6666; } + .aside-warning .aside-title, .aside-deprecated .aside-title, .aside-unavailable .aside-title { + color: #ff0000; } + +.graybox { + border-collapse: collapse; + width: 100%; } + .graybox p { + margin: 0; + word-break: break-word; + min-width: 50px; } + .graybox td { + border: 1px solid #ddd; + padding: 5px 25px 5px 10px; + vertical-align: middle; } + .graybox tr td:first-of-type { + text-align: right; + padding: 7px; + vertical-align: top; + word-break: normal; + width: 40px; } + +.slightly-smaller { + font-size: 0.9em; } + +.footer { + padding: 8px 16px; + background: #444; + color: #ddd; + font-size: 0.8em; } + .footer p { + margin: 8px 0; } + .footer a { + color: #fff; } + +html.dash .header, html.dash .breadcrumbs, html.dash .navigation { + display: none; } + +html.dash .height-container { + display: block; } + +form[role=search] input { + font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 24px; + padding: 0 10px; + margin: 0; + border: none; + border-radius: 1em; } + .loading form[role=search] input { + background: white url(../img/spinner.gif) center right 4px no-repeat; } + +form[role=search] .tt-menu { + margin: 0; + min-width: 300px; + background: #fbfbfb; + color: #333; + border: 1px solid #ddd; } + +form[role=search] .tt-highlight { + font-weight: bold; } + +form[role=search] .tt-suggestion { + font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; + padding: 0 8px; } + form[role=search] .tt-suggestion span { + display: table-cell; + white-space: nowrap; } + form[role=search] .tt-suggestion .doc-parent-name { + width: 100%; + text-align: right; + font-weight: normal; + font-size: 0.9em; + padding-left: 16px; } + +form[role=search] .tt-suggestion:hover, +form[role=search] .tt-suggestion.tt-cursor { + cursor: pointer; + background-color: #4183c4; + color: #fff; } + +form[role=search] .tt-suggestion:hover .doc-parent-name, +form[role=search] .tt-suggestion.tt-cursor .doc-parent-name { + color: #fff; } diff --git a/docs/displaying-products-with-purchases.html b/docs/displaying-products-with-purchases.html new file mode 100644 index 0000000..d77fc40 --- /dev/null +++ b/docs/displaying-products-with-purchases.html @@ -0,0 +1,231 @@ + + + + Displaying products with purchases Reference + + + + + + + + + + + + + + + +
            +

            + + InAppPurchaseLib documentation + + (77% documented) +

            + +

            +

            + +
            +

            + +

            + + + View on GitHub + +

            + +
            + + + +
            + +
            + +
            +
            + +

            Displaying products with purchases

            + +

            In your store screen, where you present your products titles and prices with a purchase button, there are some cases to handle that we skipped. Owned products and deferred purchases.

            +

            Owned products

            + +

            Non-consumables and active auto-renewing subscriptions cannot be purchased again. You should adjust your UI to reflect that state. Refer to InAppPurchase.hasActivePurchase() to and to the example later in this section.

            +

            Deferred purchases

            + +

            Apple’s Ask to Buy feature lets parents approve any purchases initiated by children, including in-app purchases.

            + +

            With Ask to Buy enabled, when a child requests to make a purchase, the app is notified that the purchase is awaiting the parent’s approval in the purchase callback:

            +
            InAppPurchase.purchase(
            +  productIdentifier: productIdentifier,
            +  callback: { result in
            +    switch result.state {
            +    case .deferred:
            +      // Pending parent approval
            +  }
            +})
            +
            + +

            In the deferred case, the child has been notified by StoreKit that the parents have to approve the purchase. He might then close the app and come back later. You don’t have much to do, but to display in your UI that there is a purchase waiting for parental approval in your views.

            + +

            We will use the hasDeferredTransaction method:

            +
            InAppPurchase.hasDeferredTransaction(for productIdentifier: String) -> Bool
            +
            +

            Example

            + +

            Here’s an example that covers what has been discussed above. We will update our example refreshView function from before:

            +
            @objc func refreshView() {
            +  guard let product: SKProduct = InAppPurchase.getProductBy(identifier: "my_product_id") else {
            +    self.titleLabel.text = "Product unavailable"
            +    return
            +  }
            +  self.titleLabel.text = product.localizedTitle
            +  // ...
            +
            +  // "Ask to Buy" deferred purchase waiting for parent's approval
            +  if InAppPurchase.hasDeferredTransaction(for: "my_product_id") {
            +    self.statusLabel.text = "Waiting for Approval..."
            +    self.purchaseButton.isPointerInteractionEnabled = false
            +  }
            +  // "Owned" product
            +  else if InAppPurchase.hasActivePurchase(for: "my_product_id") {
            +    self.statusLabel.text = "OWNED"
            +    self.purchaseButton.isPointerInteractionEnabled = false
            +  }
            +  else {
            +    self.purchaseButton.isPointerInteractionEnabled = true
            +  }
            +}
            +
            + +

            When a product is owned or has a deferred purchase, we make sure the purchase button is grayed out. We also use a status label to display some details. Of course, you are free to design your UI as you see fit.

            + +
            +
            + + +
            +
            + + + + diff --git a/docs/displaying-products.html b/docs/displaying-products.html new file mode 100644 index 0000000..cc50b1f --- /dev/null +++ b/docs/displaying-products.html @@ -0,0 +1,213 @@ + + + + Displaying products Reference + + + + + + + + + + + + + + + +
            +

            + + InAppPurchaseLib documentation + + (77% documented) +

            + +

            +

            + +
            +

            + +

            + + + View on GitHub + +

            + +
            + + + +
            + +
            + +
            +
            + +

            Displaying products

            + +

            Let’s start with the simplest case: you have a single product.

            + +

            You can retrieve all information about this product using the function InAppPurchase.getProductBy(identifier: "my_product_id"). This returns an SKProduct extended with helpful methods.

            + +

            Those are the most important:

            + +
              +
            • productIdentifier: String - The string that identifies the product to the Apple AppStore.
            • +
            • localizedTitle: String - The name of the product, in the language of the device, as retrieved from the AppStore.
            • +
            • localizedDescription: String - A description of the product, in the language of the device, as retrieved from the AppStore.
            • +
            • localizedPrice: String - The cost of the product in the local currency (read-only property added by this library).
            • +
            + +

            Example:

            + +

            You can add a function similar to this to your view.

            +
            @objc func refreshView() {
            +  guard let product: SKProduct = InAppPurchase.getProductBy(identifier: "my_product_id") else {
            +    self.titleLabel.text = "Product unavailable"
            +    return
            +  }
            +  self.titleLabel.text = product.localizedTitle
            +  self.descriptionLabel.text = product.localizedDescription
            +  self.priceLabel.text = product.localizedPrice
            +}
            +
            + +

            This example assumes self.titleLabel is a UILabel, etc.

            + +

            Make sure to call this function when the view appears on screen, for instance by calling it from viewWillAppear.

            +
            override func viewWillAppear(_ animated: Bool) {
            +  self.refreshView()
            +}
            +
            + +
            +
            + + +
            +
            + + + + diff --git a/docs/displaying-subscriptions.html b/docs/displaying-subscriptions.html new file mode 100644 index 0000000..640c8ff --- /dev/null +++ b/docs/displaying-subscriptions.html @@ -0,0 +1,214 @@ + + + + Displaying subscriptions Reference + + + + + + + + + + + + + + + +
            +

            + + InAppPurchaseLib documentation + + (77% documented) +

            + +

            +

            + +
            +

            + +

            + + + View on GitHub + +

            + +
            + + + +
            + +
            + +
            +
            + +

            Displaying subscriptions

            + +

            For subscription products, you also have some data about subscription periods and introductory offers.

            + +
              +
            • func hasIntroductoryPriceEligible() -> Bool - The product has an introductory price the user is eligible to.
            • +
            • localizedSubscriptionPeriod: String? - The period of the subscription.
            • +
            • localizedIntroductoryPrice: String? - The cost of the introductory offer if available in the local currency.
            • +
            • localizedIntroductoryPeriod: String? - The subscription period of the introductory offer.
            • +
            • localizedIntroductoryDuration: String? - The duration of the introductory offer.
            • +
            + +

            Example

            +
            @objc func refreshView() {
            +  guard let product: SKProduct = InAppPurchase.getProductBy(identifier: "my_product_id") else {
            +    self.titleLabel.text = "Product unavailable"
            +    return
            +  }
            +  self.titleLabel.text = product.localizedTitle
            +  self.descriptionLabel.text = product.localizedDescription
            +
            +  // Format price text. Example: "0,99€ / month for 3 months (then 3,99 € / month)"
            +  var priceText = "\(product.localizedPrice) / \(product.localizedSubscriptionPeriod!)"
            +  if product.hasIntroductoryPriceEligible() {
            +      if product.introductoryPrice!.numberOfPeriods == 1 {
            +          priceText = "\(product.localizedIntroductoryPrice!) for \(product.localizedIntroductoryDuration!)" +
            +          " (then \(priceText))"
            +      } else {
            +          priceText = "\(product.localizedIntroductoryPrice!) / \(product.localizedIntroductoryPeriod!)" +
            +          " for \(product.localizedIntroductoryDuration!) (then \(priceText))"
            +      }
            +  }
            +  self.priceLabel.text = priceText
            +}
            +
            + +

            Note: You have to import StoreKit wherever you use SKProduct.

            + +
            +
            + + +
            +
            + + + + diff --git a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Info.plist b/docs/docsets/InAppPurchaseLib.docset/Contents/Info.plist similarity index 100% rename from docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Info.plist rename to docs/docsets/InAppPurchaseLib.docset/Contents/Info.plist diff --git a/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/API documentation.html b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/API documentation.html new file mode 100644 index 0000000..70cd86c --- /dev/null +++ b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/API documentation.html @@ -0,0 +1,680 @@ + + + + API documentation Reference + + + + + + + + + + + + + + + + +
            +

            + + InAppPurchaseLib documentation + + (77% documented) +

            + +

            +

            + +
            +

            + +

            + + + View on GitHub + +

            + +
            + + + +
            + +
            + +
            +
            +

            API documentation

            +

            Classes and Protocols

            + +

            The most important class is InAppPurchase. All the functions you need are defined in this class.

            + +

            If you have consumable and/or non-renewing subscription products in your application, you must have a class that adopts the IAPPurchaseDelegate protocol.

            +

            Products

            + + +

            Callbacks

            + +

            refresh(), purchase() and restorePurchases() are asynchronous functions. You must provide a callback that will allow you to perform actions depending on the result.

            + + +

            Errors

            + +

            When calling refresh(), purchase() or restorePurchases(), the callback can return an IAPError if the state is failed. Look at IAPErrorCode to see the list of error codes you can receive.

            + +
            +
            + +
            +
            +
            +
              +
            • +
              + + + + InAppPurchase + +
              +
              +
              +
              +
              +
              + + See more +
              +
              +

              Declaration

              +
              +

              Swift

              +
              public class InAppPurchase : NSObject, InAppPurchaseLib
              + +
              +
              +
              +
              +
            • +
            • +
              + + + + InAppPurchaseLib + +
              +
              +
              +
              +
              +
              +

              The protocol that InAppPurchase adopts.

              + + See more +
              +
              +

              Declaration

              +
              +

              Swift

              +
              public protocol InAppPurchaseLib
              + +
              +
              +
              +
              +
            • +
            • + +
              +
              +
              +
              +
              +

              The default implementation of IAPPurchaseDelegate if no other is provided. It is enough if you only have non-consumable and/or auto-renewable subscription products.

              + + See more +
              +
              +

              Declaration

              +
              +

              Swift

              +
              public class DefaultPurchaseDelegate : IAPPurchaseDelegate
              + +
              +
              +
              +
              +
            • +
            • +
              + + + + IAPPurchaseDelegate + +
              +
              +
              +
              +
              +
              +

              The protocol that you must adopt if you have consumable and/or non-renewing subscription products.

              + + See more +
              +
              +

              Declaration

              +
              +

              Swift

              +
              public protocol IAPPurchaseDelegate
              + +
              +
              +
              +
              +
            • +
            • +
              + + + + IAPProduct + +
              +
              +
              +
              +
              +
              +

              Undocumented

              + + See more +
              +
              +

              Declaration

              +
              +

              Swift

              +
              public struct IAPProduct
              + +
              +
              +
              +
              +
            • +
            • +
              + + + + IAPProductType + +
              +
              +
              +
              +
              +
              +

              Undocumented

              + + See more +
              +
              +

              Declaration

              +
              +

              Swift

              +
              public enum IAPProductType
              + +
              +
              +
              +
              +
            • +
            • +
              + + + + SKProduct + +
              +
              +
              +
              +
              +
              + + See more +
              +
              +

              Declaration

              +
              +

              Swift

              +
              extension SKProduct
              + +
              +
              +
              +
              +
            • +
            • +
              + + + + IAPPeriodFormat + +
              +
              +
              +
              +
              +
              +

              Undocumented

              + + See more +
              +
              +

              Declaration

              +
              +

              Swift

              +
              public enum IAPPeriodFormat
              + +
              +
              +
              +
              +
            • +
            • +
              + + + + IAPPurchaseCallback + +
              +
              +
              +
              +
              +
              +

              Undocumented

              + +
              +
              +

              Declaration

              +
              +

              Swift

              +
              public typealias IAPPurchaseCallback = (IAPPurchaseResult) -> Void
              + +
              +
              +
              +
              +
            • +
            • +
              + + + + IAPRefreshCallback + +
              +
              +
              +
              +
              +
              +

              Undocumented

              + +
              +
              +

              Declaration

              +
              +

              Swift

              +
              public typealias IAPRefreshCallback = (IAPRefreshResult) -> Void
              + +
              +
              +
              +
              +
            • +
            • +
              + + + + IAPPurchaseResult + +
              +
              +
              +
              +
              +
              +

              The result returned in the purchase() callback.

              + + See more +
              +
              +

              Declaration

              +
              +

              Swift

              +
              public struct IAPPurchaseResult
              + +
              +
              +
              +
              +
            • +
            • +
              + + + + IAPRefreshResult + +
              +
              +
              +
              +
              +
              +

              The result returned in the refresh() or restorePurchases() callback.

              + + See more +
              +
              +

              Declaration

              +
              +

              Swift

              +
              public struct IAPRefreshResult
              + +
              +
              +
              +
              +
            • +
            • + +
              +
              +
              +
              +
              +

              The list of the different states of the IAPPurchaseResult.

              + + See more +
              +
              +

              Declaration

              +
              +

              Swift

              +
              public enum IAPPurchaseResultState
              + +
              +
              +
              +
              +
            • +
            • +
              + + + + IAPRefreshResultState + +
              +
              +
              +
              +
              +
              +

              The list of the different states of the IAPRefreshResult.

              + + See more +
              +
              +

              Declaration

              +
              +

              Swift

              +
              public enum IAPRefreshResultState
              + +
              +
              +
              +
              +
            • +
            • +
              + + + + IAPError + +
              +
              +
              +
              +
              +
              +

              When calling refresh(), purchase() or restorePurchases(), the callback can return an IAPError if the state is failed.

              + + See more +
              +
              +

              Declaration

              +
              +

              Swift

              +
              public struct IAPError : IAPErrorProtocol
              + +
              +
              +
              +
              +
            • +
            • +
              + + + + IAPErrorCode + +
              +
              +
              +
              +
              +
              +

              The list of error codes that can be returned by the library.

              + + See more +
              +
              +

              Declaration

              +
              +

              Swift

              +
              public enum IAPErrorCode
              + +
              +
              +
              +
              +
            • +
            • +
              + + + + IAPErrorProtocol + +
              +
              +
              +
              +
              +
              +

              Undocumented

              + + See more +
              +
              +

              Declaration

              +
              +

              Swift

              +
              public protocol IAPErrorProtocol : LocalizedError
              + +
              +
              +
              +
              +
            • +
            +
            +
            +
            + +
            +
            + + + + diff --git a/docs/jazzy/Classes/DefaultPurchaseDelegate.html b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Classes/DefaultPurchaseDelegate.html similarity index 50% rename from docs/jazzy/Classes/DefaultPurchaseDelegate.html rename to docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Classes/DefaultPurchaseDelegate.html index 3a8424b..5e4025c 100644 --- a/docs/jazzy/Classes/DefaultPurchaseDelegate.html +++ b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Classes/DefaultPurchaseDelegate.html @@ -4,116 +4,163 @@ DefaultPurchaseDelegate Class Reference - + + + + + + -
            -
            -

            InAppPurchaseLib (52% documented)

            -

            View on GitHub

            -
            -
            -
            -
            +

            + + InAppPurchaseLib documentation + + (77% documented)

            -
            + +

            +

            + +
            +

            + +

            + + + View on GitHub + +

            + + + + + + + +
            +
            -
              +
              • @@ -202,14 +252,15 @@

                Parameters

            -
            - - + + diff --git a/docs/jazzy/Classes/InAppPurchase.html b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Classes/InAppPurchase.html similarity index 87% rename from docs/jazzy/Classes/InAppPurchase.html rename to docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Classes/InAppPurchase.html index b43f50e..269267c 100644 --- a/docs/jazzy/Classes/InAppPurchase.html +++ b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Classes/InAppPurchase.html @@ -4,116 +4,163 @@ InAppPurchase Class Reference - + + + + + + -
            -
            -

            InAppPurchaseLib (52% documented)

            -

            View on GitHub

            -
            -
            -
            -
            +

            + + InAppPurchaseLib documentation + + (77% documented)

            -
            + +

            +

            + +
            +

            + +

            + + + View on GitHub + +

            + + + + +
            -
            -
              +
              • @@ -446,7 +495,7 @@

                Products information

                Gets all products retrieved from the App Store

                See

                - See also: SKProduct. + See also: SKProduct
                @@ -482,7 +531,7 @@

                Return Value

                Gets the product by its identifier from the list of products retrieved from the App Store.

                See

                - See also: SKProduct. + See also: SKProduct
                @@ -533,7 +582,7 @@

                Purchasing and Restoring

                -
                  +
                  • @@ -581,7 +630,7 @@

                    Return Value

                    Request a Payment from the App Store.

                    See

                    - See also:IAPPurchaseCallback and IAPPurchaseResult. + See also: IAPPurchaseResult
                    @@ -590,7 +639,7 @@

                    Return Value

                    Declaration

                    Swift

                    -
                    public static func purchase(productIdentifier: String, quantity: Int, callback: @escaping IAPPurchaseCallback)
                    +
                    public static func purchase(productIdentifier: String, quantity: Int, callback: @escaping IAPPurchaseCallback)
                    @@ -656,7 +705,7 @@

                    Parameters

                    Restore purchased products.

                    See

                    - See also:IAPRefreshCallback and IAPRefreshResult. + See also: IAPRefreshResult
                    @@ -665,7 +714,7 @@

                    Parameters

                    Declaration

                    Swift

                    -
                    public static func restorePurchases(callback: @escaping IAPRefreshCallback)
                    +
                    public static func restorePurchases(callback: @escaping IAPRefreshCallback)
                    @@ -799,7 +848,7 @@

                    Purchases information

                    -
                      +
                      • @@ -1014,14 +1063,15 @@

                        Return Value

                      - - - + + diff --git a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Enums/IAPErrorCode.html b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Enums/IAPErrorCode.html similarity index 62% rename from docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Enums/IAPErrorCode.html rename to docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Enums/IAPErrorCode.html index b78db89..16f2456 100644 --- a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Enums/IAPErrorCode.html +++ b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Enums/IAPErrorCode.html @@ -4,116 +4,163 @@ IAPErrorCode Enumeration Reference - + + + + + + -
                      -
                      -

                      InAppPurchaseLib (52% documented)

                      -

                      View on GitHub

                      -
                      -
                      -
                      -
                      +

                      + + InAppPurchaseLib documentation + + (77% documented)

                      -
                      + +

                      +

                      + +
                      +

                      + +

                      + + + View on GitHub + +

                      + + + + + + + +
                      +
                      -
                        +
                        + + +
                        + +

                        Errors returned by refresh(), purchase() or restorePurchases()

                        +

                        +
                        +
                        +
                        • @@ -140,7 +199,7 @@

                          IAPErrorCode

                          -

                          Undocumented

                          +

                          You must call the initialize fuction before using the library.

                          @@ -157,9 +216,9 @@

                          Declaration

                        • @@ -167,14 +226,14 @@

                          Declaration

                          -

                          Undocumented

                          +

                          The Bundle Identifier is invalid.

                          Declaration

                          Swift

                          -
                          case productNotFound
                          +
                          case bundleIdentifierInvalid
                          @@ -184,9 +243,9 @@

                          Declaration

                        • @@ -194,14 +253,14 @@

                          Declaration

                          -

                          Undocumented

                          +

                          The Validator URL String is invalid.

                          Declaration

                          Swift

                          -
                          case cannotMakePurchase
                          +
                          case validatorUrlInvalid
                          @@ -211,9 +270,9 @@

                          Declaration

                        • @@ -221,14 +280,14 @@

                          Declaration

                          -

                          Undocumented

                          +

                          Failed to refresh the App Store receipt.

                          Declaration

                          Swift

                          -
                          case alreadyPurchasing
                          +
                          case refreshReceiptFailed
                          @@ -238,9 +297,9 @@

                          Declaration

                        • @@ -248,14 +307,14 @@

                          Declaration

                          -

                          Undocumented

                          +

                          Failed to validate the App Store receipt with Fovea.

                          Declaration

                          Swift

                          -
                          case bundleIdentifierInvalid
                          +
                          case validateReceiptFailed
                          @@ -265,9 +324,9 @@

                          Declaration

                        • @@ -275,26 +334,39 @@

                          Declaration

                          -

                          Undocumented

                          +

                          Failed to read the receipt validation.

                          Declaration

                          Swift

                          -
                          case validatorUrlInvalid
                          +
                          case readReceiptFailed
                        • +
                        +
                      +
                      +
                      + + +
                      + +

                      Errors returned by refresh()

                      +

                      +
                      +
                      +
                      • @@ -302,26 +374,39 @@

                        Declaration

                        -

                        Undocumented

                        +

                        Failed to refresh products from the App Store.

                        Declaration

                        Swift

                        -
                        case refreshReceiptFailed
                        +
                        case refreshProductsFailed
                      • +
                      +
                      +
                      +
                      + + +
                      + +

                      Errors returned by purchase()

                      +

                      +
                      +
                      +
                      • @@ -329,14 +414,14 @@

                        Declaration

                        -

                        Undocumented

                        +

                        The product was not found on the App Store and cannot be purchased.

                        Declaration

                        Swift

                        -
                        case validateReceiptFailed
                        +
                        case productNotFound
                        @@ -346,9 +431,9 @@

                        Declaration

                      • @@ -356,14 +441,14 @@

                        Declaration

                        -

                        Undocumented

                        +

                        The user is not allowed to authorize payments.

                        Declaration

                        Swift

                        -
                        case readReceiptFailed
                        +
                        case cannotMakePurchase
                        @@ -373,9 +458,9 @@

                        Declaration

                      • @@ -383,14 +468,14 @@

                        Declaration

                        -

                        Undocumented

                        +

                        A purchase is already in progress.

                        Declaration

                        Swift

                        -
                        case refreshProductsFailed
                        +
                        case alreadyPurchasing
                        @@ -399,14 +484,15 @@

                        Declaration

                      -
                      - - + + diff --git a/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Enums/IAPPeriodFormat.html b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Enums/IAPPeriodFormat.html new file mode 100644 index 0000000..d2e9a5f --- /dev/null +++ b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Enums/IAPPeriodFormat.html @@ -0,0 +1,247 @@ + + + + IAPPeriodFormat Enumeration Reference + + + + + + + + + + + + + + + + +
                      +

                      + + InAppPurchaseLib documentation + + (77% documented) +

                      + +

                      +

                      + +
                      +

                      + +

                      + + + View on GitHub + +

                      + +
                      + + + +
                      + +
                      + +
                      +
                      +

                      IAPPeriodFormat

                      +
                      +
                      +
                      public enum IAPPeriodFormat
                      + +
                      +
                      +

                      Undocumented

                      + +
                      +
                      + +
                      +
                      +
                      +
                        +
                      • +
                        + + + + short + +
                        +
                        +
                        +
                        +
                        +
                        +

                        Undocumented

                        + +
                        +
                        +

                        Declaration

                        +
                        +

                        Swift

                        +
                        case short
                        + +
                        +
                        +
                        +
                        +
                      • +
                      • +
                        + + + + long + +
                        +
                        +
                        +
                        +
                        +
                        +

                        Undocumented

                        + +
                        +
                        +

                        Declaration

                        +
                        +

                        Swift

                        +
                        case long
                        + +
                        +
                        +
                        +
                        +
                      • +
                      +
                      +
                      +
                      + +
                      +
                      + + + + diff --git a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Enums/IAPProductType.html b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Enums/IAPProductType.html similarity index 55% rename from docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Enums/IAPProductType.html rename to docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Enums/IAPProductType.html index 5f397d9..645973d 100644 --- a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Enums/IAPProductType.html +++ b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Enums/IAPProductType.html @@ -4,116 +4,163 @@ IAPProductType Enumeration Reference - + + + + + + -
                      -
                      -

                      InAppPurchaseLib (52% documented)

                      -

                      View on GitHub

                      -
                      -
                      -
                      -
                      +

                      + + InAppPurchaseLib documentation + + (77% documented)

                      -
                      + +

                      +

                      + +
                      +

                      + +

                      + + + View on GitHub + +

                      + + + + + + + +
                      +
                      -
                        +
                        • @@ -237,14 +287,15 @@

                          Declaration

                      -
                      - - + + diff --git a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Enums/IAPPurchaseResultState.html b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Enums/IAPPurchaseResultState.html similarity index 53% rename from docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Enums/IAPPurchaseResultState.html rename to docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Enums/IAPPurchaseResultState.html index ade15de..42485c4 100644 --- a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Enums/IAPPurchaseResultState.html +++ b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Enums/IAPPurchaseResultState.html @@ -4,116 +4,163 @@ IAPPurchaseResultState Enumeration Reference - + + + + + + -
                      -
                      -

                      InAppPurchaseLib (52% documented)

                      -

                      View on GitHub

                      -
                      -
                      -
                      -
                      +

                      + + InAppPurchaseLib documentation + + (77% documented)

                      -
                      + +

                      +

                      + +
                      +

                      + +

                      + + + View on GitHub + +

                      + + + + + + + +
                      +
                      -
                        +
                        • @@ -140,7 +190,7 @@

                          IAPPurchaseResultState

                          -

                          Undocumented

                          +

                          The purchase was successful.

                          @@ -167,7 +217,7 @@

                          Declaration

                          -

                          Undocumented

                          +

                          Puchase failed.

                          @@ -194,7 +244,7 @@

                          Declaration

                          -

                          Undocumented

                          +

                          The purchase was cancelled by the user.

                          @@ -221,7 +271,7 @@

                          Declaration

                          -

                          Undocumented

                          +

                          The purchase is deferred.

                          @@ -237,14 +287,15 @@

                          Declaration

                      -
                      - - + + diff --git a/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Enums/IAPRefreshResultState.html b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Enums/IAPRefreshResultState.html new file mode 100644 index 0000000..f9151e4 --- /dev/null +++ b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Enums/IAPRefreshResultState.html @@ -0,0 +1,274 @@ + + + + IAPRefreshResultState Enumeration Reference + + + + + + + + + + + + + + + + +
                      +

                      + + InAppPurchaseLib documentation + + (77% documented) +

                      + +

                      +

                      + +
                      +

                      + +

                      + + + View on GitHub + +

                      + +
                      + + + +
                      + +
                      + +
                      +
                      +

                      IAPRefreshResultState

                      +
                      +
                      +
                      public enum IAPRefreshResultState
                      + +
                      +
                      +

                      The list of the different states of the IAPRefreshResult.

                      + +
                      +
                      + +
                      +
                      +
                      +
                        +
                      • +
                        + + + + succeeded + +
                        +
                        +
                        +
                        +
                        +
                        +

                        Refresh was successful.

                        + +
                        +
                        +

                        Declaration

                        +
                        +

                        Swift

                        +
                        case succeeded
                        + +
                        +
                        +
                        +
                        +
                      • +
                      • +
                        + + + + failed + +
                        +
                        +
                        +
                        +
                        +
                        +

                        Refresh failed.

                        + +
                        +
                        +

                        Declaration

                        +
                        +

                        Swift

                        +
                        case failed
                        + +
                        +
                        +
                        +
                        +
                      • +
                      • +
                        + + + + skipped + +
                        +
                        +
                        +
                        +
                        +
                        +

                        Refresh has been skipped because it is not necessary.

                        + +
                        +
                        +

                        Declaration

                        +
                        +

                        Swift

                        +
                        case skipped
                        + +
                        +
                        +
                        +
                        +
                      • +
                      +
                      +
                      +
                      + +
                      +
                      + + + + diff --git a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Extensions/SKProduct.html b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Extensions/SKProduct.html similarity index 68% rename from docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Extensions/SKProduct.html rename to docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Extensions/SKProduct.html index 19d937b..8fbe59d 100644 --- a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Extensions/SKProduct.html +++ b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Extensions/SKProduct.html @@ -4,116 +4,163 @@ SKProduct Extension Reference - + + + + + + -
                      -
                      -

                      InAppPurchaseLib (52% documented)

                      -

                      View on GitHub

                      -
                      -
                      -
                      -
                      +

                      + + InAppPurchaseLib documentation + + (77% documented)

                      -
                      + +

                      +

                      + +
                      +

                      + +

                      + + + View on GitHub + +

                      + + + + + + + +
                      +
                      -
                        +
                        • @@ -317,14 +367,15 @@

                          Declaration

                      -
                      - - + + diff --git a/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Getting Started.html b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Getting Started.html new file mode 100644 index 0000000..717182d --- /dev/null +++ b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Getting Started.html @@ -0,0 +1,221 @@ + + + + Getting Started Reference + + + + + + + + + + + + + + + + +
                      +

                      + + InAppPurchaseLib documentation + + (77% documented) +

                      + +

                      +

                      + +
                      +

                      + +

                      + + + View on GitHub + +

                      + +
                      + + + +
                      + +
                      + +
                      +
                      +

                      Getting Started

                      +

                      If you haven’t already, I highly recommend your read the Overview and Preparing section of Apple’s In-App Purchase official documentation.

                      +

                      Requirements

                      + +
                        +
                      • Configure your App and Xcode to support In-App Purchases. + +
                      • +
                      • Create and configure your Fovea.Billing project account: + +
                          +
                        • Set your bundle ID
                        • +
                        • The iOS Shared Secret (or shared key) is to be retrieved from AppStoreConnect
                        • +
                        • The iOS Subscription Status URL (only if you want subscriptions)
                        • +
                      • +
                      + +
                      +
                      + +
                      +
                      +
                      + +
                      +
                      +
                      + +
                      +
                      + + + + diff --git a/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Protocols/IAPErrorProtocol.html b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Protocols/IAPErrorProtocol.html new file mode 100644 index 0000000..8c3d780 --- /dev/null +++ b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Protocols/IAPErrorProtocol.html @@ -0,0 +1,220 @@ + + + + IAPErrorProtocol Protocol Reference + + + + + + + + + + + + + + + + +
                      +

                      + + InAppPurchaseLib documentation + + (77% documented) +

                      + +

                      +

                      + +
                      +

                      + +

                      + + + View on GitHub + +

                      + +
                      + + + +
                      + +
                      + +
                      +
                      +

                      IAPErrorProtocol

                      +
                      +
                      +
                      public protocol IAPErrorProtocol : LocalizedError
                      + +
                      +
                      +

                      Undocumented

                      + +
                      +
                      + +
                      +
                      +
                      +
                        +
                      • +
                        + + + + code + +
                        +
                        +
                        +
                        +
                        +
                        +

                        Undocumented

                        + +
                        +
                        +

                        Declaration

                        +
                        +

                        Swift

                        +
                        var code: IAPErrorCode { get }
                        + +
                        +
                        +
                        +
                        +
                      • +
                      +
                      +
                      +
                      + +
                      +
                      + + + + diff --git a/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Protocols/IAPPurchaseDelegate.html b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Protocols/IAPPurchaseDelegate.html new file mode 100644 index 0000000..34c6641 --- /dev/null +++ b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Protocols/IAPPurchaseDelegate.html @@ -0,0 +1,244 @@ + + + + IAPPurchaseDelegate Protocol Reference + + + + + + + + + + + + + + + + +
                      +

                      + + InAppPurchaseLib documentation + + (77% documented) +

                      + +

                      +

                      + +
                      +

                      + +

                      + + + View on GitHub + +

                      + +
                      + + + +
                      + +
                      + +
                      +
                      +

                      IAPPurchaseDelegate

                      +
                      +
                      +
                      public protocol IAPPurchaseDelegate
                      + +
                      +
                      +

                      The protocol that you must adopt if you have consumable and/or non-renewing subscription products.

                      + +
                      +
                      + +
                      +
                      +
                      +
                        +
                      • + +
                        +
                        +
                        +
                        +
                        +

                        Called when a product is newly purchased, updated or restored.

                        +
                        +

                        Important

                        +

                        You have to acknowledge delivery of the (virtual) item to finalize the transaction. Then you have to call InAppPurchase.finishTransactions(for: productIdentifier)as soon as you have delivered the product.

                        + +
                        + +
                        +
                        +

                        Declaration

                        +
                        +

                        Swift

                        +
                        func productPurchased(productIdentifier: String)
                        + +
                        +
                        +
                        +

                        Parameters

                        + + + + + + + +
                        + + productIdentifier + + +
                        +

                        The identifier of the product.

                        +
                        +
                        +
                        +
                        +
                        +
                      • +
                      +
                      +
                      +
                      + +
                      +
                      + + + + diff --git a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Protocols/InAppPurchaseLib.html b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Protocols/InAppPurchaseLib.html similarity index 86% rename from docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Protocols/InAppPurchaseLib.html rename to docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Protocols/InAppPurchaseLib.html index 63ceafc..cdb81d0 100644 --- a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Protocols/InAppPurchaseLib.html +++ b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Protocols/InAppPurchaseLib.html @@ -4,116 +4,163 @@ InAppPurchaseLib Protocol Reference - + + + + + + -
                      -
                      -

                      InAppPurchaseLib (52% documented)

                      -

                      View on GitHub

                      -
                      -
                      -
                      -
                      +

                      + + InAppPurchaseLib documentation + + (77% documented)

                      -
                      + +

                      +

                      + +
                      +

                      + +

                      + + + View on GitHub + +

                      + + + + + + + +
                      +
                      -
                      -
                        +
                        • @@ -431,7 +481,7 @@

                          Products information

                          Gets all products retrieved from the App Store

                          See

                          - See also: SKProduct. + See also: SKProduct
                          @@ -467,7 +517,7 @@

                          Return Value

                          Gets the product by its identifier from the list of products retrieved from the App Store.

                          See

                          - See also: SKProduct. + See also: SKProduct
                          @@ -518,7 +568,7 @@

                          Purchasing and Restoring

                          -
                      - - + + diff --git a/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Structs/IAPError.html b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Structs/IAPError.html new file mode 100644 index 0000000..3ce0582 --- /dev/null +++ b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Structs/IAPError.html @@ -0,0 +1,252 @@ + + + + IAPError Structure Reference + + + + + + + + + + + + + + + + +
                      +

                      + + InAppPurchaseLib documentation + + (77% documented) +

                      + +

                      +

                      + +
                      +

                      + +

                      + + + View on GitHub + +

                      + +
                      + + + +
                      + +
                      + +
                      +
                      +

                      IAPError

                      +
                      +
                      +
                      public struct IAPError : IAPErrorProtocol
                      + +
                      +
                      +

                      When calling refresh(), purchase() or restorePurchases(), the callback can return an IAPError if the state is failed.

                      + +
                      +
                      + +
                      +
                      +
                      +
                        +
                      • +
                        + + + + code + +
                        +
                        +
                        +
                        +
                        +
                        +

                        The error code.

                        +
                        +

                        See

                        + See also: IAPErrorCode. + +
                        + +
                        +
                        +

                        Declaration

                        +
                        +

                        Swift

                        +
                        public var code: IAPErrorCode
                        + +
                        +
                        +
                        +
                        +
                      • +
                      • +
                        + + + + localizedDescription + +
                        +
                        +
                        +
                        +
                        +
                        +

                        The error description.

                        + +
                        +
                        +

                        Declaration

                        +
                        +

                        Swift

                        +
                        public var localizedDescription: String { get }
                        + +
                        +
                        +
                        +
                        +
                      • +
                      +
                      +
                      +
                      + +
                      +
                      + + + + diff --git a/docs/jazzy/Structs/IAPProduct.html b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Structs/IAPProduct.html similarity index 58% rename from docs/jazzy/Structs/IAPProduct.html rename to docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Structs/IAPProduct.html index 889974f..f52efcb 100644 --- a/docs/jazzy/Structs/IAPProduct.html +++ b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Structs/IAPProduct.html @@ -4,116 +4,163 @@ IAPProduct Structure Reference - + + + + + + -
                      -
                      -

                      InAppPurchaseLib (52% documented)

                      -

                      View on GitHub

                      -
                      -
                      -
                      -
                      +

                      + + InAppPurchaseLib documentation + + (77% documented)

                      -
                      + +

                      +

                      + +
                      +

                      + +

                      + + + View on GitHub + +

                      + + + + + + + +
                      +
                      -
                        +
                        • @@ -251,14 +301,15 @@

                          Parameters

                      -
                      - - + + diff --git a/docs/jazzy/Structs/IAPPurchaseResult.html b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Structs/IAPPurchaseResult.html similarity index 58% rename from docs/jazzy/Structs/IAPPurchaseResult.html rename to docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Structs/IAPPurchaseResult.html index 6bc3b5d..d17b2a6 100644 --- a/docs/jazzy/Structs/IAPPurchaseResult.html +++ b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Structs/IAPPurchaseResult.html @@ -4,116 +4,163 @@ IAPPurchaseResult Structure Reference - + + + + + + -
                      -
                      -

                      InAppPurchaseLib (52% documented)

                      -

                      View on GitHub

                      -
                      -
                      -
                      -
                      +

                      + + InAppPurchaseLib documentation + + (77% documented)

                      -
                      + +

                      +

                      + +
                      +

                      + +

                      + + + View on GitHub + +

                      + + + + + + + +
                      +
                      -
                        +
                        • @@ -237,14 +287,15 @@

                          Declaration

                      -
                      - - + + diff --git a/docs/jazzy/Structs/IAPRefreshResult.html b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Structs/IAPRefreshResult.html similarity index 57% rename from docs/jazzy/Structs/IAPRefreshResult.html rename to docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Structs/IAPRefreshResult.html index 8d70385..670d0f2 100644 --- a/docs/jazzy/Structs/IAPRefreshResult.html +++ b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Structs/IAPRefreshResult.html @@ -4,116 +4,163 @@ IAPRefreshResult Structure Reference - + + + + + + -
                      -
                      -

                      InAppPurchaseLib (52% documented)

                      -

                      View on GitHub

                      -
                      -
                      -
                      -
                      +

                      + + InAppPurchaseLib documentation + + (77% documented)

                      -
                      + +

                      +

                      + +
                      +

                      + +

                      + + + View on GitHub + +

                      + + + + + + + +
                      +
                      -
                        +
                        • @@ -237,14 +287,15 @@

                          Declaration

                      -
                      - - + + diff --git a/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Usage.html b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Usage.html new file mode 100644 index 0000000..535a067 --- /dev/null +++ b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Usage.html @@ -0,0 +1,296 @@ + + + + Usage Reference + + + + + + + + + + + + + + + + +
                      +

                      + + InAppPurchaseLib documentation + + (77% documented) +

                      + +

                      +

                      + +
                      +

                      + +

                      + + + View on GitHub + +

                      + +
                      + + + +
                      + +
                      + +
                      +
                      +

                      Usage

                      +

                      The process of implementing in-app purchases involves several steps:

                      + +
                        +
                      1. Displaying the list of purchasable products
                      2. +
                      3. Initiating a purchase
                      4. +
                      5. Delivering and finalizing a purchase
                      6. +
                      7. Checking the current ownership of non-consumables and subscriptions
                      8. +
                      9. Implementing the Restore Purchases button
                      10. +
                      + +

                      Note: You have to import InAppPurchaseLib wherever you use the library.

                      + +
                      +
                      + +
                      +
                      +
                      + +
                      +
                      +
                      + +
                      +
                      + + + + diff --git a/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/analytics.html b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/analytics.html new file mode 100644 index 0000000..697340d --- /dev/null +++ b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/analytics.html @@ -0,0 +1,221 @@ + + + + Analytics Reference + + + + + + + + + + + + + + + +
                      +

                      + + InAppPurchaseLib documentation + + (77% documented) +

                      + +

                      +

                      + +
                      +

                      + +

                      + + + View on GitHub + +

                      + +
                      + + + +
                      + +
                      + +
                      +
                      + +

                      Analytics

                      + +

                      Tracking the purchase flow is a common things in apps. Especially as it’s core to your revenue model.

                      + +

                      We can track 5 events, which step in the purchase pipeline a user reached.

                      + +
                        +
                      1. purchase initiated
                      2. +
                      3. purchase cancelled
                      4. +
                      5. purchase failed
                      6. +
                      7. purchase deferred
                      8. +
                      9. purchase succeeded
                      10. +
                      + +

                      Here’s a quick example showing how to implement this correctly.

                      +
                      func makePurchase() {
                      +  Analytics.trackEvent("purchase initiated")
                      +  InAppPurchase.purchase(
                      +    productIdentifier: "my_product_id",
                      +    callback: { result in
                      +      switch result.state {
                      +      case .purchased:
                      +        // Reminder: We are not processing the purchase here, only updating your UI.
                      +        //           That's why we do not send an event to analytics.
                      +      case .failed:
                      +        Analytics.trackEvent("purchase failed")
                      +      case .deferred:
                      +        Analytics.trackEvent("purchase deferred")
                      +      case .cancelled:
                      +        Analytics.trackEvent("purchase cancelled")
                      +    }
                      +  })
                      +}
                      +
                      +// IAPPurchaseDelegate implementation
                      +func productPurchased(productIdentifier: String) {
                      +  Analytics.trackEvent("purchase succeeded")
                      +  InAppPurchase.finishTransactions(for: productIdentifier)
                      +}
                      +
                      + +

                      The important part to remember is that a purchase can occur outside your app (or be approved when the app is not running), that’s why tracking purchase succeeded has to be part of the productPurchased delegate function.

                      + +

                      Refer to the Consumables section to learn more about the productPurchased function.

                      + +
                      +
                      + + +
                      +
                      + + + + diff --git a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/css/highlight.css b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/css/highlight.css similarity index 100% rename from docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/css/highlight.css rename to docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/css/highlight.css diff --git a/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/css/jazzy.css b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/css/jazzy.css new file mode 100644 index 0000000..ff59f5f --- /dev/null +++ b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/css/jazzy.css @@ -0,0 +1,395 @@ +*, *:before, *:after { + box-sizing: inherit; } + +body { + margin: 0; + background: #fff; + color: #333; + font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; + letter-spacing: .2px; + -webkit-font-smoothing: antialiased; + box-sizing: border-box; } + +h1 { + font-size: 2rem; + font-weight: 700; + margin: 1.275em 0 0.6em; } + +h2 { + font-size: 1.75rem; + font-weight: 700; + margin: 1.275em 0 0.3em; } + +h3 { + font-size: 1.5rem; + font-weight: 700; + margin: 1em 0 0.3em; } + +h4 { + font-size: 1.25rem; + font-weight: 700; + margin: 1.275em 0 0.85em; } + +h5 { + font-size: 1rem; + font-weight: 700; + margin: 1.275em 0 0.85em; } + +h6 { + font-size: 1rem; + font-weight: 700; + margin: 1.275em 0 0.85em; + color: #777; } + +p { + margin: 0 0 1em; } + +ul, ol { + padding: 0 0 0 2em; + margin: 0 0 0.85em; } + +blockquote { + margin: 0 0 0.85em; + padding: 0 15px; + color: #858585; + border-left: 4px solid #e5e5e5; } + +img { + max-width: 100%; } + +a { + color: #4183c4; + text-decoration: none; } + a:hover, a:focus { + outline: 0; + text-decoration: underline; } + a.discouraged { + text-decoration: line-through; } + a.discouraged:hover, a.discouraged:focus { + text-decoration: underline line-through; } + +table { + background: #fff; + width: 100%; + border-collapse: collapse; + border-spacing: 0; + overflow: auto; + margin: 0 0 0.85em; } + +tr:nth-child(2n) { + background-color: #fbfbfb; } + +th, td { + padding: 6px 13px; + border: 1px solid #ddd; } + +pre { + margin: 0 0 1.275em; + padding: .85em 1em; + overflow: auto; + background: #f7f7f7; + font-size: .85em; + font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } + +code { + font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } + +.item-container p > code, .item-container li > code, .top-matter p > code, .top-matter li > code { + background: #f7f7f7; + padding: .2em; } + .item-container p > code:before, .item-container p > code:after, .item-container li > code:before, .item-container li > code:after, .top-matter p > code:before, .top-matter p > code:after, .top-matter li > code:before, .top-matter li > code:after { + letter-spacing: -.2em; + content: "\00a0"; } + +pre code { + padding: 0; + white-space: pre; } + +.content-wrapper { + display: flex; + flex-direction: column; } + @media (min-width: 768px) { + .content-wrapper { + flex-direction: row; } } +.header { + display: flex; + padding: 8px; + font-size: 0.875em; + background: #444; + color: #999; } + +.header-col { + margin: 0; + padding: 0 8px; } + +.header-col--primary { + flex: 1; } + +.header-link { + color: #fff; } + +.header-icon { + padding-right: 6px; + vertical-align: -4px; + height: 16px; } + +.breadcrumbs { + font-size: 0.875em; + padding: 8px 16px; + margin: 0; + background: #fbfbfb; + border-bottom: 1px solid #ddd; } + +.carat { + height: 10px; + margin: 0 5px; } + +.navigation { + order: 2; } + @media (min-width: 768px) { + .navigation { + order: 1; + width: 25%; + max-width: 300px; + padding-bottom: 64px; + overflow: hidden; + word-wrap: normal; + background: #fbfbfb; + border-right: 1px solid #ddd; } } +.nav-groups { + list-style-type: none; + padding-left: 0; } + +.nav-group-name { + border-bottom: 1px solid #ddd; + padding: 8px 0 8px 16px; } + +.nav-group-name-link { + color: #333; } + +.nav-group-tasks { + margin: 8px 0; + padding: 0 0 0 8px; } + +.nav-group-task { + font-size: 1em; + list-style-type: none; + white-space: nowrap; } + +.nav-group-task-link { + color: #808080; } + +.main-content { + order: 1; } + @media (min-width: 768px) { + .main-content { + order: 2; + flex: 1; + padding-bottom: 60px; } } +.section { + padding: 0 32px; + border-bottom: 1px solid #ddd; } + +.section-content { + max-width: 834px; + margin: 0 auto; + padding: 16px 0; } + +.section-name { + color: #666; + display: block; } + .section-name p { + margin-bottom: inherit; } + +.declaration .highlight { + overflow-x: initial; + padding: 8px 0; + margin: 0; + background-color: transparent; + border: none; } + +.task-group-section { + border-top: 1px solid #ddd; } + +.task-group { + padding-top: 0px; } + +.task-name-container a[name]:before { + content: ""; + display: block; } + +.section-name-container { + position: relative; } + .section-name-container .section-name-link { + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + margin-bottom: 0; } + .section-name-container .section-name { + position: relative; + pointer-events: none; + z-index: 1; } + .section-name-container .section-name a { + pointer-events: auto; } + +.item-container { + padding: 0; } + +.item { + padding-top: 8px; + width: 100%; + list-style-type: none; } + .item a[name]:before { + content: ""; + display: block; } + .item .token, .item .direct-link { + display: inline-block; + text-indent: -20px; + padding-left: 3px; + margin-left: 20px; + font-size: 1rem; } + .item .declaration-note { + font-size: .85em; + color: #808080; + font-style: italic; } + +.pointer-container { + border-bottom: 1px solid #ddd; + left: -23px; + padding-bottom: 13px; + position: relative; + width: 110%; } + +.pointer { + left: 21px; + top: 7px; + display: block; + position: absolute; + width: 12px; + height: 12px; + border-left: 1px solid #ddd; + border-top: 1px solid #ddd; + background: #fff; + transform: rotate(45deg); } + +.height-container { + display: none; + position: relative; + width: 100%; + overflow: hidden; } + .height-container .section { + background: #fff; + border: 1px solid #ddd; + border-top-width: 0; + padding-top: 10px; + padding-bottom: 5px; + padding: 8px 16px; } + +.aside, .language { + padding: 6px 12px; + margin: 12px 0; + border-left: 5px solid #dddddd; + overflow-y: hidden; } + .aside .aside-title, .language .aside-title { + font-size: 9px; + letter-spacing: 2px; + text-transform: uppercase; + padding-bottom: 0; + margin: 0; + color: #aaa; + -webkit-user-select: none; } + .aside p:last-child, .language p:last-child { + margin-bottom: 0; } + +.language { + border-left: 5px solid #cde9f4; } + .language .aside-title { + color: #4183c4; } + +.aside-warning, .aside-deprecated, .aside-unavailable { + border-left: 5px solid #ff6666; } + .aside-warning .aside-title, .aside-deprecated .aside-title, .aside-unavailable .aside-title { + color: #ff0000; } + +.graybox { + border-collapse: collapse; + width: 100%; } + .graybox p { + margin: 0; + word-break: break-word; + min-width: 50px; } + .graybox td { + border: 1px solid #ddd; + padding: 5px 25px 5px 10px; + vertical-align: middle; } + .graybox tr td:first-of-type { + text-align: right; + padding: 7px; + vertical-align: top; + word-break: normal; + width: 40px; } + +.slightly-smaller { + font-size: 0.9em; } + +.footer { + padding: 8px 16px; + background: #444; + color: #ddd; + font-size: 0.8em; } + .footer p { + margin: 8px 0; } + .footer a { + color: #fff; } + +html.dash .header, html.dash .breadcrumbs, html.dash .navigation { + display: none; } + +html.dash .height-container { + display: block; } + +form[role=search] input { + font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 24px; + padding: 0 10px; + margin: 0; + border: none; + border-radius: 1em; } + .loading form[role=search] input { + background: white url(../img/spinner.gif) center right 4px no-repeat; } + +form[role=search] .tt-menu { + margin: 0; + min-width: 300px; + background: #fbfbfb; + color: #333; + border: 1px solid #ddd; } + +form[role=search] .tt-highlight { + font-weight: bold; } + +form[role=search] .tt-suggestion { + font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; + padding: 0 8px; } + form[role=search] .tt-suggestion span { + display: table-cell; + white-space: nowrap; } + form[role=search] .tt-suggestion .doc-parent-name { + width: 100%; + text-align: right; + font-weight: normal; + font-size: 0.9em; + padding-left: 16px; } + +form[role=search] .tt-suggestion:hover, +form[role=search] .tt-suggestion.tt-cursor { + cursor: pointer; + background-color: #4183c4; + color: #fff; } + +form[role=search] .tt-suggestion:hover .doc-parent-name, +form[role=search] .tt-suggestion.tt-cursor .doc-parent-name { + color: #fff; } diff --git a/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/displaying-products-with-purchases.html b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/displaying-products-with-purchases.html new file mode 100644 index 0000000..d77fc40 --- /dev/null +++ b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/displaying-products-with-purchases.html @@ -0,0 +1,231 @@ + + + + Displaying products with purchases Reference + + + + + + + + + + + + + + + +
                      +

                      + + InAppPurchaseLib documentation + + (77% documented) +

                      + +

                      +

                      + +
                      +

                      + +

                      + + + View on GitHub + +

                      + +
                      + + + +
                      + +
                      + +
                      +
                      + +

                      Displaying products with purchases

                      + +

                      In your store screen, where you present your products titles and prices with a purchase button, there are some cases to handle that we skipped. Owned products and deferred purchases.

                      +

                      Owned products

                      + +

                      Non-consumables and active auto-renewing subscriptions cannot be purchased again. You should adjust your UI to reflect that state. Refer to InAppPurchase.hasActivePurchase() to and to the example later in this section.

                      +

                      Deferred purchases

                      + +

                      Apple’s Ask to Buy feature lets parents approve any purchases initiated by children, including in-app purchases.

                      + +

                      With Ask to Buy enabled, when a child requests to make a purchase, the app is notified that the purchase is awaiting the parent’s approval in the purchase callback:

                      +
                      InAppPurchase.purchase(
                      +  productIdentifier: productIdentifier,
                      +  callback: { result in
                      +    switch result.state {
                      +    case .deferred:
                      +      // Pending parent approval
                      +  }
                      +})
                      +
                      + +

                      In the deferred case, the child has been notified by StoreKit that the parents have to approve the purchase. He might then close the app and come back later. You don’t have much to do, but to display in your UI that there is a purchase waiting for parental approval in your views.

                      + +

                      We will use the hasDeferredTransaction method:

                      +
                      InAppPurchase.hasDeferredTransaction(for productIdentifier: String) -> Bool
                      +
                      +

                      Example

                      + +

                      Here’s an example that covers what has been discussed above. We will update our example refreshView function from before:

                      +
                      @objc func refreshView() {
                      +  guard let product: SKProduct = InAppPurchase.getProductBy(identifier: "my_product_id") else {
                      +    self.titleLabel.text = "Product unavailable"
                      +    return
                      +  }
                      +  self.titleLabel.text = product.localizedTitle
                      +  // ...
                      +
                      +  // "Ask to Buy" deferred purchase waiting for parent's approval
                      +  if InAppPurchase.hasDeferredTransaction(for: "my_product_id") {
                      +    self.statusLabel.text = "Waiting for Approval..."
                      +    self.purchaseButton.isPointerInteractionEnabled = false
                      +  }
                      +  // "Owned" product
                      +  else if InAppPurchase.hasActivePurchase(for: "my_product_id") {
                      +    self.statusLabel.text = "OWNED"
                      +    self.purchaseButton.isPointerInteractionEnabled = false
                      +  }
                      +  else {
                      +    self.purchaseButton.isPointerInteractionEnabled = true
                      +  }
                      +}
                      +
                      + +

                      When a product is owned or has a deferred purchase, we make sure the purchase button is grayed out. We also use a status label to display some details. Of course, you are free to design your UI as you see fit.

                      + +
                      +
                      + + +
                      +
                      + + + + diff --git a/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/displaying-products.html b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/displaying-products.html new file mode 100644 index 0000000..cc50b1f --- /dev/null +++ b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/displaying-products.html @@ -0,0 +1,213 @@ + + + + Displaying products Reference + + + + + + + + + + + + + + + +
                      +

                      + + InAppPurchaseLib documentation + + (77% documented) +

                      + +

                      +

                      + +
                      +

                      + +

                      + + + View on GitHub + +

                      + +
                      + + + +
                      + +
                      + +
                      +
                      + +

                      Displaying products

                      + +

                      Let’s start with the simplest case: you have a single product.

                      + +

                      You can retrieve all information about this product using the function InAppPurchase.getProductBy(identifier: "my_product_id"). This returns an SKProduct extended with helpful methods.

                      + +

                      Those are the most important:

                      + +
                        +
                      • productIdentifier: String - The string that identifies the product to the Apple AppStore.
                      • +
                      • localizedTitle: String - The name of the product, in the language of the device, as retrieved from the AppStore.
                      • +
                      • localizedDescription: String - A description of the product, in the language of the device, as retrieved from the AppStore.
                      • +
                      • localizedPrice: String - The cost of the product in the local currency (read-only property added by this library).
                      • +
                      + +

                      Example:

                      + +

                      You can add a function similar to this to your view.

                      +
                      @objc func refreshView() {
                      +  guard let product: SKProduct = InAppPurchase.getProductBy(identifier: "my_product_id") else {
                      +    self.titleLabel.text = "Product unavailable"
                      +    return
                      +  }
                      +  self.titleLabel.text = product.localizedTitle
                      +  self.descriptionLabel.text = product.localizedDescription
                      +  self.priceLabel.text = product.localizedPrice
                      +}
                      +
                      + +

                      This example assumes self.titleLabel is a UILabel, etc.

                      + +

                      Make sure to call this function when the view appears on screen, for instance by calling it from viewWillAppear.

                      +
                      override func viewWillAppear(_ animated: Bool) {
                      +  self.refreshView()
                      +}
                      +
                      + +
                      +
                      + + +
                      +
                      + + + + diff --git a/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/displaying-subscriptions.html b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/displaying-subscriptions.html new file mode 100644 index 0000000..640c8ff --- /dev/null +++ b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/displaying-subscriptions.html @@ -0,0 +1,214 @@ + + + + Displaying subscriptions Reference + + + + + + + + + + + + + + + +
                      +

                      + + InAppPurchaseLib documentation + + (77% documented) +

                      + +

                      +

                      + +
                      +

                      + +

                      + + + View on GitHub + +

                      + +
                      + + + +
                      + +
                      + +
                      +
                      + +

                      Displaying subscriptions

                      + +

                      For subscription products, you also have some data about subscription periods and introductory offers.

                      + +
                        +
                      • func hasIntroductoryPriceEligible() -> Bool - The product has an introductory price the user is eligible to.
                      • +
                      • localizedSubscriptionPeriod: String? - The period of the subscription.
                      • +
                      • localizedIntroductoryPrice: String? - The cost of the introductory offer if available in the local currency.
                      • +
                      • localizedIntroductoryPeriod: String? - The subscription period of the introductory offer.
                      • +
                      • localizedIntroductoryDuration: String? - The duration of the introductory offer.
                      • +
                      + +

                      Example

                      +
                      @objc func refreshView() {
                      +  guard let product: SKProduct = InAppPurchase.getProductBy(identifier: "my_product_id") else {
                      +    self.titleLabel.text = "Product unavailable"
                      +    return
                      +  }
                      +  self.titleLabel.text = product.localizedTitle
                      +  self.descriptionLabel.text = product.localizedDescription
                      +
                      +  // Format price text. Example: "0,99€ / month for 3 months (then 3,99 € / month)"
                      +  var priceText = "\(product.localizedPrice) / \(product.localizedSubscriptionPeriod!)"
                      +  if product.hasIntroductoryPriceEligible() {
                      +      if product.introductoryPrice!.numberOfPeriods == 1 {
                      +          priceText = "\(product.localizedIntroductoryPrice!) for \(product.localizedIntroductoryDuration!)" +
                      +          " (then \(priceText))"
                      +      } else {
                      +          priceText = "\(product.localizedIntroductoryPrice!) / \(product.localizedIntroductoryPeriod!)" +
                      +          " for \(product.localizedIntroductoryDuration!) (then \(priceText))"
                      +      }
                      +  }
                      +  self.priceLabel.text = priceText
                      +}
                      +
                      + +

                      Note: You have to import StoreKit wherever you use SKProduct.

                      + +
                      +
                      + + +
                      +
                      + + + + diff --git a/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/errors.html b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/errors.html new file mode 100644 index 0000000..33fa397 --- /dev/null +++ b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/errors.html @@ -0,0 +1,206 @@ + + + + Errors Reference + + + + + + + + + + + + + + + +
                      +

                      + + InAppPurchaseLib documentation + + (77% documented) +

                      + +

                      +

                      + +
                      +

                      + +

                      + + + View on GitHub + +

                      + +
                      + + + +
                      + +
                      + +
                      +
                      + +

                      Errors

                      + +

                      When calling refresh(), purchase() or restorePurchases(), the callback can return an IAPError if the state is failed. +Here is the list of IAPErrorCode you can receive:

                      + +
                        +
                      • Errors returned by refresh(), purchase() or restorePurchases()

                        + +
                          +
                        • libraryNotInitialized - You must call the initialize fuction before using the library.
                        • +
                        • bundleIdentifierInvalid - The Bundle Identifier is invalid.
                        • +
                        • validatorUrlInvalid - The Validator URL String is invalid.
                        • +
                        • refreshReceiptFailed - Failed to refresh the App Store receipt.
                        • +
                        • validateReceiptFailed - Failed to validate the App Store receipt with Fovea.
                        • +
                        • readReceiptFailed - Failed to read the receipt validation.
                        • +
                      • +
                      • Errors returned by refresh()

                        + +
                          +
                        • refreshProductsFailed - Failed to refresh products from the App Store.
                        • +
                      • +
                      • Errors returned by purchase()

                        + +
                          +
                        • productNotFound - The product was not found on the App Store and cannot be purchased.
                        • +
                        • cannotMakePurchase - The user is not allowed to authorize payments.
                        • +
                        • alreadyPurchasing - A purchase is already in progress.
                        • +
                      • +
                      + +
                      +
                      + + +
                      +
                      + + + + diff --git a/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/handling-purchases.html b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/handling-purchases.html new file mode 100644 index 0000000..00379e9 --- /dev/null +++ b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/handling-purchases.html @@ -0,0 +1,271 @@ + + + + Handling purchases Reference + + + + + + + + + + + + + + + +
                      +

                      + + InAppPurchaseLib documentation + + (77% documented) +

                      + +

                      +

                      + +
                      +

                      + +

                      + + + View on GitHub + +

                      + +
                      + + + +
                      + +
                      + +
                      +
                      + +

                      Handling purchases

                      + +

                      Finally, the magic happened: a user purchased one of your products! Let’s see how we handle the different types of products.

                      + + + +

                      +

                      Non-Consumables

                      + +

                      Wherever your app needs to know if a non-consumable product has been purchased, use InAppPurchase.hasActivePurchase(for: +productIdentifier). This will return true if the user currently owns the product.

                      + +

                      Note: The last known state for the user’s purchases is stored as UserDefaults. As such, their status is always available to your app, even when offline.

                      + +

                      If you have a server that needs to know about the purchase. You should rely on Fovea’s webhook instead of doing anything in here. We will see that later in the Server integration section.

                      + +

                      +

                      Auto-Renewable Subscriptions

                      + +

                      As with non-consumables, you will use InAppPurchase.hasActivePurchase(for: productIdentifier) to check if the user is an active subscriber to a given product.

                      + +

                      You might also like to call refresh regularly, for example when entering your main view. When appropriate, the library will refresh the receipt to detect subscription renewals or expiry.

                      + +

                      As we’ve seend in the Refreshing section:

                      +
                      override func viewWillAppear(_ animated: Bool) {
                      +  self.refreshView()
                      +  InAppPurchase.refresh(callback: { _ in
                      +      self.refreshView()
                      +  })
                      +}
                      +
                      + +

                      Note: Don’t be reluctant to call refresh() often. Internally, the library ensures heavy operation are only performed if necessary: for example when a subscription just expired. So in 99% of cases this call will result in no-operations.

                      + +

                      +

                      Consumables

                      + +

                      If the purchased products in a consumable, your app is responsible for delivering the purchase then acknowlege that you’ve done so. Delivering generally consists in increasing a counter for some sort of virtual currency.

                      + +

                      Your app can be notified of a purchase at any time. So the library asks you to provide an IAPPurchaseDelegate from initialization.

                      + +

                      In InAppPurchase.initialize(), we can pass an IAPPurchaseDelegate instance. This object implements the productPurchased(productIdentifier:) function, which is called whenever a purchase is approved.

                      + +

                      Here’s a example implementation:

                      +
                      class AppDelegate: UIResponder, UIApplicationDelegate, IAPPurchaseDelegate {
                      +  ...
                      +  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
                      +    InAppPurchase.initialize(
                      +      iapProducts: [...],
                      +      iapPurchaseDelegate: self, // ADDED: iapPurchaseDelegate
                      +      validatorUrlString: "https://validator.fovea.cc/v1/validate?appName=demo&apiKey=12345678")
                      +  }
                      +
                      +  // IAPPurchaseDelegate implementation
                      +  func productPurchased(productIdentifier: String) {
                      +    // TODO
                      +  }
                      +}
                      +
                      + +

                      It’s also important to know that when a purchase is approved, money isn’t yet to reach your bank account. You have to acknowledge delivery of the (virtual) item to finalize the transaction. That is why we have to call InAppPurchase.finishTransactions(for: productIdentifier) as soon as we delivered the product.

                      + +

                      Example

                      + +

                      Let’s define a class that adopts the IAPPurchaseDelegate protocol, it can very well be your application delegate.

                      +
                      func productPurchased(productIdentifier: String) {
                      +  switch productIdenfier {
                      +  case "10_silver":
                      +    addSilver(10)
                      +  case "100_silver":
                      +    addSilver(100)
                      +  }
                      +  InAppPurchase.finishTransactions(for: productIdentifier)
                      +  Analytics.trackEvent("purchase succeeded", productIdentifier)
                      +}
                      +
                      + +

                      Here, we implement our own unlocking logic and call InAppPurchase.finishTransactions() afterward (assuming addSilver is synchronous).

                      + +

                      Note: productPurchased is called when a purchase has been confirmed by Fovea’s receipt validator. If you have a server, he probably already has been notified of this purchase using the webhook.

                      + +

                      Reminder: Keep in mind that purchase notifications might occur even if you never called the InAppPurchase.purchase() function: purchases can be made from another device or the AppStore, they can be approved by parents when the app isn’t running, purchase flows can be interupted, etc. The pattern above ensures your app is always ready to handle purchase events.

                      + +

                      +

                      Non-Renewing Subscriptions

                      + +

                      For non-renewing subscriptions, delivering consists in increasing the amount of time a user can access a given feature. Apple doesn’t manage the length and expiry of non-renewing subscriptions: you have to do this yourself, as for consumables.

                      + +

                      Basically, everything is identical to consumables.

                      + +
                      +
                      + + +
                      +
                      + + + + diff --git a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/img/carat.png b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/img/carat.png similarity index 100% rename from docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/img/carat.png rename to docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/img/carat.png diff --git a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/img/dash.png b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/img/dash.png similarity index 100% rename from docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/img/dash.png rename to docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/img/dash.png diff --git a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/img/gh.png b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/img/gh.png similarity index 100% rename from docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/img/gh.png rename to docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/img/gh.png diff --git a/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/img/spinner.gif b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/img/spinner.gif new file mode 100644 index 0000000000000000000000000000000000000000..e3038d0a42c55b1a07caecb7c7a6cbac982ec09d GIT binary patch literal 1849 zcmb8wZBSF$83*voz31lM+?V7Kkqwb`LI|3K#DupH#kDs91c6du6?SMB!bsTr6|ge_{#vAVj^!DyNA-l zJ&$jDFNv;BTZXX@Qk-7+S5ErF>mkOcZ@lQv>F1VyCEMe2Ud@f<|L%#&QJi${E`2lR zqKFaW2Y$aTRxUY&ae$IHsN;Z;rdZ%CjYLTv!tMi234j-ON=CnvK-1QU|MG$YErn{gHZ@0Q6&?xSyply?S$EVNXH;gp?S5kV2-)$ga^gw`(f4Mm_Y(`RbgRkQTHF2@zL}dCiLk$RoZIc{xZL z_J*d5)Kb;#oKCFyfL*NGSs?y;e(QKvPJe1#G)h5*6E(?L9$nt?UaQJfP^$GDL0PU; z?r}C|);JQ4HES3w5VMlY7x6xfJAzDKlHE~>x;D`Fa=WygYot{pfFehH69o9pK|72W zwC6?t^AnATIJa=kewn=ep?Nk(aZ*pZo}51`S=^)jPRb`~l^VE}08>P3OJtQlXx1K8 z8Q}_u=F*fS;=k=?(fIv#+%811NTx8^}rHwvH%LbYmpFl9p1A{Idh@2x$ zuVp7)VD9}Uc(*(C**!QOdS(6B)$5^Tq5p3q*7un&_Z-NKEiEYg$D{Uq&sa>wj|za5 zJ6M~p)z+E6*X${8j6Ci+sqZ}zxeCAo0gZmZuhl+)Q%1U$Br_`NXcA-3yBdYMha+{o z{?q0Q(kaR2n`M29{!pwpgX6+CPQEgIO%x*0#!TC=c-ZPSkLO>OcmQUao5%-3w)U`F zRz?uGCEKQDh!TQPDmyd;iDX$TkMIe)%61q51Y2b-ie4r00!csilXgKL$txqj|6D(# z@(#!nQ}3R1JGeB3B5Tuqdvyg@*!-bq`9`pmasNGvy9^*+cd1Y*g>HK#rl7i79QQAG zl4SL_wW@WY1d+F?j0gFInGhsRrqvV3SKl{oqW+;9!fu|u@J)h4WM!0Cu02l@p60b#5M9c{dKh=_eRw~yl zWT0gw8RePzf%i8X&twiB|LF0bI@CYE{x1PI;Ylr4RJzU#Zc0j!c07g&q7=_eSd(sH z9VKChd?}^52IKcMqolAWiQH;HSp1Ploa$t zQhg|2sK;%Eb!By`)j9G1w?>`Wt6IK3gB}~uoue(MlRiIoZ#d{pgJZ8b{^{HO8)@%= zX)og3`*D5v1g;*Lz8@Sm(Q|&}PUytlb@Q_dzKFOzKK!Z_&?GO4+JO-)iPH=fs{(`& zZ9{oNn~LUZaeN!>i9p*0N^sHye8nw4xSi!REaP@@^Jy66|)Y9_AFoLlrlkg(42 zVq2J??I(+1*BcSKsTyO7LCho{8tVQm1b>*GQ*H~Mn71Lhy`alw%;D@CU^0)5Ng{cHz@LS7QZ o8uGHYt7)tmZjae5ge5$b`e_;HIklOseoIbqeod19BU-8d00{dbSpWb4 literal 0 HcmV?d00001 diff --git a/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/index.html b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/index.html new file mode 100644 index 0000000..268c210 --- /dev/null +++ b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/index.html @@ -0,0 +1,325 @@ + + + + InAppPurchaseLib Reference + + + + + + + + + + + + + + + +
                      +

                      + + InAppPurchaseLib documentation + + (77% documented) +

                      + +

                      +

                      + +
                      +

                      + +

                      + + + View on GitHub + +

                      + +
                      + + + +
                      + +
                      + +
                      +
                      + +

                      + +

                      + +
                      +

                      An easy-to-use Swift library for In-App Purchases, using Fovea.Billing for receipts validation.

                      +
                      +

                      Features

                      + +
                        +
                      • ✅ Purchase a product
                      • +
                      • ✅ Restore purchased products
                      • +
                      • ✅ Verify transactions with the App Store on Fovea.Billing server
                      • +
                      • ✅ Handle and notify payment transaction states
                      • +
                      • ✅ Retreive products information from the App Store
                      • +
                      • ✅ Support all product types (consumable, non-consumable, auto-renewable subscription, non-renewing subscription)
                      • +
                      • ✅ Status of purchases available when offline
                      • +
                      • ✅ Server integration with a Webhook
                      • +
                      +

                      Getting Started

                      + +

                      If you haven’t already, I highly recommend your read the Overview and Preparing section of Apple’s In-App Purchase official documentation.

                      +

                      Installation

                      + +
                        +
                      • Select your project in Xcode
                      • +
                      • Go to the section Swift Package
                      • +
                      • Click on (+) Add Package Dependency
                      • +
                      • Copy the Git URL: https://github.com/iridescent-dev/iap-swift-lib.git
                      • +
                      • Click on Next > Next
                      • +
                      • Make sure your project is selected in Add to target
                      • +
                      • Click on Finish
                      • +
                      + +

                      Note: You have to import InAppPurchaseLib wherever you use the library.

                      +

                      Micro Example

                      +
                      /** AppDelegate.swift */
                      +import UIKit
                      +import InAppPurchaseLib
                      +
                      +@UIApplicationMain
                      +class AppDelegate: UIResponder, UIApplicationDelegate {
                      +  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
                      +    // Initialize the library
                      +    InAppPurchase.initialize(
                      +      iapProducts: [
                      +        IAPProduct(productIdentifier: "my_product", productType: .nonConsumable)
                      +      ],
                      +      validatorUrlString: "https://validator.fovea.cc/v1/validate?appName=demo&apiKey=12345678"
                      +    )
                      +    return true
                      +  }
                      +
                      +  func applicationWillTerminate(_ application: UIApplication) {
                      +    // clean
                      +    InAppPurchase.stop()
                      +  }
                      +}
                      +
                      +
                      /** ViewController.swift */
                      +import UIKit
                      +import StoreKit
                      +import InAppPurchaseLib
                      +
                      +class ViewController: UIViewController {
                      +  private var loaderView = LoaderView()
                      +  @IBOutlet weak var statusLabel: UILabel!
                      +  @IBOutlet weak var productTitleLabel: UILabel!
                      +  @IBOutlet weak var productDescriptionLabel: UILabel!
                      +  @IBOutlet weak var purchaseButton: UIButton!
                      +  @IBOutlet weak var restorePurchasesButton: UIButton!
                      +
                      +  override func viewDidLoad() {
                      +    super.viewDidLoad()
                      +    // Add action for purchases and restore butons.
                      +    purchaseButton.addTarget(self, action: #selector(self.purchase), for: .touchUpInside)
                      +    restorePurchasesButton.addTarget(self, action: #selector(self.restorePurchases), for: .touchUpInside)
                      +  }
                      +
                      +  override func viewWillAppear(_ animated: Bool) {
                      +    self.refreshView()
                      +    InAppPurchase.refresh(callback: { _ in
                      +      self.refreshView()
                      +    })
                      +  }
                      +
                      +  func refreshView() {
                      +    guard let product: SKProduct = InAppPurchase.getProductBy(identifier: "my_product") else {
                      +      self.productTitleLabel.text = "Product unavailable"
                      +      return
                      +    }
                      +    // Display product information.
                      +    productTitleLabel.text = product.localizedTitle
                      +    productDescriptionLabel.text = product.localizedDescription
                      +    purchaseButton.setTitle(product.localizedPrice, for: .normal)
                      +
                      +    // Disable the button if the product has already been purchased.
                      +    if InAppPurchase.hasActivePurchase(for: "my_product") {
                      +      statusLabel.text = "OWNED"
                      +      purchaseButton.isPointerInteractionEnabled = false
                      +    }
                      +  }
                      +
                      +  @IBAction func purchase(_ sender: Any) {
                      +    self.loaderView.show()
                      +    InAppPurchase.purchase(
                      +      productIdentifier: "my_product",
                      +      callback: { result in
                      +        self.loaderView.hide()
                      +      })
                      +  }
                      +
                      +  @IBAction func restorePurchases(_ sender: Any) {
                      +    self.loaderView.show()
                      +    InAppPurchase.restorePurchases(callback: { result in
                      +      self.loaderView.hide()
                      +    })
                      +  }
                      +}
                      +
                      +

                      Documentation

                      + + + +

                      See also:

                      + + +

                      Xcode Demo Project

                      + +

                      Do not hesitate to check the demo project available on here: iap-swift-lib-demo.

                      +

                      Coding

                      + +

                      Generate the documentation, using Jazzy by running the following command:

                      +
                      jazzy
                      +
                      +

                      Troubleshooting

                      + +

                      Common issues are covered here: https://github.com/iridescent-dev/iap-swift-lib/wiki/Troubleshooting

                      +

                      License

                      + +

                      InAppPurchaseLib is open-sourced library licensed under the MIT License. See LICENSE for details.

                      + +
                      +
                      + + +
                      +
                      + + + + diff --git a/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/initialization.html b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/initialization.html new file mode 100644 index 0000000..a87c6e0 --- /dev/null +++ b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/initialization.html @@ -0,0 +1,227 @@ + + + + Initialization Reference + + + + + + + + + + + + + + + +
                      +

                      + + InAppPurchaseLib documentation + + (77% documented) +

                      + +

                      +

                      + +
                      +

                      + +

                      + + + View on GitHub + +

                      + +
                      + + + +
                      + +
                      + +
                      +
                      + +

                      Initialization

                      + +

                      Before everything else the library must be initialized. This has to happen as soon as possible. A good way is to call the InAppPurchase.initialize() method when the application did finish launching. In the background, this will load your products and refresh the status of purchases and subscriptions.

                      + +

                      InAppPurchase.initialize() requires the following arguments:

                      + +
                        +
                      • iapProducts - An array of IAPProduct
                      • +
                      • validatorUrlString - The validator url retrieved from Fovea
                      • +
                      + +

                      Each IAPProduct contains the following fields:

                      + +
                        +
                      • productIdentifier - The product unique identifier
                      • +
                      • productType - The IAPProductType (consumable, nonConsumable, nonRenewingSubscription or autoRenewableSubscription)
                      • +
                      + +

                      Example:

                      + +

                      A good place is generally in your application delegate’s didFinishLaunchingWithOptions function, like below:

                      +
                      import InAppPurchaseLib
                      +
                      +class AppDelegate: UIResponder, UIApplicationDelegate, IAPPurchaseDelegate {
                      +  ...
                      +  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
                      +    InAppPurchase.initialize(
                      +      iapProducts: [
                      +        IAPProduct(productIdentifier: "monthly_plan", productType: .autoRenewableSubscription),
                      +        IAPProduct(productIdentifier: "yearly_plan",  productType: .autoRenewableSubscription),
                      +        IAPProduct(productIdentifier: "disable_ads",  productType: .nonConsumable)
                      +      ],
                      +      validatorUrlString: "https://validator.fovea.cc/v1/validate?appName=demo&apiKey=12345678")
                      +  }
                      +
                      +  func productPurchased(productIdentifier: String) {
                      +    // ... process purchase (we'll see that later)
                      +  }
                      +}
                      +
                      + +

                      You should also call the stop method when the application will terminate, for proper cleanup.

                      +
                        func applicationWillTerminate(_ application: UIApplication) {
                      +    InAppPurchase.stop()
                      +  }
                      +
                      + +

                      For more advanced use cases, in particular when you have implemented user login, you’ll have to make some adjustments. We’ll learn more about this in the Server integration section.

                      + +

                      Tip: If initialization was successful, you should see a new receipt validation event in Fovea’s Dashboard.

                      + +
                      +
                      + + +
                      +
                      + + + + diff --git a/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/installation.html b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/installation.html new file mode 100644 index 0000000..637dbb0 --- /dev/null +++ b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/installation.html @@ -0,0 +1,194 @@ + + + + Installation Reference + + + + + + + + + + + + + + + +
                      +

                      + + InAppPurchaseLib documentation + + (77% documented) +

                      + +

                      +

                      + +
                      +

                      + +

                      + + + View on GitHub + +

                      + +
                      + + + +
                      + +
                      + +
                      +
                      + +

                      Installation

                      + +

                      + +

                      + +
                        +
                      • Select your project in Xcode
                      • +
                      • Go to the section Swift Package
                      • +
                      • Click on (+) Add Package Dependency
                      • +
                      • Copy the Git URL: https://github.com/iridescent-dev/iap-swift-lib.git
                      • +
                      • Click on Next > Next
                      • +
                      • Make sure your project is selected in Add to target
                      • +
                      • Click on Finish
                      • +
                      + +

                      Note: You have to import InAppPurchaseLib wherever you use the library.

                      + +
                      +
                      + + +
                      +
                      + + + + diff --git a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/js/jazzy.js b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/js/jazzy.js similarity index 100% rename from docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/js/jazzy.js rename to docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/js/jazzy.js diff --git a/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/js/jazzy.search.js b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/js/jazzy.search.js new file mode 100644 index 0000000..e3d1ab9 --- /dev/null +++ b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/js/jazzy.search.js @@ -0,0 +1,70 @@ +$(function(){ + var $typeahead = $('[data-typeahead]'); + var $form = $typeahead.parents('form'); + var searchURL = $form.attr('action'); + + function displayTemplate(result) { + return result.name; + } + + function suggestionTemplate(result) { + var t = '
                      '; + t += '' + result.name + ''; + if (result.parent_name) { + t += '' + result.parent_name + ''; + } + t += '
                      '; + return t; + } + + $typeahead.one('focus', function() { + $form.addClass('loading'); + + $.getJSON(searchURL).then(function(searchData) { + const searchIndex = lunr(function() { + this.ref('url'); + this.field('name'); + this.field('abstract'); + for (const [url, doc] of Object.entries(searchData)) { + this.add({url: url, name: doc.name, abstract: doc.abstract}); + } + }); + + $typeahead.typeahead( + { + highlight: true, + minLength: 3, + autoselect: true + }, + { + limit: 10, + display: displayTemplate, + templates: { suggestion: suggestionTemplate }, + source: function(query, sync) { + const lcSearch = query.toLowerCase(); + const results = searchIndex.query(function(q) { + q.term(lcSearch, { boost: 100 }); + q.term(lcSearch, { + boost: 10, + wildcard: lunr.Query.wildcard.TRAILING + }); + }).map(function(result) { + var doc = searchData[result.ref]; + doc.url = result.ref; + return doc; + }); + sync(results); + } + } + ); + $form.removeClass('loading'); + $typeahead.trigger('focus'); + }); + }); + + var baseURL = searchURL.slice(0, -"search.json".length); + + $typeahead.on('typeahead:select', function(e, result) { + window.location = baseURL + result.url; + }); +}); diff --git a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/js/jquery.min.js b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/js/jquery.min.js similarity index 100% rename from docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/js/jquery.min.js rename to docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/js/jquery.min.js diff --git a/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/js/lunr.min.js b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/js/lunr.min.js new file mode 100644 index 0000000..f45a81e --- /dev/null +++ b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/js/lunr.min.js @@ -0,0 +1 @@ +!function(){var t,l,c,e,r,h,d,f,p,y,m,g,x,v,w,Q,k,S,E,L,b,P,T,O,I,i,n,s,z=function(e){var t=new z.Builder;return t.pipeline.add(z.trimmer,z.stopWordFilter,z.stemmer),t.searchPipeline.add(z.stemmer),e.call(t,t),t.build()};z.version="2.3.5",z.utils={},z.utils.warn=(t=this,function(e){t.console&&console.warn&&console.warn(e)}),z.utils.asString=function(e){return null==e?"":e.toString()},z.utils.clone=function(e){if(null==e)return e;for(var t=Object.create(null),r=Object.keys(e),i=0;i=this.length)return z.QueryLexer.EOS;var e=this.str.charAt(this.pos);return this.pos+=1,e},z.QueryLexer.prototype.width=function(){return this.pos-this.start},z.QueryLexer.prototype.ignore=function(){this.start==this.pos&&(this.pos+=1),this.start=this.pos},z.QueryLexer.prototype.backup=function(){this.pos-=1},z.QueryLexer.prototype.acceptDigitRun=function(){for(var e,t;47<(t=(e=this.next()).charCodeAt(0))&&t<58;);e!=z.QueryLexer.EOS&&this.backup()},z.QueryLexer.prototype.more=function(){return this.pos', + menu: '
                      ' + }; + } + function buildSelectors(classes) { + var selectors = {}; + _.each(classes, function(v, k) { + selectors[k] = "." + v; + }); + return selectors; + } + function buildCss() { + var css = { + wrapper: { + position: "relative", + display: "inline-block" + }, + hint: { + position: "absolute", + top: "0", + left: "0", + borderColor: "transparent", + boxShadow: "none", + opacity: "1" + }, + input: { + position: "relative", + verticalAlign: "top", + backgroundColor: "transparent" + }, + inputWithNoHint: { + position: "relative", + verticalAlign: "top" + }, + menu: { + position: "absolute", + top: "100%", + left: "0", + zIndex: "100", + display: "none" + }, + ltr: { + left: "0", + right: "auto" + }, + rtl: { + left: "auto", + right: " 0" + } + }; + if (_.isMsie()) { + _.mixin(css.input, { + backgroundImage: "url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)" + }); + } + return css; + } + }(); + var EventBus = function() { + "use strict"; + var namespace, deprecationMap; + namespace = "typeahead:"; + deprecationMap = { + render: "rendered", + cursorchange: "cursorchanged", + select: "selected", + autocomplete: "autocompleted" + }; + function EventBus(o) { + if (!o || !o.el) { + $.error("EventBus initialized without el"); + } + this.$el = $(o.el); + } + _.mixin(EventBus.prototype, { + _trigger: function(type, args) { + var $e = $.Event(namespace + type); + this.$el.trigger.call(this.$el, $e, args || []); + return $e; + }, + before: function(type) { + var args, $e; + args = [].slice.call(arguments, 1); + $e = this._trigger("before" + type, args); + return $e.isDefaultPrevented(); + }, + trigger: function(type) { + var deprecatedType; + this._trigger(type, [].slice.call(arguments, 1)); + if (deprecatedType = deprecationMap[type]) { + this._trigger(deprecatedType, [].slice.call(arguments, 1)); + } + } + }); + return EventBus; + }(); + var EventEmitter = function() { + "use strict"; + var splitter = /\s+/, nextTick = getNextTick(); + return { + onSync: onSync, + onAsync: onAsync, + off: off, + trigger: trigger + }; + function on(method, types, cb, context) { + var type; + if (!cb) { + return this; + } + types = types.split(splitter); + cb = context ? bindContext(cb, context) : cb; + this._callbacks = this._callbacks || {}; + while (type = types.shift()) { + this._callbacks[type] = this._callbacks[type] || { + sync: [], + async: [] + }; + this._callbacks[type][method].push(cb); + } + return this; + } + function onAsync(types, cb, context) { + return on.call(this, "async", types, cb, context); + } + function onSync(types, cb, context) { + return on.call(this, "sync", types, cb, context); + } + function off(types) { + var type; + if (!this._callbacks) { + return this; + } + types = types.split(splitter); + while (type = types.shift()) { + delete this._callbacks[type]; + } + return this; + } + function trigger(types) { + var type, callbacks, args, syncFlush, asyncFlush; + if (!this._callbacks) { + return this; + } + types = types.split(splitter); + args = [].slice.call(arguments, 1); + while ((type = types.shift()) && (callbacks = this._callbacks[type])) { + syncFlush = getFlush(callbacks.sync, this, [ type ].concat(args)); + asyncFlush = getFlush(callbacks.async, this, [ type ].concat(args)); + syncFlush() && nextTick(asyncFlush); + } + return this; + } + function getFlush(callbacks, context, args) { + return flush; + function flush() { + var cancelled; + for (var i = 0, len = callbacks.length; !cancelled && i < len; i += 1) { + cancelled = callbacks[i].apply(context, args) === false; + } + return !cancelled; + } + } + function getNextTick() { + var nextTickFn; + if (window.setImmediate) { + nextTickFn = function nextTickSetImmediate(fn) { + setImmediate(function() { + fn(); + }); + }; + } else { + nextTickFn = function nextTickSetTimeout(fn) { + setTimeout(function() { + fn(); + }, 0); + }; + } + return nextTickFn; + } + function bindContext(fn, context) { + return fn.bind ? fn.bind(context) : function() { + fn.apply(context, [].slice.call(arguments, 0)); + }; + } + }(); + var highlight = function(doc) { + "use strict"; + var defaults = { + node: null, + pattern: null, + tagName: "strong", + className: null, + wordsOnly: false, + caseSensitive: false, + diacriticInsensitive: false + }; + var accented = { + A: "[AaªÀ-Åà-åĀ-ąǍǎȀ-ȃȦȧᴬᵃḀḁẚẠ-ảₐ℀℁℻⒜Ⓐⓐ㍱-㍴㎀-㎄㎈㎉㎩-㎯㏂㏊㏟㏿Aa]", + B: "[BbᴮᵇḂ-ḇℬ⒝Ⓑⓑ㍴㎅-㎇㏃㏈㏔㏝Bb]", + C: "[CcÇçĆ-čᶜ℀ℂ℃℅℆ℭⅭⅽ⒞Ⓒⓒ㍶㎈㎉㎝㎠㎤㏄-㏇Cc]", + D: "[DdĎďDŽ-džDZ-dzᴰᵈḊ-ḓⅅⅆⅮⅾ⒟Ⓓⓓ㋏㍲㍷-㍹㎗㎭-㎯㏅㏈Dd]", + E: "[EeÈ-Ëè-ëĒ-ěȄ-ȇȨȩᴱᵉḘ-ḛẸ-ẽₑ℡ℯℰⅇ⒠Ⓔⓔ㉐㋍㋎Ee]", + F: "[FfᶠḞḟ℉ℱ℻⒡Ⓕⓕ㎊-㎌㎙ff-fflFf]", + G: "[GgĜ-ģǦǧǴǵᴳᵍḠḡℊ⒢Ⓖⓖ㋌㋍㎇㎍-㎏㎓㎬㏆㏉㏒㏿Gg]", + H: "[HhĤĥȞȟʰᴴḢ-ḫẖℋ-ℎ⒣Ⓗⓗ㋌㍱㎐-㎔㏊㏋㏗Hh]", + I: "[IiÌ-Ïì-ïĨ-İIJijǏǐȈ-ȋᴵᵢḬḭỈ-ịⁱℐℑℹⅈⅠ-ⅣⅥ-ⅨⅪⅫⅰ-ⅳⅵ-ⅸⅺⅻ⒤Ⓘⓘ㍺㏌㏕fiffiIi]", + J: "[JjIJ-ĵLJ-njǰʲᴶⅉ⒥ⒿⓙⱼJj]", + K: "[KkĶķǨǩᴷᵏḰ-ḵK⒦Ⓚⓚ㎄㎅㎉㎏㎑㎘㎞㎢㎦㎪㎸㎾㏀㏆㏍-㏏Kk]", + L: "[LlĹ-ŀLJ-ljˡᴸḶḷḺ-ḽℒℓ℡Ⅼⅼ⒧Ⓛⓛ㋏㎈㎉㏐-㏓㏕㏖㏿flfflLl]", + M: "[MmᴹᵐḾ-ṃ℠™ℳⅯⅿ⒨Ⓜⓜ㍷-㍹㎃㎆㎎㎒㎖㎙-㎨㎫㎳㎷㎹㎽㎿㏁㏂㏎㏐㏔-㏖㏘㏙㏞㏟Mm]", + N: "[NnÑñŃ-ʼnNJ-njǸǹᴺṄ-ṋⁿℕ№⒩Ⓝⓝ㎁㎋㎚㎱㎵㎻㏌㏑Nn]", + O: "[OoºÒ-Öò-öŌ-őƠơǑǒǪǫȌ-ȏȮȯᴼᵒỌ-ỏₒ℅№ℴ⒪Ⓞⓞ㍵㏇㏒㏖Oo]", + P: "[PpᴾᵖṔ-ṗℙ⒫Ⓟⓟ㉐㍱㍶㎀㎊㎩-㎬㎰㎴㎺㏋㏗-㏚Pp]", + Q: "[Qqℚ⒬Ⓠⓠ㏃Qq]", + R: "[RrŔ-řȐ-ȓʳᴿᵣṘ-ṛṞṟ₨ℛ-ℝ⒭Ⓡⓡ㋍㍴㎭-㎯㏚㏛Rr]", + S: "[SsŚ-šſȘșˢṠ-ṣ₨℁℠⒮Ⓢⓢ㎧㎨㎮-㎳㏛㏜stSs]", + T: "[TtŢ-ťȚțᵀᵗṪ-ṱẗ℡™⒯Ⓣⓣ㉐㋏㎔㏏ſtstTt]", + U: "[UuÙ-Üù-üŨ-ųƯưǓǔȔ-ȗᵁᵘᵤṲ-ṷỤ-ủ℆⒰Ⓤⓤ㍳㍺Uu]", + V: "[VvᵛᵥṼ-ṿⅣ-Ⅷⅳ-ⅷ⒱Ⓥⓥⱽ㋎㍵㎴-㎹㏜㏞Vv]", + W: "[WwŴŵʷᵂẀ-ẉẘ⒲Ⓦⓦ㎺-㎿㏝Ww]", + X: "[XxˣẊ-ẍₓ℻Ⅸ-Ⅻⅸ-ⅻ⒳Ⓧⓧ㏓Xx]", + Y: "[YyÝýÿŶ-ŸȲȳʸẎẏẙỲ-ỹ⒴Ⓨⓨ㏉Yy]", + Z: "[ZzŹ-žDZ-dzᶻẐ-ẕℤℨ⒵Ⓩⓩ㎐-㎔Zz]" + }; + return function hightlight(o) { + var regex; + o = _.mixin({}, defaults, o); + if (!o.node || !o.pattern) { + return; + } + o.pattern = _.isArray(o.pattern) ? o.pattern : [ o.pattern ]; + regex = getRegex(o.pattern, o.caseSensitive, o.wordsOnly, o.diacriticInsensitive); + traverse(o.node, hightlightTextNode); + function hightlightTextNode(textNode) { + var match, patternNode, wrapperNode; + if (match = regex.exec(textNode.data)) { + wrapperNode = doc.createElement(o.tagName); + o.className && (wrapperNode.className = o.className); + patternNode = textNode.splitText(match.index); + patternNode.splitText(match[0].length); + wrapperNode.appendChild(patternNode.cloneNode(true)); + textNode.parentNode.replaceChild(wrapperNode, patternNode); + } + return !!match; + } + function traverse(el, hightlightTextNode) { + var childNode, TEXT_NODE_TYPE = 3; + for (var i = 0; i < el.childNodes.length; i++) { + childNode = el.childNodes[i]; + if (childNode.nodeType === TEXT_NODE_TYPE) { + i += hightlightTextNode(childNode) ? 1 : 0; + } else { + traverse(childNode, hightlightTextNode); + } + } + } + }; + function accent_replacer(chr) { + return accented[chr.toUpperCase()] || chr; + } + function getRegex(patterns, caseSensitive, wordsOnly, diacriticInsensitive) { + var escapedPatterns = [], regexStr; + for (var i = 0, len = patterns.length; i < len; i++) { + var escapedWord = _.escapeRegExChars(patterns[i]); + if (diacriticInsensitive) { + escapedWord = escapedWord.replace(/\S/g, accent_replacer); + } + escapedPatterns.push(escapedWord); + } + regexStr = wordsOnly ? "\\b(" + escapedPatterns.join("|") + ")\\b" : "(" + escapedPatterns.join("|") + ")"; + return caseSensitive ? new RegExp(regexStr) : new RegExp(regexStr, "i"); + } + }(window.document); + var Input = function() { + "use strict"; + var specialKeyCodeMap; + specialKeyCodeMap = { + 9: "tab", + 27: "esc", + 37: "left", + 39: "right", + 13: "enter", + 38: "up", + 40: "down" + }; + function Input(o, www) { + o = o || {}; + if (!o.input) { + $.error("input is missing"); + } + www.mixin(this); + this.$hint = $(o.hint); + this.$input = $(o.input); + this.$input.attr({ + "aria-activedescendant": "", + "aria-owns": this.$input.attr("id") + "_listbox", + role: "combobox", + "aria-readonly": "true", + "aria-autocomplete": "list" + }); + $(www.menu).attr("id", this.$input.attr("id") + "_listbox"); + this.query = this.$input.val(); + this.queryWhenFocused = this.hasFocus() ? this.query : null; + this.$overflowHelper = buildOverflowHelper(this.$input); + this._checkLanguageDirection(); + if (this.$hint.length === 0) { + this.setHint = this.getHint = this.clearHint = this.clearHintIfInvalid = _.noop; + } + this.onSync("cursorchange", this._updateDescendent); + } + Input.normalizeQuery = function(str) { + return _.toStr(str).replace(/^\s*/g, "").replace(/\s{2,}/g, " "); + }; + _.mixin(Input.prototype, EventEmitter, { + _onBlur: function onBlur() { + this.resetInputValue(); + this.trigger("blurred"); + }, + _onFocus: function onFocus() { + this.queryWhenFocused = this.query; + this.trigger("focused"); + }, + _onKeydown: function onKeydown($e) { + var keyName = specialKeyCodeMap[$e.which || $e.keyCode]; + this._managePreventDefault(keyName, $e); + if (keyName && this._shouldTrigger(keyName, $e)) { + this.trigger(keyName + "Keyed", $e); + } + }, + _onInput: function onInput() { + this._setQuery(this.getInputValue()); + this.clearHintIfInvalid(); + this._checkLanguageDirection(); + }, + _managePreventDefault: function managePreventDefault(keyName, $e) { + var preventDefault; + switch (keyName) { + case "up": + case "down": + preventDefault = !withModifier($e); + break; + + default: + preventDefault = false; + } + preventDefault && $e.preventDefault(); + }, + _shouldTrigger: function shouldTrigger(keyName, $e) { + var trigger; + switch (keyName) { + case "tab": + trigger = !withModifier($e); + break; + + default: + trigger = true; + } + return trigger; + }, + _checkLanguageDirection: function checkLanguageDirection() { + var dir = (this.$input.css("direction") || "ltr").toLowerCase(); + if (this.dir !== dir) { + this.dir = dir; + this.$hint.attr("dir", dir); + this.trigger("langDirChanged", dir); + } + }, + _setQuery: function setQuery(val, silent) { + var areEquivalent, hasDifferentWhitespace; + areEquivalent = areQueriesEquivalent(val, this.query); + hasDifferentWhitespace = areEquivalent ? this.query.length !== val.length : false; + this.query = val; + if (!silent && !areEquivalent) { + this.trigger("queryChanged", this.query); + } else if (!silent && hasDifferentWhitespace) { + this.trigger("whitespaceChanged", this.query); + } + }, + _updateDescendent: function updateDescendent(event, id) { + this.$input.attr("aria-activedescendant", id); + }, + bind: function() { + var that = this, onBlur, onFocus, onKeydown, onInput; + onBlur = _.bind(this._onBlur, this); + onFocus = _.bind(this._onFocus, this); + onKeydown = _.bind(this._onKeydown, this); + onInput = _.bind(this._onInput, this); + this.$input.on("blur.tt", onBlur).on("focus.tt", onFocus).on("keydown.tt", onKeydown); + if (!_.isMsie() || _.isMsie() > 9) { + this.$input.on("input.tt", onInput); + } else { + this.$input.on("keydown.tt keypress.tt cut.tt paste.tt", function($e) { + if (specialKeyCodeMap[$e.which || $e.keyCode]) { + return; + } + _.defer(_.bind(that._onInput, that, $e)); + }); + } + return this; + }, + focus: function focus() { + this.$input.focus(); + }, + blur: function blur() { + this.$input.blur(); + }, + getLangDir: function getLangDir() { + return this.dir; + }, + getQuery: function getQuery() { + return this.query || ""; + }, + setQuery: function setQuery(val, silent) { + this.setInputValue(val); + this._setQuery(val, silent); + }, + hasQueryChangedSinceLastFocus: function hasQueryChangedSinceLastFocus() { + return this.query !== this.queryWhenFocused; + }, + getInputValue: function getInputValue() { + return this.$input.val(); + }, + setInputValue: function setInputValue(value) { + this.$input.val(value); + this.clearHintIfInvalid(); + this._checkLanguageDirection(); + }, + resetInputValue: function resetInputValue() { + this.setInputValue(this.query); + }, + getHint: function getHint() { + return this.$hint.val(); + }, + setHint: function setHint(value) { + this.$hint.val(value); + }, + clearHint: function clearHint() { + this.setHint(""); + }, + clearHintIfInvalid: function clearHintIfInvalid() { + var val, hint, valIsPrefixOfHint, isValid; + val = this.getInputValue(); + hint = this.getHint(); + valIsPrefixOfHint = val !== hint && hint.indexOf(val) === 0; + isValid = val !== "" && valIsPrefixOfHint && !this.hasOverflow(); + !isValid && this.clearHint(); + }, + hasFocus: function hasFocus() { + return this.$input.is(":focus"); + }, + hasOverflow: function hasOverflow() { + var constraint = this.$input.width() - 2; + this.$overflowHelper.text(this.getInputValue()); + return this.$overflowHelper.width() >= constraint; + }, + isCursorAtEnd: function() { + var valueLength, selectionStart, range; + valueLength = this.$input.val().length; + selectionStart = this.$input[0].selectionStart; + if (_.isNumber(selectionStart)) { + return selectionStart === valueLength; + } else if (document.selection) { + range = document.selection.createRange(); + range.moveStart("character", -valueLength); + return valueLength === range.text.length; + } + return true; + }, + destroy: function destroy() { + this.$hint.off(".tt"); + this.$input.off(".tt"); + this.$overflowHelper.remove(); + this.$hint = this.$input = this.$overflowHelper = $("
                      "); + } + }); + return Input; + function buildOverflowHelper($input) { + return $('').css({ + position: "absolute", + visibility: "hidden", + whiteSpace: "pre", + fontFamily: $input.css("font-family"), + fontSize: $input.css("font-size"), + fontStyle: $input.css("font-style"), + fontVariant: $input.css("font-variant"), + fontWeight: $input.css("font-weight"), + wordSpacing: $input.css("word-spacing"), + letterSpacing: $input.css("letter-spacing"), + textIndent: $input.css("text-indent"), + textRendering: $input.css("text-rendering"), + textTransform: $input.css("text-transform") + }).insertAfter($input); + } + function areQueriesEquivalent(a, b) { + return Input.normalizeQuery(a) === Input.normalizeQuery(b); + } + function withModifier($e) { + return $e.altKey || $e.ctrlKey || $e.metaKey || $e.shiftKey; + } + }(); + var Dataset = function() { + "use strict"; + var keys, nameGenerator; + keys = { + dataset: "tt-selectable-dataset", + val: "tt-selectable-display", + obj: "tt-selectable-object" + }; + nameGenerator = _.getIdGenerator(); + function Dataset(o, www) { + o = o || {}; + o.templates = o.templates || {}; + o.templates.notFound = o.templates.notFound || o.templates.empty; + if (!o.source) { + $.error("missing source"); + } + if (!o.node) { + $.error("missing node"); + } + if (o.name && !isValidName(o.name)) { + $.error("invalid dataset name: " + o.name); + } + www.mixin(this); + this.highlight = !!o.highlight; + this.name = _.toStr(o.name || nameGenerator()); + this.limit = o.limit || 5; + this.displayFn = getDisplayFn(o.display || o.displayKey); + this.templates = getTemplates(o.templates, this.displayFn); + this.source = o.source.__ttAdapter ? o.source.__ttAdapter() : o.source; + this.async = _.isUndefined(o.async) ? this.source.length > 2 : !!o.async; + this._resetLastSuggestion(); + this.$el = $(o.node).attr("role", "presentation").addClass(this.classes.dataset).addClass(this.classes.dataset + "-" + this.name); + } + Dataset.extractData = function extractData(el) { + var $el = $(el); + if ($el.data(keys.obj)) { + return { + dataset: $el.data(keys.dataset) || "", + val: $el.data(keys.val) || "", + obj: $el.data(keys.obj) || null + }; + } + return null; + }; + _.mixin(Dataset.prototype, EventEmitter, { + _overwrite: function overwrite(query, suggestions) { + suggestions = suggestions || []; + if (suggestions.length) { + this._renderSuggestions(query, suggestions); + } else if (this.async && this.templates.pending) { + this._renderPending(query); + } else if (!this.async && this.templates.notFound) { + this._renderNotFound(query); + } else { + this._empty(); + } + this.trigger("rendered", suggestions, false, this.name); + }, + _append: function append(query, suggestions) { + suggestions = suggestions || []; + if (suggestions.length && this.$lastSuggestion.length) { + this._appendSuggestions(query, suggestions); + } else if (suggestions.length) { + this._renderSuggestions(query, suggestions); + } else if (!this.$lastSuggestion.length && this.templates.notFound) { + this._renderNotFound(query); + } + this.trigger("rendered", suggestions, true, this.name); + }, + _renderSuggestions: function renderSuggestions(query, suggestions) { + var $fragment; + $fragment = this._getSuggestionsFragment(query, suggestions); + this.$lastSuggestion = $fragment.children().last(); + this.$el.html($fragment).prepend(this._getHeader(query, suggestions)).append(this._getFooter(query, suggestions)); + }, + _appendSuggestions: function appendSuggestions(query, suggestions) { + var $fragment, $lastSuggestion; + $fragment = this._getSuggestionsFragment(query, suggestions); + $lastSuggestion = $fragment.children().last(); + this.$lastSuggestion.after($fragment); + this.$lastSuggestion = $lastSuggestion; + }, + _renderPending: function renderPending(query) { + var template = this.templates.pending; + this._resetLastSuggestion(); + template && this.$el.html(template({ + query: query, + dataset: this.name + })); + }, + _renderNotFound: function renderNotFound(query) { + var template = this.templates.notFound; + this._resetLastSuggestion(); + template && this.$el.html(template({ + query: query, + dataset: this.name + })); + }, + _empty: function empty() { + this.$el.empty(); + this._resetLastSuggestion(); + }, + _getSuggestionsFragment: function getSuggestionsFragment(query, suggestions) { + var that = this, fragment; + fragment = document.createDocumentFragment(); + _.each(suggestions, function getSuggestionNode(suggestion) { + var $el, context; + context = that._injectQuery(query, suggestion); + $el = $(that.templates.suggestion(context)).data(keys.dataset, that.name).data(keys.obj, suggestion).data(keys.val, that.displayFn(suggestion)).addClass(that.classes.suggestion + " " + that.classes.selectable); + fragment.appendChild($el[0]); + }); + this.highlight && highlight({ + className: this.classes.highlight, + node: fragment, + pattern: query + }); + return $(fragment); + }, + _getFooter: function getFooter(query, suggestions) { + return this.templates.footer ? this.templates.footer({ + query: query, + suggestions: suggestions, + dataset: this.name + }) : null; + }, + _getHeader: function getHeader(query, suggestions) { + return this.templates.header ? this.templates.header({ + query: query, + suggestions: suggestions, + dataset: this.name + }) : null; + }, + _resetLastSuggestion: function resetLastSuggestion() { + this.$lastSuggestion = $(); + }, + _injectQuery: function injectQuery(query, obj) { + return _.isObject(obj) ? _.mixin({ + _query: query + }, obj) : obj; + }, + update: function update(query) { + var that = this, canceled = false, syncCalled = false, rendered = 0; + this.cancel(); + this.cancel = function cancel() { + canceled = true; + that.cancel = $.noop; + that.async && that.trigger("asyncCanceled", query, that.name); + }; + this.source(query, sync, async); + !syncCalled && sync([]); + function sync(suggestions) { + if (syncCalled) { + return; + } + syncCalled = true; + suggestions = (suggestions || []).slice(0, that.limit); + rendered = suggestions.length; + that._overwrite(query, suggestions); + if (rendered < that.limit && that.async) { + that.trigger("asyncRequested", query, that.name); + } + } + function async(suggestions) { + suggestions = suggestions || []; + if (!canceled && rendered < that.limit) { + that.cancel = $.noop; + var idx = Math.abs(rendered - that.limit); + rendered += idx; + that._append(query, suggestions.slice(0, idx)); + that.async && that.trigger("asyncReceived", query, that.name); + } + } + }, + cancel: $.noop, + clear: function clear() { + this._empty(); + this.cancel(); + this.trigger("cleared"); + }, + isEmpty: function isEmpty() { + return this.$el.is(":empty"); + }, + destroy: function destroy() { + this.$el = $("
                      "); + } + }); + return Dataset; + function getDisplayFn(display) { + display = display || _.stringify; + return _.isFunction(display) ? display : displayFn; + function displayFn(obj) { + return obj[display]; + } + } + function getTemplates(templates, displayFn) { + return { + notFound: templates.notFound && _.templatify(templates.notFound), + pending: templates.pending && _.templatify(templates.pending), + header: templates.header && _.templatify(templates.header), + footer: templates.footer && _.templatify(templates.footer), + suggestion: templates.suggestion || suggestionTemplate + }; + function suggestionTemplate(context) { + return $('
                      ').attr("id", _.guid()).text(displayFn(context)); + } + } + function isValidName(str) { + return /^[_a-zA-Z0-9-]+$/.test(str); + } + }(); + var Menu = function() { + "use strict"; + function Menu(o, www) { + var that = this; + o = o || {}; + if (!o.node) { + $.error("node is required"); + } + www.mixin(this); + this.$node = $(o.node); + this.query = null; + this.datasets = _.map(o.datasets, initializeDataset); + function initializeDataset(oDataset) { + var node = that.$node.find(oDataset.node).first(); + oDataset.node = node.length ? node : $("
                      ").appendTo(that.$node); + return new Dataset(oDataset, www); + } + } + _.mixin(Menu.prototype, EventEmitter, { + _onSelectableClick: function onSelectableClick($e) { + this.trigger("selectableClicked", $($e.currentTarget)); + }, + _onRendered: function onRendered(type, dataset, suggestions, async) { + this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty()); + this.trigger("datasetRendered", dataset, suggestions, async); + }, + _onCleared: function onCleared() { + this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty()); + this.trigger("datasetCleared"); + }, + _propagate: function propagate() { + this.trigger.apply(this, arguments); + }, + _allDatasetsEmpty: function allDatasetsEmpty() { + return _.every(this.datasets, _.bind(function isDatasetEmpty(dataset) { + var isEmpty = dataset.isEmpty(); + this.$node.attr("aria-expanded", !isEmpty); + return isEmpty; + }, this)); + }, + _getSelectables: function getSelectables() { + return this.$node.find(this.selectors.selectable); + }, + _removeCursor: function _removeCursor() { + var $selectable = this.getActiveSelectable(); + $selectable && $selectable.removeClass(this.classes.cursor); + }, + _ensureVisible: function ensureVisible($el) { + var elTop, elBottom, nodeScrollTop, nodeHeight; + elTop = $el.position().top; + elBottom = elTop + $el.outerHeight(true); + nodeScrollTop = this.$node.scrollTop(); + nodeHeight = this.$node.height() + parseInt(this.$node.css("paddingTop"), 10) + parseInt(this.$node.css("paddingBottom"), 10); + if (elTop < 0) { + this.$node.scrollTop(nodeScrollTop + elTop); + } else if (nodeHeight < elBottom) { + this.$node.scrollTop(nodeScrollTop + (elBottom - nodeHeight)); + } + }, + bind: function() { + var that = this, onSelectableClick; + onSelectableClick = _.bind(this._onSelectableClick, this); + this.$node.on("click.tt", this.selectors.selectable, onSelectableClick); + this.$node.on("mouseover", this.selectors.selectable, function() { + that.setCursor($(this)); + }); + this.$node.on("mouseleave", function() { + that._removeCursor(); + }); + _.each(this.datasets, function(dataset) { + dataset.onSync("asyncRequested", that._propagate, that).onSync("asyncCanceled", that._propagate, that).onSync("asyncReceived", that._propagate, that).onSync("rendered", that._onRendered, that).onSync("cleared", that._onCleared, that); + }); + return this; + }, + isOpen: function isOpen() { + return this.$node.hasClass(this.classes.open); + }, + open: function open() { + this.$node.scrollTop(0); + this.$node.addClass(this.classes.open); + }, + close: function close() { + this.$node.attr("aria-expanded", false); + this.$node.removeClass(this.classes.open); + this._removeCursor(); + }, + setLanguageDirection: function setLanguageDirection(dir) { + this.$node.attr("dir", dir); + }, + selectableRelativeToCursor: function selectableRelativeToCursor(delta) { + var $selectables, $oldCursor, oldIndex, newIndex; + $oldCursor = this.getActiveSelectable(); + $selectables = this._getSelectables(); + oldIndex = $oldCursor ? $selectables.index($oldCursor) : -1; + newIndex = oldIndex + delta; + newIndex = (newIndex + 1) % ($selectables.length + 1) - 1; + newIndex = newIndex < -1 ? $selectables.length - 1 : newIndex; + return newIndex === -1 ? null : $selectables.eq(newIndex); + }, + setCursor: function setCursor($selectable) { + this._removeCursor(); + if ($selectable = $selectable && $selectable.first()) { + $selectable.addClass(this.classes.cursor); + this._ensureVisible($selectable); + } + }, + getSelectableData: function getSelectableData($el) { + return $el && $el.length ? Dataset.extractData($el) : null; + }, + getActiveSelectable: function getActiveSelectable() { + var $selectable = this._getSelectables().filter(this.selectors.cursor).first(); + return $selectable.length ? $selectable : null; + }, + getTopSelectable: function getTopSelectable() { + var $selectable = this._getSelectables().first(); + return $selectable.length ? $selectable : null; + }, + update: function update(query) { + var isValidUpdate = query !== this.query; + if (isValidUpdate) { + this.query = query; + _.each(this.datasets, updateDataset); + } + return isValidUpdate; + function updateDataset(dataset) { + dataset.update(query); + } + }, + empty: function empty() { + _.each(this.datasets, clearDataset); + this.query = null; + this.$node.addClass(this.classes.empty); + function clearDataset(dataset) { + dataset.clear(); + } + }, + destroy: function destroy() { + this.$node.off(".tt"); + this.$node = $("
                      "); + _.each(this.datasets, destroyDataset); + function destroyDataset(dataset) { + dataset.destroy(); + } + } + }); + return Menu; + }(); + var Status = function() { + "use strict"; + function Status(options) { + this.$el = $("", { + role: "status", + "aria-live": "polite" + }).css({ + position: "absolute", + padding: "0", + border: "0", + height: "1px", + width: "1px", + "margin-bottom": "-1px", + "margin-right": "-1px", + overflow: "hidden", + clip: "rect(0 0 0 0)", + "white-space": "nowrap" + }); + options.$input.after(this.$el); + _.each(options.menu.datasets, _.bind(function(dataset) { + if (dataset.onSync) { + dataset.onSync("rendered", _.bind(this.update, this)); + dataset.onSync("cleared", _.bind(this.cleared, this)); + } + }, this)); + } + _.mixin(Status.prototype, { + update: function update(event, suggestions) { + var length = suggestions.length; + var words; + if (length === 1) { + words = { + result: "result", + is: "is" + }; + } else { + words = { + result: "results", + is: "are" + }; + } + this.$el.text(length + " " + words.result + " " + words.is + " available, use up and down arrow keys to navigate."); + }, + cleared: function() { + this.$el.text(""); + } + }); + return Status; + }(); + var DefaultMenu = function() { + "use strict"; + var s = Menu.prototype; + function DefaultMenu() { + Menu.apply(this, [].slice.call(arguments, 0)); + } + _.mixin(DefaultMenu.prototype, Menu.prototype, { + open: function open() { + !this._allDatasetsEmpty() && this._show(); + return s.open.apply(this, [].slice.call(arguments, 0)); + }, + close: function close() { + this._hide(); + return s.close.apply(this, [].slice.call(arguments, 0)); + }, + _onRendered: function onRendered() { + if (this._allDatasetsEmpty()) { + this._hide(); + } else { + this.isOpen() && this._show(); + } + return s._onRendered.apply(this, [].slice.call(arguments, 0)); + }, + _onCleared: function onCleared() { + if (this._allDatasetsEmpty()) { + this._hide(); + } else { + this.isOpen() && this._show(); + } + return s._onCleared.apply(this, [].slice.call(arguments, 0)); + }, + setLanguageDirection: function setLanguageDirection(dir) { + this.$node.css(dir === "ltr" ? this.css.ltr : this.css.rtl); + return s.setLanguageDirection.apply(this, [].slice.call(arguments, 0)); + }, + _hide: function hide() { + this.$node.hide(); + }, + _show: function show() { + this.$node.css("display", "block"); + } + }); + return DefaultMenu; + }(); + var Typeahead = function() { + "use strict"; + function Typeahead(o, www) { + var onFocused, onBlurred, onEnterKeyed, onTabKeyed, onEscKeyed, onUpKeyed, onDownKeyed, onLeftKeyed, onRightKeyed, onQueryChanged, onWhitespaceChanged; + o = o || {}; + if (!o.input) { + $.error("missing input"); + } + if (!o.menu) { + $.error("missing menu"); + } + if (!o.eventBus) { + $.error("missing event bus"); + } + www.mixin(this); + this.eventBus = o.eventBus; + this.minLength = _.isNumber(o.minLength) ? o.minLength : 1; + this.input = o.input; + this.menu = o.menu; + this.enabled = true; + this.autoselect = !!o.autoselect; + this.active = false; + this.input.hasFocus() && this.activate(); + this.dir = this.input.getLangDir(); + this._hacks(); + this.menu.bind().onSync("selectableClicked", this._onSelectableClicked, this).onSync("asyncRequested", this._onAsyncRequested, this).onSync("asyncCanceled", this._onAsyncCanceled, this).onSync("asyncReceived", this._onAsyncReceived, this).onSync("datasetRendered", this._onDatasetRendered, this).onSync("datasetCleared", this._onDatasetCleared, this); + onFocused = c(this, "activate", "open", "_onFocused"); + onBlurred = c(this, "deactivate", "_onBlurred"); + onEnterKeyed = c(this, "isActive", "isOpen", "_onEnterKeyed"); + onTabKeyed = c(this, "isActive", "isOpen", "_onTabKeyed"); + onEscKeyed = c(this, "isActive", "_onEscKeyed"); + onUpKeyed = c(this, "isActive", "open", "_onUpKeyed"); + onDownKeyed = c(this, "isActive", "open", "_onDownKeyed"); + onLeftKeyed = c(this, "isActive", "isOpen", "_onLeftKeyed"); + onRightKeyed = c(this, "isActive", "isOpen", "_onRightKeyed"); + onQueryChanged = c(this, "_openIfActive", "_onQueryChanged"); + onWhitespaceChanged = c(this, "_openIfActive", "_onWhitespaceChanged"); + this.input.bind().onSync("focused", onFocused, this).onSync("blurred", onBlurred, this).onSync("enterKeyed", onEnterKeyed, this).onSync("tabKeyed", onTabKeyed, this).onSync("escKeyed", onEscKeyed, this).onSync("upKeyed", onUpKeyed, this).onSync("downKeyed", onDownKeyed, this).onSync("leftKeyed", onLeftKeyed, this).onSync("rightKeyed", onRightKeyed, this).onSync("queryChanged", onQueryChanged, this).onSync("whitespaceChanged", onWhitespaceChanged, this).onSync("langDirChanged", this._onLangDirChanged, this); + } + _.mixin(Typeahead.prototype, { + _hacks: function hacks() { + var $input, $menu; + $input = this.input.$input || $("
                      "); + $menu = this.menu.$node || $("
                      "); + $input.on("blur.tt", function($e) { + var active, isActive, hasActive; + active = document.activeElement; + isActive = $menu.is(active); + hasActive = $menu.has(active).length > 0; + if (_.isMsie() && (isActive || hasActive)) { + $e.preventDefault(); + $e.stopImmediatePropagation(); + _.defer(function() { + $input.focus(); + }); + } + }); + $menu.on("mousedown.tt", function($e) { + $e.preventDefault(); + }); + }, + _onSelectableClicked: function onSelectableClicked(type, $el) { + this.select($el); + }, + _onDatasetCleared: function onDatasetCleared() { + this._updateHint(); + }, + _onDatasetRendered: function onDatasetRendered(type, suggestions, async, dataset) { + this._updateHint(); + if (this.autoselect) { + var cursorClass = this.selectors.cursor.substr(1); + this.menu.$node.find(this.selectors.suggestion).first().addClass(cursorClass); + } + this.eventBus.trigger("render", suggestions, async, dataset); + }, + _onAsyncRequested: function onAsyncRequested(type, dataset, query) { + this.eventBus.trigger("asyncrequest", query, dataset); + }, + _onAsyncCanceled: function onAsyncCanceled(type, dataset, query) { + this.eventBus.trigger("asynccancel", query, dataset); + }, + _onAsyncReceived: function onAsyncReceived(type, dataset, query) { + this.eventBus.trigger("asyncreceive", query, dataset); + }, + _onFocused: function onFocused() { + this._minLengthMet() && this.menu.update(this.input.getQuery()); + }, + _onBlurred: function onBlurred() { + if (this.input.hasQueryChangedSinceLastFocus()) { + this.eventBus.trigger("change", this.input.getQuery()); + } + }, + _onEnterKeyed: function onEnterKeyed(type, $e) { + var $selectable; + if ($selectable = this.menu.getActiveSelectable()) { + if (this.select($selectable)) { + $e.preventDefault(); + $e.stopPropagation(); + } + } else if (this.autoselect) { + if (this.select(this.menu.getTopSelectable())) { + $e.preventDefault(); + $e.stopPropagation(); + } + } + }, + _onTabKeyed: function onTabKeyed(type, $e) { + var $selectable; + if ($selectable = this.menu.getActiveSelectable()) { + this.select($selectable) && $e.preventDefault(); + } else if ($selectable = this.menu.getTopSelectable()) { + this.autocomplete($selectable) && $e.preventDefault(); + } + }, + _onEscKeyed: function onEscKeyed() { + this.close(); + }, + _onUpKeyed: function onUpKeyed() { + this.moveCursor(-1); + }, + _onDownKeyed: function onDownKeyed() { + this.moveCursor(+1); + }, + _onLeftKeyed: function onLeftKeyed() { + if (this.dir === "rtl" && this.input.isCursorAtEnd()) { + this.autocomplete(this.menu.getActiveSelectable() || this.menu.getTopSelectable()); + } + }, + _onRightKeyed: function onRightKeyed() { + if (this.dir === "ltr" && this.input.isCursorAtEnd()) { + this.autocomplete(this.menu.getActiveSelectable() || this.menu.getTopSelectable()); + } + }, + _onQueryChanged: function onQueryChanged(e, query) { + this._minLengthMet(query) ? this.menu.update(query) : this.menu.empty(); + }, + _onWhitespaceChanged: function onWhitespaceChanged() { + this._updateHint(); + }, + _onLangDirChanged: function onLangDirChanged(e, dir) { + if (this.dir !== dir) { + this.dir = dir; + this.menu.setLanguageDirection(dir); + } + }, + _openIfActive: function openIfActive() { + this.isActive() && this.open(); + }, + _minLengthMet: function minLengthMet(query) { + query = _.isString(query) ? query : this.input.getQuery() || ""; + return query.length >= this.minLength; + }, + _updateHint: function updateHint() { + var $selectable, data, val, query, escapedQuery, frontMatchRegEx, match; + $selectable = this.menu.getTopSelectable(); + data = this.menu.getSelectableData($selectable); + val = this.input.getInputValue(); + if (data && !_.isBlankString(val) && !this.input.hasOverflow()) { + query = Input.normalizeQuery(val); + escapedQuery = _.escapeRegExChars(query); + frontMatchRegEx = new RegExp("^(?:" + escapedQuery + ")(.+$)", "i"); + match = frontMatchRegEx.exec(data.val); + match && this.input.setHint(val + match[1]); + } else { + this.input.clearHint(); + } + }, + isEnabled: function isEnabled() { + return this.enabled; + }, + enable: function enable() { + this.enabled = true; + }, + disable: function disable() { + this.enabled = false; + }, + isActive: function isActive() { + return this.active; + }, + activate: function activate() { + if (this.isActive()) { + return true; + } else if (!this.isEnabled() || this.eventBus.before("active")) { + return false; + } else { + this.active = true; + this.eventBus.trigger("active"); + return true; + } + }, + deactivate: function deactivate() { + if (!this.isActive()) { + return true; + } else if (this.eventBus.before("idle")) { + return false; + } else { + this.active = false; + this.close(); + this.eventBus.trigger("idle"); + return true; + } + }, + isOpen: function isOpen() { + return this.menu.isOpen(); + }, + open: function open() { + if (!this.isOpen() && !this.eventBus.before("open")) { + this.menu.open(); + this._updateHint(); + this.eventBus.trigger("open"); + } + return this.isOpen(); + }, + close: function close() { + if (this.isOpen() && !this.eventBus.before("close")) { + this.menu.close(); + this.input.clearHint(); + this.input.resetInputValue(); + this.eventBus.trigger("close"); + } + return !this.isOpen(); + }, + setVal: function setVal(val) { + this.input.setQuery(_.toStr(val)); + }, + getVal: function getVal() { + return this.input.getQuery(); + }, + select: function select($selectable) { + var data = this.menu.getSelectableData($selectable); + if (data && !this.eventBus.before("select", data.obj, data.dataset)) { + this.input.setQuery(data.val, true); + this.eventBus.trigger("select", data.obj, data.dataset); + this.close(); + return true; + } + return false; + }, + autocomplete: function autocomplete($selectable) { + var query, data, isValid; + query = this.input.getQuery(); + data = this.menu.getSelectableData($selectable); + isValid = data && query !== data.val; + if (isValid && !this.eventBus.before("autocomplete", data.obj, data.dataset)) { + this.input.setQuery(data.val); + this.eventBus.trigger("autocomplete", data.obj, data.dataset); + return true; + } + return false; + }, + moveCursor: function moveCursor(delta) { + var query, $candidate, data, suggestion, datasetName, cancelMove, id; + query = this.input.getQuery(); + $candidate = this.menu.selectableRelativeToCursor(delta); + data = this.menu.getSelectableData($candidate); + suggestion = data ? data.obj : null; + datasetName = data ? data.dataset : null; + id = $candidate ? $candidate.attr("id") : null; + this.input.trigger("cursorchange", id); + cancelMove = this._minLengthMet() && this.menu.update(query); + if (!cancelMove && !this.eventBus.before("cursorchange", suggestion, datasetName)) { + this.menu.setCursor($candidate); + if (data) { + this.input.setInputValue(data.val); + } else { + this.input.resetInputValue(); + this._updateHint(); + } + this.eventBus.trigger("cursorchange", suggestion, datasetName); + return true; + } + return false; + }, + destroy: function destroy() { + this.input.destroy(); + this.menu.destroy(); + } + }); + return Typeahead; + function c(ctx) { + var methods = [].slice.call(arguments, 1); + return function() { + var args = [].slice.call(arguments); + _.each(methods, function(method) { + return ctx[method].apply(ctx, args); + }); + }; + } + }(); + (function() { + "use strict"; + var old, keys, methods; + old = $.fn.typeahead; + keys = { + www: "tt-www", + attrs: "tt-attrs", + typeahead: "tt-typeahead" + }; + methods = { + initialize: function initialize(o, datasets) { + var www; + datasets = _.isArray(datasets) ? datasets : [].slice.call(arguments, 1); + o = o || {}; + www = WWW(o.classNames); + return this.each(attach); + function attach() { + var $input, $wrapper, $hint, $menu, defaultHint, defaultMenu, eventBus, input, menu, status, typeahead, MenuConstructor; + _.each(datasets, function(d) { + d.highlight = !!o.highlight; + }); + $input = $(this); + $wrapper = $(www.html.wrapper); + $hint = $elOrNull(o.hint); + $menu = $elOrNull(o.menu); + defaultHint = o.hint !== false && !$hint; + defaultMenu = o.menu !== false && !$menu; + defaultHint && ($hint = buildHintFromInput($input, www)); + defaultMenu && ($menu = $(www.html.menu).css(www.css.menu)); + $hint && $hint.val(""); + $input = prepInput($input, www); + if (defaultHint || defaultMenu) { + $wrapper.css(www.css.wrapper); + $input.css(defaultHint ? www.css.input : www.css.inputWithNoHint); + $input.wrap($wrapper).parent().prepend(defaultHint ? $hint : null).append(defaultMenu ? $menu : null); + } + MenuConstructor = defaultMenu ? DefaultMenu : Menu; + eventBus = new EventBus({ + el: $input + }); + input = new Input({ + hint: $hint, + input: $input + }, www); + menu = new MenuConstructor({ + node: $menu, + datasets: datasets + }, www); + status = new Status({ + $input: $input, + menu: menu + }); + typeahead = new Typeahead({ + input: input, + menu: menu, + eventBus: eventBus, + minLength: o.minLength, + autoselect: o.autoselect + }, www); + $input.data(keys.www, www); + $input.data(keys.typeahead, typeahead); + } + }, + isEnabled: function isEnabled() { + var enabled; + ttEach(this.first(), function(t) { + enabled = t.isEnabled(); + }); + return enabled; + }, + enable: function enable() { + ttEach(this, function(t) { + t.enable(); + }); + return this; + }, + disable: function disable() { + ttEach(this, function(t) { + t.disable(); + }); + return this; + }, + isActive: function isActive() { + var active; + ttEach(this.first(), function(t) { + active = t.isActive(); + }); + return active; + }, + activate: function activate() { + ttEach(this, function(t) { + t.activate(); + }); + return this; + }, + deactivate: function deactivate() { + ttEach(this, function(t) { + t.deactivate(); + }); + return this; + }, + isOpen: function isOpen() { + var open; + ttEach(this.first(), function(t) { + open = t.isOpen(); + }); + return open; + }, + open: function open() { + ttEach(this, function(t) { + t.open(); + }); + return this; + }, + close: function close() { + ttEach(this, function(t) { + t.close(); + }); + return this; + }, + select: function select(el) { + var success = false, $el = $(el); + ttEach(this.first(), function(t) { + success = t.select($el); + }); + return success; + }, + autocomplete: function autocomplete(el) { + var success = false, $el = $(el); + ttEach(this.first(), function(t) { + success = t.autocomplete($el); + }); + return success; + }, + moveCursor: function moveCursoe(delta) { + var success = false; + ttEach(this.first(), function(t) { + success = t.moveCursor(delta); + }); + return success; + }, + val: function val(newVal) { + var query; + if (!arguments.length) { + ttEach(this.first(), function(t) { + query = t.getVal(); + }); + return query; + } else { + ttEach(this, function(t) { + t.setVal(_.toStr(newVal)); + }); + return this; + } + }, + destroy: function destroy() { + ttEach(this, function(typeahead, $input) { + revert($input); + typeahead.destroy(); + }); + return this; + } + }; + $.fn.typeahead = function(method) { + if (methods[method]) { + return methods[method].apply(this, [].slice.call(arguments, 1)); + } else { + return methods.initialize.apply(this, arguments); + } + }; + $.fn.typeahead.noConflict = function noConflict() { + $.fn.typeahead = old; + return this; + }; + function ttEach($els, fn) { + $els.each(function() { + var $input = $(this), typeahead; + (typeahead = $input.data(keys.typeahead)) && fn(typeahead, $input); + }); + } + function buildHintFromInput($input, www) { + return $input.clone().addClass(www.classes.hint).removeData().css(www.css.hint).css(getBackgroundStyles($input)).prop({ + readonly: true, + required: false + }).removeAttr("id name placeholder").removeClass("required").attr({ + spellcheck: "false", + tabindex: -1 + }); + } + function prepInput($input, www) { + $input.data(keys.attrs, { + dir: $input.attr("dir"), + autocomplete: $input.attr("autocomplete"), + spellcheck: $input.attr("spellcheck"), + style: $input.attr("style") + }); + $input.addClass(www.classes.input).attr({ + spellcheck: false + }); + try { + !$input.attr("dir") && $input.attr("dir", "auto"); + } catch (e) {} + return $input; + } + function getBackgroundStyles($el) { + return { + backgroundAttachment: $el.css("background-attachment"), + backgroundClip: $el.css("background-clip"), + backgroundColor: $el.css("background-color"), + backgroundImage: $el.css("background-image"), + backgroundOrigin: $el.css("background-origin"), + backgroundPosition: $el.css("background-position"), + backgroundRepeat: $el.css("background-repeat"), + backgroundSize: $el.css("background-size") + }; + } + function revert($input) { + var www, $wrapper; + www = $input.data(keys.www); + $wrapper = $input.parent().filter(www.selectors.wrapper); + _.each($input.data(keys.attrs), function(val, key) { + _.isUndefined(val) ? $input.removeAttr(key) : $input.attr(key, val); + }); + $input.removeData(keys.typeahead).removeData(keys.www).removeData(keys.attr).removeClass(www.classes.input); + if ($wrapper.length) { + $input.detach().insertAfter($wrapper); + $wrapper.remove(); + } + } + function $elOrNull(obj) { + var isValid, $el; + isValid = _.isJQuery(obj) || _.isElement(obj); + $el = isValid ? $(obj).first() : []; + return $el.length ? $el : null; + } + })(); +}); \ No newline at end of file diff --git a/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/micro-example.html b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/micro-example.html new file mode 100644 index 0000000..3ae38ac --- /dev/null +++ b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/micro-example.html @@ -0,0 +1,262 @@ + + + + Micro Example Reference + + + + + + + + + + + + + + + +
                      +

                      + + InAppPurchaseLib documentation + + (77% documented) +

                      + +

                      +

                      + +
                      +

                      + +

                      + + + View on GitHub + +

                      + +
                      + + + +
                      + +
                      + +
                      +
                      + +

                      Micro Example

                      +
                      /** AppDelegate.swift */
                      +import UIKit
                      +import InAppPurchaseLib
                      +
                      +@UIApplicationMain
                      +class AppDelegate: UIResponder, UIApplicationDelegate {
                      +  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
                      +    // Initialize the library
                      +    InAppPurchase.initialize(
                      +      iapProducts: [
                      +        IAPProduct(productIdentifier: "my_product", productType: .nonConsumable)
                      +      ],
                      +      validatorUrlString: "https://validator.fovea.cc/v1/validate?appName=demo&apiKey=12345678"
                      +    )
                      +    return true
                      +  }
                      +
                      +  func applicationWillTerminate(_ application: UIApplication) {
                      +    // Clean
                      +    InAppPurchase.stop()
                      +  }
                      +}
                      +
                      +
                      /** ViewController.swift */
                      +import UIKit
                      +import StoreKit
                      +import InAppPurchaseLib
                      +
                      +class ViewController: UIViewController {
                      +  private var loaderView = LoaderView()
                      +  @IBOutlet weak var statusLabel: UILabel!
                      +  @IBOutlet weak var productTitleLabel: UILabel!
                      +  @IBOutlet weak var productDescriptionLabel: UILabel!
                      +  @IBOutlet weak var purchaseButton: UIButton!
                      +  @IBOutlet weak var restorePurchasesButton: UIButton!
                      +
                      +  override func viewDidLoad() {
                      +    super.viewDidLoad()
                      +    // Add action for purchases and restore butons.
                      +    purchaseButton.addTarget(self, action: #selector(self.purchase), for: .touchUpInside)
                      +    restorePurchasesButton.addTarget(self, action: #selector(self.restorePurchases), for: .touchUpInside)
                      +  }
                      +
                      +  override func viewWillAppear(_ animated: Bool) {
                      +    self.refreshView()
                      +    InAppPurchase.refresh(callback: { _ in
                      +      self.refreshView()
                      +    })
                      +  }
                      +
                      +  func refreshView() {
                      +    guard let product: SKProduct = InAppPurchase.getProductBy(identifier: "my_product") else {
                      +      self.productTitleLabel.text = "Product unavailable"
                      +      return
                      +    }
                      +    // Display product information.
                      +    productTitleLabel.text = product.localizedTitle
                      +    productDescriptionLabel.text = product.localizedDescription
                      +    purchaseButton.setTitle(product.localizedPrice, for: .normal)
                      +
                      +    // Disable the button if the product has already been purchased.
                      +    if InAppPurchase.hasActivePurchase(for: "my_product") {
                      +      statusLabel.text = "OWNED"
                      +      purchaseButton.isPointerInteractionEnabled = false
                      +    }
                      +  }
                      +
                      +  @IBAction func purchase(_ sender: Any) {
                      +    self.loaderView.show()
                      +    InAppPurchase.purchase(
                      +      productIdentifier: "my_product",
                      +      callback: { result in
                      +        self.loaderView.hide()
                      +      })
                      +  }
                      +
                      +  @IBAction func restorePurchases(_ sender: Any) {
                      +    self.loaderView.show()
                      +    InAppPurchase.restorePurchases(callback: { result in
                      +      self.loaderView.hide()
                      +    })
                      +  }
                      +}
                      +
                      + +
                      +
                      + + +
                      +
                      + + +
                      + diff --git a/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/purchasing.html b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/purchasing.html new file mode 100644 index 0000000..7a6996c --- /dev/null +++ b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/purchasing.html @@ -0,0 +1,245 @@ + + + + Purchasing Reference + + + + + + + + + + + + + + + +
                      +

                      + + InAppPurchaseLib documentation + + (77% documented) +

                      + +

                      +

                      + +
                      +

                      + +

                      + + + View on GitHub + +

                      + +
                      + + + +
                      + +
                      + +
                      +
                      + +

                      Purchasing

                      + +

                      The purchase process is generally a little bit more involving than most people would expect. Why is it not just: purchase → on success unlock the feature?

                      + +

                      Several reasons:

                      + +
                        +
                      • In-app purchases can be initiated outside the app
                      • +
                      • In-app purchases can be deferred, pending parental approval
                      • +
                      • Apple wants to be sure you delivered the product before charging the user
                      • +
                      + +

                      That is why the process looks like so:

                      + +
                        +
                      • being ready to handle purchase events from app startup
                      • +
                      • finalizing transactions when product delivery is complete
                      • +
                      • sending purchase request, for which successful doesn’t always mean complete
                      • +
                      +

                      Initiating a purchase

                      + +

                      To initiate a purchase, use the InAppPurchase.purchase() function. It takes the productIdentifier and a callback function, called when the purchase has been processed.

                      + +

                      Important: Do not process the purchase here, we’ll handle that later!

                      + +

                      From this callback, you can for example unlock the UI by hiding your loading indicator and display a message to the user.

                      + +

                      Example:

                      +
                      self.loaderView.show()
                      +InAppPurchase.purchase(
                      +  productIdentifier: "my_product_id",
                      +  callback: { _ in
                      +    self.loaderView.hide()
                      +})
                      +
                      + +

                      This simple example locks the UI with a loader when the purchase is in progress. We’ll see later how the purchase has to be processed by your applicaiton.

                      + +

                      The callback also gives more information about the outcome of the purchase, you might want to use it to update your UI as well. Note that some events are useful for analytics. So here’s a more complete example.

                      +
                      self.loaderView.show()
                      +InAppPurchase.purchase(
                      +  productIdentifier: "my_product_id",
                      +  callback: { result in
                      +    self.loaderView.hide()
                      +
                      +    switch result.state {
                      +    case .purchased:
                      +      // Product successfully purchased
                      +      // Reminder: Do not process the purchase here, only update your UI.
                      +      //           that's why we do not send data to analytics.
                      +      openThankYouScreen()
                      +    case .failed:
                      +      // Purchase failed
                      +      // - Human formated reason can be found in result.localizedDescription
                      +      // - More details in either result.skError or result.iapError
                      +      showError(result.localizedDescription)
                      +    case .deferred:
                      +      // The purchase is deferred, waiting for the parent's approval
                      +      openWaitingParentApprovalScreen()
                      +    case .cancelled:
                      +      // The user canceled the request, generally only useful for analytics.
                      +  }
                      +})
                      +
                      + +

                      If the purchase fails, result will contain either .skError, a SKError from StoreKit, or .iapError, an IAPError.

                      + +

                      Tip: After a successful purchase, you should see a new transaction in Fovea’s dashboard.

                      + +
                      +
                      + + +
                      +
                      + + +
                      + diff --git a/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/refreshing.html b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/refreshing.html new file mode 100644 index 0000000..56e094a --- /dev/null +++ b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/refreshing.html @@ -0,0 +1,189 @@ + + + + Refreshing Reference + + + + + + + + + + + + + + + +
                      +

                      + + InAppPurchaseLib documentation + + (77% documented) +

                      + +

                      +

                      + +
                      +

                      + +

                      + + + View on GitHub + +

                      + +
                      + + + +
                      + +
                      + +
                      +
                      + +

                      Refreshing

                      + +

                      Data might change or not be yet available when your “product” view is presented. In order to properly handle those cases, you should refresh your view after refreshing in-app products metadata. You want to be sure you’re displaying up-to-date information.

                      + +

                      To achieve this, call InAppPurchase.refresh() when your view is presented.

                      +
                      override func viewWillAppear(_ animated: Bool) {
                      +  self.refreshView()
                      +  InAppPurchase.refresh(callback: { _ in
                      +      self.refreshView()
                      +  })
                      +}
                      +
                      + +
                      +
                      + + +
                      +
                      + + +
                      + diff --git a/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/restoring-purchases.html b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/restoring-purchases.html new file mode 100644 index 0000000..ab44024 --- /dev/null +++ b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/restoring-purchases.html @@ -0,0 +1,201 @@ + + + + Restoring purchases Reference + + + + + + + + + + + + + + + +
                      +

                      + + InAppPurchaseLib documentation + + (77% documented) +

                      + +

                      +

                      + +
                      +

                      + +

                      + + + View on GitHub + +

                      + +
                      + + + +
                      + +
                      + +
                      +
                      + +

                      Restoring purchases

                      + +

                      Except if you only sell consumable products, Apple requires that you provide a “Restore Purchases” button to your users. In general, it is found in your application settings.

                      + +

                      Call this method when this button is pressed.

                      +
                      @IBAction func restorePurchases(_ sender: Any) {
                      +  self.loaderView.show()
                      +  InAppPurchase.restorePurchases(callback: { result in
                      +      self.loaderView.hide()
                      +      switch result.state {
                      +      case .succeeded:
                      +          if result.addedPurchases > 0 {
                      +              print("Restore purchases successful.")
                      +          } else {
                      +              print("No purchase to restore.")
                      +          }
                      +      case .failed:
                      +          print("Restore purchases failed.")
                      +      }
                      +  })
                      +}
                      +
                      + +

                      The callback method is called once the operation is complete. You can use it to unlock the UI, by hiding your loader for example, and display the adapted message to the user.

                      + +
                      +
                      + + +
                      +
                      + + +
                      + diff --git a/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/search.json b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/search.json new file mode 100644 index 0000000..ca5875b --- /dev/null +++ b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/search.json @@ -0,0 +1 @@ +{"Protocols/IAPErrorProtocol.html#/s:16InAppPurchaseLib16IAPErrorProtocolP4codeAA0E4CodeOvp":{"name":"code","abstract":"

                      Undocumented

                      ","parent_name":"IAPErrorProtocol"},"Enums/IAPErrorCode.html#/s:16InAppPurchaseLib12IAPErrorCodeO21libraryNotInitializedyA2CmF":{"name":"libraryNotInitialized","abstract":"

                      You must call the initialize fuction before using the library.

                      ","parent_name":"IAPErrorCode"},"Enums/IAPErrorCode.html#/s:16InAppPurchaseLib12IAPErrorCodeO23bundleIdentifierInvalidyA2CmF":{"name":"bundleIdentifierInvalid","abstract":"

                      The Bundle Identifier is invalid.

                      ","parent_name":"IAPErrorCode"},"Enums/IAPErrorCode.html#/s:16InAppPurchaseLib12IAPErrorCodeO19validatorUrlInvalidyA2CmF":{"name":"validatorUrlInvalid","abstract":"

                      The Validator URL String is invalid.

                      ","parent_name":"IAPErrorCode"},"Enums/IAPErrorCode.html#/s:16InAppPurchaseLib12IAPErrorCodeO20refreshReceiptFailedyA2CmF":{"name":"refreshReceiptFailed","abstract":"

                      Failed to refresh the App Store receipt.

                      ","parent_name":"IAPErrorCode"},"Enums/IAPErrorCode.html#/s:16InAppPurchaseLib12IAPErrorCodeO21validateReceiptFailedyA2CmF":{"name":"validateReceiptFailed","abstract":"

                      Failed to validate the App Store receipt with Fovea.

                      ","parent_name":"IAPErrorCode"},"Enums/IAPErrorCode.html#/s:16InAppPurchaseLib12IAPErrorCodeO17readReceiptFailedyA2CmF":{"name":"readReceiptFailed","abstract":"

                      Failed to read the receipt validation.

                      ","parent_name":"IAPErrorCode"},"Enums/IAPErrorCode.html#/s:16InAppPurchaseLib12IAPErrorCodeO21refreshProductsFailedyA2CmF":{"name":"refreshProductsFailed","abstract":"

                      Failed to refresh products from the App Store.

                      ","parent_name":"IAPErrorCode"},"Enums/IAPErrorCode.html#/s:16InAppPurchaseLib12IAPErrorCodeO15productNotFoundyA2CmF":{"name":"productNotFound","abstract":"

                      The product was not found on the App Store and cannot be purchased.

                      ","parent_name":"IAPErrorCode"},"Enums/IAPErrorCode.html#/s:16InAppPurchaseLib12IAPErrorCodeO010cannotMakeC0yA2CmF":{"name":"cannotMakePurchase","abstract":"

                      The user is not allowed to authorize payments.

                      ","parent_name":"IAPErrorCode"},"Enums/IAPErrorCode.html#/s:16InAppPurchaseLib12IAPErrorCodeO17alreadyPurchasingyA2CmF":{"name":"alreadyPurchasing","abstract":"

                      A purchase is already in progress.

                      ","parent_name":"IAPErrorCode"},"Structs/IAPError.html#/s:16InAppPurchaseLib8IAPErrorV4codeAA0E4CodeOvp":{"name":"code","abstract":"

                      The error code.

                      ","parent_name":"IAPError"},"Structs/IAPError.html#/s:16InAppPurchaseLib8IAPErrorV20localizedDescriptionSSvp":{"name":"localizedDescription","abstract":"

                      The error description.

                      ","parent_name":"IAPError"},"Enums/IAPRefreshResultState.html#/s:16InAppPurchaseLib21IAPRefreshResultStateO9succeededyA2CmF":{"name":"succeeded","abstract":"

                      Refresh was successful.

                      ","parent_name":"IAPRefreshResultState"},"Enums/IAPRefreshResultState.html#/s:16InAppPurchaseLib21IAPRefreshResultStateO6failedyA2CmF":{"name":"failed","abstract":"

                      Refresh failed.

                      ","parent_name":"IAPRefreshResultState"},"Enums/IAPRefreshResultState.html#/s:16InAppPurchaseLib21IAPRefreshResultStateO7skippedyA2CmF":{"name":"skipped","abstract":"

                      Refresh has been skipped because it is not necessary.

                      ","parent_name":"IAPRefreshResultState"},"Enums/IAPPurchaseResultState.html#/s:16InAppPurchaseLib22IAPPurchaseResultStateO9purchasedyA2CmF":{"name":"purchased","abstract":"

                      The purchase was successful.

                      ","parent_name":"IAPPurchaseResultState"},"Enums/IAPPurchaseResultState.html#/s:16InAppPurchaseLib22IAPPurchaseResultStateO6failedyA2CmF":{"name":"failed","abstract":"

                      Puchase failed.

                      ","parent_name":"IAPPurchaseResultState"},"Enums/IAPPurchaseResultState.html#/s:16InAppPurchaseLib22IAPPurchaseResultStateO9cancelledyA2CmF":{"name":"cancelled","abstract":"

                      The purchase was cancelled by the user.

                      ","parent_name":"IAPPurchaseResultState"},"Enums/IAPPurchaseResultState.html#/s:16InAppPurchaseLib22IAPPurchaseResultStateO8deferredyA2CmF":{"name":"deferred","abstract":"

                      The purchase is deferred.

                      ","parent_name":"IAPPurchaseResultState"},"Structs/IAPRefreshResult.html#/s:16InAppPurchaseLib16IAPRefreshResultV5stateAA0eF5StateOvp":{"name":"state","abstract":"

                      Undocumented

                      ","parent_name":"IAPRefreshResult"},"Structs/IAPRefreshResult.html#/s:16InAppPurchaseLib16IAPRefreshResultV8iapErrorAA8IAPErrorVSgvp":{"name":"iapError","abstract":"

                      Undocumented

                      ","parent_name":"IAPRefreshResult"},"Structs/IAPRefreshResult.html#/s:16InAppPurchaseLib16IAPRefreshResultV14addedPurchasesSivp":{"name":"addedPurchases","abstract":"

                      Undocumented

                      ","parent_name":"IAPRefreshResult"},"Structs/IAPRefreshResult.html#/s:16InAppPurchaseLib16IAPRefreshResultV16updatedPurchasesSivp":{"name":"updatedPurchases","abstract":"

                      Undocumented

                      ","parent_name":"IAPRefreshResult"},"Structs/IAPPurchaseResult.html#/s:16InAppPurchaseLib17IAPPurchaseResultV5stateAA0eF5StateOvp":{"name":"state","abstract":"

                      Undocumented

                      ","parent_name":"IAPPurchaseResult"},"Structs/IAPPurchaseResult.html#/s:16InAppPurchaseLib17IAPPurchaseResultV8iapErrorAA8IAPErrorVSgvp":{"name":"iapError","abstract":"

                      Undocumented

                      ","parent_name":"IAPPurchaseResult"},"Structs/IAPPurchaseResult.html#/s:16InAppPurchaseLib17IAPPurchaseResultV7skErrorSC11SKErrorCodeLeVSgvp":{"name":"skError","abstract":"

                      Undocumented

                      ","parent_name":"IAPPurchaseResult"},"Structs/IAPPurchaseResult.html#/s:16InAppPurchaseLib17IAPPurchaseResultV20localizedDescriptionSSSgvp":{"name":"localizedDescription","abstract":"

                      Undocumented

                      ","parent_name":"IAPPurchaseResult"},"Enums/IAPPeriodFormat.html#/s:16InAppPurchaseLib15IAPPeriodFormatO5shortyA2CmF":{"name":"short","abstract":"

                      Undocumented

                      ","parent_name":"IAPPeriodFormat"},"Enums/IAPPeriodFormat.html#/s:16InAppPurchaseLib15IAPPeriodFormatO4longyA2CmF":{"name":"long","abstract":"

                      Undocumented

                      ","parent_name":"IAPPeriodFormat"},"Extensions/SKProduct.html#/s:So9SKProductC16InAppPurchaseLibE21localizedPeriodFormatAC09IAPPeriodH0OvpZ":{"name":"localizedPeriodFormat","abstract":"

                      Undocumented

                      ","parent_name":"SKProduct"},"Extensions/SKProduct.html#/s:So9SKProductC16InAppPurchaseLibE28hasIntroductoryPriceEligibleSbyF":{"name":"hasIntroductoryPriceEligible()","abstract":"

                      Checks if the product has an introductory price the user is eligible to.

                      ","parent_name":"SKProduct"},"Extensions/SKProduct.html#/s:So9SKProductC16InAppPurchaseLibE14localizedPriceSSvp":{"name":"localizedPrice","abstract":"

                      Returns a localized string with the cost of the product in the local currency.

                      ","parent_name":"SKProduct"},"Extensions/SKProduct.html#/s:So9SKProductC16InAppPurchaseLibE27localizedSubscriptionPeriodSSSgvp":{"name":"localizedSubscriptionPeriod","abstract":"

                      Returns a localized string with the period of the subscription product.

                      ","parent_name":"SKProduct"},"Extensions/SKProduct.html#/s:So9SKProductC16InAppPurchaseLibE26localizedIntroductoryPriceSSSgvp":{"name":"localizedIntroductoryPrice","abstract":"

                      Returns a localized string with the introductory price if available, in the local currency.

                      ","parent_name":"SKProduct"},"Extensions/SKProduct.html#/s:So9SKProductC16InAppPurchaseLibE27localizedIntroductoryPeriodSSSgvp":{"name":"localizedIntroductoryPeriod","abstract":"

                      Returns a localized string with the introductory price period of the subscription product.

                      ","parent_name":"SKProduct"},"Extensions/SKProduct.html#/s:So9SKProductC16InAppPurchaseLibE29localizedIntroductoryDurationSSSgvp":{"name":"localizedIntroductoryDuration","abstract":"

                      Returns a localized string with the duration of the introductory price.

                      ","parent_name":"SKProduct"},"Enums/IAPProductType.html#/s:16InAppPurchaseLib14IAPProductTypeO10consumableyA2CmF":{"name":"consumable","abstract":"

                      Undocumented

                      ","parent_name":"IAPProductType"},"Enums/IAPProductType.html#/s:16InAppPurchaseLib14IAPProductTypeO13nonConsumableyA2CmF":{"name":"nonConsumable","abstract":"

                      Undocumented

                      ","parent_name":"IAPProductType"},"Enums/IAPProductType.html#/s:16InAppPurchaseLib14IAPProductTypeO23nonRenewingSubscriptionyA2CmF":{"name":"nonRenewingSubscription","abstract":"

                      Undocumented

                      ","parent_name":"IAPProductType"},"Enums/IAPProductType.html#/s:16InAppPurchaseLib14IAPProductTypeO25autoRenewableSubscriptionyA2CmF":{"name":"autoRenewableSubscription","abstract":"

                      Undocumented

                      ","parent_name":"IAPProductType"},"Structs/IAPProduct.html#/s:16InAppPurchaseLib10IAPProductV17productIdentifierSSvp":{"name":"productIdentifier","abstract":"

                      The identifier of the product.

                      ","parent_name":"IAPProduct"},"Structs/IAPProduct.html#/s:16InAppPurchaseLib10IAPProductV11productTypeAA0eG0Ovp":{"name":"productType","abstract":"

                      The type of the product.

                      ","parent_name":"IAPProduct"},"Structs/IAPProduct.html#/s:16InAppPurchaseLib10IAPProductV17productIdentifier0F4TypeACSS_AA0eH0Otcfc":{"name":"init(productIdentifier:productType:)","abstract":"

                      Initializes an IAPProduct with its identifier and type.

                      ","parent_name":"IAPProduct"},"Protocols/IAPPurchaseDelegate.html#/s:16InAppPurchaseLib19IAPPurchaseDelegateP16productPurchased0G10IdentifierySS_tF":{"name":"productPurchased(productIdentifier:)","abstract":"

                      Called when a product is newly purchased, updated or restored.

                      ","parent_name":"IAPPurchaseDelegate"},"Classes/DefaultPurchaseDelegate.html#/s:16InAppPurchaseLib07DefaultC8DelegateCACycfc":{"name":"init()","abstract":"

                      Undocumented

                      ","parent_name":"DefaultPurchaseDelegate"},"Classes/DefaultPurchaseDelegate.html#/s:16InAppPurchaseLib07DefaultC8DelegateC16productPurchased0G10IdentifierySS_tF":{"name":"productPurchased(productIdentifier:)","abstract":"

                      Finish the product transactions when a product is newly purchased, updated or restored.

                      ","parent_name":"DefaultPurchaseDelegate"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP11iapProductsSayAA10IAPProductVGvpZ":{"name":"iapProducts","abstract":"

                      The array of IAPProduct.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP18validatorUrlStringSSSgvpZ":{"name":"validatorUrlString","abstract":"

                      The validator url retrieved from Fovea.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP03iapC8DelegateAA011IAPPurchaseF0_pSgvpZ":{"name":"iapPurchaseDelegate","abstract":"

                      The instance of class that adopts the IAPPurchaseDelegate protocol.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP19applicationUsernameSSSgvpZ":{"name":"applicationUsername","abstract":"

                      The user name, if your app implements user login.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP10initialize11iapProducts18validatorUrlString0fC8Delegate19applicationUsernameySayAA10IAPProductVG_SSAA011IAPPurchaseK0_pSSSgtFZ":{"name":"initialize(iapProducts:validatorUrlString:iapPurchaseDelegate:applicationUsername:)","abstract":"

                      Start observing the payment queue, as soon as possible, and refresh Product list and user Receipt.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP4stopyyFZ":{"name":"stop()","abstract":"

                      Stop observing the payment queue, when the application will terminate, for proper cleanup.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP7refresh8callbackyyAA16IAPRefreshResultVc_tFZ":{"name":"refresh(callback:)","abstract":"

                      Refresh Product list and user Receipt.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP11getProductsSaySo9SKProductCGyFZ":{"name":"getProducts()","abstract":"

                      Gets all products retrieved from the App Store

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP12getProductBy10identifierSo9SKProductCSgSS_tFZ":{"name":"getProductBy(identifier:)","abstract":"

                      Gets the product by its identifier from the list of products retrieved from the App Store.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP15canMakePaymentsSbyFZ":{"name":"canMakePayments()","abstract":"

                      Checks if the user is allowed to authorize payments.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP8purchase17productIdentifier8quantity8callbackySS_SiyAA17IAPPurchaseResultVctFZ":{"name":"purchase(productIdentifier:quantity:callback:)","abstract":"

                      Request a Payment from the App Store.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP16restorePurchases8callbackyyAA16IAPRefreshResultVc_tFZ":{"name":"restorePurchases(callback:)","abstract":"

                      Restore purchased products.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP18finishTransactions3forySS_tFZ":{"name":"finishTransactions(for:)","abstract":"

                      Finish all transactions for the product.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP22hasDeferredTransaction3forSbSS_tFZ":{"name":"hasDeferredTransaction(for:)","abstract":"

                      Checks if the last transaction state for a given product was deferred.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP19hasAlreadyPurchasedSbyFZ":{"name":"hasAlreadyPurchased()","abstract":"

                      Checks if the user has already purchased at least one product.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP09hasActiveC03forSbSS_tFZ":{"name":"hasActivePurchase(for:)","abstract":"

                      Checks if the user currently own (or is subscribed to) a given product (nonConsumable or autoRenewableSubscription).

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP21hasActiveSubscriptionSbyFZ":{"name":"hasActiveSubscription()","abstract":"

                      Checks if the user has an active auto renewable subscription regardless of the product identifier.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP03getC4Date3for10Foundation0F0VSgSS_tFZ":{"name":"getPurchaseDate(for:)","abstract":"

                      Returns the latest purchased date for a given product.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP13getExpiryDate3for10Foundation0G0VSgSS_tFZ":{"name":"getExpiryDate(for:)","abstract":"

                      Returns the expiry date for a subcription. May be past or future.

                      ","parent_name":"InAppPurchaseLib"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C11iapProductsSayAA10IAPProductVGvpZ":{"name":"iapProducts","abstract":"

                      The array of IAPProduct.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C18validatorUrlStringSSSgvpZ":{"name":"validatorUrlString","abstract":"

                      The validator url retrieved from Fovea.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C03iapC8DelegateAA011IAPPurchaseF0_pSgvpZ":{"name":"iapPurchaseDelegate","abstract":"

                      The instance of class that adopts the IAPPurchaseDelegate protocol.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C19applicationUsernameSSSgvpZ":{"name":"applicationUsername","abstract":"

                      The user name, if your app implements user login.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C10initialize11iapProducts18validatorUrlString0fC8Delegate19applicationUsernameySayAA10IAPProductVG_SSAA011IAPPurchaseK0_pSSSgtFZ":{"name":"initialize(iapProducts:validatorUrlString:iapPurchaseDelegate:applicationUsername:)","abstract":"

                      Start observing the payment queue, as soon as possible, and refresh Product list and user Receipt.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C4stopyyFZ":{"name":"stop()","abstract":"

                      Stop observing the payment queue, when the application will terminate, for proper cleanup.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C7refresh8callbackyyAA16IAPRefreshResultVc_tFZ":{"name":"refresh(callback:)","abstract":"

                      Refresh Product list and user Receipt.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C11getProductsSaySo9SKProductCGyFZ":{"name":"getProducts()","abstract":"

                      Gets all products retrieved from the App Store

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C12getProductBy10identifierSo9SKProductCSgSS_tFZ":{"name":"getProductBy(identifier:)","abstract":"

                      Gets the product by its identifier from the list of products retrieved from the App Store.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C15canMakePaymentsSbyFZ":{"name":"canMakePayments()","abstract":"

                      Checks if the user is allowed to authorize payments.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C8purchase17productIdentifier8quantity8callbackySS_SiyAA17IAPPurchaseResultVctFZ":{"name":"purchase(productIdentifier:quantity:callback:)","abstract":"

                      Request a Payment from the App Store.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C16restorePurchases8callbackyyAA16IAPRefreshResultVc_tFZ":{"name":"restorePurchases(callback:)","abstract":"

                      Restore purchased products.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C18finishTransactions3forySS_tFZ":{"name":"finishTransactions(for:)","abstract":"

                      Finish all transactions for the product.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C22hasDeferredTransaction3forSbSS_tFZ":{"name":"hasDeferredTransaction(for:)","abstract":"

                      Checks if the last transaction state for a given product was deferred.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C19hasAlreadyPurchasedSbyFZ":{"name":"hasAlreadyPurchased()","abstract":"

                      Checks if the user has already purchased at least one product.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C09hasActiveC03forSbSS_tFZ":{"name":"hasActivePurchase(for:)","abstract":"

                      Checks if the user currently own (or is subscribed to) a given product (nonConsumable or autoRenewableSubscription).

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C21hasActiveSubscriptionSbyFZ":{"name":"hasActiveSubscription()","abstract":"

                      Checks if the user has an active auto renewable subscription regardless of the product identifier.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C03getC4Date3for10Foundation0F0VSgSS_tFZ":{"name":"getPurchaseDate(for:)","abstract":"

                      Returns the latest purchased date for a given product.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C13getExpiryDate3for10Foundation0G0VSgSS_tFZ":{"name":"getExpiryDate(for:)","abstract":"

                      Returns the expiry date for a subcription. May be past or future.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html":{"name":"InAppPurchase"},"Protocols/InAppPurchaseLib.html":{"name":"InAppPurchaseLib","abstract":"

                      The protocol that InAppPurchase adopts.

                      "},"Classes/DefaultPurchaseDelegate.html":{"name":"DefaultPurchaseDelegate","abstract":"

                      The default implementation of IAPPurchaseDelegate if no other is provided. It is enough if you only have non-consumable and/or auto-renewable subscription products.

                      "},"Protocols/IAPPurchaseDelegate.html":{"name":"IAPPurchaseDelegate","abstract":"

                      The protocol that you must adopt if you have consumable and/or non-renewing subscription products.

                      "},"Structs/IAPProduct.html":{"name":"IAPProduct","abstract":"

                      Undocumented

                      "},"Enums/IAPProductType.html":{"name":"IAPProductType","abstract":"

                      Undocumented

                      "},"Extensions/SKProduct.html":{"name":"SKProduct"},"Enums/IAPPeriodFormat.html":{"name":"IAPPeriodFormat","abstract":"

                      Undocumented

                      "},"API%20documentation.html#/s:16InAppPurchaseLib19IAPPurchaseCallbacka":{"name":"IAPPurchaseCallback","abstract":"

                      Undocumented

                      "},"API%20documentation.html#/s:16InAppPurchaseLib18IAPRefreshCallbacka":{"name":"IAPRefreshCallback","abstract":"

                      Undocumented

                      "},"Structs/IAPPurchaseResult.html":{"name":"IAPPurchaseResult","abstract":"

                      The result returned in the purchase() callback.

                      "},"Structs/IAPRefreshResult.html":{"name":"IAPRefreshResult","abstract":"

                      The result returned in the refresh() or restorePurchases() callback.

                      "},"Enums/IAPPurchaseResultState.html":{"name":"IAPPurchaseResultState","abstract":"

                      The list of the different states of the IAPPurchaseResult.

                      "},"Enums/IAPRefreshResultState.html":{"name":"IAPRefreshResultState","abstract":"

                      The list of the different states of the IAPRefreshResult.

                      "},"Structs/IAPError.html":{"name":"IAPError","abstract":"

                      When calling refresh(), purchase() or restorePurchases(), the callback can return an IAPError if the state is failed.

                      "},"Enums/IAPErrorCode.html":{"name":"IAPErrorCode","abstract":"

                      The list of error codes that can be returned by the library.

                      "},"Protocols/IAPErrorProtocol.html":{"name":"IAPErrorProtocol","abstract":"

                      Undocumented

                      "},"initialization.html":{"name":"Initialization"},"displaying-products.html":{"name":"Displaying products"},"displaying-subscriptions.html":{"name":"Displaying subscriptions"},"refreshing.html":{"name":"Refreshing"},"purchasing.html":{"name":"Purchasing"},"handling-purchases.html":{"name":"Handling purchases"},"restoring-purchases.html":{"name":"Restoring purchases"},"displaying-products-with-purchases.html":{"name":"Displaying products with purchases"},"errors.html":{"name":"Errors"},"analytics.html":{"name":"Analytics"},"server-integration.html":{"name":"Server integration"},"installation.html":{"name":"Installation"},"micro-example.html":{"name":"Micro Example"},"Getting%20Started.html":{"name":"Getting Started"},"Usage.html":{"name":"Usage"},"API%20documentation.html":{"name":"API documentation"}} \ No newline at end of file diff --git a/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/server-integration.html b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/server-integration.html new file mode 100644 index 0000000..ab51e7d --- /dev/null +++ b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/server-integration.html @@ -0,0 +1,198 @@ + + + + Server integration Reference + + + + + + + + + + + + + + + +
                      +

                      + + InAppPurchaseLib documentation + + (77% documented) +

                      + +

                      +

                      + +
                      +

                      + +

                      + + + View on GitHub + +

                      + +
                      + + + +
                      + +
                      + +
                      +
                      + +

                      Server integration

                      + +

                      In more advanced use cases, you have a server component. Users are logged in and you’ll like to unlock the content for this user on your server. The safest approach is to setup a Webhook on Fovea. You’ll receive notifications from Fovea that transaction have been processed and/or subscriptions updated.

                      + +

                      The information sent from Fovea has been verified from Apple’s server, which makes it way more trustable than information sent from your app itself.

                      + +

                      To take advantage of this, you have to inform the library of your application username. This applicationUsername can be provided as a parameter of the InAppPurchase.initialize method and updated later by changing the associated property.

                      + +

                      Example:

                      +
                      InAppPurchase.initialize(
                      +  iapProducts: [...],
                      +  validatorUrlString: "..."),
                      +  applicationUsername: UserSession.getUserId())
                      +
                      +// later ...
                      +InAppPurchase.applicationUsername = UserSession.getUserId()
                      +
                      + +

                      If a user account is mandatory in your app, you will want to delay calls to InAppPurchase.initialize() to when your user’s session is ready.

                      + +

                      Do not hesitate to contact Fovea for help.

                      + +
                      +
                      + + +
                      +
                      + + +
                      + diff --git a/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/docSet.dsidx b/docs/docsets/InAppPurchaseLib.docset/Contents/Resources/docSet.dsidx new file mode 100644 index 0000000000000000000000000000000000000000..f1134e88bde13f9761051061fdf36ee2b2e433c8 GIT binary patch literal 45056 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 E0Pz6mpa1{> literal 0 HcmV?d00001 diff --git a/docs/errors.html b/docs/errors.html new file mode 100644 index 0000000..33fa397 --- /dev/null +++ b/docs/errors.html @@ -0,0 +1,206 @@ + + + + Errors Reference + + + + + + + + + + + + + + + +
                      +

                      + + InAppPurchaseLib documentation + + (77% documented) +

                      + +

                      +

                      + +
                      +

                      + +

                      + + + View on GitHub + +

                      + +
                      + + + +
                      + +
                      + +
                      +
                      + +

                      Errors

                      + +

                      When calling refresh(), purchase() or restorePurchases(), the callback can return an IAPError if the state is failed. +Here is the list of IAPErrorCode you can receive:

                      + +
                        +
                      • Errors returned by refresh(), purchase() or restorePurchases()

                        + +
                          +
                        • libraryNotInitialized - You must call the initialize fuction before using the library.
                        • +
                        • bundleIdentifierInvalid - The Bundle Identifier is invalid.
                        • +
                        • validatorUrlInvalid - The Validator URL String is invalid.
                        • +
                        • refreshReceiptFailed - Failed to refresh the App Store receipt.
                        • +
                        • validateReceiptFailed - Failed to validate the App Store receipt with Fovea.
                        • +
                        • readReceiptFailed - Failed to read the receipt validation.
                        • +
                      • +
                      • Errors returned by refresh()

                        + +
                          +
                        • refreshProductsFailed - Failed to refresh products from the App Store.
                        • +
                      • +
                      • Errors returned by purchase()

                        + +
                          +
                        • productNotFound - The product was not found on the App Store and cannot be purchased.
                        • +
                        • cannotMakePurchase - The user is not allowed to authorize payments.
                        • +
                        • alreadyPurchasing - A purchase is already in progress.
                        • +
                      • +
                      + +
                      +
                      + + +
                      +
                      + + +
                      + diff --git a/docs/handling-purchases.html b/docs/handling-purchases.html new file mode 100644 index 0000000..00379e9 --- /dev/null +++ b/docs/handling-purchases.html @@ -0,0 +1,271 @@ + + + + Handling purchases Reference + + + + + + + + + + + + + + + +
                      +

                      + + InAppPurchaseLib documentation + + (77% documented) +

                      + +

                      +

                      + +
                      +

                      + +

                      + + + View on GitHub + +

                      + +
                      + + + +
                      + +
                      + +
                      +
                      + +

                      Handling purchases

                      + +

                      Finally, the magic happened: a user purchased one of your products! Let’s see how we handle the different types of products.

                      + + + +

                      +

                      Non-Consumables

                      + +

                      Wherever your app needs to know if a non-consumable product has been purchased, use InAppPurchase.hasActivePurchase(for: +productIdentifier). This will return true if the user currently owns the product.

                      + +

                      Note: The last known state for the user’s purchases is stored as UserDefaults. As such, their status is always available to your app, even when offline.

                      + +

                      If you have a server that needs to know about the purchase. You should rely on Fovea’s webhook instead of doing anything in here. We will see that later in the Server integration section.

                      + +

                      +

                      Auto-Renewable Subscriptions

                      + +

                      As with non-consumables, you will use InAppPurchase.hasActivePurchase(for: productIdentifier) to check if the user is an active subscriber to a given product.

                      + +

                      You might also like to call refresh regularly, for example when entering your main view. When appropriate, the library will refresh the receipt to detect subscription renewals or expiry.

                      + +

                      As we’ve seend in the Refreshing section:

                      +
                      override func viewWillAppear(_ animated: Bool) {
                      +  self.refreshView()
                      +  InAppPurchase.refresh(callback: { _ in
                      +      self.refreshView()
                      +  })
                      +}
                      +
                      + +

                      Note: Don’t be reluctant to call refresh() often. Internally, the library ensures heavy operation are only performed if necessary: for example when a subscription just expired. So in 99% of cases this call will result in no-operations.

                      + +

                      +

                      Consumables

                      + +

                      If the purchased products in a consumable, your app is responsible for delivering the purchase then acknowlege that you’ve done so. Delivering generally consists in increasing a counter for some sort of virtual currency.

                      + +

                      Your app can be notified of a purchase at any time. So the library asks you to provide an IAPPurchaseDelegate from initialization.

                      + +

                      In InAppPurchase.initialize(), we can pass an IAPPurchaseDelegate instance. This object implements the productPurchased(productIdentifier:) function, which is called whenever a purchase is approved.

                      + +

                      Here’s a example implementation:

                      +
                      class AppDelegate: UIResponder, UIApplicationDelegate, IAPPurchaseDelegate {
                      +  ...
                      +  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
                      +    InAppPurchase.initialize(
                      +      iapProducts: [...],
                      +      iapPurchaseDelegate: self, // ADDED: iapPurchaseDelegate
                      +      validatorUrlString: "https://validator.fovea.cc/v1/validate?appName=demo&apiKey=12345678")
                      +  }
                      +
                      +  // IAPPurchaseDelegate implementation
                      +  func productPurchased(productIdentifier: String) {
                      +    // TODO
                      +  }
                      +}
                      +
                      + +

                      It’s also important to know that when a purchase is approved, money isn’t yet to reach your bank account. You have to acknowledge delivery of the (virtual) item to finalize the transaction. That is why we have to call InAppPurchase.finishTransactions(for: productIdentifier) as soon as we delivered the product.

                      + +

                      Example

                      + +

                      Let’s define a class that adopts the IAPPurchaseDelegate protocol, it can very well be your application delegate.

                      +
                      func productPurchased(productIdentifier: String) {
                      +  switch productIdenfier {
                      +  case "10_silver":
                      +    addSilver(10)
                      +  case "100_silver":
                      +    addSilver(100)
                      +  }
                      +  InAppPurchase.finishTransactions(for: productIdentifier)
                      +  Analytics.trackEvent("purchase succeeded", productIdentifier)
                      +}
                      +
                      + +

                      Here, we implement our own unlocking logic and call InAppPurchase.finishTransactions() afterward (assuming addSilver is synchronous).

                      + +

                      Note: productPurchased is called when a purchase has been confirmed by Fovea’s receipt validator. If you have a server, he probably already has been notified of this purchase using the webhook.

                      + +

                      Reminder: Keep in mind that purchase notifications might occur even if you never called the InAppPurchase.purchase() function: purchases can be made from another device or the AppStore, they can be approved by parents when the app isn’t running, purchase flows can be interupted, etc. The pattern above ensures your app is always ready to handle purchase events.

                      + +

                      +

                      Non-Renewing Subscriptions

                      + +

                      For non-renewing subscriptions, delivering consists in increasing the amount of time a user can access a given feature. Apple doesn’t manage the length and expiry of non-renewing subscriptions: you have to do this yourself, as for consumables.

                      + +

                      Basically, everything is identical to consumables.

                      + +
                      +
                      + + +
                      +
                      + + +
                      + diff --git a/docs/jazzy/img/carat.png b/docs/img/carat.png similarity index 100% rename from docs/jazzy/img/carat.png rename to docs/img/carat.png diff --git a/docs/jazzy/img/dash.png b/docs/img/dash.png similarity index 100% rename from docs/jazzy/img/dash.png rename to docs/img/dash.png diff --git a/docs/jazzy/img/gh.png b/docs/img/gh.png similarity index 100% rename from docs/jazzy/img/gh.png rename to docs/img/gh.png diff --git a/docs/img/spinner.gif b/docs/img/spinner.gif new file mode 100644 index 0000000000000000000000000000000000000000..e3038d0a42c55b1a07caecb7c7a6cbac982ec09d GIT binary patch literal 1849 zcmb8wZBSF$83*voz31lM+?V7Kkqwb`LI|3K#DupH#kDs91c6du6?SMB!bsTr6|ge_{#vAVj^!DyNA-l zJ&$jDFNv;BTZXX@Qk-7+S5ErF>mkOcZ@lQv>F1VyCEMe2Ud@f<|L%#&QJi${E`2lR zqKFaW2Y$aTRxUY&ae$IHsN;Z;rdZ%CjYLTv!tMi234j-ON=CnvK-1QU|MG$YErn{gHZ@0Q6&?xSyply?S$EVNXH;gp?S5kV2-)$ga^gw`(f4Mm_Y(`RbgRkQTHF2@zL}dCiLk$RoZIc{xZL z_J*d5)Kb;#oKCFyfL*NGSs?y;e(QKvPJe1#G)h5*6E(?L9$nt?UaQJfP^$GDL0PU; z?r}C|);JQ4HES3w5VMlY7x6xfJAzDKlHE~>x;D`Fa=WygYot{pfFehH69o9pK|72W zwC6?t^AnATIJa=kewn=ep?Nk(aZ*pZo}51`S=^)jPRb`~l^VE}08>P3OJtQlXx1K8 z8Q}_u=F*fS;=k=?(fIv#+%811NTx8^}rHwvH%LbYmpFl9p1A{Idh@2x$ zuVp7)VD9}Uc(*(C**!QOdS(6B)$5^Tq5p3q*7un&_Z-NKEiEYg$D{Uq&sa>wj|za5 zJ6M~p)z+E6*X${8j6Ci+sqZ}zxeCAo0gZmZuhl+)Q%1U$Br_`NXcA-3yBdYMha+{o z{?q0Q(kaR2n`M29{!pwpgX6+CPQEgIO%x*0#!TC=c-ZPSkLO>OcmQUao5%-3w)U`F zRz?uGCEKQDh!TQPDmyd;iDX$TkMIe)%61q51Y2b-ie4r00!csilXgKL$txqj|6D(# z@(#!nQ}3R1JGeB3B5Tuqdvyg@*!-bq`9`pmasNGvy9^*+cd1Y*g>HK#rl7i79QQAG zl4SL_wW@WY1d+F?j0gFInGhsRrqvV3SKl{oqW+;9!fu|u@J)h4WM!0Cu02l@p60b#5M9c{dKh=_eRw~yl zWT0gw8RePzf%i8X&twiB|LF0bI@CYE{x1PI;Ylr4RJzU#Zc0j!c07g&q7=_eSd(sH z9VKChd?}^52IKcMqolAWiQH;HSp1Ploa$t zQhg|2sK;%Eb!By`)j9G1w?>`Wt6IK3gB}~uoue(MlRiIoZ#d{pgJZ8b{^{HO8)@%= zX)og3`*D5v1g;*Lz8@Sm(Q|&}PUytlb@Q_dzKFOzKK!Z_&?GO4+JO-)iPH=fs{(`& zZ9{oNn~LUZaeN!>i9p*0N^sHye8nw4xSi!REaP@@^Jy66|)Y9_AFoLlrlkg(42 zVq2J??I(+1*BcSKsTyO7LCho{8tVQm1b>*GQ*H~Mn71Lhy`alw%;D@CU^0)5Ng{cHz@LS7QZ o8uGHYt7)tmZjae5ge5$b`e_;HIklOseoIbqeod19BU-8d00{dbSpWb4 literal 0 HcmV?d00001 diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..268c210 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,325 @@ + + + + InAppPurchaseLib Reference + + + + + + + + + + + + + + + +
                      +

                      + + InAppPurchaseLib documentation + + (77% documented) +

                      + +

                      +

                      + +
                      +

                      + +

                      + + + View on GitHub + +

                      + +
                      + + + +
                      + +
                      + +
                      +
                      + +

                      + +

                      + +
                      +

                      An easy-to-use Swift library for In-App Purchases, using Fovea.Billing for receipts validation.

                      +
                      +

                      Features

                      + +
                        +
                      • ✅ Purchase a product
                      • +
                      • ✅ Restore purchased products
                      • +
                      • ✅ Verify transactions with the App Store on Fovea.Billing server
                      • +
                      • ✅ Handle and notify payment transaction states
                      • +
                      • ✅ Retreive products information from the App Store
                      • +
                      • ✅ Support all product types (consumable, non-consumable, auto-renewable subscription, non-renewing subscription)
                      • +
                      • ✅ Status of purchases available when offline
                      • +
                      • ✅ Server integration with a Webhook
                      • +
                      +

                      Getting Started

                      + +

                      If you haven’t already, I highly recommend your read the Overview and Preparing section of Apple’s In-App Purchase official documentation.

                      +

                      Installation

                      + +
                        +
                      • Select your project in Xcode
                      • +
                      • Go to the section Swift Package
                      • +
                      • Click on (+) Add Package Dependency
                      • +
                      • Copy the Git URL: https://github.com/iridescent-dev/iap-swift-lib.git
                      • +
                      • Click on Next > Next
                      • +
                      • Make sure your project is selected in Add to target
                      • +
                      • Click on Finish
                      • +
                      + +

                      Note: You have to import InAppPurchaseLib wherever you use the library.

                      +

                      Micro Example

                      +
                      /** AppDelegate.swift */
                      +import UIKit
                      +import InAppPurchaseLib
                      +
                      +@UIApplicationMain
                      +class AppDelegate: UIResponder, UIApplicationDelegate {
                      +  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
                      +    // Initialize the library
                      +    InAppPurchase.initialize(
                      +      iapProducts: [
                      +        IAPProduct(productIdentifier: "my_product", productType: .nonConsumable)
                      +      ],
                      +      validatorUrlString: "https://validator.fovea.cc/v1/validate?appName=demo&apiKey=12345678"
                      +    )
                      +    return true
                      +  }
                      +
                      +  func applicationWillTerminate(_ application: UIApplication) {
                      +    // clean
                      +    InAppPurchase.stop()
                      +  }
                      +}
                      +
                      +
                      /** ViewController.swift */
                      +import UIKit
                      +import StoreKit
                      +import InAppPurchaseLib
                      +
                      +class ViewController: UIViewController {
                      +  private var loaderView = LoaderView()
                      +  @IBOutlet weak var statusLabel: UILabel!
                      +  @IBOutlet weak var productTitleLabel: UILabel!
                      +  @IBOutlet weak var productDescriptionLabel: UILabel!
                      +  @IBOutlet weak var purchaseButton: UIButton!
                      +  @IBOutlet weak var restorePurchasesButton: UIButton!
                      +
                      +  override func viewDidLoad() {
                      +    super.viewDidLoad()
                      +    // Add action for purchases and restore butons.
                      +    purchaseButton.addTarget(self, action: #selector(self.purchase), for: .touchUpInside)
                      +    restorePurchasesButton.addTarget(self, action: #selector(self.restorePurchases), for: .touchUpInside)
                      +  }
                      +
                      +  override func viewWillAppear(_ animated: Bool) {
                      +    self.refreshView()
                      +    InAppPurchase.refresh(callback: { _ in
                      +      self.refreshView()
                      +    })
                      +  }
                      +
                      +  func refreshView() {
                      +    guard let product: SKProduct = InAppPurchase.getProductBy(identifier: "my_product") else {
                      +      self.productTitleLabel.text = "Product unavailable"
                      +      return
                      +    }
                      +    // Display product information.
                      +    productTitleLabel.text = product.localizedTitle
                      +    productDescriptionLabel.text = product.localizedDescription
                      +    purchaseButton.setTitle(product.localizedPrice, for: .normal)
                      +
                      +    // Disable the button if the product has already been purchased.
                      +    if InAppPurchase.hasActivePurchase(for: "my_product") {
                      +      statusLabel.text = "OWNED"
                      +      purchaseButton.isPointerInteractionEnabled = false
                      +    }
                      +  }
                      +
                      +  @IBAction func purchase(_ sender: Any) {
                      +    self.loaderView.show()
                      +    InAppPurchase.purchase(
                      +      productIdentifier: "my_product",
                      +      callback: { result in
                      +        self.loaderView.hide()
                      +      })
                      +  }
                      +
                      +  @IBAction func restorePurchases(_ sender: Any) {
                      +    self.loaderView.show()
                      +    InAppPurchase.restorePurchases(callback: { result in
                      +      self.loaderView.hide()
                      +    })
                      +  }
                      +}
                      +
                      +

                      Documentation

                      + + + +

                      See also:

                      + + +

                      Xcode Demo Project

                      + +

                      Do not hesitate to check the demo project available on here: iap-swift-lib-demo.

                      +

                      Coding

                      + +

                      Generate the documentation, using Jazzy by running the following command:

                      +
                      jazzy
                      +
                      +

                      Troubleshooting

                      + +

                      Common issues are covered here: https://github.com/iridescent-dev/iap-swift-lib/wiki/Troubleshooting

                      +

                      License

                      + +

                      InAppPurchaseLib is open-sourced library licensed under the MIT License. See LICENSE for details.

                      + +
                      +
                      + + +
                      +
                      + + + + diff --git a/docs/initialization.html b/docs/initialization.html new file mode 100644 index 0000000..a87c6e0 --- /dev/null +++ b/docs/initialization.html @@ -0,0 +1,227 @@ + + + + Initialization Reference + + + + + + + + + + + + + + + +
                      +

                      + + InAppPurchaseLib documentation + + (77% documented) +

                      + +

                      +

                      + +
                      +

                      + +

                      + + + View on GitHub + +

                      + +
                      + + + +
                      + +
                      + +
                      +
                      + +

                      Initialization

                      + +

                      Before everything else the library must be initialized. This has to happen as soon as possible. A good way is to call the InAppPurchase.initialize() method when the application did finish launching. In the background, this will load your products and refresh the status of purchases and subscriptions.

                      + +

                      InAppPurchase.initialize() requires the following arguments:

                      + +
                        +
                      • iapProducts - An array of IAPProduct
                      • +
                      • validatorUrlString - The validator url retrieved from Fovea
                      • +
                      + +

                      Each IAPProduct contains the following fields:

                      + +
                        +
                      • productIdentifier - The product unique identifier
                      • +
                      • productType - The IAPProductType (consumable, nonConsumable, nonRenewingSubscription or autoRenewableSubscription)
                      • +
                      + +

                      Example:

                      + +

                      A good place is generally in your application delegate’s didFinishLaunchingWithOptions function, like below:

                      +
                      import InAppPurchaseLib
                      +
                      +class AppDelegate: UIResponder, UIApplicationDelegate, IAPPurchaseDelegate {
                      +  ...
                      +  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
                      +    InAppPurchase.initialize(
                      +      iapProducts: [
                      +        IAPProduct(productIdentifier: "monthly_plan", productType: .autoRenewableSubscription),
                      +        IAPProduct(productIdentifier: "yearly_plan",  productType: .autoRenewableSubscription),
                      +        IAPProduct(productIdentifier: "disable_ads",  productType: .nonConsumable)
                      +      ],
                      +      validatorUrlString: "https://validator.fovea.cc/v1/validate?appName=demo&apiKey=12345678")
                      +  }
                      +
                      +  func productPurchased(productIdentifier: String) {
                      +    // ... process purchase (we'll see that later)
                      +  }
                      +}
                      +
                      + +

                      You should also call the stop method when the application will terminate, for proper cleanup.

                      +
                        func applicationWillTerminate(_ application: UIApplication) {
                      +    InAppPurchase.stop()
                      +  }
                      +
                      + +

                      For more advanced use cases, in particular when you have implemented user login, you’ll have to make some adjustments. We’ll learn more about this in the Server integration section.

                      + +

                      Tip: If initialization was successful, you should see a new receipt validation event in Fovea’s Dashboard.

                      + +
                      +
                      + + +
                      +
                      + + + + diff --git a/docs/installation.html b/docs/installation.html new file mode 100644 index 0000000..637dbb0 --- /dev/null +++ b/docs/installation.html @@ -0,0 +1,194 @@ + + + + Installation Reference + + + + + + + + + + + + + + + +
                      +

                      + + InAppPurchaseLib documentation + + (77% documented) +

                      + +

                      +

                      + +
                      +

                      + +

                      + + + View on GitHub + +

                      + +
                      + + + +
                      + +
                      + +
                      +
                      + +

                      Installation

                      + +

                      + +

                      + +
                        +
                      • Select your project in Xcode
                      • +
                      • Go to the section Swift Package
                      • +
                      • Click on (+) Add Package Dependency
                      • +
                      • Copy the Git URL: https://github.com/iridescent-dev/iap-swift-lib.git
                      • +
                      • Click on Next > Next
                      • +
                      • Make sure your project is selected in Add to target
                      • +
                      • Click on Finish
                      • +
                      + +

                      Note: You have to import InAppPurchaseLib wherever you use the library.

                      + +
                      +
                      + + +
                      +
                      + + + + diff --git a/docs/jazzy/Classes.html b/docs/jazzy/Classes.html deleted file mode 100644 index d33eafb..0000000 --- a/docs/jazzy/Classes.html +++ /dev/null @@ -1,205 +0,0 @@ - - - - Classes Reference - - - - - - - - - - -
                      -
                      -

                      InAppPurchaseLib (52% documented)

                      -

                      View on GitHub

                      -
                      -
                      -
                      - -
                      -
                      - -
                      -
                      -
                      -

                      Classes

                      -

                      The following classes are available globally.

                      - -
                      -
                      -
                      - -
                      -
                      -
                      - - -
                      - -

                      The protocol that you must adopt.

                      -

                      -
                      -
                      - -
                      -
                      -
                      - -
                      -
                      - - - diff --git a/docs/jazzy/Enums.html b/docs/jazzy/Enums.html deleted file mode 100644 index b724f25..0000000 --- a/docs/jazzy/Enums.html +++ /dev/null @@ -1,276 +0,0 @@ - - - - Enumerations Reference - - - - - - - - - - -
                      -
                      -

                      InAppPurchaseLib (52% documented)

                      -

                      View on GitHub

                      -
                      -
                      -
                      - -
                      -
                      - -
                      -
                      -
                      -

                      Enumerations

                      -

                      The following enumerations are available globally.

                      - -
                      -
                      -
                      -
                        -
                      • - -
                        -
                        -
                        -
                        -
                        -

                        Undocumented

                        - - See more -
                        -
                        -

                        Declaration

                        -
                        -

                        Swift

                        -
                        public enum IAPPurchaseResultState
                        - -
                        -
                        -
                        -
                        -
                      • -
                      • -
                        - - - - IAPRefreshResultState - -
                        -
                        -
                        -
                        -
                        -
                        -

                        Undocumented

                        - - See more -
                        -
                        -

                        Declaration

                        -
                        -

                        Swift

                        -
                        public enum IAPRefreshResultState
                        - -
                        -
                        -
                        -
                        -
                      • -
                      • -
                        - - - - IAPErrorCode - -
                        -
                        -
                        -
                        -
                        -
                        -

                        Undocumented

                        - - See more -
                        -
                        -

                        Declaration

                        -
                        -

                        Swift

                        -
                        public enum IAPErrorCode
                        - -
                        -
                        -
                        -
                        -
                      • -
                      • -
                        - - - - IAPProductType - -
                        -
                        -
                        -
                        -
                        -
                        -

                        Undocumented

                        - - See more -
                        -
                        -

                        Declaration

                        -
                        -

                        Swift

                        -
                        public enum IAPProductType
                        - -
                        -
                        -
                        -
                        -
                      • -
                      • -
                        - - - - IAPPeriodFormat - -
                        -
                        -
                        -
                        -
                        -
                        -

                        Undocumented

                        - - See more -
                        -
                        -

                        Declaration

                        -
                        -

                        Swift

                        -
                        public enum IAPPeriodFormat
                        - -
                        -
                        -
                        -
                        -
                      • -
                      -
                      -
                      -
                      - -
                      -
                      - - - diff --git a/docs/jazzy/Enums/IAPPeriodFormat.html b/docs/jazzy/Enums/IAPPeriodFormat.html deleted file mode 100644 index b5080fe..0000000 --- a/docs/jazzy/Enums/IAPPeriodFormat.html +++ /dev/null @@ -1,196 +0,0 @@ - - - - IAPPeriodFormat Enumeration Reference - - - - - - - - - - -
                      -
                      -

                      InAppPurchaseLib (52% documented)

                      -

                      View on GitHub

                      -
                      -
                      -
                      - -
                      -
                      - -
                      -
                      -
                      -

                      IAPPeriodFormat

                      -
                      -
                      -
                      public enum IAPPeriodFormat
                      - -
                      -
                      -

                      Undocumented

                      - -
                      -
                      -
                      -
                        -
                      • -
                        - - - - short - -
                        -
                        -
                        -
                        -
                        -
                        -

                        Undocumented

                        - -
                        -
                        -

                        Declaration

                        -
                        -

                        Swift

                        -
                        case short
                        - -
                        -
                        -
                        -
                        -
                      • -
                      • -
                        - - - - long - -
                        -
                        -
                        -
                        -
                        -
                        -

                        Undocumented

                        - -
                        -
                        -

                        Declaration

                        -
                        -

                        Swift

                        -
                        case long
                        - -
                        -
                        -
                        -
                        -
                      • -
                      -
                      -
                      -
                      - -
                      -
                      - - - diff --git a/docs/jazzy/Enums/IAPRefreshResultState.html b/docs/jazzy/Enums/IAPRefreshResultState.html deleted file mode 100644 index ce460b0..0000000 --- a/docs/jazzy/Enums/IAPRefreshResultState.html +++ /dev/null @@ -1,223 +0,0 @@ - - - - IAPRefreshResultState Enumeration Reference - - - - - - - - - - -
                      -
                      -

                      InAppPurchaseLib (52% documented)

                      -

                      View on GitHub

                      -
                      -
                      -
                      - -
                      -
                      - -
                      -
                      -
                      -

                      IAPRefreshResultState

                      -
                      -
                      -
                      public enum IAPRefreshResultState
                      - -
                      -
                      -

                      Undocumented

                      - -
                      -
                      -
                      -
                        -
                      • -
                        - - - - succeeded - -
                        -
                        -
                        -
                        -
                        -
                        -

                        Undocumented

                        - -
                        -
                        -

                        Declaration

                        -
                        -

                        Swift

                        -
                        case succeeded
                        - -
                        -
                        -
                        -
                        -
                      • -
                      • -
                        - - - - failed - -
                        -
                        -
                        -
                        -
                        -
                        -

                        Undocumented

                        - -
                        -
                        -

                        Declaration

                        -
                        -

                        Swift

                        -
                        case failed
                        - -
                        -
                        -
                        -
                        -
                      • -
                      • -
                        - - - - skipped - -
                        -
                        -
                        -
                        -
                        -
                        -

                        Undocumented

                        - -
                        -
                        -

                        Declaration

                        -
                        -

                        Swift

                        -
                        case skipped
                        - -
                        -
                        -
                        -
                        -
                      • -
                      -
                      -
                      -
                      - -
                      -
                      - - - diff --git a/docs/jazzy/Extensions.html b/docs/jazzy/Extensions.html deleted file mode 100644 index 922e9fa..0000000 --- a/docs/jazzy/Extensions.html +++ /dev/null @@ -1,163 +0,0 @@ - - - - Extensions Reference - - - - - - - - - - -
                      -
                      -

                      InAppPurchaseLib (52% documented)

                      -

                      View on GitHub

                      -
                      -
                      -
                      - -
                      -
                      - -
                      -
                      -
                      -

                      Extensions

                      -

                      The following extensions are available globally.

                      - -
                      -
                      -
                      -
                        -
                      • -
                        - - - - SKProduct - -
                        -
                        -
                        -
                        -
                        -
                        - - See more -
                        -
                        -

                        Declaration

                        -
                        -

                        Swift

                        -
                        extension SKProduct
                        - -
                        -
                        -
                        -
                        -
                      • -
                      -
                      -
                      -
                      - -
                      -
                      - - - diff --git a/docs/jazzy/Protocols.html b/docs/jazzy/Protocols.html deleted file mode 100644 index 33bac01..0000000 --- a/docs/jazzy/Protocols.html +++ /dev/null @@ -1,233 +0,0 @@ - - - - Protocols Reference - - - - - - - - - - -
                      -
                      -

                      InAppPurchaseLib (52% documented)

                      -

                      View on GitHub

                      -
                      -
                      -
                      - -
                      -
                      - -
                      -
                      -
                      -

                      Protocols

                      -

                      The following protocols are available globally.

                      - -
                      -
                      -
                      -
                        -
                      • -
                        - - - - IAPErrorProtocol - -
                        -
                        -
                        -
                        -
                        -
                        -

                        Undocumented

                        - - See more -
                        -
                        -

                        Declaration

                        -
                        -

                        Swift

                        -
                        public protocol IAPErrorProtocol : LocalizedError
                        - -
                        -
                        -
                        -
                        -
                      • -
                      • -
                        - - - - InAppPurchaseLib - -
                        -
                        -
                        -
                        -
                        -
                        -

                        The protocol that InAppPurchase` adopts.

                        - - See more -
                        -
                        -

                        Declaration

                        -
                        -

                        Swift

                        -
                        public protocol InAppPurchaseLib
                        - -
                        -
                        -
                        -
                        -
                      • -
                      -
                      -
                      -
                      - - -
                      - -

                      The protocol that you must adopt.

                      -

                      -
                      -
                      -
                        -
                      • -
                        - - - - IAPPurchaseDelegate - -
                        -
                        -
                        -
                        -
                        -
                        -

                        The protocol that you must adopt if you have consumable and/or nonRenewingSubscription products.

                        - - See more -
                        -
                        -

                        Declaration

                        -
                        -

                        Swift

                        -
                        public protocol IAPPurchaseDelegate
                        - -
                        -
                        -
                        -
                        -
                      • -
                      -
                      -
                      -
                      - -
                      -
                      - - - diff --git a/docs/jazzy/Protocols/IAPErrorProtocol.html b/docs/jazzy/Protocols/IAPErrorProtocol.html deleted file mode 100644 index 39a040c..0000000 --- a/docs/jazzy/Protocols/IAPErrorProtocol.html +++ /dev/null @@ -1,169 +0,0 @@ - - - - IAPErrorProtocol Protocol Reference - - - - - - - - - - -
                      -
                      -

                      InAppPurchaseLib (52% documented)

                      -

                      View on GitHub

                      -
                      -
                      -
                      - -
                      -
                      - -
                      -
                      -
                      -

                      IAPErrorProtocol

                      -
                      -
                      -
                      public protocol IAPErrorProtocol : LocalizedError
                      - -
                      -
                      -

                      Undocumented

                      - -
                      -
                      -
                      -
                        -
                      • -
                        - - - - code - -
                        -
                        -
                        -
                        -
                        -
                        -

                        Undocumented

                        - -
                        -
                        -

                        Declaration

                        -
                        -

                        Swift

                        -
                        var code: IAPErrorCode { get }
                        - -
                        -
                        -
                        -
                        -
                      • -
                      -
                      -
                      -
                      - -
                      -
                      - - - diff --git a/docs/jazzy/Protocols/IAPPurchaseDelegate.html b/docs/jazzy/Protocols/IAPPurchaseDelegate.html deleted file mode 100644 index ced2b91..0000000 --- a/docs/jazzy/Protocols/IAPPurchaseDelegate.html +++ /dev/null @@ -1,193 +0,0 @@ - - - - IAPPurchaseDelegate Protocol Reference - - - - - - - - - - -
                      -
                      -

                      InAppPurchaseLib (52% documented)

                      -

                      View on GitHub

                      -
                      -
                      -
                      - -
                      -
                      - -
                      -
                      -
                      -

                      IAPPurchaseDelegate

                      -
                      -
                      -
                      public protocol IAPPurchaseDelegate
                      - -
                      -
                      -

                      The protocol that you must adopt if you have consumable and/or nonRenewingSubscription products.

                      - -
                      -
                      -
                      -
                        -
                      • - -
                        -
                        -
                        -
                        -
                        -

                        Called when a product is newly purchased, updated or restored.

                        -
                        -

                        Important

                        -

                        You have to acknowledge delivery of the (virtual) item to finalize the transaction. Then you have to call InAppPurchase.finishTransactions(for: productIdentifier)as soon as you have delivered the product.

                        - -
                        - -
                        -
                        -

                        Declaration

                        -
                        -

                        Swift

                        -
                        func productPurchased(productIdentifier: String)
                        - -
                        -
                        -
                        -

                        Parameters

                        - - - - - - - -
                        - - productIdentifier - - -
                        -

                        The identifier of the product.

                        -
                        -
                        -
                        -
                        -
                        -
                      • -
                      -
                      -
                      -
                      - -
                      -
                      - - - diff --git a/docs/jazzy/Structs.html b/docs/jazzy/Structs.html deleted file mode 100644 index 7b7bec2..0000000 --- a/docs/jazzy/Structs.html +++ /dev/null @@ -1,248 +0,0 @@ - - - - Structures Reference - - - - - - - - - - -
                      -
                      -

                      InAppPurchaseLib (52% documented)

                      -

                      View on GitHub

                      -
                      -
                      -
                      - -
                      -
                      - -
                      -
                      -
                      -

                      Structures

                      -

                      The following structures are available globally.

                      - -
                      -
                      -
                      -
                        -
                      • -
                        - - - - IAPPurchaseResult - -
                        -
                        -
                        -
                        -
                        -
                        -

                        Undocumented

                        - - See more -
                        -
                        -

                        Declaration

                        -
                        -

                        Swift

                        -
                        public struct IAPPurchaseResult
                        - -
                        -
                        -
                        -
                        -
                      • -
                      • -
                        - - - - IAPRefreshResult - -
                        -
                        -
                        -
                        -
                        -
                        -

                        Undocumented

                        - - See more -
                        -
                        -

                        Declaration

                        -
                        -

                        Swift

                        -
                        public struct IAPRefreshResult
                        - -
                        -
                        -
                        -
                        -
                      • -
                      • -
                        - - - - IAPError - -
                        -
                        -
                        -
                        -
                        -
                        -

                        Undocumented

                        - - See more -
                        -
                        -

                        Declaration

                        -
                        -

                        Swift

                        -
                        public struct IAPError : IAPErrorProtocol
                        - -
                        -
                        -
                        -
                        -
                      • -
                      • -
                        - - - - IAPProduct - -
                        -
                        -
                        -
                        -
                        -
                        -

                        Undocumented

                        - - See more -
                        -
                        -

                        Declaration

                        -
                        -

                        Swift

                        -
                        public struct IAPProduct
                        - -
                        -
                        -
                        -
                        -
                      • -
                      -
                      -
                      -
                      - -
                      -
                      - - - diff --git a/docs/jazzy/Structs/IAPError.html b/docs/jazzy/Structs/IAPError.html deleted file mode 100644 index b1e121c..0000000 --- a/docs/jazzy/Structs/IAPError.html +++ /dev/null @@ -1,196 +0,0 @@ - - - - IAPError Structure Reference - - - - - - - - - - -
                      -
                      -

                      InAppPurchaseLib (52% documented)

                      -

                      View on GitHub

                      -
                      -
                      -
                      - -
                      -
                      - -
                      -
                      -
                      -

                      IAPError

                      -
                      -
                      -
                      public struct IAPError : IAPErrorProtocol
                      - -
                      -
                      -

                      Undocumented

                      - -
                      -
                      -
                      -
                        -
                      • -
                        - - - - code - -
                        -
                        -
                        -
                        -
                        -
                        -

                        Undocumented

                        - -
                        -
                        -

                        Declaration

                        -
                        -

                        Swift

                        -
                        public var code: IAPErrorCode
                        - -
                        -
                        -
                        -
                        -
                      • -
                      • -
                        - - - - localizedDescription - -
                        -
                        -
                        -
                        -
                        -
                        -

                        Undocumented

                        - -
                        -
                        -

                        Declaration

                        -
                        -

                        Swift

                        -
                        public var localizedDescription: String { get }
                        - -
                        -
                        -
                        -
                        -
                      • -
                      -
                      -
                      -
                      - -
                      -
                      - - - diff --git a/docs/jazzy/Typealiases.html b/docs/jazzy/Typealiases.html deleted file mode 100644 index 17dd0f5..0000000 --- a/docs/jazzy/Typealiases.html +++ /dev/null @@ -1,190 +0,0 @@ - - - - Type Aliases Reference - - - - - - - - - - -
                      -
                      -

                      InAppPurchaseLib (52% documented)

                      -

                      View on GitHub

                      -
                      -
                      -
                      - -
                      -
                      - -
                      -
                      -
                      -

                      Type Aliases

                      -

                      The following type aliases are available globally.

                      - -
                      -
                      -
                      - -
                      -
                      -
                      - -
                      -
                      - - - diff --git a/docs/jazzy/css/jazzy.css b/docs/jazzy/css/jazzy.css deleted file mode 100644 index c3090c0..0000000 --- a/docs/jazzy/css/jazzy.css +++ /dev/null @@ -1,374 +0,0 @@ -html, body, div, span, h1, h3, h4, p, a, code, em, img, ul, li, table, tbody, tr, td { - background: transparent; - border: 0; - margin: 0; - outline: 0; - padding: 0; - vertical-align: baseline; } - -body { - background-color: #f2f2f2; - font-family: Helvetica, freesans, Arial, sans-serif; - font-size: 14px; - -webkit-font-smoothing: subpixel-antialiased; - word-wrap: break-word; } - -h1, h2, h3 { - margin-top: 0.8em; - margin-bottom: 0.3em; - font-weight: 100; - color: black; } - -h1 { - font-size: 2.5em; } - -h2 { - font-size: 2em; - border-bottom: 1px solid #e2e2e2; } - -h4 { - font-size: 13px; - line-height: 1.5; - margin-top: 21px; } - -h5 { - font-size: 1.1em; } - -h6 { - font-size: 1.1em; - color: #777; } - -.section-name { - color: gray; - display: block; - font-family: Helvetica; - font-size: 22px; - font-weight: 100; - margin-bottom: 15px; } - -pre, code { - font: 0.95em Menlo, monospace; - color: #777; - word-wrap: normal; } - -p code, li code { - background-color: #eee; - padding: 2px 4px; - border-radius: 4px; } - -a { - color: #0088cc; - text-decoration: none; } - -ul { - padding-left: 15px; } - -li { - line-height: 1.8em; } - -img { - max-width: 100%; } - -blockquote { - margin-left: 0; - padding: 0 10px; - border-left: 4px solid #ccc; } - -.content-wrapper { - margin: 0 auto; - width: 980px; } - -header { - font-size: 0.85em; - line-height: 26px; - background-color: #414141; - position: fixed; - width: 100%; - z-index: 2; } - header img { - padding-right: 6px; - vertical-align: -4px; - height: 16px; } - header a { - color: #fff; } - header p { - float: left; - color: #999; } - header .header-right { - float: right; - margin-left: 16px; } - -#breadcrumbs { - background-color: #f2f2f2; - height: 27px; - padding-top: 17px; - position: fixed; - width: 100%; - z-index: 2; - margin-top: 26px; } - #breadcrumbs #carat { - height: 10px; - margin: 0 5px; } - -.sidebar { - background-color: #f9f9f9; - border: 1px solid #e2e2e2; - overflow-y: auto; - overflow-x: hidden; - position: fixed; - top: 70px; - bottom: 0; - width: 230px; - word-wrap: normal; } - -.nav-groups { - list-style-type: none; - background: #fff; - padding-left: 0; } - -.nav-group-name { - border-bottom: 1px solid #e2e2e2; - font-size: 1.1em; - font-weight: 100; - padding: 15px 0 15px 20px; } - .nav-group-name > a { - color: #333; } - -.nav-group-tasks { - margin-top: 5px; } - -.nav-group-task { - font-size: 0.9em; - list-style-type: none; - white-space: nowrap; } - .nav-group-task a { - color: #888; } - -.main-content { - background-color: #fff; - border: 1px solid #e2e2e2; - margin-left: 246px; - position: absolute; - overflow: hidden; - padding-bottom: 20px; - top: 70px; - width: 734px; } - .main-content p, .main-content a, .main-content code, .main-content em, .main-content ul, .main-content table, .main-content blockquote { - margin-bottom: 1em; } - .main-content p { - line-height: 1.8em; } - .main-content section .section:first-child { - margin-top: 0; - padding-top: 0; } - .main-content section .task-group-section .task-group:first-of-type { - padding-top: 10px; } - .main-content section .task-group-section .task-group:first-of-type .section-name { - padding-top: 15px; } - .main-content section .heading:before { - content: ""; - display: block; - padding-top: 70px; - margin: -70px 0 0; } - .main-content .section-name p { - margin-bottom: inherit; - line-height: inherit; } - .main-content .section-name code { - background-color: inherit; - padding: inherit; - color: inherit; } - -.section { - padding: 0 25px; } - -.highlight { - background-color: #eee; - padding: 10px 12px; - border: 1px solid #e2e2e2; - border-radius: 4px; - overflow-x: auto; } - -.declaration .highlight { - overflow-x: initial; - padding: 0 40px 40px 0; - margin-bottom: -25px; - background-color: transparent; - border: none; } - -.section-name { - margin: 0; - margin-left: 18px; } - -.task-group-section { - padding-left: 6px; - border-top: 1px solid #e2e2e2; } - -.task-group { - padding-top: 0px; } - -.task-name-container a[name]:before { - content: ""; - display: block; - padding-top: 70px; - margin: -70px 0 0; } - -.section-name-container { - position: relative; - display: inline-block; } - .section-name-container .section-name-link { - position: absolute; - top: 0; - left: 0; - bottom: 0; - right: 0; - margin-bottom: 0; } - .section-name-container .section-name { - position: relative; - pointer-events: none; - z-index: 1; } - .section-name-container .section-name a { - pointer-events: auto; } - -.item { - padding-top: 8px; - width: 100%; - list-style-type: none; } - .item a[name]:before { - content: ""; - display: block; - padding-top: 70px; - margin: -70px 0 0; } - .item code { - background-color: transparent; - padding: 0; } - .item .token, .item .direct-link { - display: inline-block; - text-indent: -20px; - padding-left: 3px; - margin-left: 35px; - font-size: 11.9px; - transition: all 300ms; } - .item .token-open { - margin-left: 20px; } - .item .discouraged { - text-decoration: line-through; } - .item .declaration-note { - font-size: .85em; - color: gray; - font-style: italic; } - -.pointer-container { - border-bottom: 1px solid #e2e2e2; - left: -23px; - padding-bottom: 13px; - position: relative; - width: 110%; } - -.pointer { - background: #f9f9f9; - border-left: 1px solid #e2e2e2; - border-top: 1px solid #e2e2e2; - height: 12px; - left: 21px; - top: -7px; - -webkit-transform: rotate(45deg); - -moz-transform: rotate(45deg); - -o-transform: rotate(45deg); - transform: rotate(45deg); - position: absolute; - width: 12px; } - -.height-container { - display: none; - left: -25px; - padding: 0 25px; - position: relative; - width: 100%; - overflow: hidden; } - .height-container .section { - background: #f9f9f9; - border-bottom: 1px solid #e2e2e2; - left: -25px; - position: relative; - width: 100%; - padding-top: 10px; - padding-bottom: 5px; } - -.aside, .language { - padding: 6px 12px; - margin: 12px 0; - border-left: 5px solid #dddddd; - overflow-y: hidden; } - .aside .aside-title, .language .aside-title { - font-size: 9px; - letter-spacing: 2px; - text-transform: uppercase; - padding-bottom: 0; - margin: 0; - color: #aaa; - -webkit-user-select: none; } - .aside p:last-child, .language p:last-child { - margin-bottom: 0; } - -.language { - border-left: 5px solid #cde9f4; } - .language .aside-title { - color: #4b8afb; } - -.aside-warning, .aside-deprecated, .aside-unavailable { - border-left: 5px solid #ff6666; } - .aside-warning .aside-title, .aside-deprecated .aside-title, .aside-unavailable .aside-title { - color: #ff0000; } - -.graybox { - border-collapse: collapse; - width: 100%; } - .graybox p { - margin: 0; - word-break: break-word; - min-width: 50px; } - .graybox td { - border: 1px solid #e2e2e2; - padding: 5px 25px 5px 10px; - vertical-align: middle; } - .graybox tr td:first-of-type { - text-align: right; - padding: 7px; - vertical-align: top; - word-break: normal; - width: 40px; } - -.slightly-smaller { - font-size: 0.9em; } - -#footer { - position: relative; - top: 10px; - bottom: 0px; - margin-left: 25px; } - #footer p { - margin: 0; - color: #aaa; - font-size: 0.8em; } - -html.dash header, html.dash #breadcrumbs, html.dash .sidebar { - display: none; } - -html.dash .main-content { - width: 980px; - margin-left: 0; - border: none; - width: 100%; - top: 0; - padding-bottom: 0; } - -html.dash .height-container { - display: block; } - -html.dash .item .token { - margin-left: 0; } - -html.dash .content-wrapper { - width: auto; } - -html.dash #footer { - position: static; } diff --git a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Classes.html b/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Classes.html deleted file mode 100644 index d33eafb..0000000 --- a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Classes.html +++ /dev/null @@ -1,205 +0,0 @@ - - - - Classes Reference - - - - - - - - - - -
                      -
                      -

                      InAppPurchaseLib (52% documented)

                      -

                      View on GitHub

                      -
                      -
                      -
                      - -
                      -
                      - -
                      -
                      -
                      -

                      Classes

                      -

                      The following classes are available globally.

                      - -
                      -
                      -
                      - -
                      -
                      -
                      - - -
                      - -

                      The protocol that you must adopt.

                      -

                      -
                      -
                      - -
                      -
                      -
                      - -
                      -
                      - - - diff --git a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Enums.html b/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Enums.html deleted file mode 100644 index b724f25..0000000 --- a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Enums.html +++ /dev/null @@ -1,276 +0,0 @@ - - - - Enumerations Reference - - - - - - - - - - -
                      -
                      -

                      InAppPurchaseLib (52% documented)

                      -

                      View on GitHub

                      -
                      -
                      -
                      - -
                      -
                      - -
                      -
                      -
                      -

                      Enumerations

                      -

                      The following enumerations are available globally.

                      - -
                      -
                      -
                      -
                        -
                      • - -
                        -
                        -
                        -
                        -
                        -

                        Undocumented

                        - - See more -
                        -
                        -

                        Declaration

                        -
                        -

                        Swift

                        -
                        public enum IAPPurchaseResultState
                        - -
                        -
                        -
                        -
                        -
                      • -
                      • -
                        - - - - IAPRefreshResultState - -
                        -
                        -
                        -
                        -
                        -
                        -

                        Undocumented

                        - - See more -
                        -
                        -

                        Declaration

                        -
                        -

                        Swift

                        -
                        public enum IAPRefreshResultState
                        - -
                        -
                        -
                        -
                        -
                      • -
                      • -
                        - - - - IAPErrorCode - -
                        -
                        -
                        -
                        -
                        -
                        -

                        Undocumented

                        - - See more -
                        -
                        -

                        Declaration

                        -
                        -

                        Swift

                        -
                        public enum IAPErrorCode
                        - -
                        -
                        -
                        -
                        -
                      • -
                      • -
                        - - - - IAPProductType - -
                        -
                        -
                        -
                        -
                        -
                        -

                        Undocumented

                        - - See more -
                        -
                        -

                        Declaration

                        -
                        -

                        Swift

                        -
                        public enum IAPProductType
                        - -
                        -
                        -
                        -
                        -
                      • -
                      • -
                        - - - - IAPPeriodFormat - -
                        -
                        -
                        -
                        -
                        -
                        -

                        Undocumented

                        - - See more -
                        -
                        -

                        Declaration

                        -
                        -

                        Swift

                        -
                        public enum IAPPeriodFormat
                        - -
                        -
                        -
                        -
                        -
                      • -
                      -
                      -
                      -
                      - -
                      -
                      - - - diff --git a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Enums/IAPPeriodFormat.html b/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Enums/IAPPeriodFormat.html deleted file mode 100644 index b5080fe..0000000 --- a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Enums/IAPPeriodFormat.html +++ /dev/null @@ -1,196 +0,0 @@ - - - - IAPPeriodFormat Enumeration Reference - - - - - - - - - - -
                      -
                      -

                      InAppPurchaseLib (52% documented)

                      -

                      View on GitHub

                      -
                      -
                      -
                      - -
                      -
                      - -
                      -
                      -
                      -

                      IAPPeriodFormat

                      -
                      -
                      -
                      public enum IAPPeriodFormat
                      - -
                      -
                      -

                      Undocumented

                      - -
                      -
                      -
                      -
                        -
                      • -
                        - - - - short - -
                        -
                        -
                        -
                        -
                        -
                        -

                        Undocumented

                        - -
                        -
                        -

                        Declaration

                        -
                        -

                        Swift

                        -
                        case short
                        - -
                        -
                        -
                        -
                        -
                      • -
                      • -
                        - - - - long - -
                        -
                        -
                        -
                        -
                        -
                        -

                        Undocumented

                        - -
                        -
                        -

                        Declaration

                        -
                        -

                        Swift

                        -
                        case long
                        - -
                        -
                        -
                        -
                        -
                      • -
                      -
                      -
                      -
                      - -
                      -
                      - - - diff --git a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Enums/IAPRefreshResultState.html b/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Enums/IAPRefreshResultState.html deleted file mode 100644 index ce460b0..0000000 --- a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Enums/IAPRefreshResultState.html +++ /dev/null @@ -1,223 +0,0 @@ - - - - IAPRefreshResultState Enumeration Reference - - - - - - - - - - -
                      -
                      -

                      InAppPurchaseLib (52% documented)

                      -

                      View on GitHub

                      -
                      -
                      -
                      - -
                      -
                      - -
                      -
                      -
                      -

                      IAPRefreshResultState

                      -
                      -
                      -
                      public enum IAPRefreshResultState
                      - -
                      -
                      -

                      Undocumented

                      - -
                      -
                      -
                      -
                        -
                      • -
                        - - - - succeeded - -
                        -
                        -
                        -
                        -
                        -
                        -

                        Undocumented

                        - -
                        -
                        -

                        Declaration

                        -
                        -

                        Swift

                        -
                        case succeeded
                        - -
                        -
                        -
                        -
                        -
                      • -
                      • -
                        - - - - failed - -
                        -
                        -
                        -
                        -
                        -
                        -

                        Undocumented

                        - -
                        -
                        -

                        Declaration

                        -
                        -

                        Swift

                        -
                        case failed
                        - -
                        -
                        -
                        -
                        -
                      • -
                      • -
                        - - - - skipped - -
                        -
                        -
                        -
                        -
                        -
                        -

                        Undocumented

                        - -
                        -
                        -

                        Declaration

                        -
                        -

                        Swift

                        -
                        case skipped
                        - -
                        -
                        -
                        -
                        -
                      • -
                      -
                      -
                      -
                      - -
                      -
                      - - - diff --git a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Extensions.html b/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Extensions.html deleted file mode 100644 index 922e9fa..0000000 --- a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Extensions.html +++ /dev/null @@ -1,163 +0,0 @@ - - - - Extensions Reference - - - - - - - - - - -
                      -
                      -

                      InAppPurchaseLib (52% documented)

                      -

                      View on GitHub

                      -
                      -
                      -
                      - -
                      -
                      - -
                      -
                      -
                      -

                      Extensions

                      -

                      The following extensions are available globally.

                      - -
                      -
                      -
                      -
                        -
                      • -
                        - - - - SKProduct - -
                        -
                        -
                        -
                        -
                        -
                        - - See more -
                        -
                        -

                        Declaration

                        -
                        -

                        Swift

                        -
                        extension SKProduct
                        - -
                        -
                        -
                        -
                        -
                      • -
                      -
                      -
                      -
                      - -
                      -
                      - - - diff --git a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Protocols.html b/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Protocols.html deleted file mode 100644 index 33bac01..0000000 --- a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Protocols.html +++ /dev/null @@ -1,233 +0,0 @@ - - - - Protocols Reference - - - - - - - - - - -
                      -
                      -

                      InAppPurchaseLib (52% documented)

                      -

                      View on GitHub

                      -
                      -
                      -
                      - -
                      -
                      - -
                      -
                      -
                      -

                      Protocols

                      -

                      The following protocols are available globally.

                      - -
                      -
                      -
                      -
                        -
                      • -
                        - - - - IAPErrorProtocol - -
                        -
                        -
                        -
                        -
                        -
                        -

                        Undocumented

                        - - See more -
                        -
                        -

                        Declaration

                        -
                        -

                        Swift

                        -
                        public protocol IAPErrorProtocol : LocalizedError
                        - -
                        -
                        -
                        -
                        -
                      • -
                      • -
                        - - - - InAppPurchaseLib - -
                        -
                        -
                        -
                        -
                        -
                        -

                        The protocol that InAppPurchase` adopts.

                        - - See more -
                        -
                        -

                        Declaration

                        -
                        -

                        Swift

                        -
                        public protocol InAppPurchaseLib
                        - -
                        -
                        -
                        -
                        -
                      • -
                      -
                      -
                      -
                      - - -
                      - -

                      The protocol that you must adopt.

                      -

                      -
                      -
                      -
                        -
                      • -
                        - - - - IAPPurchaseDelegate - -
                        -
                        -
                        -
                        -
                        -
                        -

                        The protocol that you must adopt if you have consumable and/or nonRenewingSubscription products.

                        - - See more -
                        -
                        -

                        Declaration

                        -
                        -

                        Swift

                        -
                        public protocol IAPPurchaseDelegate
                        - -
                        -
                        -
                        -
                        -
                      • -
                      -
                      -
                      -
                      - -
                      -
                      - - - diff --git a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Protocols/IAPErrorProtocol.html b/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Protocols/IAPErrorProtocol.html deleted file mode 100644 index 39a040c..0000000 --- a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Protocols/IAPErrorProtocol.html +++ /dev/null @@ -1,169 +0,0 @@ - - - - IAPErrorProtocol Protocol Reference - - - - - - - - - - -
                      -
                      -

                      InAppPurchaseLib (52% documented)

                      -

                      View on GitHub

                      -
                      -
                      -
                      - -
                      -
                      - -
                      -
                      -
                      -

                      IAPErrorProtocol

                      -
                      -
                      -
                      public protocol IAPErrorProtocol : LocalizedError
                      - -
                      -
                      -

                      Undocumented

                      - -
                      -
                      -
                      -
                        -
                      • -
                        - - - - code - -
                        -
                        -
                        -
                        -
                        -
                        -

                        Undocumented

                        - -
                        -
                        -

                        Declaration

                        -
                        -

                        Swift

                        -
                        var code: IAPErrorCode { get }
                        - -
                        -
                        -
                        -
                        -
                      • -
                      -
                      -
                      -
                      - -
                      -
                      - - - diff --git a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Protocols/IAPPurchaseDelegate.html b/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Protocols/IAPPurchaseDelegate.html deleted file mode 100644 index ced2b91..0000000 --- a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Protocols/IAPPurchaseDelegate.html +++ /dev/null @@ -1,193 +0,0 @@ - - - - IAPPurchaseDelegate Protocol Reference - - - - - - - - - - -
                      -
                      -

                      InAppPurchaseLib (52% documented)

                      -

                      View on GitHub

                      -
                      -
                      -
                      - -
                      -
                      - -
                      -
                      -
                      -

                      IAPPurchaseDelegate

                      -
                      -
                      -
                      public protocol IAPPurchaseDelegate
                      - -
                      -
                      -

                      The protocol that you must adopt if you have consumable and/or nonRenewingSubscription products.

                      - -
                      -
                      -
                      -
                        -
                      • - -
                        -
                        -
                        -
                        -
                        -

                        Called when a product is newly purchased, updated or restored.

                        -
                        -

                        Important

                        -

                        You have to acknowledge delivery of the (virtual) item to finalize the transaction. Then you have to call InAppPurchase.finishTransactions(for: productIdentifier)as soon as you have delivered the product.

                        - -
                        - -
                        -
                        -

                        Declaration

                        -
                        -

                        Swift

                        -
                        func productPurchased(productIdentifier: String)
                        - -
                        -
                        -
                        -

                        Parameters

                        - - - - - - - -
                        - - productIdentifier - - -
                        -

                        The identifier of the product.

                        -
                        -
                        -
                        -
                        -
                        -
                      • -
                      -
                      -
                      -
                      - -
                      -
                      - - - diff --git a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Structs.html b/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Structs.html deleted file mode 100644 index 7b7bec2..0000000 --- a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Structs.html +++ /dev/null @@ -1,248 +0,0 @@ - - - - Structures Reference - - - - - - - - - - -
                      -
                      -

                      InAppPurchaseLib (52% documented)

                      -

                      View on GitHub

                      -
                      -
                      -
                      - -
                      -
                      - -
                      -
                      -
                      -

                      Structures

                      -

                      The following structures are available globally.

                      - -
                      -
                      -
                      -
                        -
                      • -
                        - - - - IAPPurchaseResult - -
                        -
                        -
                        -
                        -
                        -
                        -

                        Undocumented

                        - - See more -
                        -
                        -

                        Declaration

                        -
                        -

                        Swift

                        -
                        public struct IAPPurchaseResult
                        - -
                        -
                        -
                        -
                        -
                      • -
                      • -
                        - - - - IAPRefreshResult - -
                        -
                        -
                        -
                        -
                        -
                        -

                        Undocumented

                        - - See more -
                        -
                        -

                        Declaration

                        -
                        -

                        Swift

                        -
                        public struct IAPRefreshResult
                        - -
                        -
                        -
                        -
                        -
                      • -
                      • -
                        - - - - IAPError - -
                        -
                        -
                        -
                        -
                        -
                        -

                        Undocumented

                        - - See more -
                        -
                        -

                        Declaration

                        -
                        -

                        Swift

                        -
                        public struct IAPError : IAPErrorProtocol
                        - -
                        -
                        -
                        -
                        -
                      • -
                      • -
                        - - - - IAPProduct - -
                        -
                        -
                        -
                        -
                        -
                        -

                        Undocumented

                        - - See more -
                        -
                        -

                        Declaration

                        -
                        -

                        Swift

                        -
                        public struct IAPProduct
                        - -
                        -
                        -
                        -
                        -
                      • -
                      -
                      -
                      -
                      - -
                      -
                      - - - diff --git a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Structs/IAPError.html b/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Structs/IAPError.html deleted file mode 100644 index b1e121c..0000000 --- a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Structs/IAPError.html +++ /dev/null @@ -1,196 +0,0 @@ - - - - IAPError Structure Reference - - - - - - - - - - -
                      -
                      -

                      InAppPurchaseLib (52% documented)

                      -

                      View on GitHub

                      -
                      -
                      -
                      - -
                      -
                      - -
                      -
                      -
                      -

                      IAPError

                      -
                      -
                      -
                      public struct IAPError : IAPErrorProtocol
                      - -
                      -
                      -

                      Undocumented

                      - -
                      -
                      -
                      -
                        -
                      • -
                        - - - - code - -
                        -
                        -
                        -
                        -
                        -
                        -

                        Undocumented

                        - -
                        -
                        -

                        Declaration

                        -
                        -

                        Swift

                        -
                        public var code: IAPErrorCode
                        - -
                        -
                        -
                        -
                        -
                      • -
                      • -
                        - - - - localizedDescription - -
                        -
                        -
                        -
                        -
                        -
                        -

                        Undocumented

                        - -
                        -
                        -

                        Declaration

                        -
                        -

                        Swift

                        -
                        public var localizedDescription: String { get }
                        - -
                        -
                        -
                        -
                        -
                      • -
                      -
                      -
                      -
                      - -
                      -
                      - - - diff --git a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Typealiases.html b/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Typealiases.html deleted file mode 100644 index 17dd0f5..0000000 --- a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/Typealiases.html +++ /dev/null @@ -1,190 +0,0 @@ - - - - Type Aliases Reference - - - - - - - - - - -
                      -
                      -

                      InAppPurchaseLib (52% documented)

                      -

                      View on GitHub

                      -
                      -
                      -
                      - -
                      -
                      - -
                      -
                      -
                      -

                      Type Aliases

                      -

                      The following type aliases are available globally.

                      - -
                      -
                      -
                      - -
                      -
                      -
                      - -
                      -
                      - - - diff --git a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/css/jazzy.css b/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/css/jazzy.css deleted file mode 100644 index c3090c0..0000000 --- a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/css/jazzy.css +++ /dev/null @@ -1,374 +0,0 @@ -html, body, div, span, h1, h3, h4, p, a, code, em, img, ul, li, table, tbody, tr, td { - background: transparent; - border: 0; - margin: 0; - outline: 0; - padding: 0; - vertical-align: baseline; } - -body { - background-color: #f2f2f2; - font-family: Helvetica, freesans, Arial, sans-serif; - font-size: 14px; - -webkit-font-smoothing: subpixel-antialiased; - word-wrap: break-word; } - -h1, h2, h3 { - margin-top: 0.8em; - margin-bottom: 0.3em; - font-weight: 100; - color: black; } - -h1 { - font-size: 2.5em; } - -h2 { - font-size: 2em; - border-bottom: 1px solid #e2e2e2; } - -h4 { - font-size: 13px; - line-height: 1.5; - margin-top: 21px; } - -h5 { - font-size: 1.1em; } - -h6 { - font-size: 1.1em; - color: #777; } - -.section-name { - color: gray; - display: block; - font-family: Helvetica; - font-size: 22px; - font-weight: 100; - margin-bottom: 15px; } - -pre, code { - font: 0.95em Menlo, monospace; - color: #777; - word-wrap: normal; } - -p code, li code { - background-color: #eee; - padding: 2px 4px; - border-radius: 4px; } - -a { - color: #0088cc; - text-decoration: none; } - -ul { - padding-left: 15px; } - -li { - line-height: 1.8em; } - -img { - max-width: 100%; } - -blockquote { - margin-left: 0; - padding: 0 10px; - border-left: 4px solid #ccc; } - -.content-wrapper { - margin: 0 auto; - width: 980px; } - -header { - font-size: 0.85em; - line-height: 26px; - background-color: #414141; - position: fixed; - width: 100%; - z-index: 2; } - header img { - padding-right: 6px; - vertical-align: -4px; - height: 16px; } - header a { - color: #fff; } - header p { - float: left; - color: #999; } - header .header-right { - float: right; - margin-left: 16px; } - -#breadcrumbs { - background-color: #f2f2f2; - height: 27px; - padding-top: 17px; - position: fixed; - width: 100%; - z-index: 2; - margin-top: 26px; } - #breadcrumbs #carat { - height: 10px; - margin: 0 5px; } - -.sidebar { - background-color: #f9f9f9; - border: 1px solid #e2e2e2; - overflow-y: auto; - overflow-x: hidden; - position: fixed; - top: 70px; - bottom: 0; - width: 230px; - word-wrap: normal; } - -.nav-groups { - list-style-type: none; - background: #fff; - padding-left: 0; } - -.nav-group-name { - border-bottom: 1px solid #e2e2e2; - font-size: 1.1em; - font-weight: 100; - padding: 15px 0 15px 20px; } - .nav-group-name > a { - color: #333; } - -.nav-group-tasks { - margin-top: 5px; } - -.nav-group-task { - font-size: 0.9em; - list-style-type: none; - white-space: nowrap; } - .nav-group-task a { - color: #888; } - -.main-content { - background-color: #fff; - border: 1px solid #e2e2e2; - margin-left: 246px; - position: absolute; - overflow: hidden; - padding-bottom: 20px; - top: 70px; - width: 734px; } - .main-content p, .main-content a, .main-content code, .main-content em, .main-content ul, .main-content table, .main-content blockquote { - margin-bottom: 1em; } - .main-content p { - line-height: 1.8em; } - .main-content section .section:first-child { - margin-top: 0; - padding-top: 0; } - .main-content section .task-group-section .task-group:first-of-type { - padding-top: 10px; } - .main-content section .task-group-section .task-group:first-of-type .section-name { - padding-top: 15px; } - .main-content section .heading:before { - content: ""; - display: block; - padding-top: 70px; - margin: -70px 0 0; } - .main-content .section-name p { - margin-bottom: inherit; - line-height: inherit; } - .main-content .section-name code { - background-color: inherit; - padding: inherit; - color: inherit; } - -.section { - padding: 0 25px; } - -.highlight { - background-color: #eee; - padding: 10px 12px; - border: 1px solid #e2e2e2; - border-radius: 4px; - overflow-x: auto; } - -.declaration .highlight { - overflow-x: initial; - padding: 0 40px 40px 0; - margin-bottom: -25px; - background-color: transparent; - border: none; } - -.section-name { - margin: 0; - margin-left: 18px; } - -.task-group-section { - padding-left: 6px; - border-top: 1px solid #e2e2e2; } - -.task-group { - padding-top: 0px; } - -.task-name-container a[name]:before { - content: ""; - display: block; - padding-top: 70px; - margin: -70px 0 0; } - -.section-name-container { - position: relative; - display: inline-block; } - .section-name-container .section-name-link { - position: absolute; - top: 0; - left: 0; - bottom: 0; - right: 0; - margin-bottom: 0; } - .section-name-container .section-name { - position: relative; - pointer-events: none; - z-index: 1; } - .section-name-container .section-name a { - pointer-events: auto; } - -.item { - padding-top: 8px; - width: 100%; - list-style-type: none; } - .item a[name]:before { - content: ""; - display: block; - padding-top: 70px; - margin: -70px 0 0; } - .item code { - background-color: transparent; - padding: 0; } - .item .token, .item .direct-link { - display: inline-block; - text-indent: -20px; - padding-left: 3px; - margin-left: 35px; - font-size: 11.9px; - transition: all 300ms; } - .item .token-open { - margin-left: 20px; } - .item .discouraged { - text-decoration: line-through; } - .item .declaration-note { - font-size: .85em; - color: gray; - font-style: italic; } - -.pointer-container { - border-bottom: 1px solid #e2e2e2; - left: -23px; - padding-bottom: 13px; - position: relative; - width: 110%; } - -.pointer { - background: #f9f9f9; - border-left: 1px solid #e2e2e2; - border-top: 1px solid #e2e2e2; - height: 12px; - left: 21px; - top: -7px; - -webkit-transform: rotate(45deg); - -moz-transform: rotate(45deg); - -o-transform: rotate(45deg); - transform: rotate(45deg); - position: absolute; - width: 12px; } - -.height-container { - display: none; - left: -25px; - padding: 0 25px; - position: relative; - width: 100%; - overflow: hidden; } - .height-container .section { - background: #f9f9f9; - border-bottom: 1px solid #e2e2e2; - left: -25px; - position: relative; - width: 100%; - padding-top: 10px; - padding-bottom: 5px; } - -.aside, .language { - padding: 6px 12px; - margin: 12px 0; - border-left: 5px solid #dddddd; - overflow-y: hidden; } - .aside .aside-title, .language .aside-title { - font-size: 9px; - letter-spacing: 2px; - text-transform: uppercase; - padding-bottom: 0; - margin: 0; - color: #aaa; - -webkit-user-select: none; } - .aside p:last-child, .language p:last-child { - margin-bottom: 0; } - -.language { - border-left: 5px solid #cde9f4; } - .language .aside-title { - color: #4b8afb; } - -.aside-warning, .aside-deprecated, .aside-unavailable { - border-left: 5px solid #ff6666; } - .aside-warning .aside-title, .aside-deprecated .aside-title, .aside-unavailable .aside-title { - color: #ff0000; } - -.graybox { - border-collapse: collapse; - width: 100%; } - .graybox p { - margin: 0; - word-break: break-word; - min-width: 50px; } - .graybox td { - border: 1px solid #e2e2e2; - padding: 5px 25px 5px 10px; - vertical-align: middle; } - .graybox tr td:first-of-type { - text-align: right; - padding: 7px; - vertical-align: top; - word-break: normal; - width: 40px; } - -.slightly-smaller { - font-size: 0.9em; } - -#footer { - position: relative; - top: 10px; - bottom: 0px; - margin-left: 25px; } - #footer p { - margin: 0; - color: #aaa; - font-size: 0.8em; } - -html.dash header, html.dash #breadcrumbs, html.dash .sidebar { - display: none; } - -html.dash .main-content { - width: 980px; - margin-left: 0; - border: none; - width: 100%; - top: 0; - padding-bottom: 0; } - -html.dash .height-container { - display: block; } - -html.dash .item .token { - margin-left: 0; } - -html.dash .content-wrapper { - width: auto; } - -html.dash #footer { - position: static; } diff --git a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/index.html b/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/index.html deleted file mode 100644 index e633951..0000000 --- a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/index.html +++ /dev/null @@ -1,699 +0,0 @@ - - - - InAppPurchaseLib Reference - - - - - - - - - -
                      -
                      -

                      InAppPurchaseLib (52% documented)

                      -

                      View on GitHub

                      -
                      -
                      -
                      - -
                      -
                      - -
                      -
                      -
                      - -

                      - -

                      - -
                      -

                      An easy-to-use library for In-App Purchases, using Fovea.Billing for receipts validation.

                      -
                      - - -

                      Features

                      - -
                        -
                      • ✅ Purchase a product
                      • -
                      • ✅ Restore purchased products
                      • -
                      • ✅ Verify transactions with the App Store on Fovea.Billing server
                      • -
                      • ✅ Handle and notify payment transaction states
                      • -
                      • ✅ Retreive products information from the App Store
                      • -
                      • ✅ Support all product types (consumable, non-consumable, auto-renewable subscription, non-renewing subscription)
                      • -
                      • ✅ Status of purchases available when offline
                      • -
                      • ✅ Server integration with a Webhook
                      • -
                      -

                      Getting Started

                      - -

                      If you haven’t already, I highly recommend your read the Overview and Preparing section of Apple’s In-App Purchase official documentation

                      -

                      Requirements

                      - -
                        -
                      • Configure your App and Xcode to support In-App Purchases. - -
                      • -
                      • Create and configure your Fovea.Billing project account: - -
                          -
                        • Set your bundle ID
                        • -
                        • The iOS Shared Secret (or shared key) is to be retrieved from AppStoreConnect
                        • -
                        • The iOS Subscription Status URL (only if you want subscriptions)
                        • -
                      • -
                      -

                      Installation

                      - -

                      - -

                      - -
                        -
                      • Select your project in Xcode
                      • -
                      • Go to the section Swift Package
                      • -
                      • Click on (+) Add Package Dependency
                      • -
                      • Copy the Git URL: https://github.com/iridescent-dev/iap-swift-lib.git
                      • -
                      • Click on Next > Next
                      • -
                      • Make sure your project is selected in Add to target
                      • -
                      • Click on Finish
                      • -
                      - -

                      Note: You have to import InAppPurchaseLib wherever you use the library.

                      -

                      Usage

                      - -

                      The process of implementing in-app purchases involves several steps:

                      - -
                        -
                      1. Displaying the list of purchasable products
                      2. -
                      3. Initiating a purchase
                      4. -
                      5. Delivering and finalizing a purchase
                      6. -
                      7. Checking the current ownership of non-consumables and subscriptions
                      8. -
                      9. Implementing the Restore Purchases button
                      10. -
                      -

                      Initialization

                      - -

                      Before everything else the library must be initialized. This has to happen as soon as possible. A good way is to call the InAppPurchase.initialize() method when the application did finish launching. In the background, this will load your products and refresh the status of purchases and subscriptions.

                      - -

                      InAppPurchase.initialize() accepts the following arguments:

                      - -
                        -
                      • iapProducts - An array of IAPProduct (REQUIRED)
                      • -
                      • validatorUrlString - The validator url retrieved from Fovea (REQUIRED)
                      • -
                      • applicationUsername - The user name, if your app implements user login (optional)
                      • -
                      - -

                      Each IAPProduct contains the following fields:

                      - -
                        -
                      • productIdentifier - The product unique identifier
                      • -
                      • productType - The IAPProductType (consumable, nonConsumable, nonRenewingSubscription or autoRenewableSubscription)
                      • -
                      - -

                      Example:

                      - -

                      A good place is generally in your application delegate’s didFinishLaunchingWithOptions function, like below:

                      -
                      import InAppPurchaseLib
                      -
                      -class AppDelegate: UIResponder, UIApplicationDelegate, IAPPurchaseDelegate {
                      -  ...
                      -  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
                      -    InAppPurchase.initialize(
                      -      iapProducts: [
                      -        IAPProduct(productIdentifier: "monthly_plan", productType: .autoRenewableSubscription),
                      -        IAPProduct(productIdentifier: "yearly_plan",  productType: .autoRenewableSubscription),
                      -        IAPProduct(productIdentifier: "disable_ads",  productType: .nonConsumable)
                      -      ],
                      -      validatorUrlString: "https://validator.fovea.cc/v1/validate?appName=demo&apiKey=12345678")
                      -  }
                      -
                      -  func productPurchased(productIdentifier: String) {
                      -    // ... process purchase (we'll see that later)
                      -  }
                      -}
                      -
                      - -

                      You should also call the stop method when the application will terminate, for proper cleanup.

                      -
                        func applicationWillTerminate(_ application: UIApplication) {
                      -    InAppPurchase.stop()
                      -  }
                      -
                      - -

                      For more advanced use cases, in particular when you have implemented user login, you’ll have to make some adjustments. We’ll learn more about this in the Server integration section.

                      - -

                      Tip: If initialization was successful, you should see a new receipt validation event in Fovea’s Dashboard.

                      -

                      Displaying products

                      - -

                      Let’s start with the simplest case: you have a single product.

                      - -

                      You can retrieve all information about this product using the function InAppPurchase.getProductBy(identifier: "my_product_id"). This returns an SKProduct extended with helpful methods.

                      - -

                      Those are the most important:

                      - -
                        -
                      • productIdentifier: String - The string that identifies the product to the Apple AppStore.
                      • -
                      • localizedTitle: String - The name of the product, in the language of the device, as retrieved from the AppStore.
                      • -
                      • localizedDescription: String - A description of the product, in the language of the device, as retrieved from the AppStore.
                      • -
                      • localizedPrice: String - The cost of the product in the local currency (read-only property added by this library).
                      • -
                      - -

                      Example:

                      - -

                      You can add a function similar to this to your view.

                      -
                      @objc func refreshView() {
                      -  guard let product: SKProduct = InAppPurchase.getProductBy(identifier: "my_product_id") else {
                      -    self.titleLabel.text = "Product unavailable"
                      -    return
                      -  }
                      -  self.titleLabel.text = product.localizedTitle
                      -  self.descriptionLabel.text = product.localizedDescription
                      -  self.priceLabel.text = product.localizedPrice
                      -}
                      -
                      - -

                      This example assumes self.titleLabel is a UILabel, etc.

                      - -

                      Make sure to call this function when the view appears on screen, for instance by calling it from viewWillAppear.

                      -
                      override func viewWillAppear(_ animated: Bool) {
                      -  self.refreshView()
                      -}
                      -
                      -

                      Displaying subscriptions

                      - -

                      For subscription products, you also have some data about subscription periods and introductory offers.

                      - -
                        -
                      • func hasIntroductoryPriceEligible() -> Bool - The product has an introductory price the user is eligible to.
                      • -
                      • localizedSubscriptionPeriod: String? - The period of the subscription.
                      • -
                      • localizedIntroductoryPrice: String? - The cost of the introductory offer if available in the local currency.
                      • -
                      • localizedIntroductoryPeriod: String? - The subscription period of the introductory offer.
                      • -
                      • localizedIntroductoryDuration: String? - The duration of the introductory offer.
                      • -
                      - -

                      Example

                      -
                      @objc func refreshView() {
                      -  guard let product: SKProduct = InAppPurchase.getProductBy(identifier: "my_product_id") else {
                      -    self.titleLabel.text = "Product unavailable"
                      -    return
                      -  }
                      -  self.titleLabel.text = product.localizedTitle
                      -  self.descriptionLabel.text = product.localizedDescription
                      -
                      -  // Format price text. Example: "0,99€ / month for 3 months (then 3,99 € / month)"
                      -  var priceText = "\(product.localizedPrice) / \(product.localizedSubscriptionPeriod!)"
                      -  if product.hasIntroductoryPriceEligible() {
                      -      if product.introductoryPrice!.numberOfPeriods == 1 {
                      -          priceText = "\(product.localizedIntroductoryPrice!) for \(product.localizedIntroductoryDuration!)" +
                      -          " (then \(priceText))"
                      -      } else {
                      -          priceText = "\(product.localizedIntroductoryPrice!) / \(product.localizedIntroductoryPeriod!)" +
                      -          " for \(product.localizedIntroductoryDuration!) (then \(priceText))"
                      -      }
                      -  }
                      -  self.priceLabel.text = priceText
                      -}
                      -
                      - -

                      Note: You have to import StoreKit wherever you use SKProduct.

                      -

                      Refreshing

                      - -

                      Data might change or not be yet available when your “product” view is presented. In order to properly handle those cases, you should refresh your view after refreshing in-app products metadata. You want to be sure you’re displaying up-to-date information.

                      - -

                      To achieve this, call InAppPurchase.refresh() when your view is presented.

                      -
                      override func viewWillAppear(_ animated: Bool) {
                      -  self.refreshView()
                      -  InAppPurchase.refresh(callback: { _ in
                      -      self.refreshView()
                      -  })
                      -}
                      -
                      -

                      Purchasing

                      - -

                      The purchase process is generally a little bit more involving than most people would expect. Why is it not just: purchase → on success unlock the feature?

                      - -

                      Several reasons:

                      - -
                        -
                      • In-app purchases can be initiated outside the app
                      • -
                      • In-app purchases can be deferred, pending parental approval
                      • -
                      • Apple wants to be sure you delivered the product before charging the user
                      • -
                      - -

                      That is why the process looks like so:

                      - -
                        -
                      • being ready to handle purchase events from app startup
                      • -
                      • finalizing transactions when product delivery is complete
                      • -
                      • sending purchase request, for which successful doesn’t always mean complete
                      • -
                      -

                      Initiating a purchase

                      - -

                      To initiate a purchase, use the InAppPurchase.purchase() function. It takes the productIdentifier and a callback function, called when the purchase has been processed.

                      - -

                      Important: Do not process the purchase here, we’ll handle that later!

                      - -

                      From this callback, you can for example unlock the UI by hiding your loading indicator and display a message to the user.

                      - -

                      Example:

                      -
                      self.loaderView.show()
                      -InAppPurchase.purchase(
                      -  productIdentifier: "my_product_id",
                      -  callback: { _ in
                      -    self.loaderView.hide()
                      -})
                      -
                      - -

                      This simple example locks the UI with a loader when the purchase is in progress. We’ll see later how the purchase has to be processed by your applicaiton.

                      - -

                      The callback also gives more information about the outcome of the purchase, you might want to use it to update your UI as well. Note that some events are useful for analytics. So here’s a more complete example.

                      -
                      self.loaderView.show()
                      -InAppPurchase.purchase(
                      -  productIdentifier: "my_product_id",
                      -  callback: { result in
                      -    self.loaderView.hide()
                      -
                      -    switch result.state {
                      -    case .purchased:
                      -      // Product successfully purchased
                      -      // Reminder: Do not process the purchase here, only update your UI.
                      -      //           that's why we do not send data to analytics.
                      -      openThankYouScreen()
                      -    case .failed:
                      -      // Purchase failed
                      -      // - Human formated reason can be found in result.localizedDescription
                      -      // - More details in either result.skError or result.iapError
                      -      showError(result.localizedDescription)
                      -    case .deferred:
                      -      // The purchase is deferred, waiting for the parent's approval
                      -      openWaitingParentApprovalScreen()
                      -    case .cancelled:
                      -      // The user canceled the request, generally only useful for analytics.
                      -  }
                      -})
                      -
                      - -

                      If the purchase fails, result will contain either .skError, a SKError from StoreKit, or .iapError, an IAPError.

                      - -

                      Tip: After a successful purchase, you should see a new transaction in Fovea’s dashboard.

                      -

                      Handling purchases

                      - -

                      Finally, the magic happened: a user purchased one of your products! Let’s see how we handle the different types of products.

                      -

                      Non-Consumables

                      - -

                      Wherever your app needs to know if a non-consumable product has been purchased, use InAppPurchase.hasActivePurchase(for: -productIdentifier). This will return true if the user currently owns the product.

                      - -

                      Note: The last known state for the user’s purchases is stored as UserDefaults. As such, their status is always available to your app, even when offline.

                      - -

                      If you have a server that needs to know about the purchase. You should rely on Fovea’s webhook instead of doing anything in here. We will see that later in the Server integration section.

                      -

                      Auto-Renewable Subscriptions

                      - -

                      As with non-consumables, you will use InAppPurchase.hasActivePurchase(for: productIdentifier) to check if the user is an active subscriber to a given product.

                      - -

                      You might also like to call refresh regularly, for example when entering your main view. When appropriate, the library will refresh the receipt to detect subscription renewals or expiry.

                      - -

                      As we’ve seend in the Refreshing section:

                      -
                      override func viewWillAppear(_ animated: Bool) {
                      -  self.refreshView()
                      -  InAppPurchase.refresh(callback: { _ in
                      -      self.refreshView()
                      -  })
                      -}
                      -
                      - -

                      Note: Don’t be reluctant to call refresh() often. Internally, the library ensures heavy operation are only performed if necessary: for example when a subscription just expired. So in 99% of cases this call will result in no-operations.

                      -

                      Consumables

                      - -

                      If the purchased products in a consumable, your app is responsible for delivering the purchase then acknowlege that you’ve done so. Delivering generally consists in increasing a counter for some sort of virtual currency.

                      - -

                      Your app can be notified of a purchase at any time. So the library asks you to provide an IAPPurchaseDelegate from initialization.

                      - -

                      In InAppPurchase.initialize(), we can pass an IAPPurchaseDelegate instance. This object implements the productPurchased(productIdentifier:) function, which is called whenever a purchase is approved.

                      - -

                      Here’s a example implementation:

                      -
                      class AppDelegate: UIResponder, UIApplicationDelegate, IAPPurchaseDelegate {
                      -  ...
                      -  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
                      -    InAppPurchase.initialize(
                      -      iapProducts: [...],
                      -      iapPurchaseDelegate: self, // ADDED: iapPurchaseDelegate
                      -      validatorUrlString: "https://validator.fovea.cc/v1/validate?appName=demo&apiKey=12345678")
                      -  }
                      -
                      -  // IAPPurchaseDelegate implementation
                      -  func productPurchased(productIdentifier: String) {
                      -    // TODO
                      -  }
                      -}
                      -
                      - -

                      It’s also important to know that when a purchase is approved, money isn’t yet to reach your bank account. You have to acknowledge delivery of the (virtual) item to finalize the transaction. That is why we have to call InAppPurchase.finishTransactions(for: productIdentifier) as soon as we delivered the product.

                      - -

                      Example

                      - -

                      Let’s define a class that adopts the IAPPurchaseDelegate protocol, it can very well be your application delegate.

                      -
                      func productPurchased(productIdentifier: String) {
                      -  switch productIdenfier {
                      -  case "10_silver":
                      -    addSilver(10)
                      -  case "100_silver":
                      -    addSilver(100)
                      -  }
                      -  InAppPurchase.finishTransactions(for: productIdentifier)
                      -  Analytics.trackEvent("purchase succeeded", productIdentifier)
                      -}
                      -
                      - -

                      Here, we implement our own unlocking logic and call InAppPurchase.finishTransactions() afterward (assuming addSilver is synchronous).

                      - -

                      Note: productPurchased is called when a purchase has been confirmed by Fovea’s receipt validator. If you have a server, he probably already has been notified of this purchase using the webhook.

                      - -

                      Reminder: Keep in mind that purchase notifications might occur even if you never called the InAppPurchase.purchase() function: purchases can be made from another device or the AppStore, they can be approved by parents when the app isn’t running, purchase flows can be interupted, etc. The pattern above ensures your app is always ready to handle purchase events.

                      -

                      Non-Renewing Subscriptions

                      - -

                      For non-renewing subscriptions, delivering consists in increasing the amount of time a user can access a given feature. Apple doesn’t manage the length and expiry of non-renewing subscriptions: you have to do this yourself, as for consumables.

                      - -

                      Basically, everything is identical to consumables.

                      -

                      Restoring purchases

                      - -

                      Except if you only sell consumable products, Apple requires that you provide a “Restore Purchases” button to your users. In general, it is found in your application settings.

                      - -

                      Call this method when this button is pressed.

                      -
                      @IBAction func restorePurchases(_ sender: Any) {
                      -  self.loaderView.show()
                      -  InAppPurchase.restorePurchases(callback: { result in
                      -      self.loaderView.hide()
                      -      switch result.state {
                      -      case .succeeded:
                      -          if result.addedPurchases > 0 {
                      -              print("Restore purchases successful.")
                      -          } else {
                      -              print("No purchase to restore.")
                      -          }
                      -      case .failed:
                      -          print("Restore purchases failed.")
                      -      }
                      -  })
                      -}
                      -
                      - -

                      The callback method is called once the operation is complete. You can use it to unlock the UI, by hiding your loader for example, and display the adapted message to the user.

                      -

                      Displaying products with purchases

                      - -

                      In your store screen, where you present your products titles and prices with a purchase button, there are some cases to handle that we skipped. Owned products and deferred purchases.

                      -

                      Owned products

                      - -

                      Non-consumables and active auto-renewing subscriptions cannot be purchased again. You should adjust your UI to reflect that state. Refer to InAppPurchase.hasActivePurchase() to and to the example later in this section.

                      -

                      Deferred purchases

                      - -

                      Apple’s Ask to Buy feature lets parents approve any purchases initiated by children, including in-app purchases.

                      - -

                      With Ask to Buy enabled, when a child requests to make a purchase, the app is notified that the purchase is awaiting the parent’s approval in the purchase callback:

                      -
                      InAppPurchase.purchase(
                      -  productIdentifier: productIdentifier,
                      -  callback: { result in
                      -    switch result.state {
                      -    case .deferred:
                      -      // Pending parent approval
                      -  }
                      -})
                      -
                      - -

                      In the deferred case, the child has been notified by StoreKit that the parents have to approve the purchase. He might then close the app and come back later. You don’t have much to do, but to display in your UI that there is a purchase waiting for parental approval in your views.

                      - -

                      We will use the hasDeferredTransaction method:

                      -
                      InAppPurchase.hasDeferredTransaction(for productIdentifier: String) -> Bool
                      -
                      -

                      Example

                      - -

                      Here’s an example that covers what has been discussed above. We will update our example refreshView function from before:

                      -
                      @objc func refreshView() {
                      -  guard let product: SKProduct = InAppPurchase.getProductBy(identifier: "my_product_id") else {
                      -    self.titleLabel.text = "Product unavailable"
                      -    return
                      -  }
                      -  self.titleLabel.text = product.localizedTitle
                      -  // ...
                      -
                      -  // "Ask to Buy" deferred purchase waiting for parent's approval
                      -  if InAppPurchase.hasDeferredTransaction(for: "my_product_id") {
                      -    self.statusLabel.text = "Waiting for Approval..."
                      -    self.purchaseButton.isPointerInteractionEnabled = false
                      -  }
                      -  // "Owned" product
                      -  else if InAppPurchase.hasActivePurchase(for: "my_product_id") {
                      -    self.statusLabel.text = "OWNED"
                      -    self.purchaseButton.isPointerInteractionEnabled = false
                      -  }
                      -  else {
                      -    self.purchaseButton.isPointerInteractionEnabled = true
                      -  }
                      -}
                      -
                      - -

                      When a product is owned or has a deferred purchase, we make sure the purchase button is grayed out. We also use a status label to display some details. Of course, you are free to design your UI as you see fit.

                      -

                      Errors

                      - -

                      When calling refresh(), purchase() or restorePurchases(), the callback can return an IAPError if the state is failed. -Here is the list of IAPErrorCode you can receive:

                      - -
                        -
                      • Errors returned by refresh(), purchase() or restorePurchases()

                        - -
                          -
                        • libraryNotInitialized - You must call the initialize fuction before using the library.
                        • -
                        • bundleIdentifierInvalid - The Bundle Identifier is invalid.
                        • -
                        • validatorUrlInvalid - The Validator URL String is invalid.
                        • -
                        • refreshReceiptFailed - Failed to refresh the App Store receipt.
                        • -
                        • validateReceiptFailed - Failed to validate the App Store receipt with Fovea.
                        • -
                        • readReceiptFailed - Failed to read the receipt validation.
                        • -
                      • -
                      • Errors returned by refresh()

                        - -
                          -
                        • refreshProductsFailed - Failed to refresh products from the App Store.
                        • -
                      • -
                      • Errors returned by purchase()

                        - -
                          -
                        • productNotFound - The product was not found on the App Store and cannot be purchased.
                        • -
                        • cannotMakePurchase - The user is not allowed to authorize payments.
                        • -
                        • alreadyPurchasing - A purchase is already in progress.
                        • -
                      • -
                      -

                      Analytics

                      - -

                      Tracking the purchase flow is a common things in apps. Especially as it’s core to your revenue model.

                      - -

                      We can track 5 events, which step in the purchase pipeline a user reached.

                      - -
                        -
                      1. purchase initiated
                      2. -
                      3. purchase cancelled
                      4. -
                      5. purchase failed
                      6. -
                      7. purchase deferred
                      8. -
                      9. purchase succeeded
                      10. -
                      - -

                      Here’s a quick example showing how to implement this correctly.

                      -
                      func makePurchase() {
                      -  Analytics.trackEvent("purchase initiated")
                      -  InAppPurchase.purchase(
                      -    productIdentifier: "my_product_id",
                      -    callback: { result in
                      -      switch result.state {
                      -      case .purchased:
                      -        // Reminder: We are not processing the purchase here, only updating your UI.
                      -        //           That's why we do not send an event to analytics.
                      -      case .failed:
                      -        Analytics.trackEvent("purchase failed")
                      -      case .deferred:
                      -        Analytics.trackEvent("purchase deferred")
                      -      case .cancelled:
                      -        Analytics.trackEvent("purchase cancelled")
                      -    }
                      -  })
                      -}
                      -
                      -// IAPPurchaseDelegate implementation
                      -func productPurchased(productIdentifier: String) {
                      -  Analytics.trackEvent("purchase succeeded")
                      -  InAppPurchase.finishTransactions(for: productIdentifier)
                      -}
                      -
                      - -

                      The important part to remember is that a purchase can occur outside your app (or be approved when the app is not running), that’s why tracking purchase succeeded has to be part of the productPurchased delegate function.

                      - -

                      Refer to the Consumables section to learn more about the productPurchased function.

                      -

                      Server integration

                      - -

                      In more advanced use cases, you have a server component. Users are logged in and you’ll like to unlock the content for this user on your server. The safest approach is to setup a Webhook on Fovea. You’ll receive notifications from Fovea that transaction have been processed and/or subscriptions updated.

                      - -

                      The information sent from Fovea has been verified from Apple’s server, which makes it way more trustable than information sent from your app itself.

                      - -

                      To take advantage of this, you have to inform the library of your application username. This applicationUsername can be provided as a parameter of the InAppPurchase.initialize method and updated later by changing the associated property.

                      - -

                      Example:

                      -
                      InAppPurchase.initialize(
                      -  iapProducts: [...],
                      -  validatorUrlString: "..."),
                      -  applicationUsername: UserSession.getUserId())
                      -
                      -// later ...
                      -InAppPurchase.applicationUsername = UserSession.getUserId()
                      -
                      - -

                      If a user account is mandatory in your app, you will want to delay calls to InAppPurchase.initialize() to when your user’s session is ready.

                      - -

                      Do not hesitate to contact Fovea for help.

                      -

                      Xcode Demo Project

                      - -

                      Do not hesitate to check the demo project available on here: iap-swift-lib-demo.

                      -

                      References

                      - - -

                      Coding

                      - -

                      Generate the documentation, using this fork of swift-doc (on --minimum-access-level is part of the main distrib).

                      -
                      swift-doc generate sources --module-name InAppPurchase --format html --output docs --minimum-access-level public --base-url /iap-swift-lib/
                      -
                      -

                      Troubleshooting

                      - -

                      Common issues are covered here: https://github.com/iridescent-dev/iap-swift-lib/wiki/Troubleshooting

                      -

                      License

                      - -

                      InAppPurchaseLib is open-sourced library licensed under the MIT License. See LICENSE for details.

                      - -
                      -
                      - -
                      -
                      - - - diff --git a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/search.json b/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/search.json deleted file mode 100644 index df4266d..0000000 --- a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/Documents/search.json +++ /dev/null @@ -1 +0,0 @@ -{"Typealiases.html#/s:16InAppPurchaseLib19IAPPurchaseCallbacka":{"name":"IAPPurchaseCallback","abstract":"

                      Undocumented

                      "},"Typealiases.html#/s:16InAppPurchaseLib18IAPRefreshCallbacka":{"name":"IAPRefreshCallback","abstract":"

                      Undocumented

                      "},"Structs/IAPProduct.html#/s:16InAppPurchaseLib10IAPProductV17productIdentifierSSvp":{"name":"productIdentifier","abstract":"

                      The identifier of the product.

                      ","parent_name":"IAPProduct"},"Structs/IAPProduct.html#/s:16InAppPurchaseLib10IAPProductV11productTypeAA0eG0Ovp":{"name":"productType","abstract":"

                      The type of the product.

                      ","parent_name":"IAPProduct"},"Structs/IAPProduct.html#/s:16InAppPurchaseLib10IAPProductV17productIdentifier0F4TypeACSS_AA0eH0Otcfc":{"name":"init(productIdentifier:productType:)","abstract":"

                      Initializes an IAPProduct with its identifier and type.

                      ","parent_name":"IAPProduct"},"Structs/IAPError.html#/s:16InAppPurchaseLib8IAPErrorV4codeAA0E4CodeOvp":{"name":"code","abstract":"

                      Undocumented

                      ","parent_name":"IAPError"},"Structs/IAPError.html#/s:16InAppPurchaseLib8IAPErrorV20localizedDescriptionSSvp":{"name":"localizedDescription","abstract":"

                      Undocumented

                      ","parent_name":"IAPError"},"Structs/IAPRefreshResult.html#/s:16InAppPurchaseLib16IAPRefreshResultV5stateAA0eF5StateOvp":{"name":"state","abstract":"

                      Undocumented

                      ","parent_name":"IAPRefreshResult"},"Structs/IAPRefreshResult.html#/s:16InAppPurchaseLib16IAPRefreshResultV8iapErrorAA8IAPErrorVSgvp":{"name":"iapError","abstract":"

                      Undocumented

                      ","parent_name":"IAPRefreshResult"},"Structs/IAPRefreshResult.html#/s:16InAppPurchaseLib16IAPRefreshResultV14addedPurchasesSivp":{"name":"addedPurchases","abstract":"

                      Undocumented

                      ","parent_name":"IAPRefreshResult"},"Structs/IAPRefreshResult.html#/s:16InAppPurchaseLib16IAPRefreshResultV16updatedPurchasesSivp":{"name":"updatedPurchases","abstract":"

                      Undocumented

                      ","parent_name":"IAPRefreshResult"},"Structs/IAPPurchaseResult.html#/s:16InAppPurchaseLib17IAPPurchaseResultV5stateAA0eF5StateOvp":{"name":"state","abstract":"

                      Undocumented

                      ","parent_name":"IAPPurchaseResult"},"Structs/IAPPurchaseResult.html#/s:16InAppPurchaseLib17IAPPurchaseResultV8iapErrorAA8IAPErrorVSgvp":{"name":"iapError","abstract":"

                      Undocumented

                      ","parent_name":"IAPPurchaseResult"},"Structs/IAPPurchaseResult.html#/s:16InAppPurchaseLib17IAPPurchaseResultV7skErrorSC11SKErrorCodeLeVSgvp":{"name":"skError","abstract":"

                      Undocumented

                      ","parent_name":"IAPPurchaseResult"},"Structs/IAPPurchaseResult.html#/s:16InAppPurchaseLib17IAPPurchaseResultV20localizedDescriptionSSSgvp":{"name":"localizedDescription","abstract":"

                      Undocumented

                      ","parent_name":"IAPPurchaseResult"},"Structs/IAPPurchaseResult.html":{"name":"IAPPurchaseResult","abstract":"

                      Undocumented

                      "},"Structs/IAPRefreshResult.html":{"name":"IAPRefreshResult","abstract":"

                      Undocumented

                      "},"Structs/IAPError.html":{"name":"IAPError","abstract":"

                      Undocumented

                      "},"Structs/IAPProduct.html":{"name":"IAPProduct","abstract":"

                      Undocumented

                      "},"Protocols/IAPPurchaseDelegate.html#/s:16InAppPurchaseLib19IAPPurchaseDelegateP16productPurchased0G10IdentifierySS_tF":{"name":"productPurchased(productIdentifier:)","abstract":"

                      Called when a product is newly purchased, updated or restored.

                      ","parent_name":"IAPPurchaseDelegate"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP11iapProductsSayAA10IAPProductVGvpZ":{"name":"iapProducts","abstract":"

                      The array of IAPProduct.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP18validatorUrlStringSSSgvpZ":{"name":"validatorUrlString","abstract":"

                      The validator url retrieved from Fovea.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP03iapC8DelegateAA011IAPPurchaseF0_pSgvpZ":{"name":"iapPurchaseDelegate","abstract":"

                      The instance of class that adopts the IAPPurchaseDelegate protocol.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP19applicationUsernameSSSgvpZ":{"name":"applicationUsername","abstract":"

                      The user name, if your app implements user login.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP10initialize11iapProducts18validatorUrlString0fC8Delegate19applicationUsernameySayAA10IAPProductVG_SSAA011IAPPurchaseK0_pSSSgtFZ":{"name":"initialize(iapProducts:validatorUrlString:iapPurchaseDelegate:applicationUsername:)","abstract":"

                      Start observing the payment queue, as soon as possible, and refresh Product list and user Receipt.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP4stopyyFZ":{"name":"stop()","abstract":"

                      Stop observing the payment queue, when the application will terminate, for proper cleanup.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP7refresh8callbackyyAA16IAPRefreshResultVc_tFZ":{"name":"refresh(callback:)","abstract":"

                      Refresh Product list and user Receipt.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP11getProductsSaySo9SKProductCGyFZ":{"name":"getProducts()","abstract":"

                      Gets all products retrieved from the App Store

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP12getProductBy10identifierSo9SKProductCSgSS_tFZ":{"name":"getProductBy(identifier:)","abstract":"

                      Gets the product by its identifier from the list of products retrieved from the App Store.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP15canMakePaymentsSbyFZ":{"name":"canMakePayments()","abstract":"

                      Checks if the user is allowed to authorize payments.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP8purchase17productIdentifier8quantity8callbackySS_SiyAA17IAPPurchaseResultVctFZ":{"name":"purchase(productIdentifier:quantity:callback:)","abstract":"

                      Request a Payment from the App Store.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP16restorePurchases8callbackyyAA16IAPRefreshResultVc_tFZ":{"name":"restorePurchases(callback:)","abstract":"

                      Restore purchased products.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP18finishTransactions3forySS_tFZ":{"name":"finishTransactions(for:)","abstract":"

                      Finish all transactions for the product.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP22hasDeferredTransaction3forSbSS_tFZ":{"name":"hasDeferredTransaction(for:)","abstract":"

                      Checks if the last transaction state for a given product was deferred.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP19hasAlreadyPurchasedSbyFZ":{"name":"hasAlreadyPurchased()","abstract":"

                      Checks if the user has already purchased at least one product.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP09hasActiveC03forSbSS_tFZ":{"name":"hasActivePurchase(for:)","abstract":"

                      Checks if the user currently own (or is subscribed to) a given product (nonConsumable or autoRenewableSubscription).

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP21hasActiveSubscriptionSbyFZ":{"name":"hasActiveSubscription()","abstract":"

                      Checks if the user has an active auto renewable subscription regardless of the product identifier.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP03getC4Date3for10Foundation0F0VSgSS_tFZ":{"name":"getPurchaseDate(for:)","abstract":"

                      Returns the latest purchased date for a given product.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP13getExpiryDate3for10Foundation0G0VSgSS_tFZ":{"name":"getExpiryDate(for:)","abstract":"

                      Returns the expiry date for a subcription. May be past or future.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/IAPErrorProtocol.html#/s:16InAppPurchaseLib16IAPErrorProtocolP4codeAA0E4CodeOvp":{"name":"code","abstract":"

                      Undocumented

                      ","parent_name":"IAPErrorProtocol"},"Protocols/IAPErrorProtocol.html":{"name":"IAPErrorProtocol","abstract":"

                      Undocumented

                      "},"Protocols/InAppPurchaseLib.html":{"name":"InAppPurchaseLib","abstract":"

                      The protocol that InAppPurchase` adopts.

                      "},"Protocols/IAPPurchaseDelegate.html":{"name":"IAPPurchaseDelegate","abstract":"

                      The protocol that you must adopt if you have consumable and/or nonRenewingSubscription products.

                      "},"Extensions/SKProduct.html#/s:So9SKProductC16InAppPurchaseLibE21localizedPeriodFormatAC09IAPPeriodH0OvpZ":{"name":"localizedPeriodFormat","abstract":"

                      Undocumented

                      ","parent_name":"SKProduct"},"Extensions/SKProduct.html#/s:So9SKProductC16InAppPurchaseLibE28hasIntroductoryPriceEligibleSbyF":{"name":"hasIntroductoryPriceEligible()","abstract":"

                      Checks if the product has an introductory price the user is eligible to.

                      ","parent_name":"SKProduct"},"Extensions/SKProduct.html#/s:So9SKProductC16InAppPurchaseLibE14localizedPriceSSvp":{"name":"localizedPrice","abstract":"

                      Returns a localized string with the cost of the product in the local currency.

                      ","parent_name":"SKProduct"},"Extensions/SKProduct.html#/s:So9SKProductC16InAppPurchaseLibE27localizedSubscriptionPeriodSSSgvp":{"name":"localizedSubscriptionPeriod","abstract":"

                      Returns a localized string with the period of the subscription product.

                      ","parent_name":"SKProduct"},"Extensions/SKProduct.html#/s:So9SKProductC16InAppPurchaseLibE26localizedIntroductoryPriceSSSgvp":{"name":"localizedIntroductoryPrice","abstract":"

                      Returns a localized string with the introductory price if available, in the local currency.

                      ","parent_name":"SKProduct"},"Extensions/SKProduct.html#/s:So9SKProductC16InAppPurchaseLibE27localizedIntroductoryPeriodSSSgvp":{"name":"localizedIntroductoryPeriod","abstract":"

                      Returns a localized string with the introductory price period of the subscription product.

                      ","parent_name":"SKProduct"},"Extensions/SKProduct.html#/s:So9SKProductC16InAppPurchaseLibE29localizedIntroductoryDurationSSSgvp":{"name":"localizedIntroductoryDuration","abstract":"

                      Returns a localized string with the duration of the introductory price.

                      ","parent_name":"SKProduct"},"Extensions/SKProduct.html":{"name":"SKProduct"},"Enums/IAPPeriodFormat.html#/s:16InAppPurchaseLib15IAPPeriodFormatO5shortyA2CmF":{"name":"short","abstract":"

                      Undocumented

                      ","parent_name":"IAPPeriodFormat"},"Enums/IAPPeriodFormat.html#/s:16InAppPurchaseLib15IAPPeriodFormatO4longyA2CmF":{"name":"long","abstract":"

                      Undocumented

                      ","parent_name":"IAPPeriodFormat"},"Enums/IAPProductType.html#/s:16InAppPurchaseLib14IAPProductTypeO10consumableyA2CmF":{"name":"consumable","abstract":"

                      Undocumented

                      ","parent_name":"IAPProductType"},"Enums/IAPProductType.html#/s:16InAppPurchaseLib14IAPProductTypeO13nonConsumableyA2CmF":{"name":"nonConsumable","abstract":"

                      Undocumented

                      ","parent_name":"IAPProductType"},"Enums/IAPProductType.html#/s:16InAppPurchaseLib14IAPProductTypeO23nonRenewingSubscriptionyA2CmF":{"name":"nonRenewingSubscription","abstract":"

                      Undocumented

                      ","parent_name":"IAPProductType"},"Enums/IAPProductType.html#/s:16InAppPurchaseLib14IAPProductTypeO25autoRenewableSubscriptionyA2CmF":{"name":"autoRenewableSubscription","abstract":"

                      Undocumented

                      ","parent_name":"IAPProductType"},"Enums/IAPErrorCode.html#/s:16InAppPurchaseLib12IAPErrorCodeO21libraryNotInitializedyA2CmF":{"name":"libraryNotInitialized","abstract":"

                      Undocumented

                      ","parent_name":"IAPErrorCode"},"Enums/IAPErrorCode.html#/s:16InAppPurchaseLib12IAPErrorCodeO15productNotFoundyA2CmF":{"name":"productNotFound","abstract":"

                      Undocumented

                      ","parent_name":"IAPErrorCode"},"Enums/IAPErrorCode.html#/s:16InAppPurchaseLib12IAPErrorCodeO010cannotMakeC0yA2CmF":{"name":"cannotMakePurchase","abstract":"

                      Undocumented

                      ","parent_name":"IAPErrorCode"},"Enums/IAPErrorCode.html#/s:16InAppPurchaseLib12IAPErrorCodeO17alreadyPurchasingyA2CmF":{"name":"alreadyPurchasing","abstract":"

                      Undocumented

                      ","parent_name":"IAPErrorCode"},"Enums/IAPErrorCode.html#/s:16InAppPurchaseLib12IAPErrorCodeO23bundleIdentifierInvalidyA2CmF":{"name":"bundleIdentifierInvalid","abstract":"

                      Undocumented

                      ","parent_name":"IAPErrorCode"},"Enums/IAPErrorCode.html#/s:16InAppPurchaseLib12IAPErrorCodeO19validatorUrlInvalidyA2CmF":{"name":"validatorUrlInvalid","abstract":"

                      Undocumented

                      ","parent_name":"IAPErrorCode"},"Enums/IAPErrorCode.html#/s:16InAppPurchaseLib12IAPErrorCodeO20refreshReceiptFailedyA2CmF":{"name":"refreshReceiptFailed","abstract":"

                      Undocumented

                      ","parent_name":"IAPErrorCode"},"Enums/IAPErrorCode.html#/s:16InAppPurchaseLib12IAPErrorCodeO21validateReceiptFailedyA2CmF":{"name":"validateReceiptFailed","abstract":"

                      Undocumented

                      ","parent_name":"IAPErrorCode"},"Enums/IAPErrorCode.html#/s:16InAppPurchaseLib12IAPErrorCodeO17readReceiptFailedyA2CmF":{"name":"readReceiptFailed","abstract":"

                      Undocumented

                      ","parent_name":"IAPErrorCode"},"Enums/IAPErrorCode.html#/s:16InAppPurchaseLib12IAPErrorCodeO21refreshProductsFailedyA2CmF":{"name":"refreshProductsFailed","abstract":"

                      Undocumented

                      ","parent_name":"IAPErrorCode"},"Enums/IAPRefreshResultState.html#/s:16InAppPurchaseLib21IAPRefreshResultStateO9succeededyA2CmF":{"name":"succeeded","abstract":"

                      Undocumented

                      ","parent_name":"IAPRefreshResultState"},"Enums/IAPRefreshResultState.html#/s:16InAppPurchaseLib21IAPRefreshResultStateO6failedyA2CmF":{"name":"failed","abstract":"

                      Undocumented

                      ","parent_name":"IAPRefreshResultState"},"Enums/IAPRefreshResultState.html#/s:16InAppPurchaseLib21IAPRefreshResultStateO7skippedyA2CmF":{"name":"skipped","abstract":"

                      Undocumented

                      ","parent_name":"IAPRefreshResultState"},"Enums/IAPPurchaseResultState.html#/s:16InAppPurchaseLib22IAPPurchaseResultStateO9purchasedyA2CmF":{"name":"purchased","abstract":"

                      Undocumented

                      ","parent_name":"IAPPurchaseResultState"},"Enums/IAPPurchaseResultState.html#/s:16InAppPurchaseLib22IAPPurchaseResultStateO6failedyA2CmF":{"name":"failed","abstract":"

                      Undocumented

                      ","parent_name":"IAPPurchaseResultState"},"Enums/IAPPurchaseResultState.html#/s:16InAppPurchaseLib22IAPPurchaseResultStateO9cancelledyA2CmF":{"name":"cancelled","abstract":"

                      Undocumented

                      ","parent_name":"IAPPurchaseResultState"},"Enums/IAPPurchaseResultState.html#/s:16InAppPurchaseLib22IAPPurchaseResultStateO8deferredyA2CmF":{"name":"deferred","abstract":"

                      Undocumented

                      ","parent_name":"IAPPurchaseResultState"},"Enums/IAPPurchaseResultState.html":{"name":"IAPPurchaseResultState","abstract":"

                      Undocumented

                      "},"Enums/IAPRefreshResultState.html":{"name":"IAPRefreshResultState","abstract":"

                      Undocumented

                      "},"Enums/IAPErrorCode.html":{"name":"IAPErrorCode","abstract":"

                      Undocumented

                      "},"Enums/IAPProductType.html":{"name":"IAPProductType","abstract":"

                      Undocumented

                      "},"Enums/IAPPeriodFormat.html":{"name":"IAPPeriodFormat","abstract":"

                      Undocumented

                      "},"Classes/DefaultPurchaseDelegate.html#/s:16InAppPurchaseLib07DefaultC8DelegateCACycfc":{"name":"init()","abstract":"

                      Undocumented

                      ","parent_name":"DefaultPurchaseDelegate"},"Classes/DefaultPurchaseDelegate.html#/s:16InAppPurchaseLib07DefaultC8DelegateC16productPurchased0G10IdentifierySS_tF":{"name":"productPurchased(productIdentifier:)","abstract":"

                      Finish the product transactions when a product is newly purchased, updated or restored.

                      ","parent_name":"DefaultPurchaseDelegate"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C11iapProductsSayAA10IAPProductVGvpZ":{"name":"iapProducts","abstract":"

                      The array of IAPProduct.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C18validatorUrlStringSSSgvpZ":{"name":"validatorUrlString","abstract":"

                      The validator url retrieved from Fovea.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C03iapC8DelegateAA011IAPPurchaseF0_pSgvpZ":{"name":"iapPurchaseDelegate","abstract":"

                      The instance of class that adopts the IAPPurchaseDelegate protocol.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C19applicationUsernameSSSgvpZ":{"name":"applicationUsername","abstract":"

                      The user name, if your app implements user login.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C10initialize11iapProducts18validatorUrlString0fC8Delegate19applicationUsernameySayAA10IAPProductVG_SSAA011IAPPurchaseK0_pSSSgtFZ":{"name":"initialize(iapProducts:validatorUrlString:iapPurchaseDelegate:applicationUsername:)","abstract":"

                      Start observing the payment queue, as soon as possible, and refresh Product list and user Receipt.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C4stopyyFZ":{"name":"stop()","abstract":"

                      Stop observing the payment queue, when the application will terminate, for proper cleanup.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C7refresh8callbackyyAA16IAPRefreshResultVc_tFZ":{"name":"refresh(callback:)","abstract":"

                      Refresh Product list and user Receipt.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C11getProductsSaySo9SKProductCGyFZ":{"name":"getProducts()","abstract":"

                      Gets all products retrieved from the App Store

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C12getProductBy10identifierSo9SKProductCSgSS_tFZ":{"name":"getProductBy(identifier:)","abstract":"

                      Gets the product by its identifier from the list of products retrieved from the App Store.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C15canMakePaymentsSbyFZ":{"name":"canMakePayments()","abstract":"

                      Checks if the user is allowed to authorize payments.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C8purchase17productIdentifier8quantity8callbackySS_SiyAA17IAPPurchaseResultVctFZ":{"name":"purchase(productIdentifier:quantity:callback:)","abstract":"

                      Request a Payment from the App Store.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C16restorePurchases8callbackyyAA16IAPRefreshResultVc_tFZ":{"name":"restorePurchases(callback:)","abstract":"

                      Restore purchased products.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C18finishTransactions3forySS_tFZ":{"name":"finishTransactions(for:)","abstract":"

                      Finish all transactions for the product.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C22hasDeferredTransaction3forSbSS_tFZ":{"name":"hasDeferredTransaction(for:)","abstract":"

                      Checks if the last transaction state for a given product was deferred.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C19hasAlreadyPurchasedSbyFZ":{"name":"hasAlreadyPurchased()","abstract":"

                      Checks if the user has already purchased at least one product.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C09hasActiveC03forSbSS_tFZ":{"name":"hasActivePurchase(for:)","abstract":"

                      Checks if the user currently own (or is subscribed to) a given product (nonConsumable or autoRenewableSubscription).

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C21hasActiveSubscriptionSbyFZ":{"name":"hasActiveSubscription()","abstract":"

                      Checks if the user has an active auto renewable subscription regardless of the product identifier.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C03getC4Date3for10Foundation0F0VSgSS_tFZ":{"name":"getPurchaseDate(for:)","abstract":"

                      Returns the latest purchased date for a given product.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C13getExpiryDate3for10Foundation0G0VSgSS_tFZ":{"name":"getExpiryDate(for:)","abstract":"

                      Returns the expiry date for a subcription. May be past or future.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html":{"name":"InAppPurchase","abstract":"

                      Undocumented

                      "},"Classes/DefaultPurchaseDelegate.html":{"name":"DefaultPurchaseDelegate","abstract":"

                      The default implementation of IAPPurchaseDelegate if no other is provided.

                      "},"Classes.html":{"name":"Classes","abstract":"

                      The following classes are available globally.

                      "},"Enums.html":{"name":"Enumerations","abstract":"

                      The following enumerations are available globally.

                      "},"Extensions.html":{"name":"Extensions","abstract":"

                      The following extensions are available globally.

                      "},"Protocols.html":{"name":"Protocols","abstract":"

                      The following protocols are available globally.

                      "},"Structs.html":{"name":"Structures","abstract":"

                      The following structures are available globally.

                      "},"Typealiases.html":{"name":"Type Aliases","abstract":"

                      The following type aliases are available globally.

                      "}} \ No newline at end of file diff --git a/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/docSet.dsidx b/docs/jazzy/docsets/InAppPurchaseLib.docset/Contents/Resources/docSet.dsidx deleted file mode 100644 index cf3069f1311183d6dcd7f3df0616d213b570da2b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 45056 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 diff --git a/docs/jazzy/docsets/InAppPurchaseLib.tgz b/docs/jazzy/docsets/InAppPurchaseLib.tgz deleted file mode 100644 index 1579f8147941cabc1faa3487cea265b1d9d1aad7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 71508 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

                      bOlj3WEBhCL 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 diff --git a/docs/jazzy/index.html b/docs/jazzy/index.html deleted file mode 100644 index e633951..0000000 --- a/docs/jazzy/index.html +++ /dev/null @@ -1,699 +0,0 @@ - - - - InAppPurchaseLib Reference - - - - - - - - - -

                      -
                      -

                      InAppPurchaseLib (52% documented)

                      -

                      View on GitHub

                      -
                      -
                      -
                      - -
                      -
                      - -
                      -
                      -
                      - -

                      - -

                      - -
                      -

                      An easy-to-use library for In-App Purchases, using Fovea.Billing for receipts validation.

                      -
                      - - -

                      Features

                      - -
                        -
                      • ✅ Purchase a product
                      • -
                      • ✅ Restore purchased products
                      • -
                      • ✅ Verify transactions with the App Store on Fovea.Billing server
                      • -
                      • ✅ Handle and notify payment transaction states
                      • -
                      • ✅ Retreive products information from the App Store
                      • -
                      • ✅ Support all product types (consumable, non-consumable, auto-renewable subscription, non-renewing subscription)
                      • -
                      • ✅ Status of purchases available when offline
                      • -
                      • ✅ Server integration with a Webhook
                      • -
                      -

                      Getting Started

                      - -

                      If you haven’t already, I highly recommend your read the Overview and Preparing section of Apple’s In-App Purchase official documentation

                      -

                      Requirements

                      - -
                        -
                      • Configure your App and Xcode to support In-App Purchases. - -
                      • -
                      • Create and configure your Fovea.Billing project account: - -
                          -
                        • Set your bundle ID
                        • -
                        • The iOS Shared Secret (or shared key) is to be retrieved from AppStoreConnect
                        • -
                        • The iOS Subscription Status URL (only if you want subscriptions)
                        • -
                      • -
                      -

                      Installation

                      - -

                      - -

                      - -
                        -
                      • Select your project in Xcode
                      • -
                      • Go to the section Swift Package
                      • -
                      • Click on (+) Add Package Dependency
                      • -
                      • Copy the Git URL: https://github.com/iridescent-dev/iap-swift-lib.git
                      • -
                      • Click on Next > Next
                      • -
                      • Make sure your project is selected in Add to target
                      • -
                      • Click on Finish
                      • -
                      - -

                      Note: You have to import InAppPurchaseLib wherever you use the library.

                      -

                      Usage

                      - -

                      The process of implementing in-app purchases involves several steps:

                      - -
                        -
                      1. Displaying the list of purchasable products
                      2. -
                      3. Initiating a purchase
                      4. -
                      5. Delivering and finalizing a purchase
                      6. -
                      7. Checking the current ownership of non-consumables and subscriptions
                      8. -
                      9. Implementing the Restore Purchases button
                      10. -
                      -

                      Initialization

                      - -

                      Before everything else the library must be initialized. This has to happen as soon as possible. A good way is to call the InAppPurchase.initialize() method when the application did finish launching. In the background, this will load your products and refresh the status of purchases and subscriptions.

                      - -

                      InAppPurchase.initialize() accepts the following arguments:

                      - -
                        -
                      • iapProducts - An array of IAPProduct (REQUIRED)
                      • -
                      • validatorUrlString - The validator url retrieved from Fovea (REQUIRED)
                      • -
                      • applicationUsername - The user name, if your app implements user login (optional)
                      • -
                      - -

                      Each IAPProduct contains the following fields:

                      - -
                        -
                      • productIdentifier - The product unique identifier
                      • -
                      • productType - The IAPProductType (consumable, nonConsumable, nonRenewingSubscription or autoRenewableSubscription)
                      • -
                      - -

                      Example:

                      - -

                      A good place is generally in your application delegate’s didFinishLaunchingWithOptions function, like below:

                      -
                      import InAppPurchaseLib
                      -
                      -class AppDelegate: UIResponder, UIApplicationDelegate, IAPPurchaseDelegate {
                      -  ...
                      -  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
                      -    InAppPurchase.initialize(
                      -      iapProducts: [
                      -        IAPProduct(productIdentifier: "monthly_plan", productType: .autoRenewableSubscription),
                      -        IAPProduct(productIdentifier: "yearly_plan",  productType: .autoRenewableSubscription),
                      -        IAPProduct(productIdentifier: "disable_ads",  productType: .nonConsumable)
                      -      ],
                      -      validatorUrlString: "https://validator.fovea.cc/v1/validate?appName=demo&apiKey=12345678")
                      -  }
                      -
                      -  func productPurchased(productIdentifier: String) {
                      -    // ... process purchase (we'll see that later)
                      -  }
                      -}
                      -
                      - -

                      You should also call the stop method when the application will terminate, for proper cleanup.

                      -
                        func applicationWillTerminate(_ application: UIApplication) {
                      -    InAppPurchase.stop()
                      -  }
                      -
                      - -

                      For more advanced use cases, in particular when you have implemented user login, you’ll have to make some adjustments. We’ll learn more about this in the Server integration section.

                      - -

                      Tip: If initialization was successful, you should see a new receipt validation event in Fovea’s Dashboard.

                      -

                      Displaying products

                      - -

                      Let’s start with the simplest case: you have a single product.

                      - -

                      You can retrieve all information about this product using the function InAppPurchase.getProductBy(identifier: "my_product_id"). This returns an SKProduct extended with helpful methods.

                      - -

                      Those are the most important:

                      - -
                        -
                      • productIdentifier: String - The string that identifies the product to the Apple AppStore.
                      • -
                      • localizedTitle: String - The name of the product, in the language of the device, as retrieved from the AppStore.
                      • -
                      • localizedDescription: String - A description of the product, in the language of the device, as retrieved from the AppStore.
                      • -
                      • localizedPrice: String - The cost of the product in the local currency (read-only property added by this library).
                      • -
                      - -

                      Example:

                      - -

                      You can add a function similar to this to your view.

                      -
                      @objc func refreshView() {
                      -  guard let product: SKProduct = InAppPurchase.getProductBy(identifier: "my_product_id") else {
                      -    self.titleLabel.text = "Product unavailable"
                      -    return
                      -  }
                      -  self.titleLabel.text = product.localizedTitle
                      -  self.descriptionLabel.text = product.localizedDescription
                      -  self.priceLabel.text = product.localizedPrice
                      -}
                      -
                      - -

                      This example assumes self.titleLabel is a UILabel, etc.

                      - -

                      Make sure to call this function when the view appears on screen, for instance by calling it from viewWillAppear.

                      -
                      override func viewWillAppear(_ animated: Bool) {
                      -  self.refreshView()
                      -}
                      -
                      -

                      Displaying subscriptions

                      - -

                      For subscription products, you also have some data about subscription periods and introductory offers.

                      - -
                        -
                      • func hasIntroductoryPriceEligible() -> Bool - The product has an introductory price the user is eligible to.
                      • -
                      • localizedSubscriptionPeriod: String? - The period of the subscription.
                      • -
                      • localizedIntroductoryPrice: String? - The cost of the introductory offer if available in the local currency.
                      • -
                      • localizedIntroductoryPeriod: String? - The subscription period of the introductory offer.
                      • -
                      • localizedIntroductoryDuration: String? - The duration of the introductory offer.
                      • -
                      - -

                      Example

                      -
                      @objc func refreshView() {
                      -  guard let product: SKProduct = InAppPurchase.getProductBy(identifier: "my_product_id") else {
                      -    self.titleLabel.text = "Product unavailable"
                      -    return
                      -  }
                      -  self.titleLabel.text = product.localizedTitle
                      -  self.descriptionLabel.text = product.localizedDescription
                      -
                      -  // Format price text. Example: "0,99€ / month for 3 months (then 3,99 € / month)"
                      -  var priceText = "\(product.localizedPrice) / \(product.localizedSubscriptionPeriod!)"
                      -  if product.hasIntroductoryPriceEligible() {
                      -      if product.introductoryPrice!.numberOfPeriods == 1 {
                      -          priceText = "\(product.localizedIntroductoryPrice!) for \(product.localizedIntroductoryDuration!)" +
                      -          " (then \(priceText))"
                      -      } else {
                      -          priceText = "\(product.localizedIntroductoryPrice!) / \(product.localizedIntroductoryPeriod!)" +
                      -          " for \(product.localizedIntroductoryDuration!) (then \(priceText))"
                      -      }
                      -  }
                      -  self.priceLabel.text = priceText
                      -}
                      -
                      - -

                      Note: You have to import StoreKit wherever you use SKProduct.

                      -

                      Refreshing

                      - -

                      Data might change or not be yet available when your “product” view is presented. In order to properly handle those cases, you should refresh your view after refreshing in-app products metadata. You want to be sure you’re displaying up-to-date information.

                      - -

                      To achieve this, call InAppPurchase.refresh() when your view is presented.

                      -
                      override func viewWillAppear(_ animated: Bool) {
                      -  self.refreshView()
                      -  InAppPurchase.refresh(callback: { _ in
                      -      self.refreshView()
                      -  })
                      -}
                      -
                      -

                      Purchasing

                      - -

                      The purchase process is generally a little bit more involving than most people would expect. Why is it not just: purchase → on success unlock the feature?

                      - -

                      Several reasons:

                      - -
                        -
                      • In-app purchases can be initiated outside the app
                      • -
                      • In-app purchases can be deferred, pending parental approval
                      • -
                      • Apple wants to be sure you delivered the product before charging the user
                      • -
                      - -

                      That is why the process looks like so:

                      - -
                        -
                      • being ready to handle purchase events from app startup
                      • -
                      • finalizing transactions when product delivery is complete
                      • -
                      • sending purchase request, for which successful doesn’t always mean complete
                      • -
                      -

                      Initiating a purchase

                      - -

                      To initiate a purchase, use the InAppPurchase.purchase() function. It takes the productIdentifier and a callback function, called when the purchase has been processed.

                      - -

                      Important: Do not process the purchase here, we’ll handle that later!

                      - -

                      From this callback, you can for example unlock the UI by hiding your loading indicator and display a message to the user.

                      - -

                      Example:

                      -
                      self.loaderView.show()
                      -InAppPurchase.purchase(
                      -  productIdentifier: "my_product_id",
                      -  callback: { _ in
                      -    self.loaderView.hide()
                      -})
                      -
                      - -

                      This simple example locks the UI with a loader when the purchase is in progress. We’ll see later how the purchase has to be processed by your applicaiton.

                      - -

                      The callback also gives more information about the outcome of the purchase, you might want to use it to update your UI as well. Note that some events are useful for analytics. So here’s a more complete example.

                      -
                      self.loaderView.show()
                      -InAppPurchase.purchase(
                      -  productIdentifier: "my_product_id",
                      -  callback: { result in
                      -    self.loaderView.hide()
                      -
                      -    switch result.state {
                      -    case .purchased:
                      -      // Product successfully purchased
                      -      // Reminder: Do not process the purchase here, only update your UI.
                      -      //           that's why we do not send data to analytics.
                      -      openThankYouScreen()
                      -    case .failed:
                      -      // Purchase failed
                      -      // - Human formated reason can be found in result.localizedDescription
                      -      // - More details in either result.skError or result.iapError
                      -      showError(result.localizedDescription)
                      -    case .deferred:
                      -      // The purchase is deferred, waiting for the parent's approval
                      -      openWaitingParentApprovalScreen()
                      -    case .cancelled:
                      -      // The user canceled the request, generally only useful for analytics.
                      -  }
                      -})
                      -
                      - -

                      If the purchase fails, result will contain either .skError, a SKError from StoreKit, or .iapError, an IAPError.

                      - -

                      Tip: After a successful purchase, you should see a new transaction in Fovea’s dashboard.

                      -

                      Handling purchases

                      - -

                      Finally, the magic happened: a user purchased one of your products! Let’s see how we handle the different types of products.

                      -

                      Non-Consumables

                      - -

                      Wherever your app needs to know if a non-consumable product has been purchased, use InAppPurchase.hasActivePurchase(for: -productIdentifier). This will return true if the user currently owns the product.

                      - -

                      Note: The last known state for the user’s purchases is stored as UserDefaults. As such, their status is always available to your app, even when offline.

                      - -

                      If you have a server that needs to know about the purchase. You should rely on Fovea’s webhook instead of doing anything in here. We will see that later in the Server integration section.

                      -

                      Auto-Renewable Subscriptions

                      - -

                      As with non-consumables, you will use InAppPurchase.hasActivePurchase(for: productIdentifier) to check if the user is an active subscriber to a given product.

                      - -

                      You might also like to call refresh regularly, for example when entering your main view. When appropriate, the library will refresh the receipt to detect subscription renewals or expiry.

                      - -

                      As we’ve seend in the Refreshing section:

                      -
                      override func viewWillAppear(_ animated: Bool) {
                      -  self.refreshView()
                      -  InAppPurchase.refresh(callback: { _ in
                      -      self.refreshView()
                      -  })
                      -}
                      -
                      - -

                      Note: Don’t be reluctant to call refresh() often. Internally, the library ensures heavy operation are only performed if necessary: for example when a subscription just expired. So in 99% of cases this call will result in no-operations.

                      -

                      Consumables

                      - -

                      If the purchased products in a consumable, your app is responsible for delivering the purchase then acknowlege that you’ve done so. Delivering generally consists in increasing a counter for some sort of virtual currency.

                      - -

                      Your app can be notified of a purchase at any time. So the library asks you to provide an IAPPurchaseDelegate from initialization.

                      - -

                      In InAppPurchase.initialize(), we can pass an IAPPurchaseDelegate instance. This object implements the productPurchased(productIdentifier:) function, which is called whenever a purchase is approved.

                      - -

                      Here’s a example implementation:

                      -
                      class AppDelegate: UIResponder, UIApplicationDelegate, IAPPurchaseDelegate {
                      -  ...
                      -  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
                      -    InAppPurchase.initialize(
                      -      iapProducts: [...],
                      -      iapPurchaseDelegate: self, // ADDED: iapPurchaseDelegate
                      -      validatorUrlString: "https://validator.fovea.cc/v1/validate?appName=demo&apiKey=12345678")
                      -  }
                      -
                      -  // IAPPurchaseDelegate implementation
                      -  func productPurchased(productIdentifier: String) {
                      -    // TODO
                      -  }
                      -}
                      -
                      - -

                      It’s also important to know that when a purchase is approved, money isn’t yet to reach your bank account. You have to acknowledge delivery of the (virtual) item to finalize the transaction. That is why we have to call InAppPurchase.finishTransactions(for: productIdentifier) as soon as we delivered the product.

                      - -

                      Example

                      - -

                      Let’s define a class that adopts the IAPPurchaseDelegate protocol, it can very well be your application delegate.

                      -
                      func productPurchased(productIdentifier: String) {
                      -  switch productIdenfier {
                      -  case "10_silver":
                      -    addSilver(10)
                      -  case "100_silver":
                      -    addSilver(100)
                      -  }
                      -  InAppPurchase.finishTransactions(for: productIdentifier)
                      -  Analytics.trackEvent("purchase succeeded", productIdentifier)
                      -}
                      -
                      - -

                      Here, we implement our own unlocking logic and call InAppPurchase.finishTransactions() afterward (assuming addSilver is synchronous).

                      - -

                      Note: productPurchased is called when a purchase has been confirmed by Fovea’s receipt validator. If you have a server, he probably already has been notified of this purchase using the webhook.

                      - -

                      Reminder: Keep in mind that purchase notifications might occur even if you never called the InAppPurchase.purchase() function: purchases can be made from another device or the AppStore, they can be approved by parents when the app isn’t running, purchase flows can be interupted, etc. The pattern above ensures your app is always ready to handle purchase events.

                      -

                      Non-Renewing Subscriptions

                      - -

                      For non-renewing subscriptions, delivering consists in increasing the amount of time a user can access a given feature. Apple doesn’t manage the length and expiry of non-renewing subscriptions: you have to do this yourself, as for consumables.

                      - -

                      Basically, everything is identical to consumables.

                      -

                      Restoring purchases

                      - -

                      Except if you only sell consumable products, Apple requires that you provide a “Restore Purchases” button to your users. In general, it is found in your application settings.

                      - -

                      Call this method when this button is pressed.

                      -
                      @IBAction func restorePurchases(_ sender: Any) {
                      -  self.loaderView.show()
                      -  InAppPurchase.restorePurchases(callback: { result in
                      -      self.loaderView.hide()
                      -      switch result.state {
                      -      case .succeeded:
                      -          if result.addedPurchases > 0 {
                      -              print("Restore purchases successful.")
                      -          } else {
                      -              print("No purchase to restore.")
                      -          }
                      -      case .failed:
                      -          print("Restore purchases failed.")
                      -      }
                      -  })
                      -}
                      -
                      - -

                      The callback method is called once the operation is complete. You can use it to unlock the UI, by hiding your loader for example, and display the adapted message to the user.

                      -

                      Displaying products with purchases

                      - -

                      In your store screen, where you present your products titles and prices with a purchase button, there are some cases to handle that we skipped. Owned products and deferred purchases.

                      -

                      Owned products

                      - -

                      Non-consumables and active auto-renewing subscriptions cannot be purchased again. You should adjust your UI to reflect that state. Refer to InAppPurchase.hasActivePurchase() to and to the example later in this section.

                      -

                      Deferred purchases

                      - -

                      Apple’s Ask to Buy feature lets parents approve any purchases initiated by children, including in-app purchases.

                      - -

                      With Ask to Buy enabled, when a child requests to make a purchase, the app is notified that the purchase is awaiting the parent’s approval in the purchase callback:

                      -
                      InAppPurchase.purchase(
                      -  productIdentifier: productIdentifier,
                      -  callback: { result in
                      -    switch result.state {
                      -    case .deferred:
                      -      // Pending parent approval
                      -  }
                      -})
                      -
                      - -

                      In the deferred case, the child has been notified by StoreKit that the parents have to approve the purchase. He might then close the app and come back later. You don’t have much to do, but to display in your UI that there is a purchase waiting for parental approval in your views.

                      - -

                      We will use the hasDeferredTransaction method:

                      -
                      InAppPurchase.hasDeferredTransaction(for productIdentifier: String) -> Bool
                      -
                      -

                      Example

                      - -

                      Here’s an example that covers what has been discussed above. We will update our example refreshView function from before:

                      -
                      @objc func refreshView() {
                      -  guard let product: SKProduct = InAppPurchase.getProductBy(identifier: "my_product_id") else {
                      -    self.titleLabel.text = "Product unavailable"
                      -    return
                      -  }
                      -  self.titleLabel.text = product.localizedTitle
                      -  // ...
                      -
                      -  // "Ask to Buy" deferred purchase waiting for parent's approval
                      -  if InAppPurchase.hasDeferredTransaction(for: "my_product_id") {
                      -    self.statusLabel.text = "Waiting for Approval..."
                      -    self.purchaseButton.isPointerInteractionEnabled = false
                      -  }
                      -  // "Owned" product
                      -  else if InAppPurchase.hasActivePurchase(for: "my_product_id") {
                      -    self.statusLabel.text = "OWNED"
                      -    self.purchaseButton.isPointerInteractionEnabled = false
                      -  }
                      -  else {
                      -    self.purchaseButton.isPointerInteractionEnabled = true
                      -  }
                      -}
                      -
                      - -

                      When a product is owned or has a deferred purchase, we make sure the purchase button is grayed out. We also use a status label to display some details. Of course, you are free to design your UI as you see fit.

                      -

                      Errors

                      - -

                      When calling refresh(), purchase() or restorePurchases(), the callback can return an IAPError if the state is failed. -Here is the list of IAPErrorCode you can receive:

                      - -
                        -
                      • Errors returned by refresh(), purchase() or restorePurchases()

                        - -
                          -
                        • libraryNotInitialized - You must call the initialize fuction before using the library.
                        • -
                        • bundleIdentifierInvalid - The Bundle Identifier is invalid.
                        • -
                        • validatorUrlInvalid - The Validator URL String is invalid.
                        • -
                        • refreshReceiptFailed - Failed to refresh the App Store receipt.
                        • -
                        • validateReceiptFailed - Failed to validate the App Store receipt with Fovea.
                        • -
                        • readReceiptFailed - Failed to read the receipt validation.
                        • -
                      • -
                      • Errors returned by refresh()

                        - -
                          -
                        • refreshProductsFailed - Failed to refresh products from the App Store.
                        • -
                      • -
                      • Errors returned by purchase()

                        - -
                          -
                        • productNotFound - The product was not found on the App Store and cannot be purchased.
                        • -
                        • cannotMakePurchase - The user is not allowed to authorize payments.
                        • -
                        • alreadyPurchasing - A purchase is already in progress.
                        • -
                      • -
                      -

                      Analytics

                      - -

                      Tracking the purchase flow is a common things in apps. Especially as it’s core to your revenue model.

                      - -

                      We can track 5 events, which step in the purchase pipeline a user reached.

                      - -
                        -
                      1. purchase initiated
                      2. -
                      3. purchase cancelled
                      4. -
                      5. purchase failed
                      6. -
                      7. purchase deferred
                      8. -
                      9. purchase succeeded
                      10. -
                      - -

                      Here’s a quick example showing how to implement this correctly.

                      -
                      func makePurchase() {
                      -  Analytics.trackEvent("purchase initiated")
                      -  InAppPurchase.purchase(
                      -    productIdentifier: "my_product_id",
                      -    callback: { result in
                      -      switch result.state {
                      -      case .purchased:
                      -        // Reminder: We are not processing the purchase here, only updating your UI.
                      -        //           That's why we do not send an event to analytics.
                      -      case .failed:
                      -        Analytics.trackEvent("purchase failed")
                      -      case .deferred:
                      -        Analytics.trackEvent("purchase deferred")
                      -      case .cancelled:
                      -        Analytics.trackEvent("purchase cancelled")
                      -    }
                      -  })
                      -}
                      -
                      -// IAPPurchaseDelegate implementation
                      -func productPurchased(productIdentifier: String) {
                      -  Analytics.trackEvent("purchase succeeded")
                      -  InAppPurchase.finishTransactions(for: productIdentifier)
                      -}
                      -
                      - -

                      The important part to remember is that a purchase can occur outside your app (or be approved when the app is not running), that’s why tracking purchase succeeded has to be part of the productPurchased delegate function.

                      - -

                      Refer to the Consumables section to learn more about the productPurchased function.

                      -

                      Server integration

                      - -

                      In more advanced use cases, you have a server component. Users are logged in and you’ll like to unlock the content for this user on your server. The safest approach is to setup a Webhook on Fovea. You’ll receive notifications from Fovea that transaction have been processed and/or subscriptions updated.

                      - -

                      The information sent from Fovea has been verified from Apple’s server, which makes it way more trustable than information sent from your app itself.

                      - -

                      To take advantage of this, you have to inform the library of your application username. This applicationUsername can be provided as a parameter of the InAppPurchase.initialize method and updated later by changing the associated property.

                      - -

                      Example:

                      -
                      InAppPurchase.initialize(
                      -  iapProducts: [...],
                      -  validatorUrlString: "..."),
                      -  applicationUsername: UserSession.getUserId())
                      -
                      -// later ...
                      -InAppPurchase.applicationUsername = UserSession.getUserId()
                      -
                      - -

                      If a user account is mandatory in your app, you will want to delay calls to InAppPurchase.initialize() to when your user’s session is ready.

                      - -

                      Do not hesitate to contact Fovea for help.

                      -

                      Xcode Demo Project

                      - -

                      Do not hesitate to check the demo project available on here: iap-swift-lib-demo.

                      -

                      References

                      - - -

                      Coding

                      - -

                      Generate the documentation, using this fork of swift-doc (on --minimum-access-level is part of the main distrib).

                      -
                      swift-doc generate sources --module-name InAppPurchase --format html --output docs --minimum-access-level public --base-url /iap-swift-lib/
                      -
                      -

                      Troubleshooting

                      - -

                      Common issues are covered here: https://github.com/iridescent-dev/iap-swift-lib/wiki/Troubleshooting

                      -

                      License

                      - -

                      InAppPurchaseLib is open-sourced library licensed under the MIT License. See LICENSE for details.

                      - -
                      -
                      - -
                      -
                      - - - diff --git a/docs/jazzy/search.json b/docs/jazzy/search.json deleted file mode 100644 index df4266d..0000000 --- a/docs/jazzy/search.json +++ /dev/null @@ -1 +0,0 @@ -{"Typealiases.html#/s:16InAppPurchaseLib19IAPPurchaseCallbacka":{"name":"IAPPurchaseCallback","abstract":"

                      Undocumented

                      "},"Typealiases.html#/s:16InAppPurchaseLib18IAPRefreshCallbacka":{"name":"IAPRefreshCallback","abstract":"

                      Undocumented

                      "},"Structs/IAPProduct.html#/s:16InAppPurchaseLib10IAPProductV17productIdentifierSSvp":{"name":"productIdentifier","abstract":"

                      The identifier of the product.

                      ","parent_name":"IAPProduct"},"Structs/IAPProduct.html#/s:16InAppPurchaseLib10IAPProductV11productTypeAA0eG0Ovp":{"name":"productType","abstract":"

                      The type of the product.

                      ","parent_name":"IAPProduct"},"Structs/IAPProduct.html#/s:16InAppPurchaseLib10IAPProductV17productIdentifier0F4TypeACSS_AA0eH0Otcfc":{"name":"init(productIdentifier:productType:)","abstract":"

                      Initializes an IAPProduct with its identifier and type.

                      ","parent_name":"IAPProduct"},"Structs/IAPError.html#/s:16InAppPurchaseLib8IAPErrorV4codeAA0E4CodeOvp":{"name":"code","abstract":"

                      Undocumented

                      ","parent_name":"IAPError"},"Structs/IAPError.html#/s:16InAppPurchaseLib8IAPErrorV20localizedDescriptionSSvp":{"name":"localizedDescription","abstract":"

                      Undocumented

                      ","parent_name":"IAPError"},"Structs/IAPRefreshResult.html#/s:16InAppPurchaseLib16IAPRefreshResultV5stateAA0eF5StateOvp":{"name":"state","abstract":"

                      Undocumented

                      ","parent_name":"IAPRefreshResult"},"Structs/IAPRefreshResult.html#/s:16InAppPurchaseLib16IAPRefreshResultV8iapErrorAA8IAPErrorVSgvp":{"name":"iapError","abstract":"

                      Undocumented

                      ","parent_name":"IAPRefreshResult"},"Structs/IAPRefreshResult.html#/s:16InAppPurchaseLib16IAPRefreshResultV14addedPurchasesSivp":{"name":"addedPurchases","abstract":"

                      Undocumented

                      ","parent_name":"IAPRefreshResult"},"Structs/IAPRefreshResult.html#/s:16InAppPurchaseLib16IAPRefreshResultV16updatedPurchasesSivp":{"name":"updatedPurchases","abstract":"

                      Undocumented

                      ","parent_name":"IAPRefreshResult"},"Structs/IAPPurchaseResult.html#/s:16InAppPurchaseLib17IAPPurchaseResultV5stateAA0eF5StateOvp":{"name":"state","abstract":"

                      Undocumented

                      ","parent_name":"IAPPurchaseResult"},"Structs/IAPPurchaseResult.html#/s:16InAppPurchaseLib17IAPPurchaseResultV8iapErrorAA8IAPErrorVSgvp":{"name":"iapError","abstract":"

                      Undocumented

                      ","parent_name":"IAPPurchaseResult"},"Structs/IAPPurchaseResult.html#/s:16InAppPurchaseLib17IAPPurchaseResultV7skErrorSC11SKErrorCodeLeVSgvp":{"name":"skError","abstract":"

                      Undocumented

                      ","parent_name":"IAPPurchaseResult"},"Structs/IAPPurchaseResult.html#/s:16InAppPurchaseLib17IAPPurchaseResultV20localizedDescriptionSSSgvp":{"name":"localizedDescription","abstract":"

                      Undocumented

                      ","parent_name":"IAPPurchaseResult"},"Structs/IAPPurchaseResult.html":{"name":"IAPPurchaseResult","abstract":"

                      Undocumented

                      "},"Structs/IAPRefreshResult.html":{"name":"IAPRefreshResult","abstract":"

                      Undocumented

                      "},"Structs/IAPError.html":{"name":"IAPError","abstract":"

                      Undocumented

                      "},"Structs/IAPProduct.html":{"name":"IAPProduct","abstract":"

                      Undocumented

                      "},"Protocols/IAPPurchaseDelegate.html#/s:16InAppPurchaseLib19IAPPurchaseDelegateP16productPurchased0G10IdentifierySS_tF":{"name":"productPurchased(productIdentifier:)","abstract":"

                      Called when a product is newly purchased, updated or restored.

                      ","parent_name":"IAPPurchaseDelegate"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP11iapProductsSayAA10IAPProductVGvpZ":{"name":"iapProducts","abstract":"

                      The array of IAPProduct.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP18validatorUrlStringSSSgvpZ":{"name":"validatorUrlString","abstract":"

                      The validator url retrieved from Fovea.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP03iapC8DelegateAA011IAPPurchaseF0_pSgvpZ":{"name":"iapPurchaseDelegate","abstract":"

                      The instance of class that adopts the IAPPurchaseDelegate protocol.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP19applicationUsernameSSSgvpZ":{"name":"applicationUsername","abstract":"

                      The user name, if your app implements user login.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP10initialize11iapProducts18validatorUrlString0fC8Delegate19applicationUsernameySayAA10IAPProductVG_SSAA011IAPPurchaseK0_pSSSgtFZ":{"name":"initialize(iapProducts:validatorUrlString:iapPurchaseDelegate:applicationUsername:)","abstract":"

                      Start observing the payment queue, as soon as possible, and refresh Product list and user Receipt.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP4stopyyFZ":{"name":"stop()","abstract":"

                      Stop observing the payment queue, when the application will terminate, for proper cleanup.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP7refresh8callbackyyAA16IAPRefreshResultVc_tFZ":{"name":"refresh(callback:)","abstract":"

                      Refresh Product list and user Receipt.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP11getProductsSaySo9SKProductCGyFZ":{"name":"getProducts()","abstract":"

                      Gets all products retrieved from the App Store

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP12getProductBy10identifierSo9SKProductCSgSS_tFZ":{"name":"getProductBy(identifier:)","abstract":"

                      Gets the product by its identifier from the list of products retrieved from the App Store.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP15canMakePaymentsSbyFZ":{"name":"canMakePayments()","abstract":"

                      Checks if the user is allowed to authorize payments.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP8purchase17productIdentifier8quantity8callbackySS_SiyAA17IAPPurchaseResultVctFZ":{"name":"purchase(productIdentifier:quantity:callback:)","abstract":"

                      Request a Payment from the App Store.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP16restorePurchases8callbackyyAA16IAPRefreshResultVc_tFZ":{"name":"restorePurchases(callback:)","abstract":"

                      Restore purchased products.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP18finishTransactions3forySS_tFZ":{"name":"finishTransactions(for:)","abstract":"

                      Finish all transactions for the product.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP22hasDeferredTransaction3forSbSS_tFZ":{"name":"hasDeferredTransaction(for:)","abstract":"

                      Checks if the last transaction state for a given product was deferred.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP19hasAlreadyPurchasedSbyFZ":{"name":"hasAlreadyPurchased()","abstract":"

                      Checks if the user has already purchased at least one product.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP09hasActiveC03forSbSS_tFZ":{"name":"hasActivePurchase(for:)","abstract":"

                      Checks if the user currently own (or is subscribed to) a given product (nonConsumable or autoRenewableSubscription).

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP21hasActiveSubscriptionSbyFZ":{"name":"hasActiveSubscription()","abstract":"

                      Checks if the user has an active auto renewable subscription regardless of the product identifier.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP03getC4Date3for10Foundation0F0VSgSS_tFZ":{"name":"getPurchaseDate(for:)","abstract":"

                      Returns the latest purchased date for a given product.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP13getExpiryDate3for10Foundation0G0VSgSS_tFZ":{"name":"getExpiryDate(for:)","abstract":"

                      Returns the expiry date for a subcription. May be past or future.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/IAPErrorProtocol.html#/s:16InAppPurchaseLib16IAPErrorProtocolP4codeAA0E4CodeOvp":{"name":"code","abstract":"

                      Undocumented

                      ","parent_name":"IAPErrorProtocol"},"Protocols/IAPErrorProtocol.html":{"name":"IAPErrorProtocol","abstract":"

                      Undocumented

                      "},"Protocols/InAppPurchaseLib.html":{"name":"InAppPurchaseLib","abstract":"

                      The protocol that InAppPurchase` adopts.

                      "},"Protocols/IAPPurchaseDelegate.html":{"name":"IAPPurchaseDelegate","abstract":"

                      The protocol that you must adopt if you have consumable and/or nonRenewingSubscription products.

                      "},"Extensions/SKProduct.html#/s:So9SKProductC16InAppPurchaseLibE21localizedPeriodFormatAC09IAPPeriodH0OvpZ":{"name":"localizedPeriodFormat","abstract":"

                      Undocumented

                      ","parent_name":"SKProduct"},"Extensions/SKProduct.html#/s:So9SKProductC16InAppPurchaseLibE28hasIntroductoryPriceEligibleSbyF":{"name":"hasIntroductoryPriceEligible()","abstract":"

                      Checks if the product has an introductory price the user is eligible to.

                      ","parent_name":"SKProduct"},"Extensions/SKProduct.html#/s:So9SKProductC16InAppPurchaseLibE14localizedPriceSSvp":{"name":"localizedPrice","abstract":"

                      Returns a localized string with the cost of the product in the local currency.

                      ","parent_name":"SKProduct"},"Extensions/SKProduct.html#/s:So9SKProductC16InAppPurchaseLibE27localizedSubscriptionPeriodSSSgvp":{"name":"localizedSubscriptionPeriod","abstract":"

                      Returns a localized string with the period of the subscription product.

                      ","parent_name":"SKProduct"},"Extensions/SKProduct.html#/s:So9SKProductC16InAppPurchaseLibE26localizedIntroductoryPriceSSSgvp":{"name":"localizedIntroductoryPrice","abstract":"

                      Returns a localized string with the introductory price if available, in the local currency.

                      ","parent_name":"SKProduct"},"Extensions/SKProduct.html#/s:So9SKProductC16InAppPurchaseLibE27localizedIntroductoryPeriodSSSgvp":{"name":"localizedIntroductoryPeriod","abstract":"

                      Returns a localized string with the introductory price period of the subscription product.

                      ","parent_name":"SKProduct"},"Extensions/SKProduct.html#/s:So9SKProductC16InAppPurchaseLibE29localizedIntroductoryDurationSSSgvp":{"name":"localizedIntroductoryDuration","abstract":"

                      Returns a localized string with the duration of the introductory price.

                      ","parent_name":"SKProduct"},"Extensions/SKProduct.html":{"name":"SKProduct"},"Enums/IAPPeriodFormat.html#/s:16InAppPurchaseLib15IAPPeriodFormatO5shortyA2CmF":{"name":"short","abstract":"

                      Undocumented

                      ","parent_name":"IAPPeriodFormat"},"Enums/IAPPeriodFormat.html#/s:16InAppPurchaseLib15IAPPeriodFormatO4longyA2CmF":{"name":"long","abstract":"

                      Undocumented

                      ","parent_name":"IAPPeriodFormat"},"Enums/IAPProductType.html#/s:16InAppPurchaseLib14IAPProductTypeO10consumableyA2CmF":{"name":"consumable","abstract":"

                      Undocumented

                      ","parent_name":"IAPProductType"},"Enums/IAPProductType.html#/s:16InAppPurchaseLib14IAPProductTypeO13nonConsumableyA2CmF":{"name":"nonConsumable","abstract":"

                      Undocumented

                      ","parent_name":"IAPProductType"},"Enums/IAPProductType.html#/s:16InAppPurchaseLib14IAPProductTypeO23nonRenewingSubscriptionyA2CmF":{"name":"nonRenewingSubscription","abstract":"

                      Undocumented

                      ","parent_name":"IAPProductType"},"Enums/IAPProductType.html#/s:16InAppPurchaseLib14IAPProductTypeO25autoRenewableSubscriptionyA2CmF":{"name":"autoRenewableSubscription","abstract":"

                      Undocumented

                      ","parent_name":"IAPProductType"},"Enums/IAPErrorCode.html#/s:16InAppPurchaseLib12IAPErrorCodeO21libraryNotInitializedyA2CmF":{"name":"libraryNotInitialized","abstract":"

                      Undocumented

                      ","parent_name":"IAPErrorCode"},"Enums/IAPErrorCode.html#/s:16InAppPurchaseLib12IAPErrorCodeO15productNotFoundyA2CmF":{"name":"productNotFound","abstract":"

                      Undocumented

                      ","parent_name":"IAPErrorCode"},"Enums/IAPErrorCode.html#/s:16InAppPurchaseLib12IAPErrorCodeO010cannotMakeC0yA2CmF":{"name":"cannotMakePurchase","abstract":"

                      Undocumented

                      ","parent_name":"IAPErrorCode"},"Enums/IAPErrorCode.html#/s:16InAppPurchaseLib12IAPErrorCodeO17alreadyPurchasingyA2CmF":{"name":"alreadyPurchasing","abstract":"

                      Undocumented

                      ","parent_name":"IAPErrorCode"},"Enums/IAPErrorCode.html#/s:16InAppPurchaseLib12IAPErrorCodeO23bundleIdentifierInvalidyA2CmF":{"name":"bundleIdentifierInvalid","abstract":"

                      Undocumented

                      ","parent_name":"IAPErrorCode"},"Enums/IAPErrorCode.html#/s:16InAppPurchaseLib12IAPErrorCodeO19validatorUrlInvalidyA2CmF":{"name":"validatorUrlInvalid","abstract":"

                      Undocumented

                      ","parent_name":"IAPErrorCode"},"Enums/IAPErrorCode.html#/s:16InAppPurchaseLib12IAPErrorCodeO20refreshReceiptFailedyA2CmF":{"name":"refreshReceiptFailed","abstract":"

                      Undocumented

                      ","parent_name":"IAPErrorCode"},"Enums/IAPErrorCode.html#/s:16InAppPurchaseLib12IAPErrorCodeO21validateReceiptFailedyA2CmF":{"name":"validateReceiptFailed","abstract":"

                      Undocumented

                      ","parent_name":"IAPErrorCode"},"Enums/IAPErrorCode.html#/s:16InAppPurchaseLib12IAPErrorCodeO17readReceiptFailedyA2CmF":{"name":"readReceiptFailed","abstract":"

                      Undocumented

                      ","parent_name":"IAPErrorCode"},"Enums/IAPErrorCode.html#/s:16InAppPurchaseLib12IAPErrorCodeO21refreshProductsFailedyA2CmF":{"name":"refreshProductsFailed","abstract":"

                      Undocumented

                      ","parent_name":"IAPErrorCode"},"Enums/IAPRefreshResultState.html#/s:16InAppPurchaseLib21IAPRefreshResultStateO9succeededyA2CmF":{"name":"succeeded","abstract":"

                      Undocumented

                      ","parent_name":"IAPRefreshResultState"},"Enums/IAPRefreshResultState.html#/s:16InAppPurchaseLib21IAPRefreshResultStateO6failedyA2CmF":{"name":"failed","abstract":"

                      Undocumented

                      ","parent_name":"IAPRefreshResultState"},"Enums/IAPRefreshResultState.html#/s:16InAppPurchaseLib21IAPRefreshResultStateO7skippedyA2CmF":{"name":"skipped","abstract":"

                      Undocumented

                      ","parent_name":"IAPRefreshResultState"},"Enums/IAPPurchaseResultState.html#/s:16InAppPurchaseLib22IAPPurchaseResultStateO9purchasedyA2CmF":{"name":"purchased","abstract":"

                      Undocumented

                      ","parent_name":"IAPPurchaseResultState"},"Enums/IAPPurchaseResultState.html#/s:16InAppPurchaseLib22IAPPurchaseResultStateO6failedyA2CmF":{"name":"failed","abstract":"

                      Undocumented

                      ","parent_name":"IAPPurchaseResultState"},"Enums/IAPPurchaseResultState.html#/s:16InAppPurchaseLib22IAPPurchaseResultStateO9cancelledyA2CmF":{"name":"cancelled","abstract":"

                      Undocumented

                      ","parent_name":"IAPPurchaseResultState"},"Enums/IAPPurchaseResultState.html#/s:16InAppPurchaseLib22IAPPurchaseResultStateO8deferredyA2CmF":{"name":"deferred","abstract":"

                      Undocumented

                      ","parent_name":"IAPPurchaseResultState"},"Enums/IAPPurchaseResultState.html":{"name":"IAPPurchaseResultState","abstract":"

                      Undocumented

                      "},"Enums/IAPRefreshResultState.html":{"name":"IAPRefreshResultState","abstract":"

                      Undocumented

                      "},"Enums/IAPErrorCode.html":{"name":"IAPErrorCode","abstract":"

                      Undocumented

                      "},"Enums/IAPProductType.html":{"name":"IAPProductType","abstract":"

                      Undocumented

                      "},"Enums/IAPPeriodFormat.html":{"name":"IAPPeriodFormat","abstract":"

                      Undocumented

                      "},"Classes/DefaultPurchaseDelegate.html#/s:16InAppPurchaseLib07DefaultC8DelegateCACycfc":{"name":"init()","abstract":"

                      Undocumented

                      ","parent_name":"DefaultPurchaseDelegate"},"Classes/DefaultPurchaseDelegate.html#/s:16InAppPurchaseLib07DefaultC8DelegateC16productPurchased0G10IdentifierySS_tF":{"name":"productPurchased(productIdentifier:)","abstract":"

                      Finish the product transactions when a product is newly purchased, updated or restored.

                      ","parent_name":"DefaultPurchaseDelegate"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C11iapProductsSayAA10IAPProductVGvpZ":{"name":"iapProducts","abstract":"

                      The array of IAPProduct.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C18validatorUrlStringSSSgvpZ":{"name":"validatorUrlString","abstract":"

                      The validator url retrieved from Fovea.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C03iapC8DelegateAA011IAPPurchaseF0_pSgvpZ":{"name":"iapPurchaseDelegate","abstract":"

                      The instance of class that adopts the IAPPurchaseDelegate protocol.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C19applicationUsernameSSSgvpZ":{"name":"applicationUsername","abstract":"

                      The user name, if your app implements user login.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C10initialize11iapProducts18validatorUrlString0fC8Delegate19applicationUsernameySayAA10IAPProductVG_SSAA011IAPPurchaseK0_pSSSgtFZ":{"name":"initialize(iapProducts:validatorUrlString:iapPurchaseDelegate:applicationUsername:)","abstract":"

                      Start observing the payment queue, as soon as possible, and refresh Product list and user Receipt.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C4stopyyFZ":{"name":"stop()","abstract":"

                      Stop observing the payment queue, when the application will terminate, for proper cleanup.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C7refresh8callbackyyAA16IAPRefreshResultVc_tFZ":{"name":"refresh(callback:)","abstract":"

                      Refresh Product list and user Receipt.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C11getProductsSaySo9SKProductCGyFZ":{"name":"getProducts()","abstract":"

                      Gets all products retrieved from the App Store

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C12getProductBy10identifierSo9SKProductCSgSS_tFZ":{"name":"getProductBy(identifier:)","abstract":"

                      Gets the product by its identifier from the list of products retrieved from the App Store.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C15canMakePaymentsSbyFZ":{"name":"canMakePayments()","abstract":"

                      Checks if the user is allowed to authorize payments.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C8purchase17productIdentifier8quantity8callbackySS_SiyAA17IAPPurchaseResultVctFZ":{"name":"purchase(productIdentifier:quantity:callback:)","abstract":"

                      Request a Payment from the App Store.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C16restorePurchases8callbackyyAA16IAPRefreshResultVc_tFZ":{"name":"restorePurchases(callback:)","abstract":"

                      Restore purchased products.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C18finishTransactions3forySS_tFZ":{"name":"finishTransactions(for:)","abstract":"

                      Finish all transactions for the product.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C22hasDeferredTransaction3forSbSS_tFZ":{"name":"hasDeferredTransaction(for:)","abstract":"

                      Checks if the last transaction state for a given product was deferred.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C19hasAlreadyPurchasedSbyFZ":{"name":"hasAlreadyPurchased()","abstract":"

                      Checks if the user has already purchased at least one product.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C09hasActiveC03forSbSS_tFZ":{"name":"hasActivePurchase(for:)","abstract":"

                      Checks if the user currently own (or is subscribed to) a given product (nonConsumable or autoRenewableSubscription).

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C21hasActiveSubscriptionSbyFZ":{"name":"hasActiveSubscription()","abstract":"

                      Checks if the user has an active auto renewable subscription regardless of the product identifier.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C03getC4Date3for10Foundation0F0VSgSS_tFZ":{"name":"getPurchaseDate(for:)","abstract":"

                      Returns the latest purchased date for a given product.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C13getExpiryDate3for10Foundation0G0VSgSS_tFZ":{"name":"getExpiryDate(for:)","abstract":"

                      Returns the expiry date for a subcription. May be past or future.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html":{"name":"InAppPurchase","abstract":"

                      Undocumented

                      "},"Classes/DefaultPurchaseDelegate.html":{"name":"DefaultPurchaseDelegate","abstract":"

                      The default implementation of IAPPurchaseDelegate if no other is provided.

                      "},"Classes.html":{"name":"Classes","abstract":"

                      The following classes are available globally.

                      "},"Enums.html":{"name":"Enumerations","abstract":"

                      The following enumerations are available globally.

                      "},"Extensions.html":{"name":"Extensions","abstract":"

                      The following extensions are available globally.

                      "},"Protocols.html":{"name":"Protocols","abstract":"

                      The following protocols are available globally.

                      "},"Structs.html":{"name":"Structures","abstract":"

                      The following structures are available globally.

                      "},"Typealiases.html":{"name":"Type Aliases","abstract":"

                      The following type aliases are available globally.

                      "}} \ No newline at end of file diff --git a/docs/jazzy/undocumented.json b/docs/jazzy/undocumented.json deleted file mode 100644 index e44d55a..0000000 --- a/docs/jazzy/undocumented.json +++ /dev/null @@ -1,348 +0,0 @@ -{ - "warnings": [ - { - "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Common/IAPCallback.swift", - "line": 12, - "symbol": "IAPPurchaseCallback", - "symbol_kind": "source.lang.swift.decl.typealias", - "warning": "undocumented" - }, - { - "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Common/IAPCallback.swift", - "line": 13, - "symbol": "IAPPurchaseResult", - "symbol_kind": "source.lang.swift.decl.struct", - "warning": "undocumented" - }, - { - "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Common/IAPCallback.swift", - "line": 14, - "symbol": "IAPPurchaseResult.state", - "symbol_kind": "source.lang.swift.decl.var.instance", - "warning": "undocumented" - }, - { - "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Common/IAPCallback.swift", - "line": 15, - "symbol": "IAPPurchaseResult.iapError", - "symbol_kind": "source.lang.swift.decl.var.instance", - "warning": "undocumented" - }, - { - "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Common/IAPCallback.swift", - "line": 16, - "symbol": "IAPPurchaseResult.skError", - "symbol_kind": "source.lang.swift.decl.var.instance", - "warning": "undocumented" - }, - { - "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Common/IAPCallback.swift", - "line": 18, - "symbol": "IAPPurchaseResult.localizedDescription", - "symbol_kind": "source.lang.swift.decl.var.instance", - "warning": "undocumented" - }, - { - "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Common/IAPCallback.swift", - "line": 25, - "symbol": "IAPPurchaseResultState", - "symbol_kind": "source.lang.swift.decl.enum", - "warning": "undocumented" - }, - { - "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Common/IAPCallback.swift", - "line": 26, - "symbol": "IAPPurchaseResultState.purchased", - "symbol_kind": "source.lang.swift.decl.enumelement", - "warning": "undocumented" - }, - { - "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Common/IAPCallback.swift", - "line": 27, - "symbol": "IAPPurchaseResultState.failed", - "symbol_kind": "source.lang.swift.decl.enumelement", - "warning": "undocumented" - }, - { - "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Common/IAPCallback.swift", - "line": 28, - "symbol": "IAPPurchaseResultState.cancelled", - "symbol_kind": "source.lang.swift.decl.enumelement", - "warning": "undocumented" - }, - { - "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Common/IAPCallback.swift", - "line": 29, - "symbol": "IAPPurchaseResultState.deferred", - "symbol_kind": "source.lang.swift.decl.enumelement", - "warning": "undocumented" - }, - { - "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Common/IAPCallback.swift", - "line": 33, - "symbol": "IAPRefreshCallback", - "symbol_kind": "source.lang.swift.decl.typealias", - "warning": "undocumented" - }, - { - "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Common/IAPCallback.swift", - "line": 34, - "symbol": "IAPRefreshResult", - "symbol_kind": "source.lang.swift.decl.struct", - "warning": "undocumented" - }, - { - "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Common/IAPCallback.swift", - "line": 35, - "symbol": "IAPRefreshResult.state", - "symbol_kind": "source.lang.swift.decl.var.instance", - "warning": "undocumented" - }, - { - "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Common/IAPCallback.swift", - "line": 36, - "symbol": "IAPRefreshResult.iapError", - "symbol_kind": "source.lang.swift.decl.var.instance", - "warning": "undocumented" - }, - { - "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Common/IAPCallback.swift", - "line": 37, - "symbol": "IAPRefreshResult.addedPurchases", - "symbol_kind": "source.lang.swift.decl.var.instance", - "warning": "undocumented" - }, - { - "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Common/IAPCallback.swift", - "line": 38, - "symbol": "IAPRefreshResult.updatedPurchases", - "symbol_kind": "source.lang.swift.decl.var.instance", - "warning": "undocumented" - }, - { - "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Common/IAPCallback.swift", - "line": 41, - "symbol": "IAPRefreshResultState", - "symbol_kind": "source.lang.swift.decl.enum", - "warning": "undocumented" - }, - { - "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Common/IAPCallback.swift", - "line": 42, - "symbol": "IAPRefreshResultState.succeeded", - "symbol_kind": "source.lang.swift.decl.enumelement", - "warning": "undocumented" - }, - { - "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Common/IAPCallback.swift", - "line": 43, - "symbol": "IAPRefreshResultState.failed", - "symbol_kind": "source.lang.swift.decl.enumelement", - "warning": "undocumented" - }, - { - "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Common/IAPCallback.swift", - "line": 44, - "symbol": "IAPRefreshResultState.skipped", - "symbol_kind": "source.lang.swift.decl.enumelement", - "warning": "undocumented" - }, - { - "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Common/IAPError.swift", - "line": 11, - "symbol": "IAPErrorProtocol", - "symbol_kind": "source.lang.swift.decl.protocol", - "warning": "undocumented" - }, - { - "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Common/IAPError.swift", - "line": 12, - "symbol": "IAPErrorProtocol.code", - "symbol_kind": "source.lang.swift.decl.var.instance", - "warning": "undocumented" - }, - { - "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Common/IAPError.swift", - "line": 15, - "symbol": "IAPErrorCode", - "symbol_kind": "source.lang.swift.decl.enum", - "warning": "undocumented" - }, - { - "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Common/IAPError.swift", - "line": 16, - "symbol": "IAPErrorCode.libraryNotInitialized", - "symbol_kind": "source.lang.swift.decl.enumelement", - "warning": "undocumented" - }, - { - "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Common/IAPError.swift", - "line": 17, - "symbol": "IAPErrorCode.productNotFound", - "symbol_kind": "source.lang.swift.decl.enumelement", - "warning": "undocumented" - }, - { - "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Common/IAPError.swift", - "line": 18, - "symbol": "IAPErrorCode.cannotMakePurchase", - "symbol_kind": "source.lang.swift.decl.enumelement", - "warning": "undocumented" - }, - { - "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Common/IAPError.swift", - "line": 19, - "symbol": "IAPErrorCode.alreadyPurchasing", - "symbol_kind": "source.lang.swift.decl.enumelement", - "warning": "undocumented" - }, - { - "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Common/IAPError.swift", - "line": 21, - "symbol": "IAPErrorCode.bundleIdentifierInvalid", - "symbol_kind": "source.lang.swift.decl.enumelement", - "warning": "undocumented" - }, - { - "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Common/IAPError.swift", - "line": 22, - "symbol": "IAPErrorCode.validatorUrlInvalid", - "symbol_kind": "source.lang.swift.decl.enumelement", - "warning": "undocumented" - }, - { - "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Common/IAPError.swift", - "line": 23, - "symbol": "IAPErrorCode.refreshReceiptFailed", - "symbol_kind": "source.lang.swift.decl.enumelement", - "warning": "undocumented" - }, - { - "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Common/IAPError.swift", - "line": 24, - "symbol": "IAPErrorCode.validateReceiptFailed", - "symbol_kind": "source.lang.swift.decl.enumelement", - "warning": "undocumented" - }, - { - "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Common/IAPError.swift", - "line": 25, - "symbol": "IAPErrorCode.readReceiptFailed", - "symbol_kind": "source.lang.swift.decl.enumelement", - "warning": "undocumented" - }, - { - "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Common/IAPError.swift", - "line": 27, - "symbol": "IAPErrorCode.refreshProductsFailed", - "symbol_kind": "source.lang.swift.decl.enumelement", - "warning": "undocumented" - }, - { - "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Common/IAPError.swift", - "line": 30, - "symbol": "IAPError", - "symbol_kind": "source.lang.swift.decl.struct", - "warning": "undocumented" - }, - { - "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Common/IAPError.swift", - "line": 31, - "symbol": "IAPError.code", - "symbol_kind": "source.lang.swift.decl.var.instance", - "warning": "undocumented" - }, - { - "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Common/IAPError.swift", - "line": 32, - "symbol": "IAPError.localizedDescription", - "symbol_kind": "source.lang.swift.decl.var.instance", - "warning": "undocumented" - }, - { - "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/InAppPurchase.swift", - "line": 12, - "symbol": "InAppPurchase", - "symbol_kind": "source.lang.swift.decl.class", - "warning": "undocumented" - }, - { - "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/InAppPurchaseLib.swift", - "line": 132, - "symbol": "DefaultPurchaseDelegate.init()", - "symbol_kind": "source.lang.swift.decl.function.method.instance", - "warning": "undocumented" - }, - { - "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Product/IAPProduct.swift", - "line": 11, - "symbol": "IAPProduct", - "symbol_kind": "source.lang.swift.decl.struct", - "warning": "undocumented" - }, - { - "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Product/IAPProduct.swift", - "line": 31, - "symbol": "IAPProductType", - "symbol_kind": "source.lang.swift.decl.enum", - "warning": "undocumented" - }, - { - "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Product/IAPProduct.swift", - "line": 32, - "symbol": "IAPProductType.consumable", - "symbol_kind": "source.lang.swift.decl.enumelement", - "warning": "undocumented" - }, - { - "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Product/IAPProduct.swift", - "line": 33, - "symbol": "IAPProductType.nonConsumable", - "symbol_kind": "source.lang.swift.decl.enumelement", - "warning": "undocumented" - }, - { - "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Product/IAPProduct.swift", - "line": 34, - "symbol": "IAPProductType.nonRenewingSubscription", - "symbol_kind": "source.lang.swift.decl.enumelement", - "warning": "undocumented" - }, - { - "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Product/IAPProduct.swift", - "line": 35, - "symbol": "IAPProductType.autoRenewableSubscription", - "symbol_kind": "source.lang.swift.decl.enumelement", - "warning": "undocumented" - }, - { - "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Product/SKProductExtension.swift", - "line": 12, - "symbol": "IAPPeriodFormat", - "symbol_kind": "source.lang.swift.decl.enum", - "warning": "undocumented" - }, - { - "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Product/SKProductExtension.swift", - "line": 13, - "symbol": "IAPPeriodFormat.short", - "symbol_kind": "source.lang.swift.decl.enumelement", - "warning": "undocumented" - }, - { - "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Product/SKProductExtension.swift", - "line": 14, - "symbol": "IAPPeriodFormat.long", - "symbol_kind": "source.lang.swift.decl.enumelement", - "warning": "undocumented" - }, - { - "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Product/SKProductExtension.swift", - "line": 20, - "symbol": "SKProduct.localizedPeriodFormat", - "symbol_kind": "source.lang.swift.decl.var.static", - "warning": "undocumented" - } - ], - "source_directory": "/Users/veronique/Documents/iap-swift-lib" -} \ No newline at end of file diff --git a/docs/jazzy/js/jazzy.js b/docs/js/jazzy.js similarity index 100% rename from docs/jazzy/js/jazzy.js rename to docs/js/jazzy.js diff --git a/docs/js/jazzy.search.js b/docs/js/jazzy.search.js new file mode 100644 index 0000000..e3d1ab9 --- /dev/null +++ b/docs/js/jazzy.search.js @@ -0,0 +1,70 @@ +$(function(){ + var $typeahead = $('[data-typeahead]'); + var $form = $typeahead.parents('form'); + var searchURL = $form.attr('action'); + + function displayTemplate(result) { + return result.name; + } + + function suggestionTemplate(result) { + var t = '
                      '; + t += '' + result.name + ''; + if (result.parent_name) { + t += '' + result.parent_name + ''; + } + t += '
                      '; + return t; + } + + $typeahead.one('focus', function() { + $form.addClass('loading'); + + $.getJSON(searchURL).then(function(searchData) { + const searchIndex = lunr(function() { + this.ref('url'); + this.field('name'); + this.field('abstract'); + for (const [url, doc] of Object.entries(searchData)) { + this.add({url: url, name: doc.name, abstract: doc.abstract}); + } + }); + + $typeahead.typeahead( + { + highlight: true, + minLength: 3, + autoselect: true + }, + { + limit: 10, + display: displayTemplate, + templates: { suggestion: suggestionTemplate }, + source: function(query, sync) { + const lcSearch = query.toLowerCase(); + const results = searchIndex.query(function(q) { + q.term(lcSearch, { boost: 100 }); + q.term(lcSearch, { + boost: 10, + wildcard: lunr.Query.wildcard.TRAILING + }); + }).map(function(result) { + var doc = searchData[result.ref]; + doc.url = result.ref; + return doc; + }); + sync(results); + } + } + ); + $form.removeClass('loading'); + $typeahead.trigger('focus'); + }); + }); + + var baseURL = searchURL.slice(0, -"search.json".length); + + $typeahead.on('typeahead:select', function(e, result) { + window.location = baseURL + result.url; + }); +}); diff --git a/docs/jazzy/js/jquery.min.js b/docs/js/jquery.min.js similarity index 100% rename from docs/jazzy/js/jquery.min.js rename to docs/js/jquery.min.js diff --git a/docs/js/lunr.min.js b/docs/js/lunr.min.js new file mode 100644 index 0000000..f45a81e --- /dev/null +++ b/docs/js/lunr.min.js @@ -0,0 +1 @@ +!function(){var t,l,c,e,r,h,d,f,p,y,m,g,x,v,w,Q,k,S,E,L,b,P,T,O,I,i,n,s,z=function(e){var t=new z.Builder;return t.pipeline.add(z.trimmer,z.stopWordFilter,z.stemmer),t.searchPipeline.add(z.stemmer),e.call(t,t),t.build()};z.version="2.3.5",z.utils={},z.utils.warn=(t=this,function(e){t.console&&console.warn&&console.warn(e)}),z.utils.asString=function(e){return null==e?"":e.toString()},z.utils.clone=function(e){if(null==e)return e;for(var t=Object.create(null),r=Object.keys(e),i=0;i=this.length)return z.QueryLexer.EOS;var e=this.str.charAt(this.pos);return this.pos+=1,e},z.QueryLexer.prototype.width=function(){return this.pos-this.start},z.QueryLexer.prototype.ignore=function(){this.start==this.pos&&(this.pos+=1),this.start=this.pos},z.QueryLexer.prototype.backup=function(){this.pos-=1},z.QueryLexer.prototype.acceptDigitRun=function(){for(var e,t;47<(t=(e=this.next()).charCodeAt(0))&&t<58;);e!=z.QueryLexer.EOS&&this.backup()},z.QueryLexer.prototype.more=function(){return this.pos', + menu: '
                      ' + }; + } + function buildSelectors(classes) { + var selectors = {}; + _.each(classes, function(v, k) { + selectors[k] = "." + v; + }); + return selectors; + } + function buildCss() { + var css = { + wrapper: { + position: "relative", + display: "inline-block" + }, + hint: { + position: "absolute", + top: "0", + left: "0", + borderColor: "transparent", + boxShadow: "none", + opacity: "1" + }, + input: { + position: "relative", + verticalAlign: "top", + backgroundColor: "transparent" + }, + inputWithNoHint: { + position: "relative", + verticalAlign: "top" + }, + menu: { + position: "absolute", + top: "100%", + left: "0", + zIndex: "100", + display: "none" + }, + ltr: { + left: "0", + right: "auto" + }, + rtl: { + left: "auto", + right: " 0" + } + }; + if (_.isMsie()) { + _.mixin(css.input, { + backgroundImage: "url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)" + }); + } + return css; + } + }(); + var EventBus = function() { + "use strict"; + var namespace, deprecationMap; + namespace = "typeahead:"; + deprecationMap = { + render: "rendered", + cursorchange: "cursorchanged", + select: "selected", + autocomplete: "autocompleted" + }; + function EventBus(o) { + if (!o || !o.el) { + $.error("EventBus initialized without el"); + } + this.$el = $(o.el); + } + _.mixin(EventBus.prototype, { + _trigger: function(type, args) { + var $e = $.Event(namespace + type); + this.$el.trigger.call(this.$el, $e, args || []); + return $e; + }, + before: function(type) { + var args, $e; + args = [].slice.call(arguments, 1); + $e = this._trigger("before" + type, args); + return $e.isDefaultPrevented(); + }, + trigger: function(type) { + var deprecatedType; + this._trigger(type, [].slice.call(arguments, 1)); + if (deprecatedType = deprecationMap[type]) { + this._trigger(deprecatedType, [].slice.call(arguments, 1)); + } + } + }); + return EventBus; + }(); + var EventEmitter = function() { + "use strict"; + var splitter = /\s+/, nextTick = getNextTick(); + return { + onSync: onSync, + onAsync: onAsync, + off: off, + trigger: trigger + }; + function on(method, types, cb, context) { + var type; + if (!cb) { + return this; + } + types = types.split(splitter); + cb = context ? bindContext(cb, context) : cb; + this._callbacks = this._callbacks || {}; + while (type = types.shift()) { + this._callbacks[type] = this._callbacks[type] || { + sync: [], + async: [] + }; + this._callbacks[type][method].push(cb); + } + return this; + } + function onAsync(types, cb, context) { + return on.call(this, "async", types, cb, context); + } + function onSync(types, cb, context) { + return on.call(this, "sync", types, cb, context); + } + function off(types) { + var type; + if (!this._callbacks) { + return this; + } + types = types.split(splitter); + while (type = types.shift()) { + delete this._callbacks[type]; + } + return this; + } + function trigger(types) { + var type, callbacks, args, syncFlush, asyncFlush; + if (!this._callbacks) { + return this; + } + types = types.split(splitter); + args = [].slice.call(arguments, 1); + while ((type = types.shift()) && (callbacks = this._callbacks[type])) { + syncFlush = getFlush(callbacks.sync, this, [ type ].concat(args)); + asyncFlush = getFlush(callbacks.async, this, [ type ].concat(args)); + syncFlush() && nextTick(asyncFlush); + } + return this; + } + function getFlush(callbacks, context, args) { + return flush; + function flush() { + var cancelled; + for (var i = 0, len = callbacks.length; !cancelled && i < len; i += 1) { + cancelled = callbacks[i].apply(context, args) === false; + } + return !cancelled; + } + } + function getNextTick() { + var nextTickFn; + if (window.setImmediate) { + nextTickFn = function nextTickSetImmediate(fn) { + setImmediate(function() { + fn(); + }); + }; + } else { + nextTickFn = function nextTickSetTimeout(fn) { + setTimeout(function() { + fn(); + }, 0); + }; + } + return nextTickFn; + } + function bindContext(fn, context) { + return fn.bind ? fn.bind(context) : function() { + fn.apply(context, [].slice.call(arguments, 0)); + }; + } + }(); + var highlight = function(doc) { + "use strict"; + var defaults = { + node: null, + pattern: null, + tagName: "strong", + className: null, + wordsOnly: false, + caseSensitive: false, + diacriticInsensitive: false + }; + var accented = { + A: "[AaªÀ-Åà-åĀ-ąǍǎȀ-ȃȦȧᴬᵃḀḁẚẠ-ảₐ℀℁℻⒜Ⓐⓐ㍱-㍴㎀-㎄㎈㎉㎩-㎯㏂㏊㏟㏿Aa]", + B: "[BbᴮᵇḂ-ḇℬ⒝Ⓑⓑ㍴㎅-㎇㏃㏈㏔㏝Bb]", + C: "[CcÇçĆ-čᶜ℀ℂ℃℅℆ℭⅭⅽ⒞Ⓒⓒ㍶㎈㎉㎝㎠㎤㏄-㏇Cc]", + D: "[DdĎďDŽ-džDZ-dzᴰᵈḊ-ḓⅅⅆⅮⅾ⒟Ⓓⓓ㋏㍲㍷-㍹㎗㎭-㎯㏅㏈Dd]", + E: "[EeÈ-Ëè-ëĒ-ěȄ-ȇȨȩᴱᵉḘ-ḛẸ-ẽₑ℡ℯℰⅇ⒠Ⓔⓔ㉐㋍㋎Ee]", + F: "[FfᶠḞḟ℉ℱ℻⒡Ⓕⓕ㎊-㎌㎙ff-fflFf]", + G: "[GgĜ-ģǦǧǴǵᴳᵍḠḡℊ⒢Ⓖⓖ㋌㋍㎇㎍-㎏㎓㎬㏆㏉㏒㏿Gg]", + H: "[HhĤĥȞȟʰᴴḢ-ḫẖℋ-ℎ⒣Ⓗⓗ㋌㍱㎐-㎔㏊㏋㏗Hh]", + I: "[IiÌ-Ïì-ïĨ-İIJijǏǐȈ-ȋᴵᵢḬḭỈ-ịⁱℐℑℹⅈⅠ-ⅣⅥ-ⅨⅪⅫⅰ-ⅳⅵ-ⅸⅺⅻ⒤Ⓘⓘ㍺㏌㏕fiffiIi]", + J: "[JjIJ-ĵLJ-njǰʲᴶⅉ⒥ⒿⓙⱼJj]", + K: "[KkĶķǨǩᴷᵏḰ-ḵK⒦Ⓚⓚ㎄㎅㎉㎏㎑㎘㎞㎢㎦㎪㎸㎾㏀㏆㏍-㏏Kk]", + L: "[LlĹ-ŀLJ-ljˡᴸḶḷḺ-ḽℒℓ℡Ⅼⅼ⒧Ⓛⓛ㋏㎈㎉㏐-㏓㏕㏖㏿flfflLl]", + M: "[MmᴹᵐḾ-ṃ℠™ℳⅯⅿ⒨Ⓜⓜ㍷-㍹㎃㎆㎎㎒㎖㎙-㎨㎫㎳㎷㎹㎽㎿㏁㏂㏎㏐㏔-㏖㏘㏙㏞㏟Mm]", + N: "[NnÑñŃ-ʼnNJ-njǸǹᴺṄ-ṋⁿℕ№⒩Ⓝⓝ㎁㎋㎚㎱㎵㎻㏌㏑Nn]", + O: "[OoºÒ-Öò-öŌ-őƠơǑǒǪǫȌ-ȏȮȯᴼᵒỌ-ỏₒ℅№ℴ⒪Ⓞⓞ㍵㏇㏒㏖Oo]", + P: "[PpᴾᵖṔ-ṗℙ⒫Ⓟⓟ㉐㍱㍶㎀㎊㎩-㎬㎰㎴㎺㏋㏗-㏚Pp]", + Q: "[Qqℚ⒬Ⓠⓠ㏃Qq]", + R: "[RrŔ-řȐ-ȓʳᴿᵣṘ-ṛṞṟ₨ℛ-ℝ⒭Ⓡⓡ㋍㍴㎭-㎯㏚㏛Rr]", + S: "[SsŚ-šſȘșˢṠ-ṣ₨℁℠⒮Ⓢⓢ㎧㎨㎮-㎳㏛㏜stSs]", + T: "[TtŢ-ťȚțᵀᵗṪ-ṱẗ℡™⒯Ⓣⓣ㉐㋏㎔㏏ſtstTt]", + U: "[UuÙ-Üù-üŨ-ųƯưǓǔȔ-ȗᵁᵘᵤṲ-ṷỤ-ủ℆⒰Ⓤⓤ㍳㍺Uu]", + V: "[VvᵛᵥṼ-ṿⅣ-Ⅷⅳ-ⅷ⒱Ⓥⓥⱽ㋎㍵㎴-㎹㏜㏞Vv]", + W: "[WwŴŵʷᵂẀ-ẉẘ⒲Ⓦⓦ㎺-㎿㏝Ww]", + X: "[XxˣẊ-ẍₓ℻Ⅸ-Ⅻⅸ-ⅻ⒳Ⓧⓧ㏓Xx]", + Y: "[YyÝýÿŶ-ŸȲȳʸẎẏẙỲ-ỹ⒴Ⓨⓨ㏉Yy]", + Z: "[ZzŹ-žDZ-dzᶻẐ-ẕℤℨ⒵Ⓩⓩ㎐-㎔Zz]" + }; + return function hightlight(o) { + var regex; + o = _.mixin({}, defaults, o); + if (!o.node || !o.pattern) { + return; + } + o.pattern = _.isArray(o.pattern) ? o.pattern : [ o.pattern ]; + regex = getRegex(o.pattern, o.caseSensitive, o.wordsOnly, o.diacriticInsensitive); + traverse(o.node, hightlightTextNode); + function hightlightTextNode(textNode) { + var match, patternNode, wrapperNode; + if (match = regex.exec(textNode.data)) { + wrapperNode = doc.createElement(o.tagName); + o.className && (wrapperNode.className = o.className); + patternNode = textNode.splitText(match.index); + patternNode.splitText(match[0].length); + wrapperNode.appendChild(patternNode.cloneNode(true)); + textNode.parentNode.replaceChild(wrapperNode, patternNode); + } + return !!match; + } + function traverse(el, hightlightTextNode) { + var childNode, TEXT_NODE_TYPE = 3; + for (var i = 0; i < el.childNodes.length; i++) { + childNode = el.childNodes[i]; + if (childNode.nodeType === TEXT_NODE_TYPE) { + i += hightlightTextNode(childNode) ? 1 : 0; + } else { + traverse(childNode, hightlightTextNode); + } + } + } + }; + function accent_replacer(chr) { + return accented[chr.toUpperCase()] || chr; + } + function getRegex(patterns, caseSensitive, wordsOnly, diacriticInsensitive) { + var escapedPatterns = [], regexStr; + for (var i = 0, len = patterns.length; i < len; i++) { + var escapedWord = _.escapeRegExChars(patterns[i]); + if (diacriticInsensitive) { + escapedWord = escapedWord.replace(/\S/g, accent_replacer); + } + escapedPatterns.push(escapedWord); + } + regexStr = wordsOnly ? "\\b(" + escapedPatterns.join("|") + ")\\b" : "(" + escapedPatterns.join("|") + ")"; + return caseSensitive ? new RegExp(regexStr) : new RegExp(regexStr, "i"); + } + }(window.document); + var Input = function() { + "use strict"; + var specialKeyCodeMap; + specialKeyCodeMap = { + 9: "tab", + 27: "esc", + 37: "left", + 39: "right", + 13: "enter", + 38: "up", + 40: "down" + }; + function Input(o, www) { + o = o || {}; + if (!o.input) { + $.error("input is missing"); + } + www.mixin(this); + this.$hint = $(o.hint); + this.$input = $(o.input); + this.$input.attr({ + "aria-activedescendant": "", + "aria-owns": this.$input.attr("id") + "_listbox", + role: "combobox", + "aria-readonly": "true", + "aria-autocomplete": "list" + }); + $(www.menu).attr("id", this.$input.attr("id") + "_listbox"); + this.query = this.$input.val(); + this.queryWhenFocused = this.hasFocus() ? this.query : null; + this.$overflowHelper = buildOverflowHelper(this.$input); + this._checkLanguageDirection(); + if (this.$hint.length === 0) { + this.setHint = this.getHint = this.clearHint = this.clearHintIfInvalid = _.noop; + } + this.onSync("cursorchange", this._updateDescendent); + } + Input.normalizeQuery = function(str) { + return _.toStr(str).replace(/^\s*/g, "").replace(/\s{2,}/g, " "); + }; + _.mixin(Input.prototype, EventEmitter, { + _onBlur: function onBlur() { + this.resetInputValue(); + this.trigger("blurred"); + }, + _onFocus: function onFocus() { + this.queryWhenFocused = this.query; + this.trigger("focused"); + }, + _onKeydown: function onKeydown($e) { + var keyName = specialKeyCodeMap[$e.which || $e.keyCode]; + this._managePreventDefault(keyName, $e); + if (keyName && this._shouldTrigger(keyName, $e)) { + this.trigger(keyName + "Keyed", $e); + } + }, + _onInput: function onInput() { + this._setQuery(this.getInputValue()); + this.clearHintIfInvalid(); + this._checkLanguageDirection(); + }, + _managePreventDefault: function managePreventDefault(keyName, $e) { + var preventDefault; + switch (keyName) { + case "up": + case "down": + preventDefault = !withModifier($e); + break; + + default: + preventDefault = false; + } + preventDefault && $e.preventDefault(); + }, + _shouldTrigger: function shouldTrigger(keyName, $e) { + var trigger; + switch (keyName) { + case "tab": + trigger = !withModifier($e); + break; + + default: + trigger = true; + } + return trigger; + }, + _checkLanguageDirection: function checkLanguageDirection() { + var dir = (this.$input.css("direction") || "ltr").toLowerCase(); + if (this.dir !== dir) { + this.dir = dir; + this.$hint.attr("dir", dir); + this.trigger("langDirChanged", dir); + } + }, + _setQuery: function setQuery(val, silent) { + var areEquivalent, hasDifferentWhitespace; + areEquivalent = areQueriesEquivalent(val, this.query); + hasDifferentWhitespace = areEquivalent ? this.query.length !== val.length : false; + this.query = val; + if (!silent && !areEquivalent) { + this.trigger("queryChanged", this.query); + } else if (!silent && hasDifferentWhitespace) { + this.trigger("whitespaceChanged", this.query); + } + }, + _updateDescendent: function updateDescendent(event, id) { + this.$input.attr("aria-activedescendant", id); + }, + bind: function() { + var that = this, onBlur, onFocus, onKeydown, onInput; + onBlur = _.bind(this._onBlur, this); + onFocus = _.bind(this._onFocus, this); + onKeydown = _.bind(this._onKeydown, this); + onInput = _.bind(this._onInput, this); + this.$input.on("blur.tt", onBlur).on("focus.tt", onFocus).on("keydown.tt", onKeydown); + if (!_.isMsie() || _.isMsie() > 9) { + this.$input.on("input.tt", onInput); + } else { + this.$input.on("keydown.tt keypress.tt cut.tt paste.tt", function($e) { + if (specialKeyCodeMap[$e.which || $e.keyCode]) { + return; + } + _.defer(_.bind(that._onInput, that, $e)); + }); + } + return this; + }, + focus: function focus() { + this.$input.focus(); + }, + blur: function blur() { + this.$input.blur(); + }, + getLangDir: function getLangDir() { + return this.dir; + }, + getQuery: function getQuery() { + return this.query || ""; + }, + setQuery: function setQuery(val, silent) { + this.setInputValue(val); + this._setQuery(val, silent); + }, + hasQueryChangedSinceLastFocus: function hasQueryChangedSinceLastFocus() { + return this.query !== this.queryWhenFocused; + }, + getInputValue: function getInputValue() { + return this.$input.val(); + }, + setInputValue: function setInputValue(value) { + this.$input.val(value); + this.clearHintIfInvalid(); + this._checkLanguageDirection(); + }, + resetInputValue: function resetInputValue() { + this.setInputValue(this.query); + }, + getHint: function getHint() { + return this.$hint.val(); + }, + setHint: function setHint(value) { + this.$hint.val(value); + }, + clearHint: function clearHint() { + this.setHint(""); + }, + clearHintIfInvalid: function clearHintIfInvalid() { + var val, hint, valIsPrefixOfHint, isValid; + val = this.getInputValue(); + hint = this.getHint(); + valIsPrefixOfHint = val !== hint && hint.indexOf(val) === 0; + isValid = val !== "" && valIsPrefixOfHint && !this.hasOverflow(); + !isValid && this.clearHint(); + }, + hasFocus: function hasFocus() { + return this.$input.is(":focus"); + }, + hasOverflow: function hasOverflow() { + var constraint = this.$input.width() - 2; + this.$overflowHelper.text(this.getInputValue()); + return this.$overflowHelper.width() >= constraint; + }, + isCursorAtEnd: function() { + var valueLength, selectionStart, range; + valueLength = this.$input.val().length; + selectionStart = this.$input[0].selectionStart; + if (_.isNumber(selectionStart)) { + return selectionStart === valueLength; + } else if (document.selection) { + range = document.selection.createRange(); + range.moveStart("character", -valueLength); + return valueLength === range.text.length; + } + return true; + }, + destroy: function destroy() { + this.$hint.off(".tt"); + this.$input.off(".tt"); + this.$overflowHelper.remove(); + this.$hint = this.$input = this.$overflowHelper = $("
                      "); + } + }); + return Input; + function buildOverflowHelper($input) { + return $('').css({ + position: "absolute", + visibility: "hidden", + whiteSpace: "pre", + fontFamily: $input.css("font-family"), + fontSize: $input.css("font-size"), + fontStyle: $input.css("font-style"), + fontVariant: $input.css("font-variant"), + fontWeight: $input.css("font-weight"), + wordSpacing: $input.css("word-spacing"), + letterSpacing: $input.css("letter-spacing"), + textIndent: $input.css("text-indent"), + textRendering: $input.css("text-rendering"), + textTransform: $input.css("text-transform") + }).insertAfter($input); + } + function areQueriesEquivalent(a, b) { + return Input.normalizeQuery(a) === Input.normalizeQuery(b); + } + function withModifier($e) { + return $e.altKey || $e.ctrlKey || $e.metaKey || $e.shiftKey; + } + }(); + var Dataset = function() { + "use strict"; + var keys, nameGenerator; + keys = { + dataset: "tt-selectable-dataset", + val: "tt-selectable-display", + obj: "tt-selectable-object" + }; + nameGenerator = _.getIdGenerator(); + function Dataset(o, www) { + o = o || {}; + o.templates = o.templates || {}; + o.templates.notFound = o.templates.notFound || o.templates.empty; + if (!o.source) { + $.error("missing source"); + } + if (!o.node) { + $.error("missing node"); + } + if (o.name && !isValidName(o.name)) { + $.error("invalid dataset name: " + o.name); + } + www.mixin(this); + this.highlight = !!o.highlight; + this.name = _.toStr(o.name || nameGenerator()); + this.limit = o.limit || 5; + this.displayFn = getDisplayFn(o.display || o.displayKey); + this.templates = getTemplates(o.templates, this.displayFn); + this.source = o.source.__ttAdapter ? o.source.__ttAdapter() : o.source; + this.async = _.isUndefined(o.async) ? this.source.length > 2 : !!o.async; + this._resetLastSuggestion(); + this.$el = $(o.node).attr("role", "presentation").addClass(this.classes.dataset).addClass(this.classes.dataset + "-" + this.name); + } + Dataset.extractData = function extractData(el) { + var $el = $(el); + if ($el.data(keys.obj)) { + return { + dataset: $el.data(keys.dataset) || "", + val: $el.data(keys.val) || "", + obj: $el.data(keys.obj) || null + }; + } + return null; + }; + _.mixin(Dataset.prototype, EventEmitter, { + _overwrite: function overwrite(query, suggestions) { + suggestions = suggestions || []; + if (suggestions.length) { + this._renderSuggestions(query, suggestions); + } else if (this.async && this.templates.pending) { + this._renderPending(query); + } else if (!this.async && this.templates.notFound) { + this._renderNotFound(query); + } else { + this._empty(); + } + this.trigger("rendered", suggestions, false, this.name); + }, + _append: function append(query, suggestions) { + suggestions = suggestions || []; + if (suggestions.length && this.$lastSuggestion.length) { + this._appendSuggestions(query, suggestions); + } else if (suggestions.length) { + this._renderSuggestions(query, suggestions); + } else if (!this.$lastSuggestion.length && this.templates.notFound) { + this._renderNotFound(query); + } + this.trigger("rendered", suggestions, true, this.name); + }, + _renderSuggestions: function renderSuggestions(query, suggestions) { + var $fragment; + $fragment = this._getSuggestionsFragment(query, suggestions); + this.$lastSuggestion = $fragment.children().last(); + this.$el.html($fragment).prepend(this._getHeader(query, suggestions)).append(this._getFooter(query, suggestions)); + }, + _appendSuggestions: function appendSuggestions(query, suggestions) { + var $fragment, $lastSuggestion; + $fragment = this._getSuggestionsFragment(query, suggestions); + $lastSuggestion = $fragment.children().last(); + this.$lastSuggestion.after($fragment); + this.$lastSuggestion = $lastSuggestion; + }, + _renderPending: function renderPending(query) { + var template = this.templates.pending; + this._resetLastSuggestion(); + template && this.$el.html(template({ + query: query, + dataset: this.name + })); + }, + _renderNotFound: function renderNotFound(query) { + var template = this.templates.notFound; + this._resetLastSuggestion(); + template && this.$el.html(template({ + query: query, + dataset: this.name + })); + }, + _empty: function empty() { + this.$el.empty(); + this._resetLastSuggestion(); + }, + _getSuggestionsFragment: function getSuggestionsFragment(query, suggestions) { + var that = this, fragment; + fragment = document.createDocumentFragment(); + _.each(suggestions, function getSuggestionNode(suggestion) { + var $el, context; + context = that._injectQuery(query, suggestion); + $el = $(that.templates.suggestion(context)).data(keys.dataset, that.name).data(keys.obj, suggestion).data(keys.val, that.displayFn(suggestion)).addClass(that.classes.suggestion + " " + that.classes.selectable); + fragment.appendChild($el[0]); + }); + this.highlight && highlight({ + className: this.classes.highlight, + node: fragment, + pattern: query + }); + return $(fragment); + }, + _getFooter: function getFooter(query, suggestions) { + return this.templates.footer ? this.templates.footer({ + query: query, + suggestions: suggestions, + dataset: this.name + }) : null; + }, + _getHeader: function getHeader(query, suggestions) { + return this.templates.header ? this.templates.header({ + query: query, + suggestions: suggestions, + dataset: this.name + }) : null; + }, + _resetLastSuggestion: function resetLastSuggestion() { + this.$lastSuggestion = $(); + }, + _injectQuery: function injectQuery(query, obj) { + return _.isObject(obj) ? _.mixin({ + _query: query + }, obj) : obj; + }, + update: function update(query) { + var that = this, canceled = false, syncCalled = false, rendered = 0; + this.cancel(); + this.cancel = function cancel() { + canceled = true; + that.cancel = $.noop; + that.async && that.trigger("asyncCanceled", query, that.name); + }; + this.source(query, sync, async); + !syncCalled && sync([]); + function sync(suggestions) { + if (syncCalled) { + return; + } + syncCalled = true; + suggestions = (suggestions || []).slice(0, that.limit); + rendered = suggestions.length; + that._overwrite(query, suggestions); + if (rendered < that.limit && that.async) { + that.trigger("asyncRequested", query, that.name); + } + } + function async(suggestions) { + suggestions = suggestions || []; + if (!canceled && rendered < that.limit) { + that.cancel = $.noop; + var idx = Math.abs(rendered - that.limit); + rendered += idx; + that._append(query, suggestions.slice(0, idx)); + that.async && that.trigger("asyncReceived", query, that.name); + } + } + }, + cancel: $.noop, + clear: function clear() { + this._empty(); + this.cancel(); + this.trigger("cleared"); + }, + isEmpty: function isEmpty() { + return this.$el.is(":empty"); + }, + destroy: function destroy() { + this.$el = $("
                      "); + } + }); + return Dataset; + function getDisplayFn(display) { + display = display || _.stringify; + return _.isFunction(display) ? display : displayFn; + function displayFn(obj) { + return obj[display]; + } + } + function getTemplates(templates, displayFn) { + return { + notFound: templates.notFound && _.templatify(templates.notFound), + pending: templates.pending && _.templatify(templates.pending), + header: templates.header && _.templatify(templates.header), + footer: templates.footer && _.templatify(templates.footer), + suggestion: templates.suggestion || suggestionTemplate + }; + function suggestionTemplate(context) { + return $('
                      ').attr("id", _.guid()).text(displayFn(context)); + } + } + function isValidName(str) { + return /^[_a-zA-Z0-9-]+$/.test(str); + } + }(); + var Menu = function() { + "use strict"; + function Menu(o, www) { + var that = this; + o = o || {}; + if (!o.node) { + $.error("node is required"); + } + www.mixin(this); + this.$node = $(o.node); + this.query = null; + this.datasets = _.map(o.datasets, initializeDataset); + function initializeDataset(oDataset) { + var node = that.$node.find(oDataset.node).first(); + oDataset.node = node.length ? node : $("
                      ").appendTo(that.$node); + return new Dataset(oDataset, www); + } + } + _.mixin(Menu.prototype, EventEmitter, { + _onSelectableClick: function onSelectableClick($e) { + this.trigger("selectableClicked", $($e.currentTarget)); + }, + _onRendered: function onRendered(type, dataset, suggestions, async) { + this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty()); + this.trigger("datasetRendered", dataset, suggestions, async); + }, + _onCleared: function onCleared() { + this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty()); + this.trigger("datasetCleared"); + }, + _propagate: function propagate() { + this.trigger.apply(this, arguments); + }, + _allDatasetsEmpty: function allDatasetsEmpty() { + return _.every(this.datasets, _.bind(function isDatasetEmpty(dataset) { + var isEmpty = dataset.isEmpty(); + this.$node.attr("aria-expanded", !isEmpty); + return isEmpty; + }, this)); + }, + _getSelectables: function getSelectables() { + return this.$node.find(this.selectors.selectable); + }, + _removeCursor: function _removeCursor() { + var $selectable = this.getActiveSelectable(); + $selectable && $selectable.removeClass(this.classes.cursor); + }, + _ensureVisible: function ensureVisible($el) { + var elTop, elBottom, nodeScrollTop, nodeHeight; + elTop = $el.position().top; + elBottom = elTop + $el.outerHeight(true); + nodeScrollTop = this.$node.scrollTop(); + nodeHeight = this.$node.height() + parseInt(this.$node.css("paddingTop"), 10) + parseInt(this.$node.css("paddingBottom"), 10); + if (elTop < 0) { + this.$node.scrollTop(nodeScrollTop + elTop); + } else if (nodeHeight < elBottom) { + this.$node.scrollTop(nodeScrollTop + (elBottom - nodeHeight)); + } + }, + bind: function() { + var that = this, onSelectableClick; + onSelectableClick = _.bind(this._onSelectableClick, this); + this.$node.on("click.tt", this.selectors.selectable, onSelectableClick); + this.$node.on("mouseover", this.selectors.selectable, function() { + that.setCursor($(this)); + }); + this.$node.on("mouseleave", function() { + that._removeCursor(); + }); + _.each(this.datasets, function(dataset) { + dataset.onSync("asyncRequested", that._propagate, that).onSync("asyncCanceled", that._propagate, that).onSync("asyncReceived", that._propagate, that).onSync("rendered", that._onRendered, that).onSync("cleared", that._onCleared, that); + }); + return this; + }, + isOpen: function isOpen() { + return this.$node.hasClass(this.classes.open); + }, + open: function open() { + this.$node.scrollTop(0); + this.$node.addClass(this.classes.open); + }, + close: function close() { + this.$node.attr("aria-expanded", false); + this.$node.removeClass(this.classes.open); + this._removeCursor(); + }, + setLanguageDirection: function setLanguageDirection(dir) { + this.$node.attr("dir", dir); + }, + selectableRelativeToCursor: function selectableRelativeToCursor(delta) { + var $selectables, $oldCursor, oldIndex, newIndex; + $oldCursor = this.getActiveSelectable(); + $selectables = this._getSelectables(); + oldIndex = $oldCursor ? $selectables.index($oldCursor) : -1; + newIndex = oldIndex + delta; + newIndex = (newIndex + 1) % ($selectables.length + 1) - 1; + newIndex = newIndex < -1 ? $selectables.length - 1 : newIndex; + return newIndex === -1 ? null : $selectables.eq(newIndex); + }, + setCursor: function setCursor($selectable) { + this._removeCursor(); + if ($selectable = $selectable && $selectable.first()) { + $selectable.addClass(this.classes.cursor); + this._ensureVisible($selectable); + } + }, + getSelectableData: function getSelectableData($el) { + return $el && $el.length ? Dataset.extractData($el) : null; + }, + getActiveSelectable: function getActiveSelectable() { + var $selectable = this._getSelectables().filter(this.selectors.cursor).first(); + return $selectable.length ? $selectable : null; + }, + getTopSelectable: function getTopSelectable() { + var $selectable = this._getSelectables().first(); + return $selectable.length ? $selectable : null; + }, + update: function update(query) { + var isValidUpdate = query !== this.query; + if (isValidUpdate) { + this.query = query; + _.each(this.datasets, updateDataset); + } + return isValidUpdate; + function updateDataset(dataset) { + dataset.update(query); + } + }, + empty: function empty() { + _.each(this.datasets, clearDataset); + this.query = null; + this.$node.addClass(this.classes.empty); + function clearDataset(dataset) { + dataset.clear(); + } + }, + destroy: function destroy() { + this.$node.off(".tt"); + this.$node = $("
                      "); + _.each(this.datasets, destroyDataset); + function destroyDataset(dataset) { + dataset.destroy(); + } + } + }); + return Menu; + }(); + var Status = function() { + "use strict"; + function Status(options) { + this.$el = $("", { + role: "status", + "aria-live": "polite" + }).css({ + position: "absolute", + padding: "0", + border: "0", + height: "1px", + width: "1px", + "margin-bottom": "-1px", + "margin-right": "-1px", + overflow: "hidden", + clip: "rect(0 0 0 0)", + "white-space": "nowrap" + }); + options.$input.after(this.$el); + _.each(options.menu.datasets, _.bind(function(dataset) { + if (dataset.onSync) { + dataset.onSync("rendered", _.bind(this.update, this)); + dataset.onSync("cleared", _.bind(this.cleared, this)); + } + }, this)); + } + _.mixin(Status.prototype, { + update: function update(event, suggestions) { + var length = suggestions.length; + var words; + if (length === 1) { + words = { + result: "result", + is: "is" + }; + } else { + words = { + result: "results", + is: "are" + }; + } + this.$el.text(length + " " + words.result + " " + words.is + " available, use up and down arrow keys to navigate."); + }, + cleared: function() { + this.$el.text(""); + } + }); + return Status; + }(); + var DefaultMenu = function() { + "use strict"; + var s = Menu.prototype; + function DefaultMenu() { + Menu.apply(this, [].slice.call(arguments, 0)); + } + _.mixin(DefaultMenu.prototype, Menu.prototype, { + open: function open() { + !this._allDatasetsEmpty() && this._show(); + return s.open.apply(this, [].slice.call(arguments, 0)); + }, + close: function close() { + this._hide(); + return s.close.apply(this, [].slice.call(arguments, 0)); + }, + _onRendered: function onRendered() { + if (this._allDatasetsEmpty()) { + this._hide(); + } else { + this.isOpen() && this._show(); + } + return s._onRendered.apply(this, [].slice.call(arguments, 0)); + }, + _onCleared: function onCleared() { + if (this._allDatasetsEmpty()) { + this._hide(); + } else { + this.isOpen() && this._show(); + } + return s._onCleared.apply(this, [].slice.call(arguments, 0)); + }, + setLanguageDirection: function setLanguageDirection(dir) { + this.$node.css(dir === "ltr" ? this.css.ltr : this.css.rtl); + return s.setLanguageDirection.apply(this, [].slice.call(arguments, 0)); + }, + _hide: function hide() { + this.$node.hide(); + }, + _show: function show() { + this.$node.css("display", "block"); + } + }); + return DefaultMenu; + }(); + var Typeahead = function() { + "use strict"; + function Typeahead(o, www) { + var onFocused, onBlurred, onEnterKeyed, onTabKeyed, onEscKeyed, onUpKeyed, onDownKeyed, onLeftKeyed, onRightKeyed, onQueryChanged, onWhitespaceChanged; + o = o || {}; + if (!o.input) { + $.error("missing input"); + } + if (!o.menu) { + $.error("missing menu"); + } + if (!o.eventBus) { + $.error("missing event bus"); + } + www.mixin(this); + this.eventBus = o.eventBus; + this.minLength = _.isNumber(o.minLength) ? o.minLength : 1; + this.input = o.input; + this.menu = o.menu; + this.enabled = true; + this.autoselect = !!o.autoselect; + this.active = false; + this.input.hasFocus() && this.activate(); + this.dir = this.input.getLangDir(); + this._hacks(); + this.menu.bind().onSync("selectableClicked", this._onSelectableClicked, this).onSync("asyncRequested", this._onAsyncRequested, this).onSync("asyncCanceled", this._onAsyncCanceled, this).onSync("asyncReceived", this._onAsyncReceived, this).onSync("datasetRendered", this._onDatasetRendered, this).onSync("datasetCleared", this._onDatasetCleared, this); + onFocused = c(this, "activate", "open", "_onFocused"); + onBlurred = c(this, "deactivate", "_onBlurred"); + onEnterKeyed = c(this, "isActive", "isOpen", "_onEnterKeyed"); + onTabKeyed = c(this, "isActive", "isOpen", "_onTabKeyed"); + onEscKeyed = c(this, "isActive", "_onEscKeyed"); + onUpKeyed = c(this, "isActive", "open", "_onUpKeyed"); + onDownKeyed = c(this, "isActive", "open", "_onDownKeyed"); + onLeftKeyed = c(this, "isActive", "isOpen", "_onLeftKeyed"); + onRightKeyed = c(this, "isActive", "isOpen", "_onRightKeyed"); + onQueryChanged = c(this, "_openIfActive", "_onQueryChanged"); + onWhitespaceChanged = c(this, "_openIfActive", "_onWhitespaceChanged"); + this.input.bind().onSync("focused", onFocused, this).onSync("blurred", onBlurred, this).onSync("enterKeyed", onEnterKeyed, this).onSync("tabKeyed", onTabKeyed, this).onSync("escKeyed", onEscKeyed, this).onSync("upKeyed", onUpKeyed, this).onSync("downKeyed", onDownKeyed, this).onSync("leftKeyed", onLeftKeyed, this).onSync("rightKeyed", onRightKeyed, this).onSync("queryChanged", onQueryChanged, this).onSync("whitespaceChanged", onWhitespaceChanged, this).onSync("langDirChanged", this._onLangDirChanged, this); + } + _.mixin(Typeahead.prototype, { + _hacks: function hacks() { + var $input, $menu; + $input = this.input.$input || $("
                      "); + $menu = this.menu.$node || $("
                      "); + $input.on("blur.tt", function($e) { + var active, isActive, hasActive; + active = document.activeElement; + isActive = $menu.is(active); + hasActive = $menu.has(active).length > 0; + if (_.isMsie() && (isActive || hasActive)) { + $e.preventDefault(); + $e.stopImmediatePropagation(); + _.defer(function() { + $input.focus(); + }); + } + }); + $menu.on("mousedown.tt", function($e) { + $e.preventDefault(); + }); + }, + _onSelectableClicked: function onSelectableClicked(type, $el) { + this.select($el); + }, + _onDatasetCleared: function onDatasetCleared() { + this._updateHint(); + }, + _onDatasetRendered: function onDatasetRendered(type, suggestions, async, dataset) { + this._updateHint(); + if (this.autoselect) { + var cursorClass = this.selectors.cursor.substr(1); + this.menu.$node.find(this.selectors.suggestion).first().addClass(cursorClass); + } + this.eventBus.trigger("render", suggestions, async, dataset); + }, + _onAsyncRequested: function onAsyncRequested(type, dataset, query) { + this.eventBus.trigger("asyncrequest", query, dataset); + }, + _onAsyncCanceled: function onAsyncCanceled(type, dataset, query) { + this.eventBus.trigger("asynccancel", query, dataset); + }, + _onAsyncReceived: function onAsyncReceived(type, dataset, query) { + this.eventBus.trigger("asyncreceive", query, dataset); + }, + _onFocused: function onFocused() { + this._minLengthMet() && this.menu.update(this.input.getQuery()); + }, + _onBlurred: function onBlurred() { + if (this.input.hasQueryChangedSinceLastFocus()) { + this.eventBus.trigger("change", this.input.getQuery()); + } + }, + _onEnterKeyed: function onEnterKeyed(type, $e) { + var $selectable; + if ($selectable = this.menu.getActiveSelectable()) { + if (this.select($selectable)) { + $e.preventDefault(); + $e.stopPropagation(); + } + } else if (this.autoselect) { + if (this.select(this.menu.getTopSelectable())) { + $e.preventDefault(); + $e.stopPropagation(); + } + } + }, + _onTabKeyed: function onTabKeyed(type, $e) { + var $selectable; + if ($selectable = this.menu.getActiveSelectable()) { + this.select($selectable) && $e.preventDefault(); + } else if ($selectable = this.menu.getTopSelectable()) { + this.autocomplete($selectable) && $e.preventDefault(); + } + }, + _onEscKeyed: function onEscKeyed() { + this.close(); + }, + _onUpKeyed: function onUpKeyed() { + this.moveCursor(-1); + }, + _onDownKeyed: function onDownKeyed() { + this.moveCursor(+1); + }, + _onLeftKeyed: function onLeftKeyed() { + if (this.dir === "rtl" && this.input.isCursorAtEnd()) { + this.autocomplete(this.menu.getActiveSelectable() || this.menu.getTopSelectable()); + } + }, + _onRightKeyed: function onRightKeyed() { + if (this.dir === "ltr" && this.input.isCursorAtEnd()) { + this.autocomplete(this.menu.getActiveSelectable() || this.menu.getTopSelectable()); + } + }, + _onQueryChanged: function onQueryChanged(e, query) { + this._minLengthMet(query) ? this.menu.update(query) : this.menu.empty(); + }, + _onWhitespaceChanged: function onWhitespaceChanged() { + this._updateHint(); + }, + _onLangDirChanged: function onLangDirChanged(e, dir) { + if (this.dir !== dir) { + this.dir = dir; + this.menu.setLanguageDirection(dir); + } + }, + _openIfActive: function openIfActive() { + this.isActive() && this.open(); + }, + _minLengthMet: function minLengthMet(query) { + query = _.isString(query) ? query : this.input.getQuery() || ""; + return query.length >= this.minLength; + }, + _updateHint: function updateHint() { + var $selectable, data, val, query, escapedQuery, frontMatchRegEx, match; + $selectable = this.menu.getTopSelectable(); + data = this.menu.getSelectableData($selectable); + val = this.input.getInputValue(); + if (data && !_.isBlankString(val) && !this.input.hasOverflow()) { + query = Input.normalizeQuery(val); + escapedQuery = _.escapeRegExChars(query); + frontMatchRegEx = new RegExp("^(?:" + escapedQuery + ")(.+$)", "i"); + match = frontMatchRegEx.exec(data.val); + match && this.input.setHint(val + match[1]); + } else { + this.input.clearHint(); + } + }, + isEnabled: function isEnabled() { + return this.enabled; + }, + enable: function enable() { + this.enabled = true; + }, + disable: function disable() { + this.enabled = false; + }, + isActive: function isActive() { + return this.active; + }, + activate: function activate() { + if (this.isActive()) { + return true; + } else if (!this.isEnabled() || this.eventBus.before("active")) { + return false; + } else { + this.active = true; + this.eventBus.trigger("active"); + return true; + } + }, + deactivate: function deactivate() { + if (!this.isActive()) { + return true; + } else if (this.eventBus.before("idle")) { + return false; + } else { + this.active = false; + this.close(); + this.eventBus.trigger("idle"); + return true; + } + }, + isOpen: function isOpen() { + return this.menu.isOpen(); + }, + open: function open() { + if (!this.isOpen() && !this.eventBus.before("open")) { + this.menu.open(); + this._updateHint(); + this.eventBus.trigger("open"); + } + return this.isOpen(); + }, + close: function close() { + if (this.isOpen() && !this.eventBus.before("close")) { + this.menu.close(); + this.input.clearHint(); + this.input.resetInputValue(); + this.eventBus.trigger("close"); + } + return !this.isOpen(); + }, + setVal: function setVal(val) { + this.input.setQuery(_.toStr(val)); + }, + getVal: function getVal() { + return this.input.getQuery(); + }, + select: function select($selectable) { + var data = this.menu.getSelectableData($selectable); + if (data && !this.eventBus.before("select", data.obj, data.dataset)) { + this.input.setQuery(data.val, true); + this.eventBus.trigger("select", data.obj, data.dataset); + this.close(); + return true; + } + return false; + }, + autocomplete: function autocomplete($selectable) { + var query, data, isValid; + query = this.input.getQuery(); + data = this.menu.getSelectableData($selectable); + isValid = data && query !== data.val; + if (isValid && !this.eventBus.before("autocomplete", data.obj, data.dataset)) { + this.input.setQuery(data.val); + this.eventBus.trigger("autocomplete", data.obj, data.dataset); + return true; + } + return false; + }, + moveCursor: function moveCursor(delta) { + var query, $candidate, data, suggestion, datasetName, cancelMove, id; + query = this.input.getQuery(); + $candidate = this.menu.selectableRelativeToCursor(delta); + data = this.menu.getSelectableData($candidate); + suggestion = data ? data.obj : null; + datasetName = data ? data.dataset : null; + id = $candidate ? $candidate.attr("id") : null; + this.input.trigger("cursorchange", id); + cancelMove = this._minLengthMet() && this.menu.update(query); + if (!cancelMove && !this.eventBus.before("cursorchange", suggestion, datasetName)) { + this.menu.setCursor($candidate); + if (data) { + this.input.setInputValue(data.val); + } else { + this.input.resetInputValue(); + this._updateHint(); + } + this.eventBus.trigger("cursorchange", suggestion, datasetName); + return true; + } + return false; + }, + destroy: function destroy() { + this.input.destroy(); + this.menu.destroy(); + } + }); + return Typeahead; + function c(ctx) { + var methods = [].slice.call(arguments, 1); + return function() { + var args = [].slice.call(arguments); + _.each(methods, function(method) { + return ctx[method].apply(ctx, args); + }); + }; + } + }(); + (function() { + "use strict"; + var old, keys, methods; + old = $.fn.typeahead; + keys = { + www: "tt-www", + attrs: "tt-attrs", + typeahead: "tt-typeahead" + }; + methods = { + initialize: function initialize(o, datasets) { + var www; + datasets = _.isArray(datasets) ? datasets : [].slice.call(arguments, 1); + o = o || {}; + www = WWW(o.classNames); + return this.each(attach); + function attach() { + var $input, $wrapper, $hint, $menu, defaultHint, defaultMenu, eventBus, input, menu, status, typeahead, MenuConstructor; + _.each(datasets, function(d) { + d.highlight = !!o.highlight; + }); + $input = $(this); + $wrapper = $(www.html.wrapper); + $hint = $elOrNull(o.hint); + $menu = $elOrNull(o.menu); + defaultHint = o.hint !== false && !$hint; + defaultMenu = o.menu !== false && !$menu; + defaultHint && ($hint = buildHintFromInput($input, www)); + defaultMenu && ($menu = $(www.html.menu).css(www.css.menu)); + $hint && $hint.val(""); + $input = prepInput($input, www); + if (defaultHint || defaultMenu) { + $wrapper.css(www.css.wrapper); + $input.css(defaultHint ? www.css.input : www.css.inputWithNoHint); + $input.wrap($wrapper).parent().prepend(defaultHint ? $hint : null).append(defaultMenu ? $menu : null); + } + MenuConstructor = defaultMenu ? DefaultMenu : Menu; + eventBus = new EventBus({ + el: $input + }); + input = new Input({ + hint: $hint, + input: $input + }, www); + menu = new MenuConstructor({ + node: $menu, + datasets: datasets + }, www); + status = new Status({ + $input: $input, + menu: menu + }); + typeahead = new Typeahead({ + input: input, + menu: menu, + eventBus: eventBus, + minLength: o.minLength, + autoselect: o.autoselect + }, www); + $input.data(keys.www, www); + $input.data(keys.typeahead, typeahead); + } + }, + isEnabled: function isEnabled() { + var enabled; + ttEach(this.first(), function(t) { + enabled = t.isEnabled(); + }); + return enabled; + }, + enable: function enable() { + ttEach(this, function(t) { + t.enable(); + }); + return this; + }, + disable: function disable() { + ttEach(this, function(t) { + t.disable(); + }); + return this; + }, + isActive: function isActive() { + var active; + ttEach(this.first(), function(t) { + active = t.isActive(); + }); + return active; + }, + activate: function activate() { + ttEach(this, function(t) { + t.activate(); + }); + return this; + }, + deactivate: function deactivate() { + ttEach(this, function(t) { + t.deactivate(); + }); + return this; + }, + isOpen: function isOpen() { + var open; + ttEach(this.first(), function(t) { + open = t.isOpen(); + }); + return open; + }, + open: function open() { + ttEach(this, function(t) { + t.open(); + }); + return this; + }, + close: function close() { + ttEach(this, function(t) { + t.close(); + }); + return this; + }, + select: function select(el) { + var success = false, $el = $(el); + ttEach(this.first(), function(t) { + success = t.select($el); + }); + return success; + }, + autocomplete: function autocomplete(el) { + var success = false, $el = $(el); + ttEach(this.first(), function(t) { + success = t.autocomplete($el); + }); + return success; + }, + moveCursor: function moveCursoe(delta) { + var success = false; + ttEach(this.first(), function(t) { + success = t.moveCursor(delta); + }); + return success; + }, + val: function val(newVal) { + var query; + if (!arguments.length) { + ttEach(this.first(), function(t) { + query = t.getVal(); + }); + return query; + } else { + ttEach(this, function(t) { + t.setVal(_.toStr(newVal)); + }); + return this; + } + }, + destroy: function destroy() { + ttEach(this, function(typeahead, $input) { + revert($input); + typeahead.destroy(); + }); + return this; + } + }; + $.fn.typeahead = function(method) { + if (methods[method]) { + return methods[method].apply(this, [].slice.call(arguments, 1)); + } else { + return methods.initialize.apply(this, arguments); + } + }; + $.fn.typeahead.noConflict = function noConflict() { + $.fn.typeahead = old; + return this; + }; + function ttEach($els, fn) { + $els.each(function() { + var $input = $(this), typeahead; + (typeahead = $input.data(keys.typeahead)) && fn(typeahead, $input); + }); + } + function buildHintFromInput($input, www) { + return $input.clone().addClass(www.classes.hint).removeData().css(www.css.hint).css(getBackgroundStyles($input)).prop({ + readonly: true, + required: false + }).removeAttr("id name placeholder").removeClass("required").attr({ + spellcheck: "false", + tabindex: -1 + }); + } + function prepInput($input, www) { + $input.data(keys.attrs, { + dir: $input.attr("dir"), + autocomplete: $input.attr("autocomplete"), + spellcheck: $input.attr("spellcheck"), + style: $input.attr("style") + }); + $input.addClass(www.classes.input).attr({ + spellcheck: false + }); + try { + !$input.attr("dir") && $input.attr("dir", "auto"); + } catch (e) {} + return $input; + } + function getBackgroundStyles($el) { + return { + backgroundAttachment: $el.css("background-attachment"), + backgroundClip: $el.css("background-clip"), + backgroundColor: $el.css("background-color"), + backgroundImage: $el.css("background-image"), + backgroundOrigin: $el.css("background-origin"), + backgroundPosition: $el.css("background-position"), + backgroundRepeat: $el.css("background-repeat"), + backgroundSize: $el.css("background-size") + }; + } + function revert($input) { + var www, $wrapper; + www = $input.data(keys.www); + $wrapper = $input.parent().filter(www.selectors.wrapper); + _.each($input.data(keys.attrs), function(val, key) { + _.isUndefined(val) ? $input.removeAttr(key) : $input.attr(key, val); + }); + $input.removeData(keys.typeahead).removeData(keys.www).removeData(keys.attr).removeClass(www.classes.input); + if ($wrapper.length) { + $input.detach().insertAfter($wrapper); + $wrapper.remove(); + } + } + function $elOrNull(obj) { + var isValid, $el; + isValid = _.isJQuery(obj) || _.isElement(obj); + $el = isValid ? $(obj).first() : []; + return $el.length ? $el : null; + } + })(); +}); \ No newline at end of file diff --git a/docs/micro-example.html b/docs/micro-example.html new file mode 100644 index 0000000..3ae38ac --- /dev/null +++ b/docs/micro-example.html @@ -0,0 +1,262 @@ + + + + Micro Example Reference + + + + + + + + + + + + + + + +
                      +

                      + + InAppPurchaseLib documentation + + (77% documented) +

                      + +

                      +

                      + +
                      +

                      + +

                      + + + View on GitHub + +

                      + +
                      + + + +
                      + +
                      + +
                      +
                      + +

                      Micro Example

                      +
                      /** AppDelegate.swift */
                      +import UIKit
                      +import InAppPurchaseLib
                      +
                      +@UIApplicationMain
                      +class AppDelegate: UIResponder, UIApplicationDelegate {
                      +  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
                      +    // Initialize the library
                      +    InAppPurchase.initialize(
                      +      iapProducts: [
                      +        IAPProduct(productIdentifier: "my_product", productType: .nonConsumable)
                      +      ],
                      +      validatorUrlString: "https://validator.fovea.cc/v1/validate?appName=demo&apiKey=12345678"
                      +    )
                      +    return true
                      +  }
                      +
                      +  func applicationWillTerminate(_ application: UIApplication) {
                      +    // Clean
                      +    InAppPurchase.stop()
                      +  }
                      +}
                      +
                      +
                      /** ViewController.swift */
                      +import UIKit
                      +import StoreKit
                      +import InAppPurchaseLib
                      +
                      +class ViewController: UIViewController {
                      +  private var loaderView = LoaderView()
                      +  @IBOutlet weak var statusLabel: UILabel!
                      +  @IBOutlet weak var productTitleLabel: UILabel!
                      +  @IBOutlet weak var productDescriptionLabel: UILabel!
                      +  @IBOutlet weak var purchaseButton: UIButton!
                      +  @IBOutlet weak var restorePurchasesButton: UIButton!
                      +
                      +  override func viewDidLoad() {
                      +    super.viewDidLoad()
                      +    // Add action for purchases and restore butons.
                      +    purchaseButton.addTarget(self, action: #selector(self.purchase), for: .touchUpInside)
                      +    restorePurchasesButton.addTarget(self, action: #selector(self.restorePurchases), for: .touchUpInside)
                      +  }
                      +
                      +  override func viewWillAppear(_ animated: Bool) {
                      +    self.refreshView()
                      +    InAppPurchase.refresh(callback: { _ in
                      +      self.refreshView()
                      +    })
                      +  }
                      +
                      +  func refreshView() {
                      +    guard let product: SKProduct = InAppPurchase.getProductBy(identifier: "my_product") else {
                      +      self.productTitleLabel.text = "Product unavailable"
                      +      return
                      +    }
                      +    // Display product information.
                      +    productTitleLabel.text = product.localizedTitle
                      +    productDescriptionLabel.text = product.localizedDescription
                      +    purchaseButton.setTitle(product.localizedPrice, for: .normal)
                      +
                      +    // Disable the button if the product has already been purchased.
                      +    if InAppPurchase.hasActivePurchase(for: "my_product") {
                      +      statusLabel.text = "OWNED"
                      +      purchaseButton.isPointerInteractionEnabled = false
                      +    }
                      +  }
                      +
                      +  @IBAction func purchase(_ sender: Any) {
                      +    self.loaderView.show()
                      +    InAppPurchase.purchase(
                      +      productIdentifier: "my_product",
                      +      callback: { result in
                      +        self.loaderView.hide()
                      +      })
                      +  }
                      +
                      +  @IBAction func restorePurchases(_ sender: Any) {
                      +    self.loaderView.show()
                      +    InAppPurchase.restorePurchases(callback: { result in
                      +      self.loaderView.hide()
                      +    })
                      +  }
                      +}
                      +
                      + +
                      +
                      + + +
                      +
                      + + +
                      + diff --git a/docs/purchasing.html b/docs/purchasing.html new file mode 100644 index 0000000..7a6996c --- /dev/null +++ b/docs/purchasing.html @@ -0,0 +1,245 @@ + + + + Purchasing Reference + + + + + + + + + + + + + + + +
                      +

                      + + InAppPurchaseLib documentation + + (77% documented) +

                      + +

                      +

                      + +
                      +

                      + +

                      + + + View on GitHub + +

                      + +
                      + + + +
                      + +
                      + +
                      +
                      + +

                      Purchasing

                      + +

                      The purchase process is generally a little bit more involving than most people would expect. Why is it not just: purchase → on success unlock the feature?

                      + +

                      Several reasons:

                      + +
                        +
                      • In-app purchases can be initiated outside the app
                      • +
                      • In-app purchases can be deferred, pending parental approval
                      • +
                      • Apple wants to be sure you delivered the product before charging the user
                      • +
                      + +

                      That is why the process looks like so:

                      + +
                        +
                      • being ready to handle purchase events from app startup
                      • +
                      • finalizing transactions when product delivery is complete
                      • +
                      • sending purchase request, for which successful doesn’t always mean complete
                      • +
                      +

                      Initiating a purchase

                      + +

                      To initiate a purchase, use the InAppPurchase.purchase() function. It takes the productIdentifier and a callback function, called when the purchase has been processed.

                      + +

                      Important: Do not process the purchase here, we’ll handle that later!

                      + +

                      From this callback, you can for example unlock the UI by hiding your loading indicator and display a message to the user.

                      + +

                      Example:

                      +
                      self.loaderView.show()
                      +InAppPurchase.purchase(
                      +  productIdentifier: "my_product_id",
                      +  callback: { _ in
                      +    self.loaderView.hide()
                      +})
                      +
                      + +

                      This simple example locks the UI with a loader when the purchase is in progress. We’ll see later how the purchase has to be processed by your applicaiton.

                      + +

                      The callback also gives more information about the outcome of the purchase, you might want to use it to update your UI as well. Note that some events are useful for analytics. So here’s a more complete example.

                      +
                      self.loaderView.show()
                      +InAppPurchase.purchase(
                      +  productIdentifier: "my_product_id",
                      +  callback: { result in
                      +    self.loaderView.hide()
                      +
                      +    switch result.state {
                      +    case .purchased:
                      +      // Product successfully purchased
                      +      // Reminder: Do not process the purchase here, only update your UI.
                      +      //           that's why we do not send data to analytics.
                      +      openThankYouScreen()
                      +    case .failed:
                      +      // Purchase failed
                      +      // - Human formated reason can be found in result.localizedDescription
                      +      // - More details in either result.skError or result.iapError
                      +      showError(result.localizedDescription)
                      +    case .deferred:
                      +      // The purchase is deferred, waiting for the parent's approval
                      +      openWaitingParentApprovalScreen()
                      +    case .cancelled:
                      +      // The user canceled the request, generally only useful for analytics.
                      +  }
                      +})
                      +
                      + +

                      If the purchase fails, result will contain either .skError, a SKError from StoreKit, or .iapError, an IAPError.

                      + +

                      Tip: After a successful purchase, you should see a new transaction in Fovea’s dashboard.

                      + +
                      +
                      + + +
                      +
                      + + +
                      + diff --git a/docs/refreshing.html b/docs/refreshing.html new file mode 100644 index 0000000..56e094a --- /dev/null +++ b/docs/refreshing.html @@ -0,0 +1,189 @@ + + + + Refreshing Reference + + + + + + + + + + + + + + + +
                      +

                      + + InAppPurchaseLib documentation + + (77% documented) +

                      + +

                      +

                      + +
                      +

                      + +

                      + + + View on GitHub + +

                      + +
                      + + + +
                      + +
                      + +
                      +
                      + +

                      Refreshing

                      + +

                      Data might change or not be yet available when your “product” view is presented. In order to properly handle those cases, you should refresh your view after refreshing in-app products metadata. You want to be sure you’re displaying up-to-date information.

                      + +

                      To achieve this, call InAppPurchase.refresh() when your view is presented.

                      +
                      override func viewWillAppear(_ animated: Bool) {
                      +  self.refreshView()
                      +  InAppPurchase.refresh(callback: { _ in
                      +      self.refreshView()
                      +  })
                      +}
                      +
                      + +
                      +
                      + + +
                      +
                      + + +
                      + diff --git a/docs/restoring-purchases.html b/docs/restoring-purchases.html new file mode 100644 index 0000000..ab44024 --- /dev/null +++ b/docs/restoring-purchases.html @@ -0,0 +1,201 @@ + + + + Restoring purchases Reference + + + + + + + + + + + + + + + +
                      +

                      + + InAppPurchaseLib documentation + + (77% documented) +

                      + +

                      +

                      + +
                      +

                      + +

                      + + + View on GitHub + +

                      + +
                      + + + +
                      + +
                      + +
                      +
                      + +

                      Restoring purchases

                      + +

                      Except if you only sell consumable products, Apple requires that you provide a “Restore Purchases” button to your users. In general, it is found in your application settings.

                      + +

                      Call this method when this button is pressed.

                      +
                      @IBAction func restorePurchases(_ sender: Any) {
                      +  self.loaderView.show()
                      +  InAppPurchase.restorePurchases(callback: { result in
                      +      self.loaderView.hide()
                      +      switch result.state {
                      +      case .succeeded:
                      +          if result.addedPurchases > 0 {
                      +              print("Restore purchases successful.")
                      +          } else {
                      +              print("No purchase to restore.")
                      +          }
                      +      case .failed:
                      +          print("Restore purchases failed.")
                      +      }
                      +  })
                      +}
                      +
                      + +

                      The callback method is called once the operation is complete. You can use it to unlock the UI, by hiding your loader for example, and display the adapted message to the user.

                      + +
                      +
                      + + +
                      +
                      + + +
                      + diff --git a/docs/search.json b/docs/search.json new file mode 100644 index 0000000..ca5875b --- /dev/null +++ b/docs/search.json @@ -0,0 +1 @@ +{"Protocols/IAPErrorProtocol.html#/s:16InAppPurchaseLib16IAPErrorProtocolP4codeAA0E4CodeOvp":{"name":"code","abstract":"

                      Undocumented

                      ","parent_name":"IAPErrorProtocol"},"Enums/IAPErrorCode.html#/s:16InAppPurchaseLib12IAPErrorCodeO21libraryNotInitializedyA2CmF":{"name":"libraryNotInitialized","abstract":"

                      You must call the initialize fuction before using the library.

                      ","parent_name":"IAPErrorCode"},"Enums/IAPErrorCode.html#/s:16InAppPurchaseLib12IAPErrorCodeO23bundleIdentifierInvalidyA2CmF":{"name":"bundleIdentifierInvalid","abstract":"

                      The Bundle Identifier is invalid.

                      ","parent_name":"IAPErrorCode"},"Enums/IAPErrorCode.html#/s:16InAppPurchaseLib12IAPErrorCodeO19validatorUrlInvalidyA2CmF":{"name":"validatorUrlInvalid","abstract":"

                      The Validator URL String is invalid.

                      ","parent_name":"IAPErrorCode"},"Enums/IAPErrorCode.html#/s:16InAppPurchaseLib12IAPErrorCodeO20refreshReceiptFailedyA2CmF":{"name":"refreshReceiptFailed","abstract":"

                      Failed to refresh the App Store receipt.

                      ","parent_name":"IAPErrorCode"},"Enums/IAPErrorCode.html#/s:16InAppPurchaseLib12IAPErrorCodeO21validateReceiptFailedyA2CmF":{"name":"validateReceiptFailed","abstract":"

                      Failed to validate the App Store receipt with Fovea.

                      ","parent_name":"IAPErrorCode"},"Enums/IAPErrorCode.html#/s:16InAppPurchaseLib12IAPErrorCodeO17readReceiptFailedyA2CmF":{"name":"readReceiptFailed","abstract":"

                      Failed to read the receipt validation.

                      ","parent_name":"IAPErrorCode"},"Enums/IAPErrorCode.html#/s:16InAppPurchaseLib12IAPErrorCodeO21refreshProductsFailedyA2CmF":{"name":"refreshProductsFailed","abstract":"

                      Failed to refresh products from the App Store.

                      ","parent_name":"IAPErrorCode"},"Enums/IAPErrorCode.html#/s:16InAppPurchaseLib12IAPErrorCodeO15productNotFoundyA2CmF":{"name":"productNotFound","abstract":"

                      The product was not found on the App Store and cannot be purchased.

                      ","parent_name":"IAPErrorCode"},"Enums/IAPErrorCode.html#/s:16InAppPurchaseLib12IAPErrorCodeO010cannotMakeC0yA2CmF":{"name":"cannotMakePurchase","abstract":"

                      The user is not allowed to authorize payments.

                      ","parent_name":"IAPErrorCode"},"Enums/IAPErrorCode.html#/s:16InAppPurchaseLib12IAPErrorCodeO17alreadyPurchasingyA2CmF":{"name":"alreadyPurchasing","abstract":"

                      A purchase is already in progress.

                      ","parent_name":"IAPErrorCode"},"Structs/IAPError.html#/s:16InAppPurchaseLib8IAPErrorV4codeAA0E4CodeOvp":{"name":"code","abstract":"

                      The error code.

                      ","parent_name":"IAPError"},"Structs/IAPError.html#/s:16InAppPurchaseLib8IAPErrorV20localizedDescriptionSSvp":{"name":"localizedDescription","abstract":"

                      The error description.

                      ","parent_name":"IAPError"},"Enums/IAPRefreshResultState.html#/s:16InAppPurchaseLib21IAPRefreshResultStateO9succeededyA2CmF":{"name":"succeeded","abstract":"

                      Refresh was successful.

                      ","parent_name":"IAPRefreshResultState"},"Enums/IAPRefreshResultState.html#/s:16InAppPurchaseLib21IAPRefreshResultStateO6failedyA2CmF":{"name":"failed","abstract":"

                      Refresh failed.

                      ","parent_name":"IAPRefreshResultState"},"Enums/IAPRefreshResultState.html#/s:16InAppPurchaseLib21IAPRefreshResultStateO7skippedyA2CmF":{"name":"skipped","abstract":"

                      Refresh has been skipped because it is not necessary.

                      ","parent_name":"IAPRefreshResultState"},"Enums/IAPPurchaseResultState.html#/s:16InAppPurchaseLib22IAPPurchaseResultStateO9purchasedyA2CmF":{"name":"purchased","abstract":"

                      The purchase was successful.

                      ","parent_name":"IAPPurchaseResultState"},"Enums/IAPPurchaseResultState.html#/s:16InAppPurchaseLib22IAPPurchaseResultStateO6failedyA2CmF":{"name":"failed","abstract":"

                      Puchase failed.

                      ","parent_name":"IAPPurchaseResultState"},"Enums/IAPPurchaseResultState.html#/s:16InAppPurchaseLib22IAPPurchaseResultStateO9cancelledyA2CmF":{"name":"cancelled","abstract":"

                      The purchase was cancelled by the user.

                      ","parent_name":"IAPPurchaseResultState"},"Enums/IAPPurchaseResultState.html#/s:16InAppPurchaseLib22IAPPurchaseResultStateO8deferredyA2CmF":{"name":"deferred","abstract":"

                      The purchase is deferred.

                      ","parent_name":"IAPPurchaseResultState"},"Structs/IAPRefreshResult.html#/s:16InAppPurchaseLib16IAPRefreshResultV5stateAA0eF5StateOvp":{"name":"state","abstract":"

                      Undocumented

                      ","parent_name":"IAPRefreshResult"},"Structs/IAPRefreshResult.html#/s:16InAppPurchaseLib16IAPRefreshResultV8iapErrorAA8IAPErrorVSgvp":{"name":"iapError","abstract":"

                      Undocumented

                      ","parent_name":"IAPRefreshResult"},"Structs/IAPRefreshResult.html#/s:16InAppPurchaseLib16IAPRefreshResultV14addedPurchasesSivp":{"name":"addedPurchases","abstract":"

                      Undocumented

                      ","parent_name":"IAPRefreshResult"},"Structs/IAPRefreshResult.html#/s:16InAppPurchaseLib16IAPRefreshResultV16updatedPurchasesSivp":{"name":"updatedPurchases","abstract":"

                      Undocumented

                      ","parent_name":"IAPRefreshResult"},"Structs/IAPPurchaseResult.html#/s:16InAppPurchaseLib17IAPPurchaseResultV5stateAA0eF5StateOvp":{"name":"state","abstract":"

                      Undocumented

                      ","parent_name":"IAPPurchaseResult"},"Structs/IAPPurchaseResult.html#/s:16InAppPurchaseLib17IAPPurchaseResultV8iapErrorAA8IAPErrorVSgvp":{"name":"iapError","abstract":"

                      Undocumented

                      ","parent_name":"IAPPurchaseResult"},"Structs/IAPPurchaseResult.html#/s:16InAppPurchaseLib17IAPPurchaseResultV7skErrorSC11SKErrorCodeLeVSgvp":{"name":"skError","abstract":"

                      Undocumented

                      ","parent_name":"IAPPurchaseResult"},"Structs/IAPPurchaseResult.html#/s:16InAppPurchaseLib17IAPPurchaseResultV20localizedDescriptionSSSgvp":{"name":"localizedDescription","abstract":"

                      Undocumented

                      ","parent_name":"IAPPurchaseResult"},"Enums/IAPPeriodFormat.html#/s:16InAppPurchaseLib15IAPPeriodFormatO5shortyA2CmF":{"name":"short","abstract":"

                      Undocumented

                      ","parent_name":"IAPPeriodFormat"},"Enums/IAPPeriodFormat.html#/s:16InAppPurchaseLib15IAPPeriodFormatO4longyA2CmF":{"name":"long","abstract":"

                      Undocumented

                      ","parent_name":"IAPPeriodFormat"},"Extensions/SKProduct.html#/s:So9SKProductC16InAppPurchaseLibE21localizedPeriodFormatAC09IAPPeriodH0OvpZ":{"name":"localizedPeriodFormat","abstract":"

                      Undocumented

                      ","parent_name":"SKProduct"},"Extensions/SKProduct.html#/s:So9SKProductC16InAppPurchaseLibE28hasIntroductoryPriceEligibleSbyF":{"name":"hasIntroductoryPriceEligible()","abstract":"

                      Checks if the product has an introductory price the user is eligible to.

                      ","parent_name":"SKProduct"},"Extensions/SKProduct.html#/s:So9SKProductC16InAppPurchaseLibE14localizedPriceSSvp":{"name":"localizedPrice","abstract":"

                      Returns a localized string with the cost of the product in the local currency.

                      ","parent_name":"SKProduct"},"Extensions/SKProduct.html#/s:So9SKProductC16InAppPurchaseLibE27localizedSubscriptionPeriodSSSgvp":{"name":"localizedSubscriptionPeriod","abstract":"

                      Returns a localized string with the period of the subscription product.

                      ","parent_name":"SKProduct"},"Extensions/SKProduct.html#/s:So9SKProductC16InAppPurchaseLibE26localizedIntroductoryPriceSSSgvp":{"name":"localizedIntroductoryPrice","abstract":"

                      Returns a localized string with the introductory price if available, in the local currency.

                      ","parent_name":"SKProduct"},"Extensions/SKProduct.html#/s:So9SKProductC16InAppPurchaseLibE27localizedIntroductoryPeriodSSSgvp":{"name":"localizedIntroductoryPeriod","abstract":"

                      Returns a localized string with the introductory price period of the subscription product.

                      ","parent_name":"SKProduct"},"Extensions/SKProduct.html#/s:So9SKProductC16InAppPurchaseLibE29localizedIntroductoryDurationSSSgvp":{"name":"localizedIntroductoryDuration","abstract":"

                      Returns a localized string with the duration of the introductory price.

                      ","parent_name":"SKProduct"},"Enums/IAPProductType.html#/s:16InAppPurchaseLib14IAPProductTypeO10consumableyA2CmF":{"name":"consumable","abstract":"

                      Undocumented

                      ","parent_name":"IAPProductType"},"Enums/IAPProductType.html#/s:16InAppPurchaseLib14IAPProductTypeO13nonConsumableyA2CmF":{"name":"nonConsumable","abstract":"

                      Undocumented

                      ","parent_name":"IAPProductType"},"Enums/IAPProductType.html#/s:16InAppPurchaseLib14IAPProductTypeO23nonRenewingSubscriptionyA2CmF":{"name":"nonRenewingSubscription","abstract":"

                      Undocumented

                      ","parent_name":"IAPProductType"},"Enums/IAPProductType.html#/s:16InAppPurchaseLib14IAPProductTypeO25autoRenewableSubscriptionyA2CmF":{"name":"autoRenewableSubscription","abstract":"

                      Undocumented

                      ","parent_name":"IAPProductType"},"Structs/IAPProduct.html#/s:16InAppPurchaseLib10IAPProductV17productIdentifierSSvp":{"name":"productIdentifier","abstract":"

                      The identifier of the product.

                      ","parent_name":"IAPProduct"},"Structs/IAPProduct.html#/s:16InAppPurchaseLib10IAPProductV11productTypeAA0eG0Ovp":{"name":"productType","abstract":"

                      The type of the product.

                      ","parent_name":"IAPProduct"},"Structs/IAPProduct.html#/s:16InAppPurchaseLib10IAPProductV17productIdentifier0F4TypeACSS_AA0eH0Otcfc":{"name":"init(productIdentifier:productType:)","abstract":"

                      Initializes an IAPProduct with its identifier and type.

                      ","parent_name":"IAPProduct"},"Protocols/IAPPurchaseDelegate.html#/s:16InAppPurchaseLib19IAPPurchaseDelegateP16productPurchased0G10IdentifierySS_tF":{"name":"productPurchased(productIdentifier:)","abstract":"

                      Called when a product is newly purchased, updated or restored.

                      ","parent_name":"IAPPurchaseDelegate"},"Classes/DefaultPurchaseDelegate.html#/s:16InAppPurchaseLib07DefaultC8DelegateCACycfc":{"name":"init()","abstract":"

                      Undocumented

                      ","parent_name":"DefaultPurchaseDelegate"},"Classes/DefaultPurchaseDelegate.html#/s:16InAppPurchaseLib07DefaultC8DelegateC16productPurchased0G10IdentifierySS_tF":{"name":"productPurchased(productIdentifier:)","abstract":"

                      Finish the product transactions when a product is newly purchased, updated or restored.

                      ","parent_name":"DefaultPurchaseDelegate"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP11iapProductsSayAA10IAPProductVGvpZ":{"name":"iapProducts","abstract":"

                      The array of IAPProduct.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP18validatorUrlStringSSSgvpZ":{"name":"validatorUrlString","abstract":"

                      The validator url retrieved from Fovea.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP03iapC8DelegateAA011IAPPurchaseF0_pSgvpZ":{"name":"iapPurchaseDelegate","abstract":"

                      The instance of class that adopts the IAPPurchaseDelegate protocol.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP19applicationUsernameSSSgvpZ":{"name":"applicationUsername","abstract":"

                      The user name, if your app implements user login.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP10initialize11iapProducts18validatorUrlString0fC8Delegate19applicationUsernameySayAA10IAPProductVG_SSAA011IAPPurchaseK0_pSSSgtFZ":{"name":"initialize(iapProducts:validatorUrlString:iapPurchaseDelegate:applicationUsername:)","abstract":"

                      Start observing the payment queue, as soon as possible, and refresh Product list and user Receipt.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP4stopyyFZ":{"name":"stop()","abstract":"

                      Stop observing the payment queue, when the application will terminate, for proper cleanup.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP7refresh8callbackyyAA16IAPRefreshResultVc_tFZ":{"name":"refresh(callback:)","abstract":"

                      Refresh Product list and user Receipt.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP11getProductsSaySo9SKProductCGyFZ":{"name":"getProducts()","abstract":"

                      Gets all products retrieved from the App Store

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP12getProductBy10identifierSo9SKProductCSgSS_tFZ":{"name":"getProductBy(identifier:)","abstract":"

                      Gets the product by its identifier from the list of products retrieved from the App Store.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP15canMakePaymentsSbyFZ":{"name":"canMakePayments()","abstract":"

                      Checks if the user is allowed to authorize payments.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP8purchase17productIdentifier8quantity8callbackySS_SiyAA17IAPPurchaseResultVctFZ":{"name":"purchase(productIdentifier:quantity:callback:)","abstract":"

                      Request a Payment from the App Store.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP16restorePurchases8callbackyyAA16IAPRefreshResultVc_tFZ":{"name":"restorePurchases(callback:)","abstract":"

                      Restore purchased products.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP18finishTransactions3forySS_tFZ":{"name":"finishTransactions(for:)","abstract":"

                      Finish all transactions for the product.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP22hasDeferredTransaction3forSbSS_tFZ":{"name":"hasDeferredTransaction(for:)","abstract":"

                      Checks if the last transaction state for a given product was deferred.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP19hasAlreadyPurchasedSbyFZ":{"name":"hasAlreadyPurchased()","abstract":"

                      Checks if the user has already purchased at least one product.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP09hasActiveC03forSbSS_tFZ":{"name":"hasActivePurchase(for:)","abstract":"

                      Checks if the user currently own (or is subscribed to) a given product (nonConsumable or autoRenewableSubscription).

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP21hasActiveSubscriptionSbyFZ":{"name":"hasActiveSubscription()","abstract":"

                      Checks if the user has an active auto renewable subscription regardless of the product identifier.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP03getC4Date3for10Foundation0F0VSgSS_tFZ":{"name":"getPurchaseDate(for:)","abstract":"

                      Returns the latest purchased date for a given product.

                      ","parent_name":"InAppPurchaseLib"},"Protocols/InAppPurchaseLib.html#/s:16InAppPurchaseLibAAP13getExpiryDate3for10Foundation0G0VSgSS_tFZ":{"name":"getExpiryDate(for:)","abstract":"

                      Returns the expiry date for a subcription. May be past or future.

                      ","parent_name":"InAppPurchaseLib"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C11iapProductsSayAA10IAPProductVGvpZ":{"name":"iapProducts","abstract":"

                      The array of IAPProduct.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C18validatorUrlStringSSSgvpZ":{"name":"validatorUrlString","abstract":"

                      The validator url retrieved from Fovea.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C03iapC8DelegateAA011IAPPurchaseF0_pSgvpZ":{"name":"iapPurchaseDelegate","abstract":"

                      The instance of class that adopts the IAPPurchaseDelegate protocol.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C19applicationUsernameSSSgvpZ":{"name":"applicationUsername","abstract":"

                      The user name, if your app implements user login.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C10initialize11iapProducts18validatorUrlString0fC8Delegate19applicationUsernameySayAA10IAPProductVG_SSAA011IAPPurchaseK0_pSSSgtFZ":{"name":"initialize(iapProducts:validatorUrlString:iapPurchaseDelegate:applicationUsername:)","abstract":"

                      Start observing the payment queue, as soon as possible, and refresh Product list and user Receipt.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C4stopyyFZ":{"name":"stop()","abstract":"

                      Stop observing the payment queue, when the application will terminate, for proper cleanup.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C7refresh8callbackyyAA16IAPRefreshResultVc_tFZ":{"name":"refresh(callback:)","abstract":"

                      Refresh Product list and user Receipt.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C11getProductsSaySo9SKProductCGyFZ":{"name":"getProducts()","abstract":"

                      Gets all products retrieved from the App Store

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C12getProductBy10identifierSo9SKProductCSgSS_tFZ":{"name":"getProductBy(identifier:)","abstract":"

                      Gets the product by its identifier from the list of products retrieved from the App Store.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C15canMakePaymentsSbyFZ":{"name":"canMakePayments()","abstract":"

                      Checks if the user is allowed to authorize payments.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C8purchase17productIdentifier8quantity8callbackySS_SiyAA17IAPPurchaseResultVctFZ":{"name":"purchase(productIdentifier:quantity:callback:)","abstract":"

                      Request a Payment from the App Store.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C16restorePurchases8callbackyyAA16IAPRefreshResultVc_tFZ":{"name":"restorePurchases(callback:)","abstract":"

                      Restore purchased products.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C18finishTransactions3forySS_tFZ":{"name":"finishTransactions(for:)","abstract":"

                      Finish all transactions for the product.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C22hasDeferredTransaction3forSbSS_tFZ":{"name":"hasDeferredTransaction(for:)","abstract":"

                      Checks if the last transaction state for a given product was deferred.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C19hasAlreadyPurchasedSbyFZ":{"name":"hasAlreadyPurchased()","abstract":"

                      Checks if the user has already purchased at least one product.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C09hasActiveC03forSbSS_tFZ":{"name":"hasActivePurchase(for:)","abstract":"

                      Checks if the user currently own (or is subscribed to) a given product (nonConsumable or autoRenewableSubscription).

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C21hasActiveSubscriptionSbyFZ":{"name":"hasActiveSubscription()","abstract":"

                      Checks if the user has an active auto renewable subscription regardless of the product identifier.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C03getC4Date3for10Foundation0F0VSgSS_tFZ":{"name":"getPurchaseDate(for:)","abstract":"

                      Returns the latest purchased date for a given product.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html#/s:16InAppPurchaseLib0abC0C13getExpiryDate3for10Foundation0G0VSgSS_tFZ":{"name":"getExpiryDate(for:)","abstract":"

                      Returns the expiry date for a subcription. May be past or future.

                      ","parent_name":"InAppPurchase"},"Classes/InAppPurchase.html":{"name":"InAppPurchase"},"Protocols/InAppPurchaseLib.html":{"name":"InAppPurchaseLib","abstract":"

                      The protocol that InAppPurchase adopts.

                      "},"Classes/DefaultPurchaseDelegate.html":{"name":"DefaultPurchaseDelegate","abstract":"

                      The default implementation of IAPPurchaseDelegate if no other is provided. It is enough if you only have non-consumable and/or auto-renewable subscription products.

                      "},"Protocols/IAPPurchaseDelegate.html":{"name":"IAPPurchaseDelegate","abstract":"

                      The protocol that you must adopt if you have consumable and/or non-renewing subscription products.

                      "},"Structs/IAPProduct.html":{"name":"IAPProduct","abstract":"

                      Undocumented

                      "},"Enums/IAPProductType.html":{"name":"IAPProductType","abstract":"

                      Undocumented

                      "},"Extensions/SKProduct.html":{"name":"SKProduct"},"Enums/IAPPeriodFormat.html":{"name":"IAPPeriodFormat","abstract":"

                      Undocumented

                      "},"API%20documentation.html#/s:16InAppPurchaseLib19IAPPurchaseCallbacka":{"name":"IAPPurchaseCallback","abstract":"

                      Undocumented

                      "},"API%20documentation.html#/s:16InAppPurchaseLib18IAPRefreshCallbacka":{"name":"IAPRefreshCallback","abstract":"

                      Undocumented

                      "},"Structs/IAPPurchaseResult.html":{"name":"IAPPurchaseResult","abstract":"

                      The result returned in the purchase() callback.

                      "},"Structs/IAPRefreshResult.html":{"name":"IAPRefreshResult","abstract":"

                      The result returned in the refresh() or restorePurchases() callback.

                      "},"Enums/IAPPurchaseResultState.html":{"name":"IAPPurchaseResultState","abstract":"

                      The list of the different states of the IAPPurchaseResult.

                      "},"Enums/IAPRefreshResultState.html":{"name":"IAPRefreshResultState","abstract":"

                      The list of the different states of the IAPRefreshResult.

                      "},"Structs/IAPError.html":{"name":"IAPError","abstract":"

                      When calling refresh(), purchase() or restorePurchases(), the callback can return an IAPError if the state is failed.

                      "},"Enums/IAPErrorCode.html":{"name":"IAPErrorCode","abstract":"

                      The list of error codes that can be returned by the library.

                      "},"Protocols/IAPErrorProtocol.html":{"name":"IAPErrorProtocol","abstract":"

                      Undocumented

                      "},"initialization.html":{"name":"Initialization"},"displaying-products.html":{"name":"Displaying products"},"displaying-subscriptions.html":{"name":"Displaying subscriptions"},"refreshing.html":{"name":"Refreshing"},"purchasing.html":{"name":"Purchasing"},"handling-purchases.html":{"name":"Handling purchases"},"restoring-purchases.html":{"name":"Restoring purchases"},"displaying-products-with-purchases.html":{"name":"Displaying products with purchases"},"errors.html":{"name":"Errors"},"analytics.html":{"name":"Analytics"},"server-integration.html":{"name":"Server integration"},"installation.html":{"name":"Installation"},"micro-example.html":{"name":"Micro Example"},"Getting%20Started.html":{"name":"Getting Started"},"Usage.html":{"name":"Usage"},"API%20documentation.html":{"name":"API documentation"}} \ No newline at end of file diff --git a/docs/server-integration.html b/docs/server-integration.html new file mode 100644 index 0000000..ab51e7d --- /dev/null +++ b/docs/server-integration.html @@ -0,0 +1,198 @@ + + + + Server integration Reference + + + + + + + + + + + + + + + +
                      +

                      + + InAppPurchaseLib documentation + + (77% documented) +

                      + +

                      +

                      + +
                      +

                      + +

                      + + + View on GitHub + +

                      + +
                      + + + +
                      + +
                      + +
                      +
                      + +

                      Server integration

                      + +

                      In more advanced use cases, you have a server component. Users are logged in and you’ll like to unlock the content for this user on your server. The safest approach is to setup a Webhook on Fovea. You’ll receive notifications from Fovea that transaction have been processed and/or subscriptions updated.

                      + +

                      The information sent from Fovea has been verified from Apple’s server, which makes it way more trustable than information sent from your app itself.

                      + +

                      To take advantage of this, you have to inform the library of your application username. This applicationUsername can be provided as a parameter of the InAppPurchase.initialize method and updated later by changing the associated property.

                      + +

                      Example:

                      +
                      InAppPurchase.initialize(
                      +  iapProducts: [...],
                      +  validatorUrlString: "..."),
                      +  applicationUsername: UserSession.getUserId())
                      +
                      +// later ...
                      +InAppPurchase.applicationUsername = UserSession.getUserId()
                      +
                      + +

                      If a user account is mandatory in your app, you will want to delay calls to InAppPurchase.initialize() to when your user’s session is ready.

                      + +

                      Do not hesitate to contact Fovea for help.

                      + +
                      +
                      + + +
                      +
                      + + +
                      + diff --git a/docs/swift-doc/DefaultPurchaseDelegate/index.html b/docs/swift-doc/DefaultPurchaseDelegate/index.html deleted file mode 100755 index 60ef6bd..0000000 --- a/docs/swift-doc/DefaultPurchaseDelegate/index.html +++ /dev/null @@ -1,151 +0,0 @@ - - - - - - InAppPurchase - DefaultPurchaseDelegate - - - - -
                      - - - InAppPurchase - - Documentation - - Beta -
                      - - - - - -
                      -
                      -

                      - Class - Default​Purchase​Delegate -

                      - -
                      public class DefaultPurchaseDelegate: IAPPurchaseDelegate
                      -
                      -

                      The default implementation of IAPPurchaseDelegate if no other is provided.

                      - -
                      -
                      - -
                      - - - - - - - - - -DefaultPurchaseDelegate - - -DefaultPurchaseDelegate - - - - - -IAPPurchaseDelegate - - -IAPPurchaseDelegate - - - - - -DefaultPurchaseDelegate->IAPPurchaseDelegate - - - - - - - - -
                      -

                      Conforms To

                      -
                      -
                      IAPPurchaseDelegate
                      -

                      The protocol that you must adopt if you have consumable and/or nonRenewingSubscription products.

                      -
                      -
                      -
                      -
                      -

                      Initializers

                      - -
                      -

                      - init() -

                      -
                      public init()
                      -
                      -
                      -
                      -

                      Methods

                      - -
                      -

                      - product​Purchased(product​Identifier:​) -

                      -
                      public func productPurchased(productIdentifier: String)
                      -
                      -

                      Finish the product transactions when a product is newly purchased, updated or restored.

                      - -
                      -

                      Parameters

                      - - - - - - - - - - - - - - - - -
                      product​IdentifierString

                      The identifier of the product.

                      -
                      -
                      -
                      - - - -
                      -
                      - -
                      -

                      - Generated on using swift-doc. -

                      -
                      - - diff --git a/docs/swift-doc/IAPError/index.html b/docs/swift-doc/IAPError/index.html deleted file mode 100755 index 948cd13..0000000 --- a/docs/swift-doc/IAPError/index.html +++ /dev/null @@ -1,119 +0,0 @@ - - - - - - InAppPurchase - IAPError - - - - -
                      - - - InAppPurchase - - Documentation - - Beta -
                      - - - - - -
                      -
                      -

                      - Structure - IAPError -

                      - -
                      public struct IAPError: IAPErrorProtocol
                      -
                      - -
                      - - - - - - - - - -IAPError - - -IAPError - - - - - -IAPErrorProtocol - - -IAPErrorProtocol - - - - - -IAPError->IAPErrorProtocol - - - - - - - - -
                      -

                      Conforms To

                      -
                      -
                      IAPErrorProtocol
                      -
                      -
                      -
                      -
                      -

                      Properties

                      - -
                      -

                      - code -

                      -
                      var code: IAPErrorCode
                      -
                      -
                      -

                      - localized​Description -

                      -
                      var localizedDescription: String
                      -
                      -
                      - - - -
                      -
                      - -
                      -

                      - Generated on using swift-doc. -

                      -
                      - - diff --git a/docs/swift-doc/IAPErrorCode/index.html b/docs/swift-doc/IAPErrorCode/index.html deleted file mode 100755 index aa4e9ad..0000000 --- a/docs/swift-doc/IAPErrorCode/index.html +++ /dev/null @@ -1,119 +0,0 @@ - - - - - - InAppPurchase - IAPErrorCode - - - - -
                      - - - InAppPurchase - - Documentation - - Beta -
                      - - - - - -
                      -
                      -

                      - Enumeration - IAPError​Code -

                      - -
                      public enum IAPErrorCode
                      - -
                      -

                      Enumeration Cases

                      - -
                      -

                      - library​Not​Initialized -

                      -
                      case libraryNotInitialized
                      -
                      -
                      -

                      - product​Not​Found -

                      -
                      case productNotFound
                      -
                      -
                      -

                      - cannot​Make​Purchase -

                      -
                      case cannotMakePurchase
                      -
                      -
                      -

                      - already​Purchasing -

                      -
                      case alreadyPurchasing
                      -
                      -
                      -

                      - bundle​Identifier​Invalid -

                      -
                      case bundleIdentifierInvalid
                      -
                      -
                      -

                      - validator​Url​Invalid -

                      -
                      case validatorUrlInvalid
                      -
                      -
                      -

                      - refresh​Receipt​Failed -

                      -
                      case refreshReceiptFailed
                      -
                      -
                      -

                      - validate​Receipt​Failed -

                      -
                      case validateReceiptFailed
                      -
                      -
                      -

                      - read​Receipt​Failed -

                      -
                      case readReceiptFailed
                      -
                      -
                      -

                      - refresh​Products​Failed -

                      -
                      case refreshProductsFailed
                      -
                      -
                      - - - -
                      -
                      - -
                      -

                      - Generated on using swift-doc. -

                      -
                      - - diff --git a/docs/swift-doc/IAPErrorProtocol/index.html b/docs/swift-doc/IAPErrorProtocol/index.html deleted file mode 100755 index b4490ac..0000000 --- a/docs/swift-doc/IAPErrorProtocol/index.html +++ /dev/null @@ -1,129 +0,0 @@ - - - - - - InAppPurchase - IAPErrorProtocol - - - - -
                      - - - InAppPurchase - - Documentation - - Beta -
                      - - - - - -
                      -
                      -

                      - Protocol - IAPError​Protocol -

                      - -
                      public protocol IAPErrorProtocol: LocalizedError
                      -
                      - -
                      - - - - - - - - - -IAPErrorProtocol - - -IAPErrorProtocol - - - - - -LocalizedError - -LocalizedError - - - -IAPErrorProtocol->LocalizedError - - - - - -IAPError - - -IAPError - - - - - -IAPError->IAPErrorProtocol - - - - - - - - -
                      -

                      Conforms To

                      -
                      -
                      LocalizedError
                      -
                      -

                      Types Conforming to IAPError​Protocol

                      -
                      -
                      IAPError
                      -
                      -
                      -
                      - - - -
                      -

                      Requirements

                      - -
                      -

                      - code -

                      -
                      var code: IAPErrorCode
                      -
                      -
                      -
                      -
                      - -
                      -

                      - Generated on using swift-doc. -

                      -
                      - - diff --git a/docs/swift-doc/IAPPeriodFormat/index.html b/docs/swift-doc/IAPPeriodFormat/index.html deleted file mode 100755 index 551b536..0000000 --- a/docs/swift-doc/IAPPeriodFormat/index.html +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - InAppPurchase - IAPPeriodFormat - - - - -
                      - - - InAppPurchase - - Documentation - - Beta -
                      - - - - - -
                      -
                      -

                      - Enumeration - IAPPeriod​Format -

                      - -
                      public enum IAPPeriodFormat
                      - -
                      -

                      Enumeration Cases

                      - -
                      -

                      - short -

                      -
                      case short
                      -
                      -
                      -

                      - long -

                      -
                      case long
                      -
                      -
                      - - - -
                      -
                      - -
                      -

                      - Generated on using swift-doc. -

                      -
                      - - diff --git a/docs/swift-doc/IAPProduct/index.html b/docs/swift-doc/IAPProduct/index.html deleted file mode 100755 index db7e392..0000000 --- a/docs/swift-doc/IAPProduct/index.html +++ /dev/null @@ -1,130 +0,0 @@ - - - - - - InAppPurchase - IAPProduct - - - - -
                      - - - InAppPurchase - - Documentation - - Beta -
                      - - - - - -
                      -
                      -

                      - Structure - IAPProduct -

                      - -
                      public struct IAPProduct
                      - -
                      -

                      Initializers

                      - -
                      -

                      - init(product​Identifier:​product​Type:​) -

                      -
                      public init(productIdentifier: String, productType: IAPProductType)
                      -
                      -

                      Initializes an IAPProduct with its identifier and type.

                      - -
                      -
                      - - -
                      -

                      Parameters

                      - - - - - - - - - - - - - - - - - - - - - -
                      product​IdentifierString

                      The identifier of the product.

                      -
                      product​TypeIAPProduct​Type

                      The type of the product.

                      -
                      -
                      -
                      -
                      -

                      Properties

                      - -
                      -

                      - product​Identifier -

                      -
                      var productIdentifier: String
                      -
                      -

                      The identifier of the product.

                      - -
                      -
                      -
                      -

                      - product​Type -

                      -
                      var productType: IAPProductType
                      -
                      -

                      The type of the product.

                      - -
                      -
                      - - -
                      -
                      -
                      - - - -
                      -
                      - -
                      -

                      - Generated on using swift-doc. -

                      -
                      - - diff --git a/docs/swift-doc/IAPProductType/index.html b/docs/swift-doc/IAPProductType/index.html deleted file mode 100755 index 3f641db..0000000 --- a/docs/swift-doc/IAPProductType/index.html +++ /dev/null @@ -1,83 +0,0 @@ - - - - - - InAppPurchase - IAPProductType - - - - -
                      - - - InAppPurchase - - Documentation - - Beta -
                      - - - - - -
                      -
                      -

                      - Enumeration - IAPProduct​Type -

                      - -
                      public enum IAPProductType
                      - -
                      -

                      Enumeration Cases

                      - -
                      -

                      - consumable -

                      -
                      case consumable
                      -
                      -
                      -

                      - non​Consumable -

                      -
                      case nonConsumable
                      -
                      -
                      -

                      - non​Renewing​Subscription -

                      -
                      case nonRenewingSubscription
                      -
                      -
                      -

                      - auto​Renewable​Subscription -

                      -
                      case autoRenewableSubscription
                      -
                      -
                      - - - -
                      -
                      - -
                      -

                      - Generated on using swift-doc. -

                      -
                      - - diff --git a/docs/swift-doc/IAPPurchaseCallback/index.html b/docs/swift-doc/IAPPurchaseCallback/index.html deleted file mode 100755 index 7b663d9..0000000 --- a/docs/swift-doc/IAPPurchaseCallback/index.html +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - InAppPurchase - IAPPurchaseCallback - - - - -
                      - - - InAppPurchase - - Documentation - - Beta -
                      - - - - - -
                      -
                      -

                      - Typealias - IAPPurchase​Callback -

                      - -
                      public typealias IAPPurchaseCallback = (IAPPurchaseResult) -> Void
                      -
                      -
                      - -
                      -

                      - Generated on using swift-doc. -

                      -
                      - - diff --git a/docs/swift-doc/IAPPurchaseDelegate/index.html b/docs/swift-doc/IAPPurchaseDelegate/index.html deleted file mode 100755 index 58bda97..0000000 --- a/docs/swift-doc/IAPPurchaseDelegate/index.html +++ /dev/null @@ -1,147 +0,0 @@ - - - - - - InAppPurchase - IAPPurchaseDelegate - - - - -
                      - - - InAppPurchase - - Documentation - - Beta -
                      - - - - - -
                      -
                      -

                      - Protocol - IAPPurchase​Delegate -

                      - -
                      public protocol IAPPurchaseDelegate
                      -
                      -

                      The protocol that you must adopt if you have consumable and/or nonRenewingSubscription products.

                      - -
                      -
                      - -
                      - - - - - - - - - -IAPPurchaseDelegate - - -IAPPurchaseDelegate - - - - - -DefaultPurchaseDelegate - - -DefaultPurchaseDelegate - - - - - -DefaultPurchaseDelegate->IAPPurchaseDelegate - - - - - - - - -
                      -

                      Types Conforming to IAPPurchase​Delegate

                      -
                      -
                      DefaultPurchaseDelegate
                      -

                      The default implementation of IAPPurchaseDelegate if no other is provided.

                      -
                      -
                      -
                      - - - -
                      -

                      Requirements

                      - -
                      -

                      - product​Purchased(product​Identifier:​) -

                      -
                      func productPurchased(productIdentifier: String) -> Void
                      -
                      -

                      Called when a product is newly purchased, updated or restored.

                      - -
                      -
                      - -
                      -

                      Parameters

                      - - - - - - - - - - - - - - - - -
                      product​IdentifierString

                      The identifier of the product.

                      -
                      -
                      -
                      -
                      -
                      - -
                      -

                      - Generated on using swift-doc. -

                      -
                      - - diff --git a/docs/swift-doc/IAPPurchaseResult/index.html b/docs/swift-doc/IAPPurchaseResult/index.html deleted file mode 100755 index 19f0064..0000000 --- a/docs/swift-doc/IAPPurchaseResult/index.html +++ /dev/null @@ -1,83 +0,0 @@ - - - - - - InAppPurchase - IAPPurchaseResult - - - - -
                      - - - InAppPurchase - - Documentation - - Beta -
                      - - - - - -
                      -
                      -

                      - Structure - IAPPurchase​Result -

                      - -
                      public struct IAPPurchaseResult
                      - -
                      -

                      Properties

                      - -
                      -

                      - state -

                      -
                      var state: IAPPurchaseResultState
                      -
                      -
                      -

                      - iap​Error -

                      -
                      var iapError: IAPError?
                      -
                      -
                      -

                      - sk​Error -

                      -
                      var skError: SKError?
                      -
                      -
                      -

                      - localized​Description -

                      -
                      var localizedDescription: String?
                      -
                      -
                      - - - -
                      -
                      - -
                      -

                      - Generated on using swift-doc. -

                      -
                      - - diff --git a/docs/swift-doc/IAPPurchaseResultState/index.html b/docs/swift-doc/IAPPurchaseResultState/index.html deleted file mode 100755 index 9722438..0000000 --- a/docs/swift-doc/IAPPurchaseResultState/index.html +++ /dev/null @@ -1,83 +0,0 @@ - - - - - - InAppPurchase - IAPPurchaseResultState - - - - -
                      - - - InAppPurchase - - Documentation - - Beta -
                      - - - - - -
                      -
                      -

                      - Enumeration - IAPPurchase​Result​State -

                      - -
                      public enum IAPPurchaseResultState
                      - -
                      -

                      Enumeration Cases

                      - -
                      -

                      - purchased -

                      -
                      case purchased
                      -
                      -
                      -

                      - failed -

                      -
                      case failed
                      -
                      -
                      -

                      - cancelled -

                      -
                      case cancelled
                      -
                      -
                      -

                      - deferred -

                      -
                      case deferred
                      -
                      -
                      - - - -
                      -
                      - -
                      -

                      - Generated on using swift-doc. -

                      -
                      - - diff --git a/docs/swift-doc/IAPRefreshCallback/index.html b/docs/swift-doc/IAPRefreshCallback/index.html deleted file mode 100755 index 5184db2..0000000 --- a/docs/swift-doc/IAPRefreshCallback/index.html +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - InAppPurchase - IAPRefreshCallback - - - - -
                      - - - InAppPurchase - - Documentation - - Beta -
                      - - - - - -
                      -
                      -

                      - Typealias - IAPRefresh​Callback -

                      - -
                      public typealias IAPRefreshCallback = (IAPRefreshResult) -> Void
                      -
                      -
                      - -
                      -

                      - Generated on using swift-doc. -

                      -
                      - - diff --git a/docs/swift-doc/IAPRefreshResult/index.html b/docs/swift-doc/IAPRefreshResult/index.html deleted file mode 100755 index 7d40928..0000000 --- a/docs/swift-doc/IAPRefreshResult/index.html +++ /dev/null @@ -1,83 +0,0 @@ - - - - - - InAppPurchase - IAPRefreshResult - - - - -
                      - - - InAppPurchase - - Documentation - - Beta -
                      - - - - - -
                      -
                      -

                      - Structure - IAPRefresh​Result -

                      - -
                      public struct IAPRefreshResult
                      - -
                      -

                      Properties

                      - -
                      -

                      - state -

                      -
                      var state: IAPRefreshResultState
                      -
                      -
                      -

                      - iap​Error -

                      -
                      var iapError: IAPError?
                      -
                      -
                      -

                      - added​Purchases -

                      -
                      var addedPurchases: Int
                      -
                      -
                      -

                      - updated​Purchases -

                      -
                      var updatedPurchases: Int
                      -
                      -
                      - - - -
                      -
                      - -
                      -

                      - Generated on using swift-doc. -

                      -
                      - - diff --git a/docs/swift-doc/IAPRefreshResultState/index.html b/docs/swift-doc/IAPRefreshResultState/index.html deleted file mode 100755 index 34365cd..0000000 --- a/docs/swift-doc/IAPRefreshResultState/index.html +++ /dev/null @@ -1,77 +0,0 @@ - - - - - - InAppPurchase - IAPRefreshResultState - - - - -
                      - - - InAppPurchase - - Documentation - - Beta -
                      - - - - - -
                      -
                      -

                      - Enumeration - IAPRefresh​Result​State -

                      - -
                      public enum IAPRefreshResultState
                      - -
                      -

                      Enumeration Cases

                      - -
                      -

                      - succeeded -

                      -
                      case succeeded
                      -
                      -
                      -

                      - failed -

                      -
                      case failed
                      -
                      -
                      -

                      - skipped -

                      -
                      case skipped
                      -
                      -
                      - - - -
                      -
                      - -
                      -

                      - Generated on using swift-doc. -

                      -
                      - - diff --git a/docs/swift-doc/InAppPurchase/index.html b/docs/swift-doc/InAppPurchase/index.html deleted file mode 100755 index 559e980..0000000 --- a/docs/swift-doc/InAppPurchase/index.html +++ /dev/null @@ -1,592 +0,0 @@ - - - - - - InAppPurchase - InAppPurchase - - - - -
                      - - - InAppPurchase - - Documentation - - Beta -
                      - - - - - -
                      -
                      -

                      - Class - InApp​Purchase -

                      - -
                      public class InAppPurchase: NSObject, InAppPurchaseLib
                      -
                      - -
                      - - - - - - - - - -InAppPurchase - - -InAppPurchase - - - - - -NSObject - -NSObject - - - -InAppPurchase->NSObject - - - - - -InAppPurchaseLib - - -InAppPurchaseLib - - - - - -InAppPurchase->InAppPurchaseLib - - - - - - - - -
                      -

                      Conforms To

                      -
                      -
                      InAppPurchaseLib
                      -

                      The protocol that `InAppPurchase`` adopts.

                      -
                      -
                      NSObject
                      -
                      -
                      -
                      -

                      Properties

                      - -
                      -

                      - iap​Products -

                      -
                      var iapProducts: Array<IAPProduct>
                      -
                      -

                      The array of IAPProduct.

                      - -
                      -
                      -
                      -

                      - validator​Url​String -

                      -
                      var validatorUrlString: String?
                      -
                      -

                      The validator url retrieved from Fovea.

                      - -
                      -
                      -
                      -

                      - iap​Purchase​Delegate -

                      -
                      var iapPurchaseDelegate: IAPPurchaseDelegate?
                      -
                      -

                      The instance of class that adopts the IAPPurchaseDelegate protocol.

                      - -
                      -
                      -
                      -

                      - application​Username -

                      -
                      var applicationUsername: String?
                      -
                      -

                      The user name, if your app implements user login.

                      - -
                      -
                      -
                      -
                      -

                      Methods

                      - -
                      -

                      - initialize(iap​Products:​validator​Url​String:​iap​Purchase​Delegate:​application​Username:​) -

                      -
                      public static func initialize(iapProducts: Array<IAPProduct>, validatorUrlString: String, iapPurchaseDelegate: IAPPurchaseDelegate, applicationUsername: String?)
                      -
                      -

                      Start observing the payment queue, as soon as possible, and refresh Product list and user Receipt.

                      - -
                      -

                      Parameters

                      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
                      iap​ProductsArray<IAPProduct>

                      An array of IAPProduct.

                      -
                      validator​Url​StringString

                      The validator url retrieved from Fovea.

                      -
                      iap​Purchase​DelegateIAPPurchase​Delegate

                      An instance of class that adopts the IAPPurchaseDelegate protocol (default value = DefaultPurchaseDelegate).

                      -
                      application​UsernameString?

                      The user name, if your app implements user login.

                      -
                      -
                      -
                      -

                      - stop() -

                      -
                      public static func stop()
                      -
                      -

                      Stop observing the payment queue, when the application will terminate, for proper cleanup.

                      - -
                      -
                      -
                      -

                      - refresh(callback:​) -

                      -
                      public static func refresh(callback: @escaping IAPRefreshCallback)
                      -
                      -

                      Refresh Product list and user Receipt.

                      - -
                      -
                      - - -
                      -

                      Parameters

                      - - - - - - - - - - - - - - - - -
                      callback@escaping IAPRefresh​Callback

                      The function that will be called after processing.

                      -
                      -
                      -
                      -

                      - get​Products() -

                      -
                      public static func getProducts() -> Array<SKProduct>
                      -
                      -

                      Gets all products retrieved from the App Store

                      - -
                      -
                      -
                        -
                      • See also: SKProduct.
                      • -
                      - -
                      -

                      Returns

                      -

                      An array of products.

                      - -
                      -
                      -

                      - get​Product​By(identifier:​) -

                      -
                      public static func getProductBy(identifier: String) -> SKProduct?
                      -
                      -

                      Gets the product by its identifier from the list of products retrieved from the App Store.

                      - -
                      -
                      -
                        -
                      • See also: SKProduct.
                      • -
                      - -
                      -

                      Parameters

                      - - - - - - - - - - - - - - - - -
                      identifierString

                      The identifier of the product.

                      -
                      -

                      Returns

                      -

                      The product if it was retrieved from the App Store.

                      - -
                      -
                      -

                      - can​Make​Payments() -

                      -
                      public static func canMakePayments() -> Bool
                      -
                      -

                      Checks if the user is allowed to authorize payments.

                      - -
                      -

                      Returns

                      -

                      A boolean indicates if the user is allowed.

                      - -
                      -
                      -

                      - purchase(product​Identifier:​quantity:​callback:​) -

                      -
                      public static func purchase(productIdentifier: String, quantity: Int, callback: @escaping IAPPurchaseCallback)
                      -
                      -

                      Request a Payment from the App Store.

                      - -
                      -
                      - - -
                      -

                      Parameters

                      - - - - - - - - - - - - - - - - - - - - - - - - - - -
                      product​IdentifierString

                      The identifier of the product to purchase.

                      -
                      quantityInt

                      The quantity to purchase (default value = 1).

                      -
                      callback@escaping IAPPurchase​Callback

                      The function that will be called after processing.

                      -
                      -
                      -
                      -

                      - restore​Purchases(callback:​) -

                      -
                      public static func restorePurchases(callback: @escaping IAPRefreshCallback)
                      -
                      -

                      Restore purchased products.

                      - -
                      -
                      - - -
                      -

                      Parameters

                      - - - - - - - - - - - - - - - - -
                      callback@escaping IAPRefresh​Callback

                      The function that will be called after processing.

                      -
                      -
                      -
                      -

                      - finish​Transactions(for:​) -

                      -
                      public static func finishTransactions(for productIdentifier: String)
                      -
                      -

                      Finish all transactions for the product.

                      - -
                      -

                      Parameters

                      - - - - - - - - - - - - - - - - -
                      product​IdentifierString

                      The identifier of the product.

                      -
                      -
                      -
                      -

                      - has​Deferred​Transaction(for:​) -

                      -
                      public static func hasDeferredTransaction(for productIdentifier: String) -> Bool
                      -
                      -

                      Checks if the last transaction state for a given product was deferred.

                      - -
                      -

                      Parameters

                      - - - - - - - - - - - - - - - - -
                      product​IdentifierString

                      The identifier of the product.

                      -
                      -

                      Returns

                      -

                      A boolean indicates if the last transaction state was deferred.

                      - -
                      -
                      -

                      - has​Already​Purchased() -

                      -
                      public static func hasAlreadyPurchased() -> Bool
                      -
                      -

                      Checks if the user has already purchased at least one product.

                      - -
                      -

                      Returns

                      -

                      A boolean indicates if the .

                      - -
                      -
                      -

                      - has​Active​Purchase(for:​) -

                      -
                      public static func hasActivePurchase(for productIdentifier: String) -> Bool
                      -
                      -

                      Checks if the user currently own (or is subscribed to) a given product (nonConsumable or autoRenewableSubscription).

                      - -
                      -

                      Parameters

                      - - - - - - - - - - - - - - - - -
                      product​IdentifierString

                      The identifier of the product.

                      -
                      -

                      Returns

                      -

                      A boolean indicates if the user currently own (or is subscribed to) a given product.

                      - -
                      -
                      -

                      - has​Active​Subscription() -

                      -
                      public static func hasActiveSubscription() -> Bool
                      -
                      -

                      Checks if the user has an active auto renewable subscription regardless of the product identifier.

                      - -
                      -

                      Returns

                      -

                      A boolean indicates if the user has an active auto renewable subscription.

                      - -
                      -
                      -

                      - get​Purchase​Date(for:​) -

                      -
                      public static func getPurchaseDate(for productIdentifier: String) -> Date?
                      -
                      -

                      Returns the latest purchased date for a given product.

                      - -
                      -

                      Parameters

                      - - - - - - - - - - - - - - - - -
                      product​IdentifierString

                      The identifier of the product.

                      -
                      -

                      Returns

                      -

                      The latest purchase Date if set or nil.

                      - -
                      -
                      -

                      - get​Expiry​Date(for:​) -

                      -
                      public static func getExpiryDate(for productIdentifier: String) -> Date?
                      -
                      -

                      Returns the expiry date for a subcription. May be past or future.

                      - -
                      -

                      Parameters

                      - - - - - - - - - - - - - - - - -
                      product​IdentifierString

                      The identifier of the product.

                      -
                      -

                      Returns

                      -

                      The expiry Date is set or nil.

                      - -
                      -
                      - - - -
                      -
                      - -
                      -

                      - Generated on using swift-doc. -

                      -
                      - - diff --git a/docs/swift-doc/InAppPurchaseLib/index.html b/docs/swift-doc/InAppPurchaseLib/index.html deleted file mode 100755 index 5f0d7ed..0000000 --- a/docs/swift-doc/InAppPurchaseLib/index.html +++ /dev/null @@ -1,578 +0,0 @@ - - - - - - InAppPurchase - InAppPurchaseLib - - - - -
                      - - - InAppPurchase - - Documentation - - Beta -
                      - - - - - -
                      -
                      -

                      - Protocol - InApp​Purchase​Lib -

                      - -
                      public protocol InAppPurchaseLib
                      -
                      -

                      The protocol that `InAppPurchase`` adopts.

                      - -
                      -
                      - -
                      - - - - - - - - - -InAppPurchaseLib - - -InAppPurchaseLib - - - - - -InAppPurchase - - -InAppPurchase - - - - - -InAppPurchase->InAppPurchaseLib - - - - - - - - -
                      -

                      Types Conforming to InApp​Purchase​Lib

                      -
                      -
                      InAppPurchase
                      -
                      -
                      -
                      - - - -
                      -

                      Requirements

                      - -
                      -

                      - iap​Products -

                      -
                      var iapProducts: Array<IAPProduct>
                      -
                      -

                      The array of IAPProduct.

                      - -
                      -
                      -
                      -

                      - validator​Url​String -

                      -
                      var validatorUrlString: String?
                      -
                      -

                      The validator url retrieved from Fovea.

                      - -
                      -
                      -
                      -

                      - iap​Purchase​Delegate -

                      -
                      var iapPurchaseDelegate: IAPPurchaseDelegate?
                      -
                      -

                      The instance of class that adopts the IAPPurchaseDelegate protocol.

                      - -
                      -
                      -
                      -

                      - application​Username -

                      -
                      var applicationUsername: String?
                      -
                      -

                      The user name, if your app implements user login.

                      - -
                      -
                      -
                      -

                      - initialize(iap​Products:​validator​Url​String:​iap​Purchase​Delegate:​application​Username:​) -

                      -
                      static func initialize(iapProducts: Array<IAPProduct>, validatorUrlString: String, iapPurchaseDelegate: IAPPurchaseDelegate, applicationUsername: String?) -> Void
                      -
                      -

                      Start observing the payment queue, as soon as possible, and refresh Product list and user Receipt.

                      - -
                      -

                      Parameters

                      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
                      iap​ProductsArray<IAPProduct>

                      An array of IAPProduct.

                      -
                      validator​Url​StringString

                      The validator url retrieved from Fovea.

                      -
                      iap​Purchase​DelegateIAPPurchase​Delegate

                      An instance of class that adopts the IAPPurchaseDelegate protocol (default value = DefaultPurchaseDelegate).

                      -
                      application​UsernameString?

                      The user name, if your app implements user login.

                      -
                      -
                      -
                      -

                      - stop() -

                      -
                      static func stop() -> Void
                      -
                      -

                      Stop observing the payment queue, when the application will terminate, for proper cleanup.

                      - -
                      -
                      -
                      -

                      - refresh(callback:​) -

                      -
                      static func refresh(callback: @escaping IAPRefreshCallback) -> Void
                      -
                      -

                      Refresh Product list and user Receipt.

                      - -
                      -
                      - - -
                      -

                      Parameters

                      - - - - - - - - - - - - - - - - -
                      callback@escaping IAPRefresh​Callback

                      The function that will be called after processing.

                      -
                      -
                      -
                      -

                      - get​Products() -

                      -
                      static func getProducts() -> Array<SKProduct>
                      -
                      -

                      Gets all products retrieved from the App Store

                      - -
                      -
                      -
                        -
                      • See also: SKProduct.
                      • -
                      - -
                      -

                      Returns

                      -

                      An array of products.

                      - -
                      -
                      -

                      - get​Product​By(identifier:​) -

                      -
                      static func getProductBy(identifier: String) -> SKProduct?
                      -
                      -

                      Gets the product by its identifier from the list of products retrieved from the App Store.

                      - -
                      -
                      -
                        -
                      • See also: SKProduct.
                      • -
                      - -
                      -

                      Parameters

                      - - - - - - - - - - - - - - - - -
                      identifierString

                      The identifier of the product.

                      -
                      -

                      Returns

                      -

                      The product if it was retrieved from the App Store.

                      - -
                      -
                      -

                      - can​Make​Payments() -

                      -
                      static func canMakePayments() -> Bool
                      -
                      -

                      Checks if the user is allowed to authorize payments.

                      - -
                      -

                      Returns

                      -

                      A boolean indicates if the user is allowed.

                      - -
                      -
                      -

                      - purchase(product​Identifier:​quantity:​callback:​) -

                      -
                      static func purchase(productIdentifier: String, quantity: Int, callback: @escaping IAPPurchaseCallback) -> Void
                      -
                      -

                      Request a Payment from the App Store.

                      - -
                      -
                      - - -
                      -

                      Parameters

                      - - - - - - - - - - - - - - - - - - - - - - - - - - -
                      product​IdentifierString

                      The identifier of the product to purchase.

                      -
                      quantityInt

                      The quantity to purchase (default value = 1).

                      -
                      callback@escaping IAPPurchase​Callback

                      The function that will be called after processing.

                      -
                      -
                      -
                      -

                      - restore​Purchases(callback:​) -

                      -
                      static func restorePurchases(callback: @escaping IAPRefreshCallback) -> Void
                      -
                      -

                      Restore purchased products.

                      - -
                      -
                      - - -
                      -

                      Parameters

                      - - - - - - - - - - - - - - - - -
                      callback@escaping IAPRefresh​Callback

                      The function that will be called after processing.

                      -
                      -
                      -
                      -

                      - finish​Transactions(for:​) -

                      -
                      static func finishTransactions(for productIdentifier: String) -> Void
                      -
                      -

                      Finish all transactions for the product.

                      - -
                      -

                      Parameters

                      - - - - - - - - - - - - - - - - -
                      product​IdentifierString

                      The identifier of the product.

                      -
                      -
                      -
                      -

                      - has​Deferred​Transaction(for:​) -

                      -
                      static func hasDeferredTransaction(for productIdentifier: String) -> Bool
                      -
                      -

                      Checks if the last transaction state for a given product was deferred.

                      - -
                      -

                      Parameters

                      - - - - - - - - - - - - - - - - -
                      product​IdentifierString

                      The identifier of the product.

                      -
                      -

                      Returns

                      -

                      A boolean indicates if the last transaction state was deferred.

                      - -
                      -
                      -

                      - has​Already​Purchased() -

                      -
                      static func hasAlreadyPurchased() -> Bool
                      -
                      -

                      Checks if the user has already purchased at least one product.

                      - -
                      -

                      Returns

                      -

                      A boolean indicates if the .

                      - -
                      -
                      -

                      - has​Active​Purchase(for:​) -

                      -
                      static func hasActivePurchase(for productIdentifier: String) -> Bool
                      -
                      -

                      Checks if the user currently own (or is subscribed to) a given product (nonConsumable or autoRenewableSubscription).

                      - -
                      -

                      Parameters

                      - - - - - - - - - - - - - - - - -
                      product​IdentifierString

                      The identifier of the product.

                      -
                      -

                      Returns

                      -

                      A boolean indicates if the user currently own (or is subscribed to) a given product.

                      - -
                      -
                      -

                      - has​Active​Subscription() -

                      -
                      static func hasActiveSubscription() -> Bool
                      -
                      -

                      Checks if the user has an active auto renewable subscription regardless of the product identifier.

                      - -
                      -

                      Returns

                      -

                      A boolean indicates if the user has an active auto renewable subscription.

                      - -
                      -
                      -

                      - get​Purchase​Date(for:​) -

                      -
                      static func getPurchaseDate(for productIdentifier: String) -> Date?
                      -
                      -

                      Returns the latest purchased date for a given product.

                      - -
                      -

                      Parameters

                      - - - - - - - - - - - - - - - - -
                      product​IdentifierString

                      The identifier of the product.

                      -
                      -

                      Returns

                      -

                      The latest purchase Date if set or nil.

                      - -
                      -
                      -

                      - get​Expiry​Date(for:​) -

                      -
                      static func getExpiryDate(for productIdentifier: String) -> Date?
                      -
                      -

                      Returns the expiry date for a subcription. May be past or future.

                      - -
                      -

                      Parameters

                      - - - - - - - - - - - - - - - - -
                      product​IdentifierString

                      The identifier of the product.

                      -
                      -

                      Returns

                      -

                      The expiry Date is set or nil.

                      - -
                      -
                      -
                      -
                      - -
                      -

                      - Generated on using swift-doc. -

                      -
                      - - diff --git a/docs/swift-doc/all.css b/docs/swift-doc/all.css deleted file mode 100755 index 4e9b4a7..0000000 --- a/docs/swift-doc/all.css +++ /dev/null @@ -1 +0,0 @@ -:root{--system-red:#ff3b30;--system-orange:#ff9500;--system-yellow:#fc0;--system-green:#34c759;--system-teal:#5ac8fa;--system-blue:#007aff;--system-indigo:#5856d6;--system-purple:#af52de;--system-pink:#ff2d55;--system-gray:#8e8e93;--system-gray2:#aeaeb2;--system-gray3:#c7c7cc;--system-gray4:#d1d1d6;--system-gray5:#e5e5ea;--system-gray6:#f2f2f7;--label:#000;--secondary-label:#3c3c43;--tertiary-label:#48484a;--quaternary-label:#636366;--placeholder-text:#8e8e93;--link:#007aff;--separator:#e5e5ea;--opaque-separator:#c6c6c8;--system-fill:#787880;--secondary-system-fill:#787880;--tertiary-system-fill:#767680;--quaternary-system-fill:#747480;--system-background:#fff;--secondary-system-background:#f2f2f7;--tertiary-system-background:#fff;--system-grouped-background:#f2f2f7;--secondary-system-grouped-background:#fff;--tertiary-system-grouped-background:#f2f2f7}@supports (color:color(display-p3 1 1 1)){:root{--system-red:color(display-p3 1 0.2314 0.1882);--system-orange:color(display-p3 1 0.5843 0);--system-yellow:color(display-p3 1 0.8 0);--system-green:color(display-p3 0.2039 0.7804 0.349);--system-teal:color(display-p3 0.3529 0.7843 0.9804);--system-blue:color(display-p3 0 0.4784 1);--system-indigo:color(display-p3 0.3451 0.3373 0.8392);--system-purple:color(display-p3 0.6863 0.3216 0.8706);--system-pink:color(display-p3 1 0.1765 0.3333);--system-gray:color(display-p3 0.5569 0.5569 0.5765);--system-gray2:color(display-p3 0.6824 0.6824 0.698);--system-gray3:color(display-p3 0.7804 0.7804 0.8);--system-gray4:color(display-p3 0.8196 0.8196 0.8392);--system-gray5:color(display-p3 0.898 0.898 0.9176);--system-gray6:color(display-p3 0.949 0.949 0.9686);--label:color(display-p3 0 0 0);--secondary-label:color(display-p3 0.2353 0.2353 0.2627);--tertiary-label:color(display-p3 0.2823 0.2823 0.2901);--quaternary-label:color(display-p3 0.4627 0.4627 0.5019);--placeholder-text:color(display-p3 0.5568 0.5568 0.5764);--link:color(display-p3 0 0.4784 1);--separator:color(display-p3 0.898 0.898 0.9176);--opaque-separator:color(display-p3 0.7765 0.7765 0.7843);--system-fill:color(display-p3 0.4706 0.4706 0.502);--secondary-system-fill:color(display-p3 0.4706 0.4706 0.502);--tertiary-system-fill:color(display-p3 0.4627 0.4627 0.502);--quaternary-system-fill:color(display-p3 0.4549 0.4549 0.502);--system-background:color(display-p3 1 1 1);--secondary-system-background:color(display-p3 0.949 0.949 0.9686);--tertiary-system-background:color(display-p3 1 1 1);--system-grouped-background:color(display-p3 0.949 0.949 0.9686);--secondary-system-grouped-background:color(display-p3 1 1 1);--tertiary-system-grouped-background:color(display-p3 0.949 0.949 0.9686)}}:root{--large-title:600 32pt/39pt sans-serif;--title-1:600 26pt/32pt sans-serif;--title-2:600 20pt/25pt sans-serif;--title-3:500 18pt/23pt sans-serif;--headline:500 15pt/20pt sans-serif;--body:300 15pt/20pt sans-serif;--callout:300 14pt/19pt sans-serif;--subhead:300 13pt/18pt sans-serif;--footnote:300 12pt/16pt sans-serif;--caption-1:300 11pt/13pt sans-serif;--caption-2:300 11pt/13pt sans-serif;--icon-case:url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'%3E%3Crect fill='%2389c5e6' height='90' rx='8' stroke='%236bb7e1' stroke-miterlimit='10' stroke-width='4' width='90' x='5' y='5'/%3E%3Cpath d='M20.21 50c0-20.7 11.9-32.79 30.8-32.79 16 0 28.21 10.33 28.7 25.32H64.19C63.4 35 58.09 30.11 51 30.11c-8.79 0-14.37 7.52-14.37 19.82s5.54 20 14.41 20c7.08 0 12.22-4.66 13.23-12.09h15.52c-.74 15.07-12.43 25-28.78 25C32 82.81 20.21 70.72 20.21 50z' fill='%23fff'/%3E%3C/svg%3E");--icon-class:url("data:image/svg+xml;utf8,%3Csvg viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'%3E%3Crect fill='%239b98e6' height='90' rx='8' stroke='%235856d6' stroke-miterlimit='10' stroke-width='4' width='90' x='5' y='5'/%3E%3Cpath d='m20.21 50c0-20.7 11.9-32.79 30.8-32.79 16 0 28.21 10.33 28.7 25.32h-15.52c-.79-7.53-6.1-12.42-13.19-12.42-8.79 0-14.37 7.52-14.37 19.82s5.54 20 14.41 20c7.08 0 12.22-4.66 13.23-12.09h15.52c-.74 15.07-12.43 25-28.78 25-19.01-.03-30.8-12.12-30.8-32.84z' fill='%23fff'/%3E%3C/svg%3E");--icon-enumeration:url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'%3E%3Crect fill='%23eca95b' height='90' rx='8' stroke='%23e89234' stroke-miterlimit='10' stroke-width='4' width='90' x='5.17' y='5'/%3E%3Cpath d='M71.9 81.71H28.43V18.29H71.9v13H44.56v12.62h25.71v11.87H44.56V68.7H71.9z' fill='%23fff'/%3E%3C/svg%3E");--icon-extension:url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'%3E%3Crect fill='%23eca95b' height='90' rx='8' stroke='%23e89234' stroke-miterlimit='10' stroke-width='4' width='90' x='5' y='5'/%3E%3Cg fill='%23fff'%3E%3Cpath d='M54.43 81.93H20.51V18.07h33.92v12.26H32.61v13.8h20.45v11.32H32.61v14.22h21.82zM68.74 74.58h-.27l-2.78 7.35h-7.28L64 69.32l-6-12.54h8l2.74 7.3h.27l2.76-7.3h7.64l-6.14 12.54 5.89 12.61h-7.64z'/%3E%3C/g%3E%3C/svg%3E");--icon-function:url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'%3E%3Crect fill='%237ac673' height='90' rx='8' stroke='%235bb74f' stroke-miterlimit='10' stroke-width='4' width='90' x='5' y='5'/%3E%3Cpath d='M24.25 75.66A5.47 5.47 0 0130 69.93c1.55 0 3.55.41 6.46.41 3.19 0 4.78-1.55 5.46-6.65l1.5-10.14h-9.34a6 6 0 110-12h11.1l1.09-7.27C47.82 23.39 54.28 17.7 64 17.7c6.69 0 11.74 1.77 11.74 6.64A5.47 5.47 0 0170 30.07c-1.55 0-3.55-.41-6.46-.41-3.14 0-4.73 1.51-5.46 6.65l-.78 5.27h11.44a6 6 0 11.05 12H55.6l-1.78 12.11C52.23 76.61 45.72 82.3 36 82.3c-6.7 0-11.75-1.77-11.75-6.64z' fill='%23fff'/%3E%3C/svg%3E");--icon-method:url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'%3E%3Crect fill='%235a98f8' height='90' rx='8' stroke='%232974ed' stroke-miterlimit='10' stroke-width='4' width='90' x='5' y='5'/%3E%3Cpath d='M70.61 81.71v-39.6h-.31l-15.69 39.6h-9.22l-15.65-39.6h-.35v39.6H15.2V18.29h18.63l16 41.44h.36l16-41.44H84.8v63.42z' fill='%23fff'/%3E%3C/svg%3E");--icon-property:url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'%3E%3Crect fill='%2389c5e6' height='90' rx='8' stroke='%236bb7e1' stroke-miterlimit='10' stroke-width='4' width='90' x='5' y='5'/%3E%3Cpath d='M52.31 18.29c13.62 0 22.85 8.84 22.85 22.46s-9.71 22.37-23.82 22.37H41v18.59H24.84V18.29zM41 51h7c6.85 0 10.89-3.56 10.89-10.2S54.81 30.64 48 30.64h-7z' fill='%23fff'/%3E%3C/svg%3E");--icon-protocol:url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'%3E%3Crect fill='%23ff6682' height='90' rx='8' stroke='%23ff2d55' stroke-miterlimit='10' stroke-width='4' width='90' x='5' y='5'/%3E%3Cg fill='%23fff'%3E%3Cpath d='M46.28 18.29c11.84 0 20 8.66 20 21.71s-8.44 21.71-20.6 21.71H34.87v20H22.78V18.29zM34.87 51.34H43c6.93 0 11-4 11-11.29S50 28.8 43.07 28.8h-8.2zM62 57.45h8v4.77h.16c.84-3.45 2.54-5.12 5.17-5.12a5.06 5.06 0 011.92.35V65a5.69 5.69 0 00-2.39-.51c-3.08 0-4.66 1.74-4.66 5.12v12.1H62z'/%3E%3C/g%3E%3C/svg%3E");--icon-structure:url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'%3E%3Crect fill='%23b57edf' height='90' rx='8' stroke='%239454c2' stroke-miterlimit='10' stroke-width='4' width='90' x='5' y='5'/%3E%3Cpath d='M38.38 63c.74 4.53 5.62 7.16 11.82 7.16s10.37-2.81 10.37-6.68c0-3.51-2.73-5.31-10.24-6.76l-6.5-1.23C31.17 53.14 24.62 47 24.62 37.28c0-12.22 10.59-20.09 25.18-20.09 16 0 25.36 7.83 25.53 19.91h-15c-.26-4.57-4.57-7.29-10.42-7.29s-9.31 2.63-9.31 6.37c0 3.34 2.9 5.18 9.8 6.5l6.5 1.23C70.46 46.51 76.61 52 76.61 62c0 12.74-10 20.83-26.72 20.83-15.82 0-26.28-7.3-26.5-19.78z' fill='%23fff'/%3E%3C/svg%3E");--icon-typealias:url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'%3E%3Crect fill='%237ac673' height='90' rx='8' stroke='%235bb74f' stroke-miterlimit='10' stroke-width='4' width='90' x='5' y='5'/%3E%3Cpath d='M42 81.71V31.3H24.47v-13h51.06v13H58v50.41z' fill='%23fff'/%3E%3C/svg%3E");--icon-variable:url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'%3E%3Crect fill='%237ac673' height='90' rx='8' stroke='%235bb74f' stroke-miterlimit='10' stroke-width='4' width='90' x='5' y='5'/%3E%3Cpath d='M39.85 81.71L19.63 18.29H38l12.18 47.64h.35L62.7 18.29h17.67L60.15 81.71z' fill='%23fff'/%3E%3C/svg%3E")}body,button,input,select,textarea{-moz-font-feature-settings:"kern";-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;direction:ltr;font-synthesis:none;text-align:left}h1:first-of-type,h2:first-of-type,h3:first-of-type,h4:first-of-type,h5:first-of-type,h6:first-of-type{margin-top:0}h1 code,h2 code,h3 code,h4 code,h5 code,h6 code{font-family:inherit;font-weight:inherit}h1 img,h2 img,h3 img,h4 img,h5 img,h6 img{margin:0 .5em .2em 0;vertical-align:middle;display:inline-block}h1+*,h2+*,h3+*,h4+*,h5+*,h6+*{margin-top:.8em}img+h1{margin-top:.5em}img+h1,img+h2,img+h3,img+h4,img+h5,img+h6{margin-top:.3em}:is(h1,h2,h3,h4,h5,h6)+:is(h1,h2,h3,h4,h5,h6){margin-top:.4em}:matches(h1,h2,h3,h4,h5,h6)+:matches(h1,h2,h3,h4,h5,h6){margin-top:.4em}:is(p,ul,ol)+:is(h1,h2,h3,h4,h5,h6){margin-top:1.6em}:matches(p,ul,ol)+:matches(h1,h2,h3,h4,h5,h6){margin-top:1.6em}:is(p,ul,ol)+*{margin-top:.8em}:matches(p,ul,ol)+*{margin-top:.8em}ol,ul{margin-left:1.17647em}:matches(ul,ol) :matches(ul,ol){margin-bottom:0;margin-top:0}nav h2{color:#3c3c43;color:var(--secondary-label);font-size:1rem;font-feature-settings:"c2sc";font-variant:small-caps;font-weight:600;text-transform:uppercase}nav ol,nav ul{margin:0;list-style:none}nav li li{font-size:smaller}a:link,a:visited{text-decoration:none}a:hover{text-decoration:underline}a:active{text-decoration:none}a+a{display:inline-block}b,strong{font-weight:600}.discussion,.summary{font:300 14pt/19pt sans-serif;font:var(--callout)}article>.discussion{margin-bottom:2em}.discussion .highlight{background:transparent;border:1px solid #e5e5ea;border:1px solid var(--separator);font:300 11pt/13pt sans-serif;font:var(--caption-1);padding:1em;text-indent:0}cite,dfn,em,i{font-style:italic}:matches(h1,h2,h3) sup{font-size:.4em}sup a{color:inherit;vertical-align:inherit}sup a:hover{color:#007aff;color:var(--link);text-decoration:none}sub{line-height:1}abbr{border:0}:lang(ja),:lang(ko),:lang(th),:lang(zh){font-style:normal}:lang(ko){word-break:keep-all}form fieldset{margin:1em auto;max-width:450px;width:95%}form label{display:block;font-size:1em;font-weight:400;line-height:1.5em;margin-bottom:14px;position:relative;width:100%}input[type=email],input[type=number],input[type=password],input[type=tel],input[type=text],input[type=url],textarea{border-radius:4px;border:1px solid #e5e5ea;border:1px solid var(--separator);color:#333;font-family:inherit;font-size:100%;font-weight:400;height:34px;margin:0;padding:0 1em;position:relative;vertical-align:top;width:100%;z-index:1}input[type=email],input [type=email]:focus,input[type=number],input [type=number]:focus,input[type=password],input [type=password]:focus,input[type=tel],input [type=tel]:focus,input[type=text],input [type=text]:focus,input[type=url],input [type=url]:focus,textarea,textarea:focus{-webkit-appearance:none;-moz-appearance:none;appearance:none}input[type=email]:focus,input[type=number]:focus,input[type=password]:focus,input[type=tel]:focus,input[type=text]:focus,input[type=url]:focus,textarea:focus{border-color:#08c;box-shadow:0 0 0 3px rgba(0,136,204,.3);outline:0;z-index:9}input[type=email]:-moz-read-only,input[type=number]:-moz-read-only,input[type=password]:-moz-read-only,input[type=tel]:-moz-read-only,input[type=text]:-moz-read-only,input[type=url]:-moz-read-only,textarea:-moz-read-only{background:none;border:none;box-shadow:none;padding-left:0}input[type=email]:read-only,input[type=number]:read-only,input[type=password]:read-only,input[type=tel]:read-only,input[type=text]:read-only,input[type=url]:read-only,textarea:read-only{background:none;border:none;box-shadow:none;padding-left:0}::-webkit-input-placeholder{color:#8e8e93;color:var(--placeholder-text)}::-moz-placeholder{color:#8e8e93;color:var(--placeholder-text)}:-ms-input-placeholder{color:#8e8e93;color:var(--placeholder-text)}::-ms-input-placeholder{color:#8e8e93;color:var(--placeholder-text)}::placeholder{color:#8e8e93;color:var(--placeholder-text)}textarea{-webkit-overflow-scrolling:touch;line-height:1.4737;min-height:134px;overflow-y:auto;resize:vertical;transform:translateZ(0)}textarea,textarea:focus{-webkit-appearance:none;-moz-appearance:none;appearance:none}select{background:transparent;border-radius:4px;border:none;cursor:pointer;font-family:inherit;font-size:1em;height:34px;margin:0;padding:0 1em;width:100%}select,select:focus{-webkit-appearance:none;-moz-appearance:none;appearance:none}select:focus{border-color:#08c;box-shadow:0 0 0 3px rgba(0,136,204,.3);outline:0;z-index:9}input[type=file]{background:#fafafa;border-radius:4px;color:#333;cursor:pointer;font-family:inherit;font-size:100%;height:34px;margin:0;padding:6px 1em;position:relative;vertical-align:top;width:100%;z-index:1}input[type=file]:focus{border-color:#08c;outline:0;box-shadow:0 0 0 3px rgba(0,136,204,.3);z-index:9}button,button:focus,input[type=file]:focus,input[type=file]:focus:focus,input[type=reset],input[type=reset]:focus,input[type=submit],input[type=submit]:focus{-webkit-appearance:none;-moz-appearance:none;appearance:none}:matches(button,input[type=reset],input[type=submit]){background-color:#e3e3e3;background:linear-gradient(#fff,#e3e3e3);border-color:#d6d6d6;color:#0070c9}:matches(button,input[type=reset],input[type=submit]):hover{background-color:#eee;background:linear-gradient(#fff,#eee);border-color:#d9d9d9}:matches(button,input[type=reset],input[type=submit]):active{background-color:#dcdcdc;background:linear-gradient(#f7f7f7,#dcdcdc);border-color:#d0d0d0}:matches(button,input[type=reset],input[type=submit]):disabled{background-color:#e3e3e3;background:linear-gradient(#fff,#e3e3e3);border-color:#d6d6d6;color:#0070c9}body{background:#f2f2f7;background:var(--system-grouped-background);color:#000;color:var(--label);font-family:ui-system,-apple-system,BlinkMacSystemFont,sans-serif;font:300 15pt/20pt sans-serif;font:var(--body)}h1{font:600 32pt/39pt sans-serif;font:var(--large-title)}h2{font:600 20pt/25pt sans-serif;font:var(--title-2)}h3{font:500 18pt/23pt sans-serif;font:var(--title-3)}h4,h5,h6{font:500 15pt/20pt sans-serif;font:var(--headline)}a{color:#007aff;color:var(--link)}label{font:300 14pt/19pt sans-serif;font:var(--callout)}input,label{display:block}input{margin-bottom:1em}hr{border:none;border-top:1px solid #e5e5ea;border-top:1px solid var(--separator);margin:1em 0}table{width:100%;font:300 11pt/13pt sans-serif;font:var(--caption-1);caption-side:bottom;margin-bottom:2em}td,th{padding:0 1em}th{font-weight:600;text-align:left}thead th{border-bottom:1px solid #e5e5ea;border-bottom:1px solid var(--separator)}tr:last-of-type td,tr:last-of-type th{border-bottom:none}td,th{border-bottom:1px solid #e5e5ea;border-bottom:1px solid var(--separator);color:#3c3c43;color:var(--secondary-label)}caption{color:#48484a;color:var(--tertiary-label);font:300 11pt/13pt sans-serif;font:var(--caption-2);margin-top:2em;text-align:left}.graph text,code{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-weight:300}.graph>polygon{display:none}.graph text{fill:currentColor!important}.graph ellipse,.graph path,.graph polygon,.graph rect{stroke:currentColor!important}body{width:90vw;max-width:1280px;margin:1em auto}body>header{font:600 26pt/32pt sans-serif;font:var(--title-1);padding:.5em 0}body>header a{color:#000;color:var(--label)}body>header span{font-weight:400}body>header sup{text-transform:uppercase;font-size:small;font-weight:300;letter-spacing:.1ch}body>footer,body>header sup{color:#3c3c43;color:var(--secondary-label)}body>footer{clear:both;padding:1em 0;font:300 11pt/13pt sans-serif;font:var(--caption-1)}@media screen and (max-width:768px){body{width:96vw;max-width:100%}body>header{font:500 18pt/23pt sans-serif;font:var(--title-3);text-align:left;padding:1em 0}body>nav{display:none}body>main{padding:0 1em}}@media screen and (max-width:768px){#relationships figure{display:none}section>[role=article][class] pre{margin-left:-2.5em}section>[role=article][class] div{margin-left:-2em}}main,nav{overflow-x:scroll}main{background:#fff;background:var(--system-background);border-radius:8px;padding:0 2em}main section{border-bottom:1px solid #e5e5ea;border-bottom:1px solid var(--separator);margin-bottom:2em;padding-bottom:1em}main section:last-of-type{border-bottom:none;margin-bottom:0}nav{float:right;margin-left:1em;max-height:100vh;overflow:scroll;padding:0 1em 3em;position:-webkit-sticky;position:sticky;top:1em;width:20vw}nav a{color:#3c3c43;color:var(--secondary-label)}nav ul a{color:#48484a;color:var(--tertiary-label)}nav ol,nav ul{padding:0}nav ul{font:300 14pt/19pt sans-serif;font:var(--callout);margin-bottom:1em}nav ol>li>a{display:block;font-size:smaller;font:500 15pt/20pt sans-serif;font:var(--headline);margin:.5em 0}nav li{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}blockquote{--link:var(--secondary-label);border-left:4px solid #e5e5ea;border-left:4px solid var(--separator);color:#3c3c43;color:var(--secondary-label);font-size:smaller;margin-left:0;padding-left:2em}blockquote a{text-decoration:underline}article{padding:2em 0 1em}article>.summary{border-bottom:1px solid #e5e5ea;border-bottom:1px solid var(--separator);margin-bottom:2em;padding-bottom:1em}article>.summary:last-child{border-bottom:none}.parameters th{text-align:right}.parameters td{color:#3c3c43;color:var(--secondary-label)}.parameters th+td{text-align:center}dl{padding-top:1em}dt{font:500 15pt/20pt sans-serif;font:var(--headline)}dd{margin-left:2em;margin-bottom:1em}dd p{margin-top:0}.highlight{background:#f2f2f7;background:var(--secondary-system-background);border-radius:8px;font-size:smaller;margin-bottom:2em;overflow-x:scroll;padding:1em 1em 1em 3em;text-indent:-2em;white-space:pre-line}.highlight .p{white-space:nowrap}.highlight .placeholder{color:#000;color:var(--label)}.highlight a{text-decoration:underline;color:#8e8e93;color:var(--placeholder-text)}.highlight .attribute,.highlight .keyword,.highlight .literal{color:#af52de;color:var(--system-purple)}.highlight .number{color:#007aff;color:var(--system-blue)}.highlight .declaration{color:#5ac8fa;color:var(--system-teal)}.highlight .type{color:#5856d6;color:var(--system-indigo)}.highlight .directive{color:#ff9500;color:var(--system-orange)}.highlight .comment{color:#8e8e93;color:var(--system-gray)}main summary:hover{text-decoration:underline}figure{margin:2em 0;padding:1em 0}figure svg{max-width:100%;height:auto!important;margin:0 auto;display:block}h1 small{font-size:.5em;line-height:1.5;display:block;font-weight:400;color:#636366;color:var(--quaternary-label)}dd code,li code,p code{font-size:smaller;color:#3c3c43;color:var(--secondary-label)}a code{text-decoration:underline}dl dt[class],nav li[class],section>[role=article][class]{background-image:var(--background-image);background-size:1em;background-repeat:no-repeat;background-position:left .25em;padding-left:3em}dl dt[class]{background-position-y:.125em}section>[role=article]{margin-bottom:1em;padding-bottom:1em;border-bottom:1px solid #e5e5ea;border-bottom:1px solid var(--separator);padding-left:2em!important}section>[role=article]:last-of-type{margin-bottom:0;padding-bottom:0;border-bottom:none}dl dt[class],nav li[class]{list-style:none;text-indent:-1em;margin-bottom:.5em}nav li[class]{padding-left:2.5em}.case,.enumeration_case{--background-image:var(--icon-case);--link:var(--system-teal)}.class{--background-image:var(--icon-class);--link:var(--system-indigo)}.enumeration{--background-image:var(--icon-enumeration)}.enumeration,.extension{--link:var(--system-orange)}.extension{--background-image:var(--icon-extension)}.function{--background-image:var(--icon-function);--link:var(--system-green)}.initializer,.method{--background-image:var(--icon-method);--link:var(--system-blue)}.property{--background-image:var(--icon-property);--link:var(--system-teal)}.protocol{--background-image:var(--icon-protocol);--link:var(--system-pink)}.structure{--background-image:var(--icon-structure);--link:var(--system-purple)}.typealias{--background-image:var(--icon-typealias)}.typealias,.variable{--link:var(--system-green)}.variable{--background-image:var(--icon-variable)}.unknown{--link:var(--quaternary-label);color:#007aff;color:var(--link)} \ No newline at end of file diff --git a/docs/swift-doc/index.html b/docs/swift-doc/index.html deleted file mode 100755 index 666ea3b..0000000 --- a/docs/swift-doc/index.html +++ /dev/null @@ -1,204 +0,0 @@ - - - - - - InAppPurchase - InAppPurchase - - - - -
                      - - - InAppPurchase - - Documentation - - Beta -
                      - - - - - -
                      -
                      -
                      -

                      Classes

                      -
                      -
                      - - InApp​Purchase - -
                      -
                      - -
                      -
                      - - Default​Purchase​Delegate - -
                      -
                      -

                      The default implementation of IAPPurchaseDelegate if no other is provided.

                      - -
                      -
                      -
                      -
                      -

                      Structures

                      -
                      -
                      - - IAPPurchase​Result - -
                      -
                      - -
                      -
                      - - IAPRefresh​Result - -
                      -
                      - -
                      -
                      - - IAPError - -
                      -
                      - -
                      -
                      - - IAPProduct - -
                      -
                      - -
                      -
                      -
                      -
                      -

                      Enumerations

                      -
                      -
                      - - IAPPurchase​Result​State - -
                      -
                      - -
                      -
                      - - IAPRefresh​Result​State - -
                      -
                      - -
                      -
                      - - IAPError​Code - -
                      -
                      - -
                      -
                      - - IAPProduct​Type - -
                      -
                      - -
                      -
                      - - IAPPeriod​Format - -
                      -
                      - -
                      -
                      -
                      -
                      -

                      Protocols

                      -
                      -
                      - - IAPError​Protocol - -
                      -
                      - -
                      -
                      - - InApp​Purchase​Lib - -
                      -
                      -

                      The protocol that `InAppPurchase`` adopts.

                      - -
                      -
                      - - IAPPurchase​Delegate - -
                      -
                      -

                      The protocol that you must adopt if you have consumable and/or nonRenewingSubscription products.

                      - -
                      -
                      -
                      -
                      -

                      Globals

                      -
                      -

                      Typealiases

                      -
                      -
                      - - IAPPurchase​Callback - -
                      -
                      - -
                      -
                      - - IAPRefresh​Callback - -
                      -
                      - -
                      -
                      -
                      -
                      -
                      -
                      - - - - diff --git a/docs/undocumented.json b/docs/undocumented.json new file mode 100644 index 0000000..f5e8bed --- /dev/null +++ b/docs/undocumented.json @@ -0,0 +1,166 @@ +{ + "warnings": [ + { + "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Common/IAPCallback.swift", + "line": 12, + "symbol": "IAPPurchaseCallback", + "symbol_kind": "source.lang.swift.decl.typealias", + "warning": "undocumented" + }, + { + "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Common/IAPCallback.swift", + "line": 16, + "symbol": "IAPPurchaseResult.state", + "symbol_kind": "source.lang.swift.decl.var.instance", + "warning": "undocumented" + }, + { + "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Common/IAPCallback.swift", + "line": 17, + "symbol": "IAPPurchaseResult.iapError", + "symbol_kind": "source.lang.swift.decl.var.instance", + "warning": "undocumented" + }, + { + "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Common/IAPCallback.swift", + "line": 18, + "symbol": "IAPPurchaseResult.skError", + "symbol_kind": "source.lang.swift.decl.var.instance", + "warning": "undocumented" + }, + { + "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Common/IAPCallback.swift", + "line": 20, + "symbol": "IAPPurchaseResult.localizedDescription", + "symbol_kind": "source.lang.swift.decl.var.instance", + "warning": "undocumented" + }, + { + "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Common/IAPCallback.swift", + "line": 40, + "symbol": "IAPRefreshCallback", + "symbol_kind": "source.lang.swift.decl.typealias", + "warning": "undocumented" + }, + { + "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Common/IAPCallback.swift", + "line": 44, + "symbol": "IAPRefreshResult.state", + "symbol_kind": "source.lang.swift.decl.var.instance", + "warning": "undocumented" + }, + { + "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Common/IAPCallback.swift", + "line": 45, + "symbol": "IAPRefreshResult.iapError", + "symbol_kind": "source.lang.swift.decl.var.instance", + "warning": "undocumented" + }, + { + "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Common/IAPCallback.swift", + "line": 46, + "symbol": "IAPRefreshResult.addedPurchases", + "symbol_kind": "source.lang.swift.decl.var.instance", + "warning": "undocumented" + }, + { + "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Common/IAPCallback.swift", + "line": 47, + "symbol": "IAPRefreshResult.updatedPurchases", + "symbol_kind": "source.lang.swift.decl.var.instance", + "warning": "undocumented" + }, + { + "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Common/IAPError.swift", + "line": 11, + "symbol": "IAPErrorProtocol", + "symbol_kind": "source.lang.swift.decl.protocol", + "warning": "undocumented" + }, + { + "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Common/IAPError.swift", + "line": 12, + "symbol": "IAPErrorProtocol.code", + "symbol_kind": "source.lang.swift.decl.var.instance", + "warning": "undocumented" + }, + { + "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/InAppPurchaseLib.swift", + "line": 131, + "symbol": "DefaultPurchaseDelegate.init()", + "symbol_kind": "source.lang.swift.decl.function.method.instance", + "warning": "undocumented" + }, + { + "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Product/IAPProduct.swift", + "line": 10, + "symbol": "IAPProduct", + "symbol_kind": "source.lang.swift.decl.struct", + "warning": "undocumented" + }, + { + "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Product/IAPProduct.swift", + "line": 30, + "symbol": "IAPProductType", + "symbol_kind": "source.lang.swift.decl.enum", + "warning": "undocumented" + }, + { + "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Product/IAPProduct.swift", + "line": 31, + "symbol": "IAPProductType.consumable", + "symbol_kind": "source.lang.swift.decl.enumelement", + "warning": "undocumented" + }, + { + "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Product/IAPProduct.swift", + "line": 32, + "symbol": "IAPProductType.nonConsumable", + "symbol_kind": "source.lang.swift.decl.enumelement", + "warning": "undocumented" + }, + { + "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Product/IAPProduct.swift", + "line": 33, + "symbol": "IAPProductType.nonRenewingSubscription", + "symbol_kind": "source.lang.swift.decl.enumelement", + "warning": "undocumented" + }, + { + "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Product/IAPProduct.swift", + "line": 34, + "symbol": "IAPProductType.autoRenewableSubscription", + "symbol_kind": "source.lang.swift.decl.enumelement", + "warning": "undocumented" + }, + { + "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Product/SKProductExtension.swift", + "line": 12, + "symbol": "IAPPeriodFormat", + "symbol_kind": "source.lang.swift.decl.enum", + "warning": "undocumented" + }, + { + "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Product/SKProductExtension.swift", + "line": 13, + "symbol": "IAPPeriodFormat.short", + "symbol_kind": "source.lang.swift.decl.enumelement", + "warning": "undocumented" + }, + { + "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Product/SKProductExtension.swift", + "line": 14, + "symbol": "IAPPeriodFormat.long", + "symbol_kind": "source.lang.swift.decl.enumelement", + "warning": "undocumented" + }, + { + "file": "/Users/veronique/Documents/iap-swift-lib/Sources/InAppPurchaseLib/Product/SKProductExtension.swift", + "line": 20, + "symbol": "SKProduct.localizedPeriodFormat", + "symbol_kind": "source.lang.swift.decl.var.static", + "warning": "undocumented" + } + ], + "source_directory": "/Users/veronique/Documents/iap-swift-lib" +} \ No newline at end of file