# iOS Integration

## Getting Started

### Cocoapods Plugin

In order to resolve the anybill SDK using Cocoapods, you need the cocoapods-art plugin which presents Artifactory repositories as Specs repos and pod sources.

You can download the [cocoapods-art](https://rubygems.org/gems/cocoapods-art) plugin as a Gem, and its sources can be found on  [Github](https://github.com/JFrog/cocoapods-art)

To resolve the artifactory repository, execute the following steps:

1. Install cocoapods-art plugin

```
gem install cocoapods-art
```

2. The next step is to add the repository by using the pod 'repo-art add' command:

```
pod repo-art add anybill_ios_sdk "https://anybill.jfrog.io/artifactory/api/pods/anybill_ios_sdk"
```

3. Once the repository is added, add the following in your Podfile:

```
plugin 'cocoapods-art', :sources => [
        'anybill_ios_sdk'
    ]
```

repo-art uses authentication as specified in your standard netrc file.

```
machine anybill.jfrog.io
login <USERNAME>
password <PASSWORD>
```

Further information about the cocoapods plugin can be found on the artifactory [documentation](https://www.jfrog.com/confluence/display/JFROG/CocoaPods+Repositories).

### Integration with Cocoapods

1. First of all you need to specify the usage of dynamic frameworks in your target/project with `use_frameworks!`, if you did not already.

2. Then you can add the desired artifacts to your podfile (The AnybillBase module is required for the basic usage of the SDK)

Below is the podfile of an example project, which implements the anybill SDK: 

```swift
platform :ios, '14.0'

source 'https://github.com/CocoaPods/Specs.git'
plugin 'cocoapods-art', :sources => [
  'anybill_ios_sdk'
]

target 'your target name' do
  
  use_frameworks!
  
  pod 'AnybillBase'

end

# Add this script for Cocoapods <1.11.0 to fix a Cocoapods problem with XCFrameworks
post_install do |installer|
  installer.pods_project.targets.each do |target|
    target.build_configurations.each do |config|
      config.build_settings['BUILD_LIBRARY_FOR_DISTRIBUTION'] = 'YES'
    end
  end
end

```

### Integration with Swift Package Manager

1. First of all you need to set the package-registry in the root folder of your application using the following command: 

```
swift package-registry set --global "https://anybill.jfrog.io/artifactory/api/swift/anybill_ios_sdk-spm"
```

2. Then you need to make sure you are logged in to jfrog in order to resolve the package

```
swift package-registry login https://anybill.jfrog.io/artifactory/api/swift/anybill_ios_sdk-spm  --username yourUsername  --password yourPassword
```

3. Add the desired anybill modules in Xcode: File > Add package dependencies. Search for e.g. 'anybill.AnybillBase' and add the dependency. 

### Instantiate the log module in your AppDelegate file

In your AppDelegate file import `AnybillBase` and add `AnybillLogger.initLogger()` to the body of the application(_:didFinishLaunchingWithOptions:) method

### Setting the client Id

To set the Client Id in your app, add the Id as value for the key `anybill_client_id` in your info.plist file. This is going to allow us to hook all of your API activity to your Client Id which can be used for analytics or support purposes later on.

### Change the api mode to staging

For developing purposes you can change the api environment to staging by adding the staging url (https://app.stg.anybill.de/) as value for the key `anybill_base_url` in your info.plist file

## Usage of the SDK

The usage of the individual methods in the SDK are mostly consistent. Below is an example of the usage of the login method in a view controller. A detailed documentation of the individual methods including return types and error codes can be found in the [technical documentation](./native_sdks.md) of the corresponding module.

```swift
import AnybillBase

class ViewController: UIViewController {

    let authService = AuthProvider.shared

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        loginUser()
    }

    func loginUser() {
        authService.loginUser(username: "username", password: "password") { result in
            switch result {
                case .success:
                    // Continue
                case .failure(let error):
                    // Handle error
                    // For example:
                switch error.type {
                    case .genericError:
                        // handle errors 400..499
                    case .networkError:
                        // handle network error
                    case .noUserError:
                        // handle no user error
                    case .invalidRefreshTokenError:
                        // handle error when access token could not be refreshed
                    default:
                        // handle unknown error
                }
            }
        }
    }
}
```

### Error Handling

The anybill sdk uses a custom error handling model. All methods return either the result object or an `AnybillError`object containing the error type, description and code.

```swift
/**
 Error model used to describe api call failures
 - code: Int with the response code of the api call
 - title: String with the title of the error
 - message: String with a description of the error
 - errors: Dictionary with error descriptions returned from our backend
 - traceId: String used for error logging
 - type: AnybillErrorType used to distinguish between error types
 */
public struct AnybillError: Error {
    public let code: Int
    public let title: String?
    public let message: String?
    public let errors: [String: [String]]?
    public let traceId: String?
    public let type: AnybillErrorType
}

/**
 Enum used to distinguish between generic and network errors
 */
public enum AnybillErrorType: Codable {
    /// This error type is used for generic errors that do not fit into any other category.
    case genericError,
         
         /// This error type is used when there is a network error while making a request.
         networkError,
         
         /// This error type is used when there is an issue with refreshing the user's access token.
         invalidRefreshTokenError,
         
         /// This error type is used when there is no user logged in.
         noUserError,
         
         /// This error type is used when the issue can't be defined. Typically a critical error.
         unknown
}
```

Example usage of the error model based on the login method of the AuthProvider.

```swift
authService.loginUser(username: username, password: password) { result in
        switch result {
            case .success(()):
                //Continue
            case .failure(let error):
                switch error.type {
                    case .genericError:
                        // handle errors 400..499
                    case .networkError:
                        // handle network error
                    case .noUserError:
                        // handle no user error
                    case .invalidRefreshTokenError:
                        // handle error when access token could not be refreshed
                    default:
                        // handle unknown error
                }
        }
    }
```

[Back to top](#)

## App Link

Anybill Applink is our way to enable linking into mobile apps from our receipt website by directly opening the app or redirecting to the app stores while persisting data. If you are integrating our sdk and want to make use of this feature, please contact us beforehand.

### Integrate app link and automatically open the new receipt

To automatically add a receipt and open the app, that integrates the anybill sdk, you have to set up universal links. Therefore you have to enable the capability 'Associated Domains' and register the domain "applinks:applink.anybill.de".
Additionally the bundle identifier of your application has to be in the apple-app-site-association file of our server hosting the app link web page (If that is not the case please contact us). 

Next you have to get the link that opened your application. In swiftUI you can use the '.onOpenURL' handler for that task:

```swift 

struct ContentView: View {
    @State private var tab = 1
    
    var body: some View {
        TabView(selection: $tab) {
            Home()
                .tabItem {
                    Label("Home", image: "home")
                        .foregroundColor(.black)
                }
                .tag(1)
            AnybillUIModule()
                .tabItem {
                    Label("Receipts", image: "anybillLogo")
                        .foregroundColor(.black)
                }
                .tag(2)
            Settings()
                .tabItem {
                    Label("Settings", systemImage: "gearshape")
                        .foregroundColor(.black)
                }
                .tag(3)
        }
        .onOpenURL { url in
            if AnybillAppLink.checkForAnybillAppLink(url: url) != nil {
                self.tab = 2
            }
        }
    }
}

```

In UIKit you can get the url in the SceneDelegate:

```swift

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    if let url = connectionOptions.userActivities.first?.webpageURL {
            if AnybillAppLink.checkForAnybillAppLink(url: url) != nil, let tabBarController = window?.rootViewController as? UITabBarController {
                tabBarController.selectedIndex = 1
            }
        }
}

```

And if the app is already running: 

```swift

func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
    if let url = userActivity.webpageURL {
        if AnybillAppLink.checkForAnybillAppLink(url: url) != nil, let tabBarController = window?.rootViewController as? UITabBarController {
            tabBarController.selectedIndex = 1
        }
    }
}

```

Next use 'AnybillAppLink.checkForAnybillAppLink(url: URL)' to check if the received url was a valid anybill url and if it contains information to add a new receipt. If that is the case simply navigate to the anybill ui module or use the returning 'AnybillURLData' object to manually add the receipt to the users account.

### Get the receipt if the app is not installed yet

If the user tries to add the receipt via your application and it is not installed on the users device, the page will redirect to your application in the app store and the url is saved to the users clipboard. To ensure that the user still gets the receipt, you need to call 'AnybillAppLink.checkForAnybillAppLinkData()' on the app start. This method checks if it is the first app start and if that is the case it checks the users clipboard for any saved url data. If the returning data is not equal to nil simply navigate to the anybill ui module or handle the data manually.

SwiftUI example:

```swift

struct ContentView: View {
    @State private var tab = 1
    
    var body: some View {
        TabView(selection: $tab) {
            Home()
                .tabItem {
                    Label("Home", image: "home")
                        .foregroundColor(.black)
                }
                .tag(1)
            AnybillUIModule()
                .tabItem {
                    Label("Receipts", image: "anybillLogo")
                        .foregroundColor(.black)
                }
                .tag(2)
            Settings()
                .tabItem {
                    Label("Settings", systemImage: "gearshape")
                        .foregroundColor(.black)
                }
                .tag(3)
        }
        .onAppear {
            Task {
                if await AnybillAppLink.checkForAnybillAppLinkData() != nil {
                    self.tab = 2
                }
            }
        }
        .onOpenURL { url in
            if AnybillAppLink.checkForAnybillAppLink(url: url) != nil {
                self.tab = 2
            }
        }
    }
}

```

UIKit example:

```swift 

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        if let tabBarController = window?.rootViewController as? UITabBarController {
            Task {
                if await AnybillAppLink.checkForAnybillAppLinkData() != nil {
                    tabBarController.selectedIndex = 1
                }
            }
        guard let _ = (scene as? UIWindowScene) else { return }
    }
}

```

## Push Notifications

The anybill SDK uses push notifications to inform the user about certain events. A detailed list of these events can be found in the push notification tab of the App API documentation.

To enable Push Notifications you'll have to provide a APN Authentication Key of your application. After creating a Firebase application in the anybill Firebase project, you are going to receive a Google App Id and Firebase Client Id, which you will have to use in your application code. Please contact us before implementing anybill Push Notifications (<dev@anybill.de>).

### Integration without firebase already being integrated in your project

First of all you need to import Firebase and AnybillBase in your AppDelegate.

Then add the following code to the body of your `application(_:didFinishLaunchingWithOptions:)` function:

```swift
AnybillFirebaseInitializer().initFirebaseProject(googleAppId: "yourGoogleAppId", firebaseClientId: "yourFirebaseClientId")

Messaging.messaging().delegate = self

if #available(iOS 10.0, *) {
  // For iOS 10 display notification (sent via APNS)
  UNUserNotificationCenter.current().delegate = self

  let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
  UNUserNotificationCenter.current().requestAuthorization( options: authOptions, completionHandler: {_, _ in })
} else {
  let settings: UIUserNotificationSettings = UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
  UIApplication.shared.registerUserNotificationSettings(settings)
}
application.registerForRemoteNotifications()

```

Subsequently add these extensions to the end of your AppDelegate file:

```swift

@available(iOS 10, *)
extension AppDelegate : UNUserNotificationCenterDelegate {

  // Receive displayed notifications for iOS 10 devices.
  func userNotificationCenter(_ center: UNUserNotificationCenter,
                              willPresent notification: UNNotification,
    withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
    
    //Called when the app is about to show a notification
    
    // Change this to your preferred presentation option
    completionHandler([[.banner, .sound]])
  }

  func userNotificationCenter(_ center: UNUserNotificationCenter,
                              didReceive response: UNNotificationResponse,
                              withCompletionHandler completionHandler: @escaping () -> Void) {
                              
    //Called when the users clicks the notification
    
    completionHandler()
  }
}

extension AppDelegate : MessagingDelegate {
    
  func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
    
    let dataDict:[String: String] = ["token": fcmToken ?? ""]
    NotificationCenter.default.post(name: Notification.Name("FCMToken"), object: nil, userInfo: dataDict)
    
    //Notify the anybill sdk to update its FCM token
    
    AnybillFirebaseInitializer().didReceiveRegistrationToken()
}

```

### Integration with firebase already being integrated in your project

If you already have firebase integrated in your app, simply follow the steps mentioned above and initialize the anybill firebase project **after** you initialized your firebase project

```swift

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

// Initialize your Firebase project:

FirebaseApp.configure()

// Initialize the anybill Firebase project:

AnybillFirebaseInitializer().initFirebaseProject(googleAppId: "yourGoogleAppId", firebaseClientId: "yourFirebaseClientId")

Messaging.messaging().delegate = self

...
}

```

### Default handling of push notifications

As just demonstrated, notifications are handled inside the `userNotificationCenter()` method. The simplest way of handling the notifications is to just display them 
as local iOS notifications. However the SDK also offers the possibility to display them on UI components of the SDK as a snackbar. Additionally some special notifications trigger
specific actions when handled by the SDK such as automatically updating the list of bills when a notifications arrives, stating that a new receipt has been added to the logged in account. To enable the SDK internal handling of notifications, the method `handleAnybillNotification` of the class `AnybillNotificationCenter` can be called like so:

```swift
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification,  withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
      AnybillNotificationCenter.shared.handleAnybillNotification(notification: notification)
      completionHandler(.sound)
}
```

To avoid displaying of multiple snackbars/notifications it is advised to first check if the SDK UI is currently being used and based on the result either let the SDK handle the notification or let the implementing app display the notification.

### Open added receipt after push notification

To open a receipt in the anybill ui module, that was added though a push notification you need to implement the observable object 'NotificationManager.shared' and pass it to the AnybillUIModule view as an environmentObject. 

Additionally you have to listen for push notifications and pass the received 'UNNotification' object to the 'dumbData' parameter of the 'NotificationManager.shared'. 

If the notification is a valid add receipt notification from anybill the notification data will be parsed and the relevant information will be available through the @Published variable 'anybillNotificationResult'.

You should listen for changes of the 'anybillNotificationResult' parameter and navigate to the anybill ui module if it is not equal to nil.

Passing the notification info to the NotificationManager:

```swift

    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                willPresent notification: UNNotification,
                                withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions)
                                -> Void) {
        self.notificationManager.dumbData = notification
    }

    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                didReceive response: UNNotificationResponse,
                                withCompletionHandler completionHandler: @escaping () -> Void) {
        self.notificationManager.dumbData = response.notification
        completionHandler()
    }   

```

Example of navigating to the anybill ui module after an add receipt push notification in swiftUI:

(Note that the NotificationManager instance is passed to the ContentView as an environmentObject)

```swift

struct ContentView: View {
    @State private var tab = 1
    @EnvironmentObject var notificationCenter: NotificationManager
    
    var body: some View {
        TabView(selection: $tab) {
            Home()
                .tabItem {
                    Label("Home", image: "home")
                        .foregroundColor(.black)
                }
                .tag(1)
            AnybillUIModule()
                .tabItem {
                    Label("Receipts", image: "anybillLogo")
                        .foregroundColor(.black)
                }
                .tag(2)
            Settings()
                .tabItem {
                    Label("Settings", systemImage: "gearshape")
                        .foregroundColor(.black)
                }
                .tag(3)
        }
        .onAppear {
            if notificationCenter.anybillNotificationResult != nil {
                self.tab = 2
            }
        }
        .onChange(of: notificationCenter.anybillNotificationResult) { _ in
            self.tab = 2
        }
    }
}
```

Example of navigating to the anybill ui module in UIKit:

```swift

class CustomTabBarController: UITabBarController {
    
    let notificationManager = NotificationManager.shared
    
    var cancellableBag = Set<AnyCancellable>()

        override func viewDidLoad() {
            super.viewDidLoad()
            
            notificationManager.$anybillNotificationResult.sink { value in
                self.selectedIndex = 1
            }.store(in: &cancellableBag)
        }
}

```

[Back to top](#)

## AnybillBase

### Authentication

The AnybillBase module handles among other things the authentication in the SDK. 

The authentication methods are accessible through the AuthProvider singleton.

```swift
  let authService = AuthProvider.shared
```

### Login

**Login Anybill User**

Anybill users can be logged in using the `loginUser()` method of the AuthProvider. It requires valid login information of a registered anybill user (email and password).

```swift
    authService.loginUser(username: username, password: password) { result in
        switch result {
            case .success(()):
                //Continue
            case .failure(let error):
                switch error.type {
                    case .genericError:
                        // handle errors 400..499
                    case .networkError:
                        // handle network error
                    case .noUserError:
                        // handle no user error
                    case .invalidRefreshTokenError:
                        // handle error when access token could not be refreshed
                    default:
                        // handle unknown error
                }
        }
    }
```
> **Warning: [DEPRECATED]**
>
> **Login Anonymous User**
>
> With the `createAnonymousUser()` method, you can create an anonymous user account without an email or password. The account can use the anybill services with certain restrictions. Later on the account can be converted to a normal anybill account with email, password and further user information.
>
> ```swift
>     authService.createAnonymousUser { result in
>         switch result {
>             case .success(let anonymousUser):
>                 //Continue
>             case .failure(let error):
>                switch error.type {
>                     case .genericError:
>                         // handle errors 400..499
>                     case .networkError:
>                         // handle network error
>                     case .noUserError:
>                         // handle no user error
>                     case .invalidRefreshTokenError:
>                         // handle error when access token could not be refreshed
>                     default:
>                         // handle unknown error
>                 }
>         }
>     }
> ```
>
> Note: After an App or its data gets deleted, the anonymous account cannot be restored because there is no reference to the old account left. 
>

**Linked token user**

If your App has an own user account system you can link an anybill user to your user account by using the linked token user:

* Get a token from the anybill Partner API by linking your account system to an anybill id.
* Create an instance of `TokenUser` with the received token-information
* Login the TokenUser with the anybill sdk

```swift
    let tokenUser = TokenUser.init(accessToken: accessToken, expiresIn: expiresIn, refreshToken: refreshToken)
    AuthProvider.loginTokenUser(tokenUser){ result in
        switch result {
            case .success(()):
                //Continue
            case .failure(let error):
                switch error.type {
                    case .genericError:
                        // handle errors 400..499
                    case .networkError:
                        // handle network error
                    case .noUserError:
                        // handle no user error
                    case .invalidRefreshTokenError:
                        // handle error when access token could not be refreshed
                    default:
                        // handle unknown error
                }
        }
    }
    
```

Further information about the login possibilities can be found on the anybill sdk authentication page.

### Listen for refresh token failure with Token User

If you are using the anybill token user, you have to register a notification observer in your app to listen if the refresh token call fails and get a new Token for the user via our partnerplatform API. 

1. Register notification observer:
    ```swift
        NotificationCenter.default.addObserver(self, selector: #selector(getNewToken(_:)), name: Notification.Name(rawValue: ApiConfig.REFRESH_TOKEN_FAILED_OBSERVER), object: nil)
    ```
2. Write function that gets a new token from the anybill api and log in the user with the new token

> **Warning: [DEPRECATED]**
>
> ### Register
>
> **Register Anybill User**
>
> To register a new anybill user use the `preregister()` method, which sends a validation code to the given user email. You can validate a register code with the `validateRegistrationCode()` method. Use the `finalizeRegistration()` method to complete the registration process. 
> By setting `autoLogin` to `true` the user is going to be automatically logged in after a successful registration.
>
> ```swift
>     authService.preregisterUser(email: email, firstname: firstname) { result in
>         switch result {
>         case .success:
>             //Continue
>         case .failure(let error):
>             switch error.type {
>                     case .genericError:
>                         // handle errors 400..499
>                     case .networkError:
>                         // handle network error
>                     case .noUserError:
>                         // handle no user error
>                     case .invalidRefreshTokenError:
>                         // handle error when access token could not be refreshed
>                     default:
>                         // handle unknown error
>                 }
>         }
>     }
> ```
>
> ```swift
>     authService.finalizeRegistration(email: email, code: validationCode, password: password, autoLogin: autoLogin) { result in
>         switch result {
>         case .success(let registerUserResponse):
>             //Continue
>         case .failure(let error):
>            switch error.type {
>                     case .genericError:
>                         // handle errors 400..499
>                     case .networkError:
>                         // handle network error
>                     case .noUserError:
>                         // handle no user error
>                     case .invalidRefreshTokenError:
>                         // handle error when access token could not be refreshed
>                     default:
>                         // handle unknown error
>                 }
>         }
>     }
> ```
>
> **Convert Anonymous User**
>
> Converting an anonymous user to an anybill User. Anonymous users have restricted access to the anybill functions (e.g. restricted amount of receipts). If the app user wants to take use of all the anybill functions he has to create an anybill account. 
> To retain the data of an anonymous user the anybill sdk provides a function to convert an anonymous user to an anybill user. 
>
> ```swift
>     authService.convertAnonymousUser(email: email, password: password, firstname: firstname, gender: gender) { result in
>         switch result {
>             case .success:
>                 //Continue
>             case .failure(let error):
>                 switch error.type {
>                     case .genericError:
>                         // handle errors 400..499
>                     case .networkError:
>                         // handle network error
>                     case .noUserError:
>                         // handle no user error
>                     case .invalidRefreshTokenError:
>                         // handle error when access token could not be refreshed
>                     default:
>                         // handle unknown error
>                 }
>         }
>     }
> ```
>
> Afterwards the user can login into his anybill account using his login credentials.
>

### Logout

Logging out an user deletes all of user's app data including cached receipts, authentication information and app settings (of the anybill sdk).

```swift
    authService.logoutUser { result in
        switch result {
            case .success:
                //Continue
            case .failure(let error):
                switch error.type {
                    case .genericError:
                        // handle errors 400..499
                    case .networkError:
                        // handle network error
                    case .noUserError:
                        // handle no user error
                    case .invalidRefreshTokenError:
                        // handle error when access token could not be refreshed
                    default:
                        // handle unknown error
                }
        }
    }
```

### Delete Account

Deleting an anybill account is irreversibly. The account can not be retrieved afterwards.
        
```swift
    authService.deleteCurrentUser { result in
        switch result {
            case .success:
                //Continue
            case .failure(let error):
                switch error.type {
                    case .genericError:
                        // handle errors 400..499
                    case .networkError:
                        // handle network error
                    case .noUserError:
                        // handle no user error
                    case .invalidRefreshTokenError:
                        // handle error when access token could not be refreshed
                    default:
                        // handle unknown error
                }
        }
    }
```

### Get QR Code Data

Certain points of sale allow anybill users to receive receipts by scanning a QR code on the user's phone. To get the jsonObject which should be included in the QR Code use the `getQRCodeData`method.

Following code shows an example how to generate a QR Code and display it in your view:

```swift
    authService.getQRCodeData{ result in
        switch result {
            case .success(let qrCodeData):
                let data = try? JSONEncoder().encode(qrCodeData)
                if let filter = CIFilter(name: "CIQRCodeGenerator") {
                    filter.setValue(data, forKey: "inputMessage")
                    let transform = CGAffineTransform(scaleX: 3, y: 3)
                    if let output = filter.outputImage?.transformed(by: transform) {
                        self.yourImageView.image = UIImage(ciImage: output)
                    }
                }
            case .failure(let error):
                switch error.type {
                    case .genericError:
                        // handle errors 400..499
                    case .networkError:
                        // handle network error
                    case .noUserError:
                        // handle no user error
                    case .invalidRefreshTokenError:
                        // handle error when access token could not be refreshed
                    default:
                        // handle unknown error
                }
        }
    }
```

More information about QR Code data can be found in the anybill app documentation.

### TokenProvider

The TokenProvider is used for internal authentication operations. The public methods should **not** be accessed from the app itself. It might cause problem during the anybill authentication process.

[Back to top](#)

### Receipt

The AnybillBase module enables the user to manage his receipts.

The methods are accessible through the ReceiptProvider singleton.

```swift
  let receiptService = ReceiptProvider.shared
```

> **Warning: [DEPRECATED]**
>
> ```swift
>   let billService = BillProvider.shared
> ```
>

### Get receipt list

**Get receipts** 

The `getReceiptsFromApi(take: Int?, continuationToken: String?)` method to used retrieve user receipts. The result contains the receipts and the continuation token, which can be used to retrieve the next batch of receipts.

```swift
    billService.getReceiptsFromApi(take: 100, continuationToken: token) { result in
        switch result {
            case .success:
                //Access receipts through the observableReceipts observable in the ReceiptProvider
            case .failure(let error):
               switch error.type {
                    case .genericError:
                        // handle errors 400..499
                    case .networkError:
                        // handle network error
                    case .noUserError:
                        // handle no user error
                    case .invalidRefreshTokenError:
                        // handle error when access token could not be refreshed
                    default:
                        // handle unknown error
                }
        }
    }
```

**Get initial receipts (recommended)** 

Use the `getInitialReceipts(take: Int?)` method to initially retrieve the first user receipts. With the `getNextReceipts(take: Int?)` method the next batch of receipts can be retrieved. When called the receipts are cached in a local database. 

```swift
    billService.getInitialReceipts(take: 100) { result in
        switch result {
            case .success:
                //Access receipts through the observableReceipts observable in the ReceiptProvider
            case .failure(let error):
               switch error.type {
                    case .genericError:
                        // handle errors 400..499
                    case .networkError:
                        // handle network error
                    case .noUserError:
                        // handle no user error
                    case .invalidRefreshTokenError:
                        // handle error when access token could not be refreshed
                    default:
                        // handle unknown error
                }
        }
    }
```

**Get next receipts (recommended)** 

After calling the `getInitialReceipts(take: Int?)` method to retrieve the first user receipts, the `getNextReceipts(take: Int?)` method can be used to retrieve the next batch of receipts. When called the receipts are cached in a local database. 

```swift
    billService.getNextReceipts(take: 100) { result in
        switch result {
            case .success:
                //Access receipts through the observableReceipts observable in the ReceiptProvider
            case .failure(let error):
               switch error.type {
                    case .genericError:
                        // handle errors 400..499
                    case .networkError:
                        // handle network error
                    case .noUserError:
                        // handle no user error
                    case .invalidRefreshTokenError:
                        // handle error when access token could not be refreshed
                    default:
                        // handle unknown error
                }
        }
    }
```

> **Warning: [DEPRECATED]**
>
> **Get all user bills**
>
> Use the `getAllBillsFromApi()` method to retrieve the complete list of user bills. When called the bills are cached in a local database.
>
> ```swift
>     billService.getAllBillsFromApi { result in
>         switch result {
>             case .success(let bills):
>                 //Continue
>             case .failure(let error):
>                switch error.type {
>                     case .genericError:
>                         // handle errors 400..499
>                     case .networkError:
>                         // handle network error
>                     case .noUserError:
>                         // handle no user error
>                     case .invalidRefreshTokenError:
>                         // handle error when access token could not be refreshed
>                     default:
>                         // handle unknown error
>                 }
>         }
>     }
> ```
>
> **Get changed user bills (recommended)**
>
> Use the `updateBills()` method to fill an observable object, containing the list of user bills. The method first fills the observable object with the cached bills and then updates the list with the newly modified/added bills as soon as the API operation completed.
>
> ```swift
>     //Observe changes on the observable object
>     private let billService = BillProvider.shared
>     private var cancellables = Set<AnyCancellable>() 
>
>     billService.$observableBills
>                     .sink { [weak self] in
>                         self?.setBills(bills: $0)
>                     }
>                     .store(in: &cancellables)
>
>     //Call this to update the categories using the caching process 
>     billService.updateBills { result in
>         switch result {
>             case .success(let _):
>             // Continue
>             case .failure(let error):
>                 switch error.type {
>                     case .genericError:
>                         // handle errors 400..499
>                     case .networkError:
>                         // handle network error
>                     case .noUserError:
>                         // handle no user error
>                     case .invalidRefreshTokenError:
>                         // handle error when access token could not be refreshed
>                     default:
>                         // handle unknown error
>                 }
>         }
>     }
> ```
>

### Add receipt with qr code

Anybill allows adding receipts to the user's receipt list by scanning a QR Code on the screen of a point of sale. Using an in app QR Code Scanner (recommended), you can retrieve the new receipt ID and add it to the user's receipt list using the anybill sdk. The user can also scan the QR Code using the QR Code Scanner of his device. This will redirect him to a web page with deep links into your app, for which you will have to use the AnybillAppLink feature.

```swift

    //Depending on the QR Code Scanner you are going to receive a String or URL object.
    //
    //Format:
    //  https://getmy.anybill.de/#/bill/b45f4b77-9c3c-454a-8b87-08d8bbd02f7e
    //
    //You can choose to parse and validate the URL before using the sdk or call one of the following methods:

    receiptService.addReceiptByUrl(url: "https://getmy.anybill.de/#/bill/b45f4b77-9c3c-454a-8b87-08d8bbd02f7e"){ result in
        switch result {
            case .success(let receiptId):
                // Continue
            case .failure(let error):
                switch error.type {
                    case .genericError:
                        // handle errors 400..499
                    case .networkError:
                        // handle network error
                    case .noUserError:
                        // handle no user error
                    case .invalidRefreshTokenError:
                        // handle error when access token could not be refreshed
                    default:
                        // handle unknown error
                }
        }            
    }

    billService.addReceiptByUrl(url: URL){ result in
        switch result {
            case .success(let receiptId):
                // Continue
            case .failure(let error):
               switch error.type {
                    case .genericError:
                        // handle errors 400..499
                    case .networkError:
                        // handle network error
                    case .noUserError:
                        // handle no user error
                    case .invalidRefreshTokenError:
                        // handle error when access token could not be refreshed
                    default:
                        // handle unknown error
                }
        }            
    }  

    billService.addReceiptById(receiptId: "b45f4b77-9c3c-454a-8b87-08d8bbd02f7e"){ result in
        switch result {
            case .success(let receiptId):
                // Continue
            case .failure(let error):
               switch error.type {
                    case .genericError:
                        // handle errors 400..499
                    case .networkError:
                        // handle network error
                    case .noUserError:
                        // handle no user error
                    case .invalidRefreshTokenError:
                        // handle error when access token could not be refreshed
                    default:
                        // handle unknown error
                }
        }
                    
    }
```

### Export receipts

**Export receipt as PDF**

To export or save a receipt as PDF call `exportReceiptAsPDF()`. This will generate a Byte Array (`UInt8`) including a PDF of the given receipt. After converting the Array to a File you can save the file on the device or share to other apps.

```swift
    receiptsService.exportReceiptAsPDF(receipt: Receipt) {result in
        switch result {
        case .success(let UInt8Array):
        // Convert to File
        case .failure(let error):
            switch error.type {
                    case .genericError:
                        // handle errors 400..499
                    case .networkError:
                        // handle network error
                    case .noUserError:
                        // handle no user error
                    case .invalidRefreshTokenError:
                        // handle error when access token could not be refreshed
                    default:
                        // handle unknown error
                }
        }     
    }
```

**Export all receipts as zip**

Anybill also provides the possibility to combine all receipts of a user in a .zip file and send it to the registered email address (e.g. if the user would like to delete his account but keep his receipt information). You can either specify receipt Ids or export all receipts. Anonymous AppUsers can not use this feature.

```swift
    receiptsService.exportUserReceipts(receiptIds: [String]?) {result in
        switch result {
        case .success(let UInt8Array):
        // Convert to File
        case .failure(let error):
            switch error.type {
                    case .genericError:
                        // handle errors 400..499
                    case .networkError:
                        // handle network error
                    case .noUserError:
                        // handle no user error
                    case .invalidRefreshTokenError:
                        // handle error when access token could not be refreshed
                    default:
                        // handle unknown error
                }
        }     
    }
```

[Back to top](#)

> **Warning: [DEPRECATED]**
>
> ### Category 
>
> The AnybillBase module provides categories, to categorize bills. 
>
> The category methods are accessible through the CategoryProvider singleton.
>
> ```swift
>   let categoryService = CategoryProvider.shared
> ```
>
> ### Get category list
>
> **Get all categories**
>
> Use the `getAllCategoriesFromApi()` method to retrieve the complete list of categories. When called the categories are cached in a local database.
>
> ```swift
>     billService.getAllCategoriesFromApi { result in
>         switch result {
>             case .success(let categories):
>                 //Continue
>             case .failure(let error):
>                 switch error.type {
>                     case .genericError:
>                         // handle errors 400..499
>                     case .networkError:
>                         // handle network error
>                     case .noUserError:
>                         // handle no user error
>                     case .invalidRefreshTokenError:
>                         // handle error when access token could not be refreshed
>                     default:
>                         // handle unknown error
>                 }
>         }
>     }
> ```
>
> **Get changed categories (recommended)**
>
> Use the `updateCategories()` method to fill an observable object, containing the list of categories. The method first fills the observable object with the cached categories and then updates the list with the newly modified/added categoires as soon as the API operation completed.
>
> ```swift
>
>     //Observe changes on the observable object
>     private let categoryService = CategoryProvider.shared
>     private var cancellables = Set<AnyCancellable>() 
>
>     categoryService.$observableCategories
>                     .sink { [weak self] in
>                         self?.setCategories(bills: $0)
>                     }
>                     .store(in: &cancellables)
>
>     //Call this to update the categories using the caching process 
>     categoryService.updateCategories { result in
>         switch result {
>             case .success(let _):
>             // Continue
>             case .failure(let error):
>                 switch error.type {
>                     case .genericError:
>                         // handle errors 400..499
>                     case .networkError:
>                         // handle network error
>                     case .noUserError:
>                         // handle no user error
>                     case .invalidRefreshTokenError:
>                         // handle error when access token could not be refreshed
>                     default:
>                         // handle unknown error
>                 }
>         }
>     }
>
> ```
>
> ### Get specific category
>
> **Get category by Id**
>
> Retrieving a single Category object by its ID can be done with the `getCategoryById()` method.
>
> ```swift
>     categoryService.getCategoryById(categoryId: "f4a97778-4607-49a9-b57c-d798dc3fb586"){ result in
>         switch result {
>             case .success(let category):
>                 // Continue
>             case .failure(let error):
>                switch error.type {
>                     case .genericError:
>                         // handle errors 400..499
>                     case .networkError:
>                         // handle network error
>                     case .noUserError:
>                         // handle no user error
>                     case .invalidRefreshTokenError:
>                         // handle error when access token could not be refreshed
>                     default:
>                         // handle unknown error
>                 }
>         }
>                     
>     }
> ```
>
>
> **Get category by name**
>
> Retrieving a category by the name of a store, that belongs to the desired category can be done with the `getCategoriesByName()` method. For instance it can be used when creating a ScanBill, where the name of the vendor can be set manually. Using `getCategoriesByName()` the vendor name can be matched with a BillCategory/List of BillCategory which then can be displayed to the user as a suggestion
>
> ```swift
>     categoryService.getCategoriesByName(name: "Aldi") { result in
>         switch result {
>         case .success(let categories):
>             // Continue
>         case .failure(let error):
>             switch error.type {
>                     case .genericError:
>                         // handle errors 400..499
>                     case .networkError:
>                         // handle network error
>                     case .noUserError:
>                         // handle no user error
>                     case .invalidRefreshTokenError:
>                         // handle error when access token could not be refreshed
>                     default:
>                         // handle unknown error
>                 }
>         }
>     }
> ```
>
> [Back to top](#)
>

### Log

The AnybillBase module is used by other modules to log internal errors of the anybill SDK to Datadog. No information of your users or your app are being logged. For initialization instructions take a look at the [Getting started section](./ios_integration)

## AnybillContentArea

The AnybillContentArea module enables the user to fetch a specific content area object.

The methods are accessible through the ContentAreaProvider singleton.

```swift
  let contentAreaService = ContentAreaProvider.shared
```

### Get content area

**Update content area (recommended)**

This is the recommended way of fetching a content area object. First you need to call `updateContentArea()` with the bill id. If the content area is cached you will get the cached content area first and then the updated one as soon as the api call finished. The fetched content areas are stored in an observable dictionary with the billIds as keys.  

```swift

   private let contentAreaService = ContentAreaProvider.shared
    private var cancellables = Set<AnyCancellable>() 

    contentAreaService.$contentAreaDict
                    .sink { [weak self] in
                        if let contentArea = $0[billId] {
                            self?.handleContentArea(contentArea: contentArea)
                        }
                    }
                    .store(in: &cancellables)

    contentAreaService.updateContentArea(contentAreaId: id){ _ in
        // can be used to handle the failure of the api call
    }

```

**Get content area**

Use the `getContentArea()` method to retrieve a content area object. When called the content area is cached in a local database.

```swift
    contentAreaService.getContentArea(billId: String) { result in
        switch result {
            case .success(let contentArea):
                //Continue
            case .failure(let error):
               switch error.type {
                    case .genericError:
                        // handle errors 400..499
                    case .networkError:
                        // handle network error
                    case .noUserError:
                        // handle no user error
                    case .invalidRefreshTokenError:
                        // handle error when access token could not be refreshed
                    default:
                        // handle unknown error
                }
        }
    }
```

[Back to top](#)

> **Warning: [DEPRECATED]**
>
> ## AnybillStore
>
> The AnybillStore module enables the user to fetch shops and vendors which are associated with anybill.
>
> The methods are accessible through the StoreProvider singleton.
>
> ```swift
>   let storeService = StoreProvider.shared
> ```
>
> ### Get stores
>
> **Get all stores**
>
> Use the `getAllStoresFromApi()` method to retrieve the complete list of stores. When called the stores are cached in a local database.
>
> ```swift
>     storeService.getAllStoresFromApi { result in
>         switch result {
>             case .success(let stores):
>                 //Continue
>             case .failure(let error):
>                 switch error.type {
>                     case .genericError:
>                         // handle errors 400..499
>                     case .networkError:
>                         // handle network error
>                     case .noUserError:
>                         // handle no user error
>                     case .invalidRefreshTokenError:
>                         // handle error when access token could not be refreshed
>                     default:
>                         // handle unknown error
>                 }
>         }
>     }
> ```
>
> **Get changed stores (recommended)**
>
> Use the `updateStores()` method to fill an observable object, containing the list of stores. The method first fills the observable object with the cached stores and then updates the list with the newly modified/added stores as soon as the API operation completed.
>
> ```swift
>
>     //Observe changes on the store observable object
>     private let storeService = StoreProvider.shared
>     private var cancellables = Set<AnyCancellable>() 
>
>     storeService.$observableStores
>                     .sink { [weak self] in
>                         self?.setStores(bills: $0)
>                     }
>                     .store(in: &cancellables)
>
>     //Call this to update the stores using the caching process 
>     storeService.updateStores { result in
>         switch result {
>             case .success(let _):
>             // Continue
>             case .failure(let error):
>                 switch error.type {
>                     case .genericError:
>                         // handle errors 400..499
>                     case .networkError:
>                         // handle network error
>                     case .noUserError:
>                         // handle no user error
>                     case .invalidRefreshTokenError:
>                         // handle error when access token could not be refreshed
>                     default:
>                         // handle unknown error
>                 }
>         }
>     }
> ```
>
> ### Get store details
>
> **Get details about a specific store**
>
> Use the `getStoreDetails()` method to fetch information about a specific store.
>
> ```swift
>     storeService.getStoreDetails(storeId: String) { result in
>         switch result {
>             case .success(let storeDetails):
>             // Continue
>             case .failure(let error):
>                 switch error.type {
>                     case .genericError:
>                         // handle errors 400..499
>                     case .networkError:
>                         // handle network error
>                     case .noUserError:
>                         // handle no user error
>                     case .invalidRefreshTokenError:
>                         // handle error when access token could not be refreshed
>                     default:
>                         // handle unknown error
>                 }
>         }
>     }
> ```
>
> [Back to top](#)
>
>
