Skip to content

Commit

Permalink
Swift Concurrency Support [#1]
Browse files Browse the repository at this point in the history
Swift Concurrency Support
  • Loading branch information
ns-vasilev authored Sep 13, 2023
2 parents f13742b + aff52fa commit 8402875
Show file tree
Hide file tree
Showing 59 changed files with 2,143 additions and 586 deletions.
22 changes: 20 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,27 @@ on:
- main
- dev
pull_request:
branches: [ main ]
paths:
- '.swiftlint.yml'
branches:
- main
- dev

concurrency:
group: ci
cancel-in-progress: true

jobs:
SwiftLint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: GitHub Action for SwiftLint
uses: norio-nomura/[email protected]
with:
args: --strict
env:
DIFF_BASE: ${{ github.base_ref }}
Latest:
name: Test Latest (iOS, macOS, tvOS, watchOS)
runs-on: macOS-12
Expand All @@ -38,4 +52,8 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: ${{ matrix.name }}
run: xcodebuild test -scheme "${{ matrix.scheme }}" -destination "${{ matrix.destination }}" clean
run: xcodebuild test -scheme "${{ matrix.scheme }}" -destination "${{ matrix.destination }}" clean -enableCodeCoverage YES
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
7 changes: 4 additions & 3 deletions .swiftlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,10 @@ disabled_rules:
opt_in_rules: # some rules are only opt-in
- anyobject_protocol
- array_init
- attributes
- closure_body_length
- closure_end_indentation
- closure_spacing
- collection_alignment
- conditional_returns_on_newline
- contains_over_filter_count
- contains_over_filter_is_empty
- contains_over_first_not_nil
Expand Down Expand Up @@ -131,4 +129,7 @@ nesting:
type_name:
max_length:
warning: 40
error: 50
error: 50

file_name:
excluded: ["Types.swift"]
102 changes: 102 additions & 0 deletions .swiftpm/xcode/xcshareddata/xcschemes/Flare.xcscheme
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1410"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "Flare"
BuildableName = "Flare"
BlueprintName = "Flare"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "FlareTests"
BuildableName = "FlareTests"
BlueprintName = "FlareTests"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
codeCoverageEnabled = "YES"
onlyGenerateCoverageForSpecifiedTargets = "YES">
<CodeCoverageTargets>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "Flare"
BuildableName = "Flare"
BlueprintName = "Flare"
ReferencedContainer = "container:">
</BuildableReference>
</CodeCoverageTargets>
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "FlareTests"
BuildableName = "FlareTests"
BlueprintName = "FlareTests"
ReferencedContainer = "container:">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "Flare"
BuildableName = "Flare"
BlueprintName = "Flare"
ReferencedContainer = "container:">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
# Change Log
All notable changes to this project will be documented in this file.

#### 2.x Releases
- `2.0.x` Releases - [2.0.0](#200)

## [2.0.0](https://github.com/space-code/flare/releases/tag/2.0.0)
Released on 2023-09-13.

#### Added
- Support for Swift Concurrency, including async-await for requests.

#### Updated
- Rename public methods and parameters to increase readability.

#### 1.x Releases
- `1.0.x` Releases - [1.0.0](#100)

Expand Down
Binary file added Documentation/Resources/flare.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
75 changes: 66 additions & 9 deletions Documentation/Usage.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
# Documentation

### Overview

* [Introduction](#introduction)
* [Flare Diagram](#flare-diagram)
* [In-App Purchases](#in-app-purchases)
- [Getting Products](#getting-products)
- [Purchasing Product](#purchasing-product)
Expand All @@ -10,18 +15,29 @@

## Introduction

Flare provides an elegant interface for In-App Purchases. It supports non-consumable and consumable purchases and subscriptions.
Flare provides an elegant interface for In-App Purchases, supporting non-consumable and consumable purchases as well as subscriptions.

## Flare Diagram

![Flare: Components](https://raw.githubusercontent.com/space-code/flare/dev/Documentation/Resources/flare.png)

- `Flare` is a central component that serves as the client API for managing various aspects of in-app purchases and payments in your application. It is designed to simplify the integration of payment processing and in-app purchase functionality into your software.
- `IAPProvider` is a fundamental component of `Flare` that handles all in-app purchase operations. It offers an API to initiate, verify, and manage in-app purchases within your application.
- `IPaymentProvider` is a central component in `Flare` that orchestrates various payment-related operations within your application. It acts as the bridge between the payment gateway and your app's logic.
- `IProductProvider` is a component of `Flare` that helps managing the products or services available for purchase within your app.
- `IReceiptRefreshProvider` is responsible for refreshing and managing receipt associated with in-app purchases.
- `IAppStoreReceiptProvider` manages and provides access to the app's receipt, which contains a record of all in-app purchases made by the user.

## In-App Purchases

### Getting Products

Before attempting to add a payment always check if the user can actually make payments. The `Flare` does it under the hood, if a user cannot make payments, you will get an `IAPError` with the value `paymentNotAllowed`.

The `fetch` method sends a request to the App Store, which retrieves the products if they are available. The `ids` parameter takes the product ids, which should be given from the App Store.
The `fetch` method sends a request to the App Store, which retrieves the products if they are available. The `productIDs` parameter takes the product ids, which should be given from the App Store.

```swift
Flare.default.fetch(ids: Set(arrayLiteral: ["product_id"])) { result in
Flare.default.fetch(productIDs: Set(arrayLiteral: ["product_id"])) { result in
switch result {
case let .success(products):
debugPrint("Fetched products: \(products)")
Expand All @@ -31,12 +47,22 @@ Flare.default.fetch(ids: Set(arrayLiteral: ["product_id"])) { result in
}
```

Additionally, there are versions of both `fetch` that provide an `async` method, allowing the use of `await`.

```swift
do {
let products = try await Flare.default.fetch(productIDs: Set(arrayLiteral: ["product_id"]))
} catch {
debugPrint("An error occurred while fetching products: \(error.localizedDescription)")
}
```

### Purchasing Product

The `buy` method performs a purchase of the product. The method accepts an `id` parameter which represents a product's id.
The `purchase` method performs a purchase of the product. The method accepts an `productID` parameter which represents a product's id.

```swift
Flare.default.buy(id: "product_id") { result in
Flare.default.purchase(productID: "product_id") { result in
switch result {
case let .success(transaction):
debugPrint("A transaction was received: \(transaction)")
Expand All @@ -46,9 +72,19 @@ Flare.default.buy(id: "product_id") { result in
}
```

You can also use the `async/await` implementation of the `purchase` method.

```swift
do {
let products = try await Flare.default.purchase(productID: "product_id")
} catch {
debugPrint("An error occurred while purchasing product: \(error.localizedDescription)")
}
```

### Refreshing Receipt

The `refresh` method refreshes the receipt, which represents the user's transactions with your app.
The `refresh` method refreshes the receipt, representing the user's transactions with your app.

```swift
Flare.default.refresh { result in
Expand All @@ -61,6 +97,16 @@ Flare.default.refresh { result in
}
```

You can also use the `async/await` implementation of the `refresh` method.

```swift
do {
let receipt = try await Flare.default.refresh()
} catch {
debugPrint("An error occurred while fetching receipt: \(error.localizedDescription)")
}
```

### Finishing Transaction

The `finish` method removes a finished (i.e. failed or completed) transaction from the queue.
Expand All @@ -75,7 +121,7 @@ The transactions array will only be synchronized with the server while the queue
It is important to set an observer on this queue as early as possible after your app launch. Observer is responsible for processing all events triggered by the queue.

```swift
// Add transaction observer and handle payment transactions.
// Adds transaction observer to the payment queue and handles payment transactions.
Flare.default.addTransactionObserver { result in
switch result {
case let .success(transaction):
Expand All @@ -87,7 +133,7 @@ Flare.default.addTransactionObserver { result in
```

```swift
// Remove transaction observer.
// Removes transaction observer from the payment queue.
Flare.default.removeTransactionObserver()
```

Expand All @@ -98,17 +144,28 @@ Flare.default.removeTransactionObserver()
By default, all methods handlers in public interfaces produced the `IAPError` error type. The `IAPError` describes frequently used error types in the app.

```swift
/// `IAPError` is the error type returned by Flare.
/// It encompasses a few different types of errors, each with their own associated reasons.
public enum IAPError: Swift.Error {
/// The empty array of products were fetched.
case emptyProducts
/// The attempt to fetch products with invalid identifiers.
case invalid(productIds: [String])
/// The attempt to purchase a product when payments are not allowed.
case paymentNotAllowed
/// The payment was cancelled.
case paymentCancelled
/// The attempt to fetch a product that doesn't available.
case storeProductNotAvailable
/// The `SKPayment` returned unknown error.
case storeTrouble
/// The operation failed with an underlying error.
case with(error: Swift.Error)
/// The App Store receipt wasn't found.
case receiptNotFound
/// The unknown error occurred.
case unknown
}
```

If you need a `SKError` you can just look at the `plainError` property in the `IAPError`.
If you need a `SKError`, you can simply look at the `plainError` property in the `IAPError`.
16 changes: 9 additions & 7 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,27 @@ let package = Package(
.macOS(.v10_15),
.iOS(.v13),
.watchOS(.v7),
.tvOS(.v11),
.tvOS(.v13),
],
products: [
.library(
name: "Flare",
targets: ["Flare"]
),
.library(name: "Flare", targets: ["Flare"]),
],
dependencies: [
.package(url: "https://github.com/space-code/concurrency.git", .upToNextMajor(from: "0.0.1")),
],
targets: [
.target(
name: "Flare",
dependencies: [.product(name: "Concurrency", package: "concurrency")]
dependencies: [
.product(name: "Concurrency", package: "concurrency"),
]
),
.testTarget(
name: "FlareTests",
dependencies: ["Flare", .product(name: "TestConcurrency", package: "concurrency")]
dependencies: [
"Flare",
.product(name: "TestConcurrency", package: "concurrency"),
]
),
]
)
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ Flare is a framework written in Swift that makes it easy for you to work with in
Check out [flare documentation](https://github.com/space-code/flare/blob/main/Documentation/Usage.md).

## Requirements
- iOS 13.0+ / macOS 10.15+ / tvOS 11.0+ / watchOS 7.0+
- iOS 13.0+ / macOS 10.15+ / tvOS 13.0+ / watchOS 7.0+
- Xcode 14.0
- Swift 5.5

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
//
// Flare
// Copyright © 2023 Space Code. All rights reserved.
//

import Foundation

extension Bundle: IAppStoreReceiptProvider {}
Loading

0 comments on commit 8402875

Please sign in to comment.