# iOS SDK

This is the iOS SDK for VYou (opens new window), a service to provide user identity to your applications. This library includes a client SDK along some artifacts and a sample to streamline your workflow.

The SDK is written in Kotlin using Kotlin Multiplatform for the client side and in Swift for the integration with third-party social libraries.

# Overview

The SDK consist of various artifacts:

  • KMM-Client: Client to manage operations with VYou authentication server.
  • iOS-Apple: Library to sign in/up in VYou through Apple libraries.
  • iOS-Google: Library to sign in/up in VYou through Google libraries.
  • iOS-Facebook: Library to sign in/up in VYou through Facebook libraries.
  • iOS-Stripe: Library to make payments and subscriptions through Stripe.

In addition, we have created a sample application (opens new window) using SwiftUI to test how each artefact can be implemented.

# Client

# Installation

The library is available through SPM (Swift Package Manager), just go to your Xcode project, click File > Add Packages and paste the git url in the search field. You can also use it as a library inside your Package.swift file.

.package(url: "https://github.com/apiumhub/vyou-ios",.upToNextMajor(from: "{latest-release-version}")),

The library will provide you four targets:

  • VYou: Client library and required for other targets.
  • VYouApple: Authenticate using Apple Sign In
  • VYouGoogle: Authenticate using Google Sign In
  • VYouFacebook: Authenticate using Facebook Log In

# Getting Started

The first step is to initialise the VYou class, which is the main entry point for all operations in the library. VYou is a class that contains all the operations you can perform using the SDK; you will use this method once, generating a single instance and using the rest of the methods in your application.

To create the client, we need to invoke the constructor class related to the VYou class and pass it the required parameters:

let builder = VYou.Builder(clientId: "{VYOU_CLIENT_ID}", serverUrl: "{VYOU_SERVER_URL}")

These variables are provided by VYou staff, the approach used in the samples is to include this data through Process Info as environment variables:

let clientId = ProcessInfo.processInfo.environment["CLIENT_ID"]
let serverUrl = ProcessInfo.processInfo.environment["SERVER_URL"]

In addition, the constructor can be configured with some optional functions. To track network operations the logging level can be changed, default is none.

//level - VYouLogLevel class
builder.enableNetworkLogs(level: .all)

VYouLogLevel is an enum class class and you can select between all, info, headers, body or none which will affect our implementation Ktor (opens new window) used by the client.

Finally, we have two callbacks, first we have addOnRefreshTokenFailure when the refresh token is invalidated by the server and addOnSignOut after the signOut completes successfully. Both callbacks are intended to help manage the state of the application when these events occur.

builder.addOnRefreshTokenFailure { error in
    //To do after event
}
builder.addOnSignOut { 
    //To do after event
}

Whether you use the optional functions or not, to finally initialise the client you need to invoke the build function of the builder.

builder.build()

A recommended practice is to initialize VYou in the AppDelegate class:

import VYou

class AppDelegate : UIApplicationDelegate {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        let clientId = ProcessInfo.processInfo.environment["CLIENT_ID"]
        let serverUrl = ProcessInfo.processInfo.environment["SERVER_URL"]
        VYou.Builder(clientId: clientId, serverUrl: serverUrl)
        //additional functions if needed
        .build()
    }
}

Alternatively, if you are using Swift UI, you can build the client into the main entry point of the application.

import VYou

@main
struct MainApp: App {
    init() {
        let clientId = ProcessInfo.processInfo.environment["CLIENT_ID"]
        let serverUrl = ProcessInfo.processInfo.environment["SERVER_URL"]
        VYou.Builder(clientId: clientId, serverUrl: serverUrl)
        //additional functions if needed
        .build()
    }
}

# Using the client

After building the client, we can use it throughout the application by calling VYou.shared. If the client is not initialised before calling the instance method, it will throw a related error. The client provides functionality to authenticate a user and manage user information.

All methods related to network operations are native functions using Kotlin coroutines. The SDK provides two types of functions to support iOS compatibility.

First, each function can be resolved through a completion handler method. For example:

func signIn(params: VYouSignInParams, completionHandler: (VYouCredentials) -> Void)

This approach may help in some projects but in most projects an asynchronous solution such as Async/Await, Combine or RxSwift should be used. To support these cases, as we have mentioned each function is native and can be encapsulated to support each case and this is done using KMP-NativeCoroutines (opens new window) which can be installed using SPM.

When you install it, you have multiple targets to support each asynchronous framework:

  • KMPNativeCoroutines: Core part of the framework and required
  • KMPNativeCoroutinesAsync: To use async/await operations with VYou native functions.
  • KMPNativeCoroutinesCombine: To use task operations with VYou native functions.
  • KMPNativeCoroutinesRxSwift: To use rx single/observable operations with VYou native functions.
import KMPNativeCoroutines
import VYou

//Async/Await
import KMPNativeCoroutinesAsync

func signIn() -> VYouCredentials {
    return asyncFunction(for: VYou.shared.signIn(params: VYouSignInparams))
}

//Combine
import KMPNativeCoroutinesCombine

func signIn() -> AnyPublisher<VYouCredentials, Error> {
    return createFuture(for: VYou.shared.signIn(params: VYouSignInparams))
}

//RxSwift
import KMPNativeCoroutinesRxSwift

func signIn() -> Single<VYouCredentials> {
    return createSingle(for: VYou.shared.signIn(params: VYouSignInparams))
}

These are some examples but you can check all the options in the [KMP-NativeCoroutines] repository (https://github.com/rickclephas/KMP-NativeCoroutines).

# Sign in

//VYouSignInParams(username, password)
VYou.shared.signIn(params: VYouSignInParams) -> VYouCredentials

//VYouSignInSocialParams(apple)
VYou.shared.signInFacebook(params: VYouSignInSocialParams) -> VYouCredentials

//VYouSignInSocialParams(googleIdToken)
VYou.shared.signInGoogle(params: VYouSignInSocialParams) -> VYouCredentials

//VYouSignInSocialParams(facebookAccessToken)
VYou.shared.signInFacebook(params: VYouSignInSocialParams) -> VYouCredentials

Each method returns a VYouCredentials class that contains the information to authorise any call covered by the VYou auth server. Internally, this information is stored in an encrypted keyring and can be retrieved through these credentials methods along with helper methods to check the user's session.

//Ex: VYou.shared.accessToken()
func isLoggedIn() -> Boolean
func tokenType() -> String
func accessToken() -> String
func credentials() -> VYouCredentials
func isValidToken() -> Boolean

If you already have a Google or Facebook implementation in your application, you can use these methods to log in to VYou, plus we will explain the social artefacts mentioned in the overview that covers the implementation of the social libraries.

# Sign up

To register a new user on the platform, the following three steps are necessary. Firstly the new user is registered by providing an email along with the acceptance of the required terms of use and the privacy policy provided by the application, optionally a boolean information can be added for marketing purposes.

//VYouSignUpParams(email, termsOfUseAccepted, privacyPolicyAccepted, infoAccepted)
VYou.shared.signUp(params: VYouSignUpParams)

After sign up, the user will receive a confirmation code in the email provided in the previous step. The user must copy this code and introduce it to verify the register.

//VYouSignUpVerifyParams(code)
VYou.shared.signUpVerify(params: VYouSignUpVerifyParams)

Finally, if the provided code is valid, the user must register a password related to the provided email using this method:

//VYouSignUpPasswordParams(password)
VYou.shared.signUpPassword(params: VYouSignUpPasswordParams)

All these steps are necessary to ensure all security steps related to the OAuth protocol. After successfully registering the password, the user can use the sign in method to log in to the platform.

# Profile

Users have access to the information they provide to the application using VYou. To retrieve the current information they have the following method.

VYou.shared.getProfile() -> VYouProfile

VYouProfile is a class containing the registered email, custom fields and the compliance status related to the tenant. If the status is false, the application should request the required fields from the user.

To update your information you have another method to modify the custom fields related to the tenant.

//VYouEditProfileParams(fields)
VYou.shared.editProfile(params: VYouEditProfileParams) -> VYouProfile

It is not necessary to provide all fields, only the ones you want to modify.

# Payments

To make payments through Stripe, the SDK provides two methods to create payments through the VYou platform.

//Ex: VYou.shared.createPayment(params)

//VYouPaymentParams(amount: Long (in cents))
func createPayment(params: VYouPaymentParams) -> String
func createAnonymousPayment(params: VYouPaymentParams) -> String

Both methods require the desired amount in cents and will return the secret key requested by Stripe's payment SDK to display the payment sheet. In case you don't have a Stripe integration, the VYouStripe library explained below covers all Stripe integration through the VYou platform.

# Subscriptions

To make subscriptions through Stripe, the SDK provides a few methods to manage the whole subscription process.

Firstly, we have the method to return all the products related to the tenant, along with their price range, plus the priceId field will be required to create any subscription.

//Ex: VYou.shared.subscriptionProducts()

//VYouProduct(name, description, prices)
//VYouPrice(priceId, amount, currency, interval, intervalCount)
func subscriptionProducts()) -> List<VYouProduct>

Then, to create a subscription, similar to payments, we have a method that will return the secret key, but in this case we need the priceId related to the desire interval and the product. Also, this functionality is covered in the VYouStripe library.

//Ex: VYou.shared.createSubscription(params)

//VYouSubscriptionParams(priceId)
func createSubscription(params: VYouSubscriptionParams) -> String

In addition, there is a method to display the subscriptions to which a user is subscribed.

//Ex: VYou.shared.mySubscriptions()

//VYouSubscription(subscriptionId, productName, amount, currency, nextBillingDate, created)
func mySubscriptions() -> List<VYouSubscription>

Finally, a user can cancel a subscription through the following method

//Ex: VYou.shared.cancelSubscription(params)

//VYouSubscriptionCancelParams(subscriptionId)
func cancelSubscription(params: VYouSubscriptionCancelParams)

# Other methods

To log out of the platform and clear the saved credentials, there is a sign out method

VYou.shared.signOut()

Users also can reset password using the next method, after a successful call they will receive an email with a confirmation token and they must proceed like in the sign up process after register the email.

//VYouResetPasswordParams(email)
VYou.shared.resetPassword(params: VYouResetPasswordParams)

If any call returns a 401 - Forbidden http code it means that the access token used has expired and must be refreshed, to do this you must use the refresh token method.

VYou.shared.refreshToken() -> VYouCredentials

This must be implemented in a network interceptor to provide a smooth user experience, if the refresh token fails, the user must log in again to retrieve a new access token.

# Integration with social libraries

To facilitate integration with Apple, Google and Facebook, we have created several artefacts to manage login across each platform.

# Getting started (Social)

The use of each library is very similar but each one needs a previous configuration.

# Apple

To use Apple Sign In, you need to activate the Sign In through Application's target > Signing&Capabilities > Add Capability > Sign in with Apple

This will generate an entitlements file associated with the target and validate the functions.

# Google

Inside Info.plist must be provided the url related with Google Client Id of your project inside URLTypes

<key>CFBundleURLTypes</key>
<array>
  <dict>
  <key>CFBundleURLSchemes</key>
  <array>
    <string>{GOOGLE_CLIENT_URL}</string>
  </array>
  </dict>
</array>

# Facebook

In case of Facebook, you need to include Facebook Client Id of your project plus some data to be displayed in the web dialog.

<key>CFBundleURLTypes</key>
<array>
  <dict>
  <key>CFBundleURLSchemes</key>
  <array>
    <string>fb{FACEBOOK-CLIENT-ID}</string>
  </array>
  </dict>
</array>
<key>FacebookAppID</key>
<string>{FACEBOOK-CLIENT-ID}</string>
<key>FacebookDisplayName</key>
<string>{FACEBOOK-CLIENT-NAME}</string>

# Initialize the client (Social)

Each library has a Builder class to create the shared instance to be used. This can be initialised in the AppDelegate or main entry point of the Swift UI application as in client section

VYouApple.Builder().build()
VYouFacebook.Builder().build()
VYouGoogle.Builder(clientId: "{GOOGLE_CLIENT_ID}").build()

In addition, for Google and Facebook we need to add some methods related to application events.

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        VYouFacebook.shared.didFinishLaunchingWithOptions(application: application, didFinishLaunchingWithOptions: launchOptions)
    }

    func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
        VYouGoogle.shared.handle(url: url)
        VYouFacebook.shared.handle(application: app, url: url)
    }
}

Then, we can start using both libraries inside our application through their shared method. Remember, that if you don't build the libraries, the shared instance will throw an exception.

# Sign in (Social)

Each of the libraries has similar functions for signing in to the VYou platform.

After apply these methods, we can use the signIn() method of each library.

//Ex: VYouApple.shared.signIn
//Ex: VYouGoogle.shared.signIn
//Ex: VYouFacebook.shared.signIn
func signIn(presenting: UIViewController, onFailure: @escaping (Error) -> Void, onSuccess: @escaping (VYouCredentials) -> Void)

Each method returns a VYouCredentials that you can use as you wish.

# Sign out (Social)

To sign out of the social libraries, we have the same method for each library, except Apple which doesn't need it.

//Ex: VYouGoogle.shared.signOut()
//Ex: VYouFacebook.shared.signOut()
func signOut()

If you use both platforms in your application, we encourage you to use the signOut() method in the client section when the user wants to log out and then use these social signOut() methods within addOnSignOut() of the client builder class.

let builder = VYou.Builder()
builder.addOnSignOut {
    VYouGoogle.shared.signOut()
    VYouFacebook.shared.signOut()
}

# Integration with payment methods

To enhance the customer experience, the SDK also provides a solution that uses Stripe to make payments and create subscription methods.

# Initialize the client (Stripe)

The library have a Builder class to create the shared instance to be used. This can be initialized in the AppDelegate or main entry point of the Swift UI app like in the client section.

// publishableKey - Key from Stripe's dashboard
// merchantDisplayName - The name of the merchant that you want to display in the payment sheet.
VYouStripe.Builder(
    publishableKey: String,
    merchantDisplayName: String
).build()

Then, we can start using the library inside our application through their shared instance. Remember, that if you don't build the library, the shared instance will throw an exception.

# Payments (Stripe)

Through the SDK, we can create any payment to our users, the SDK provides two ways to do it, for users logged into the platform, or doing anonymously, when you don't need the user information.

VYouStripe.shared.createPayment(presenting: UIViewController, params: VYouPaymentsParams)
VYouStripe.shared.createAnonymousPayment(presenting: UIViewController, params: VYouPaymentsParams)

VYouPaymentParams is a data class that requires the desire amount to be displayed to the user in cents, so if you want to display 1 euro, you must create an object VYouPaymentParams(amount: 100).

In the case of user cancels the payment or the operation fails, the SDK will return a VYouError.

# Subscriptions (Stripe)

In the case of subscriptions, the process is similar, the only changes is the params object, that now it is a VYouSubscriptionParams(priceId: String). The priceId is the payment interval option related to the subscription (product in Stripe). The SDK through the Client library provides a way to display all the subscriptions related with the tenant. You can check this method and others related in the section

VYouStripe.shared.createSubscription(presenting: UIViewController, params: VYouSubscriptionParams)

In the case of user cancels the subscription or the operation fails, the SDK will return a VYouError.