# iOS Integration

Getting Started
Push Notificiations
AnybillCore
AnybillAuth
AnybillBill
AnybillCategory
AnybillLog
AnybillExpenses
  • Coming soon
AnybillOCR
AnybillTree
AnybillWarranty
AnybillDocsManager
AnybillPockets
AnybillYoli
AnybillUI

# Getting Started

# 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 following modules are required for the basic usage of the SDK: AnybillCore, AnybillAuth, AnybillBill, AnybillCategory and AnybillLog)

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

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 'AnybillLog'
  pod 'AnybillCore'
  pod 'AnybillAuth',
  pod 'AnybillBill'
  pod 'AnybillCategory'

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

# Instantiate the log module in your AppDelegate file

In your AppDelegate file import AnybillLog and add LoggerDD.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 of the corresponding module.

import AnybillAuth

class ViewController: UIViewController {

    let authService = AuthServiceFactory.shared.create()

    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:
                    let moyaError = error as! MoyaError
                    switch moyaError.response?.statusCode {
                        case 400:
                            // 400 response error handling
                        case 500:
                            // 500 response error handling
                        ...
                    }
            }
        }
    }
}

# 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 of Push Notifications

# Integration without firebase already being integrated in your project

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

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

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:


@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


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

...
}

Back to top

# AnybillCore

The AnybillCore module contains several constansts like the api urls, which are used in the other Anybill modules.

# AnybillAuth

The AnybillAuth module handles the authentication in the SDK.

The authentication methods are accessable through the AuthProvider.

Initialize the AuthProvider using the AuthServiceFactory

  let authService = AuthServiceFactory.shared.create()

# 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).

    authService.loginUser(username: username, password: password) { result in
        switch result {
            case .success(()):
                //Continue
            case .failure(let error):
                //Error Message
        }
    }

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.

    authService.createAnonymousUser { result in
        switch result {
            case .success(let anonymousUser):
                //Continue
            case .failure(let error):
                //Error Message
        }
    }

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
    let tokenUser = TokenUser.init(accessToken: accessToken, expiresIn: expiresIn, refreshToken: refreshToken)
    AuthProvider.loginTokenUser(tokenUser){ result in
        switch result {
            case .success(()):
                //Continue
            case .failure(let error):
                //Error Message
        }
    }
    

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

# 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.

    authService.preregisterUser(email: email, firstname: firstname) { result in
        switch result {
        case .success:
            //Continue
        case .failure(let error):
            //Error Message
        }
    }
    authService.finalizeRegistration(email: email, code: validationCode, password: password, autoLogin: autoLogin) { result in
        switch result {
        case .success(let registerUserResponse):
            //Continue
        case .failure(let error):
            //Error Message
        }
    }

Register Anybill User (deprecated)

To register a new anybill user use the registerUser() method. It requires a valid email, a password which fulfills anybill's password policy (at least 8 characters with 1 lowercase, 1 capital letters and 1 number) and the first name of the user. Other information of the user is optional. By setting autoLogin to true the user is going to be automatically logged in after a successful registration (default = false).

    authService.registerUser(email: email, firstname: firstname, gender: gender, password: password, lastname: lastName, birthday: birthday, autoLogin: autoLogin) { result in
        switch result {
            case .success(let anybillUser):
                //Continue
            case .failure(let error):
                //Error Message
        }
    }

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 bills). 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.

    authService.convertAnonymousUser(email: email, password: password, firstname: firstname, gender: gender) { result in
        switch result {
            case .success:
                //Continue
            case .failure(let error):
                //Error Message
        }
    }

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 bills, authentication information and app settings (of the anybill sdk).

    authService.logoutUser { result in
        switch result {
            case .success:
                //Continue
            case .failure(let error):
                //Error Message
        }
    }

# Delete Account

Deleting an anybill account is irreversibly. The account can not be retrieved afterwards.

    authService.deleteCurrentUser { result in
        switch result {
            case .success:
                //Continue
            case .failure(let error):
                //Error Message
        }
    }

# Get QR Code Data

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

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

    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):
                //Error Message
        }
    }

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.

# Profile Picture

The upload method takes the image as a Data object, so before passing the image as a prarameter it has to be converted.

The following code shows an example how an image can be uplaoded

    let image = Image(named: "ProfilePicture")
    let imageData = image?.jpegData(compressionQuality: 1.0)
    authService.uploadProfilePicture(image: imageData!) { result in
        switch result {
        case .success:
            //Continue
        case .failure(let error):
            //Error Message
        }

    }

Back to top

# AnybillBill

The AnybillBill module enables the user to manage his bills.

The methods are accessable through the BillProvider.

Initialize the BillProvider using the BillServiceFactory

  let billService = BillServiceFactory.shared.create()

# Get bill list

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.

    billService.getAllBillsFromApi { result in
        switch result {
            case .success(let bills):
                //Continue
            case .failure(let error):
                //Error Message
        }
    }

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.

    //Observe changes on the observable object using NotificationCenter
    NotificationCenter.default.addObserver(self, selector: #selector(recieveBillObservable(_:)), name: NSNotification.Name(rawValue: ApiConfig.LIVE_DATA_OBSERVER), object: nil)

    // This gets executed every time the bills object changes
    @objc func recieveBillObservable(_ notification: Notification){
        if let bills = notification.userInfo?[BillConfig.BILL_OBSERVABLE_TAG] {
            // Continue using categories
        }
    }

    //Call this to update the categories using the caching process 
    billService.updateBills { result in
        switch result {
            case .success(let _):
            // Continue
            case .failure(let error):
            // Error Message
        }
    }

# Get vendor/store list

Get vendor or store list

The fetching, caching and update process of the vendor/store lists are similar to the one of the bill list (see above for more detail). Use updateStores() and updateVendors() respectively to fill the observable objects with the updated information. Analogous to the bill process, you can use getAllStoresFromApi() or getAllVendorsFromApi() to get the list of stores/vendors without the use of an observable object.

# Add bill with qr code

Anybill allows adding bills to the user's bill 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 bill ID and add it to the user's bill 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 register Universal Links in your app.


    //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:

    BillService.addBillByUrl(url: "https://getmy.anybill.de/#/bill/b45f4b77-9c3c-454a-8b87-08d8bbd02f7e"){ result in
        switch result {
            case .success(let billId):
                // Continue
            case .failure(let error):
                // Error Message
        }            
    }

    BillService.addBillByUrl(url: URL){ result in
        switch result {
            case .success(let billId):
                // Continue
            case .failure(let error):
                // Error Message
        }            
    }  


    BillService.addBillById(billId: "b45f4b77-9c3c-454a-8b87-08d8bbd02f7e"){ result in
        switch result {
            case .success(let billId):
                // Continue
            case .failure(let error):
                // Error Message
        }
                    
    }

# Export bills

Export bill as PDF

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

    billsService.exportBillAsPDF(bill: Bill) {result in
        switch result {
        case .success(let UInt8Array):
        // Convert to File
        case .failure(let error):
        // Error Message
        }     
    }

Export all bills as zip

Anybill also provides the possibility to combine all bills 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 bill information). You can either specify Bill Ids or export all Bills. Anonymous AppUsers can not use this feature.

exportUserBills
    billsService.exportUserBills(billIds: [String]?) {result in
        switch result {
        case .success(let UInt8Array):
        // Convert to File
        case .failure(let error):
        // Error Message
        }     
    }

Back to top

# AnybillCategory

The AnybillCategory module provides categories, to categorize bills.

The category methods are accessable through the CategoryProvider.

Initialize the CategoryProvider using the CategoryServiceFactory

  let categoryService = CategoryServiceFactory.shared.create()

# 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.

    billService.getAllCategoriesFromApi { result in
        switch result {
            case .success(let categories):
                //Continue
            case .failure(let error):
                //Error Message
        }
    }

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.


    //Observe changes on the observable object using NotificationCenter
    NotificationCenter.default.addObserver(self, selector: #selector(recieveCategoryObservable(_:)), name: NSNotification.Name(rawValue: ApiConfig.LIVE_DATA_OBSERVER), object: nil)

    // This gets executed every time the categories object changes
    @objc func recieveCategoryObservable(_ notification: Notification){
        if let categories = notification.userInfo?[CategoryConfig.CATEGORY_OBSERVABLE_TAG] {
            // Continue using categories
        }
    }

    //Call this to update the categories using the caching process 
    categoryService.updateCategories { result in
        switch result {
            case .success(let _):
            // Continue
            case .failure(let error):
            // Error Message
        }
    }

# Get specific category

Get category by Id

Retrieving a single Category object by its ID can be done with the getCategoryById() method.

    categoryService.getCategoryById(categoryId: "f4a97778-4607-49a9-b57c-d798dc3fb586"){ result in
        switch result {
            case .success(let category):
                // Continue
            case .failure(let error):
                // Error Message
        }
                    
    }

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

    categoryService.getCategoriesByName(name: "Aldi") { result in
        switch result {
        case .success(let categories):
            // Continue
        case .failure(let error):
            // Error message
        }
    }

Back to top

# AnybillLog

The Log 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

# AnybillExpenses

Coming soon

# AnybillOCR

The AnybillOCR module can be used to scan paper receipts.

The OCR scan methods are accessable through the OCRProvider.

Initialize the OCRProvider using the OCRServiceFactory

  let ocrService = OCRServiceFactory.shared.create()

# Adding a bill using the OCR scan

Adding a bill with anybill's OCR scan requires 2 steps:

  1. Upload image of a paper receipt
  2. Edit and update the values generated by the OCR Scan

Uploading image of paper receipt

To upload an image of the paper receipt use the addBillWithOCRScan() function with an InputStream of the image which should be scanned.

    let image = Image(named: "ScanBill")
    let imageData = image.jpegData(compressionQuality: 1.0)
    let inputStream = InputStream(data: imageData)
    ocrService.addBillWithOCRScan(inputStream: inputStream) { result in
        switch result {
        case .success(let scanBillDto):
            //Continue editing scanBillDto
        case .failure(let error):
            //Error message
    }
    }

OCR Scan can take up to 20 seconds.

Editing and finalizing the new bill

The returned ScanBillDTO contains all values which were recognized by the OCR Scan. These values can now be edited and/or extended.

    scanBillDTO.vendorName = "YourVendor"
    scanBillDTO.totalGrossAmount = 15.50
    scanBillDTO.addLineItem(lineItem: lineItem)

    ocrService.updateScannedBill(billDTO: scanBillDTO) { result in
        switch result {
        case .success(let scanBill):
            //Continue with finalized bill
        case .failure(let error):
            //Error message
        }
    }

Back to top

# AnybillTree

The AnybillTree module offers a „tree level system“ to enhance the motivation of the user by planting trees for scanned receipts.

The AnybillTree methods are accessable through the TreeProvider.

Initialize the TreeProvider using the TreeServiceFactory

  let treeService = TreeServiceFactory.shared.create()

# Get tree levels

To retrieve a list of all TreeLevels use the getTotalTreeLevel() functions

    treeService.getTotalTreeLevels { result in
        switch result {
            case .success(let treeLevels):
                //Continue
            case .failure(let error):
                //Error Message
        }
    }

# Get trees planted by the user

Returns the amount of trees which were planted by the user's bills

    treeService.getTreesPlantedByUser{ result in
        switch result {
            case .success(let amountOfTrees):
                //Continue
            case .failure(let error):
                //Error Message
        }
    }

# Retrieve complete tree info

Information like the amount of trees planted by the user, the total amount of trees planted and the tree levels can all be accessed separately. By calling getTreeInfo() all this information can be retrived within one call.

    treeService.getTreeInfo { result in
        switch result {
            case .success(let treeInfo):
                //Continue
            case .failure(let error):
                //Error Model
        }
    }

Back to top

# AnybillWarranty

The AnybillWarranty module notifies the user about due warranties.

The AnybillWarranty methods are accessable through the WarrantyProvider.

Initialize the WarrantyProvider using the WarrantyServiceFactory

  let warrantyService = WarrantyServiceFactory.shared.create()

# Retrieve and update warranty list

With its caching technique the anybill sdk stores the warranties in a local database and updates them using the anybill backend when needed. The anybill sdk provides an observable object representing a list of warranties which can be observed in your app. By calling WarrantyProvider's updateWarranties() method the observable object gets filled with the locally cached warranties first and updated with the newly modified/added warranties as soon as the API operation completed.

Sample-Code for using the observable object:


    //Observe changes on the observable object using NotificationCenter
    NotificationCenter.default.addObserver(self, selector: #selector(recieveWarrantyObservable(_:)), name: NSNotification.Name(rawValue: ApiConfig.LIVE_DATA_OBSERVER), object: nil)

    // This gets executed every time the warranties object changes
    @objc func recieveWarrantyObservable(_ notification: Notification){
        if let warranties = notification.userInfo?[WarrantyConfig.Warranty_OBSERVABLE_TAG] {
            // Continue using warranties
        }
    }

    //Call this to update the warranties using the caching process 
    warrantyService.updateWarrantyLiveData { result in
        switch result {
            case .success(let _):
            // Continue
            case .failure(let error):
            // Error Message
        }
    }

If you don't want to use observable objects, you can use getAllWarranties()which will return all Warranties of the user.

# Getting a warranty by id

Retrieving a single Warranty object by its ID can be done with the getWarrantyById() functions.

    warrantyService.getWarrantyById(warrantyId: "f4a97778-4607-49a9-b57c-d798dc3fb586"){ result in
        switch result {
            case .success(let warranty):
                // Continue
            case .failure(let error):
                // Error Message
        }
                    
    }

# Adding a new warranty

Using the WarrantyDTO struct, you can create a new warranty for a user's bill:

* `articleName`:    Specifies the article's name        (Optional)
* `billId`:         The bill's id                       (Required)
* `lineItemId`:     The lineitem's id                   (Optional)
* `warrantyDate`:   Warranty's due date in ISO 8601     (Required)
* `reminders`:      Reminder object                     (Optional)

Each LineItem and articleName can only have one Warranty. The warranty's due date can not be in the past.

    let warranty = WarrantyDTO(
                    articleName: "Apple", 
                    billid: "ce763c6a-4ae8-4l7c-df7f-08d9dfc2g23a", 
                    lineItemId: nil, 
                    warrantyDate: "2021-12-31T14:00:00Z", 
                    reminders: [])

    warrantyService.addWarranty(warranty: warranty) {  result in
        switch result {
            case .success(let warranty):
                //Continue
            case .failure(let error):
                //Error Message
        }
    }

# Update notifications setting for a certain warranty

Notifications can be enabled or disabled for a certain warranty with updateWarrantyNotificationIsActivated()

    warrantyService.updateWarrantyNotificationIsActivated(warrantyId: "ce763c6a-4ae8-4l7c-df7f-08d9dfc2g23a", notificationsIsActivated: true) { result in
            switch result {
            case .success(let warranty):
                // Continue
            case .failure(let error):
                // Error Message
            }
        }
    }

Back to top

# AnybillDocsManager

The AnybillDocsManager module allows the user to export and manage his bills with the third-party document managers fileee, gmi and datev.

The AnybillDocsManager methods are accessable through the DocsManagerProvider.

Initialize the DocsManagerProvider using the DocsManagerServiceFactory

  let docsManagerService = DocsManagerServiceFactory.shared.create()

# Fileee

Fileee enables mutliple document related feature:

  • Export receipts to fileee directly and automatically with one click
  • Link email accounts and services such as Dropbox or GoogleDrive
  • All private documents in a single overview
  • Share receipts and documents with your family and friends

Link fileee account with anybill account

Connecting a fileee account with an anybill account can be done with connectFileeeAccount(), which opens an inapp browser, where the user can login to an existing fileee account or create a new one and then authorize the connection to the anybill account.

    docsManagerService.connectFileeeAccount(presenting: self) { result in
        switch result {
        case .success(let fileeeAccountInformation):
            // Continue
        case .failure(let error):
            // Error Message
        }
    }

Export bills to Fileee account

This method can be called to link a specified list of bills with a user's Fileee account.

    docsManagerService.exportBillsToFileeeAccount(bills: [bill ids]) { result in
        switch result {
        case .success(let bills):
            // Continue
        case .failure(let error):
            // Error Message
        }
    }

# GMI

GetMyInvoice is a central invoice management software for your business with several features:

  • Invoice management with automatic download
  • Export receipts to GetMyInvoices directly and automatically with one click
  • Overview of all exported receipts
  • Export to many accounting tools

With the anybill sdk anybill accounts can either be binded with an existing GMI Account or connected by creating a new GMI Account.

Note: The methods getGmiApiKey(), getGmiAccountInformation(), deleteLinkedGmiAccount(), activateLinkedGmiAccount(), deactivateLinkedGmiAccount() are only available, if the gmi account was created with the createGmiAccount() method.

Create GMI account

Create and link a gmi account to the user account with createGmiAccount()

    docsManagerService.createGmiAccount(companyName: companyName, country: countryCode, email: email, firstname: firstname, language: GmiLanguage, package: package, lastname: lastname, password: password, externalId: externalId) { result in
        switch result {
        case .success:
            // Continue
        case .failure(let error):
            // Error Message
        }
    }

Binding with an existing GMI Account

To bind an anybill account with an existing GMI Account, the user has to manually copy an API Key from its GMI Account:

To access the API Key the user has to follow these steps:

  1. Login on the GMI page: https://de4-login.getmyinvoices.com/api_access.php
  2. Click on the plus symbol in the top left corner
  3. Select your name
  4. Click Save
  5. Copy the API Key shown next to your name

Link both accounts by using updateGMIAccountApiKey(apiKey: String) with the received API Key as parameter.

Check the link status of the connected gmi account

The status of the link between the anybill account and the gmi account can be checked with checkGmiAccountLinkStatus()

    docsManagerService.checkGmiAccountLinkStatus { result in
        switch result {
        case .success(let status):
            // Continue
        case .failure(let error):
            // Error Message
        }
    }

The method callback contains an AccountLinkStatus struct


public enum AccountLinkStatus: Int, Decodable, Encodable {
    case NotLinked
    case LinkedWithApiKey
    case LinkedWithoutApiKey
    case ApiKeyOnly
}

Update/set GMI api key

The method can be called to link a gmi account to an anybill account with the gmi api key initially or update the api key if needed.

Note: As stated before some methods are not available when the account is linked with the api key only

    docsManagerService.updateGmiAccountApiKey(apiKey: apiKey) { result in
        switch result {
        case .success:
            // Continue
        case .failure(let error):
            // Error Message
        }
    }

Export Bills to GMI

This method can be called to link a specified list of bills with a user's GMI account.

    docsManagerService.exportBillsToGmi(bills: [bill ids]) { result in
        switch result {
        case .success(let bills):
            // Continue
        case .failure(let error):
            // Error Message
        }
    }

# AnybillPockets

The AnybillPockets module allows to group bills in pockets.

The AnybillPockets methods are accessable through the PocketsProvider.

Initialize the PocketsProvider using the PocketsServiceFactory

  let pocketsService = PocketsServiceFactory.shared.create()

To link a bill with a pocket use the linkBillWithPocket() method.

    pocketsService.linkBillWithPocket(pocketId: pocketId, billId: billId) { result in
        switch result {
        case .success:
            // Continue
        case .failure(let error):
            // Error Message
        }
    }

# Getting a pocket by id

Retrieving a single Pocket object by its ID can be done with the getPocketById() function.

    pocketsService.getPocketById(pocketId: pocketId){ result in
        switch result {
            case .success(let pocket):
                // Continue
            case .failure(let error):
                // Error Message
        }
                    
    }

Back to top

# AnybillYoli

The AnybillYoli module allows to group bills in pockets.

The AnybillYoli methods are accessable through the YoliProvider.

Initialize the YoliProvider using the YoliServiceFactory

  let yoliService = YoliServiceFactory.shared.create()

# Managing the bank accounts with Yoli

There are several steps necessary to create new bank account connections and manage existing ones.

  1. Implement the manageBankAccounts(addBankLogin: Bool) function, which returns a redirectUri
  2. Open the redirectUri in our WebViewViewController(request: String, observerName: String), which listens for url changes and sends those via the NotificationCenter. An example implementation, using our WebView can be seen below.
yoliService.manageBankAccounts(addBankLogin: false){ result in
    switch result {
    case .success(let url):
        let request = URLRequest(url: url)
        self.viewController = WebViewViewController(request: request, observerName: self.observerName)
        let navVC = UINavigationController(rootViewController: self.viewController!)
        self.present(navVC, animated: true)
    case .failure(let error):
        print("Failed to get fino uri: ", error)
    }
}

You can then use the same observerName, you set in the WebViewViewController in the NotificationCenter observer to listen for the url changes.

NotificationCenter.default.addObserver(self, selector: #selector(recieveUrlChange(_:)), name: NSNotification.Name(rawValue: observerName), object: nil)

Note: The observed url is saved in the userInfo attribute of the notification in form of a dictionary with key "urlValue" (see below)

if let url = notification.userInfo?["urlValue"] {
   Continue with step four...
}
  1. The last step is to implement the handleObservedWebViewUrl(url: String, webViewController: UIViewController), which takes the observed url from step three and the UIViewController of the WebView as parameters.

Back to top

# Sync bank accounts

For security reasons the connected bank accounts will be flagged as unsync after 30 days, then the account has to be synced again. This can be achieved with the syncBankAccount(bankLoginId: String) method.

Please follow the steps of the manageBankAccounts() method, as the workflow is the same.

# AnybillUI

The AnybillUI module is an easy integratable ui extension to your app, which allows your users to manage their anybill bills.

# Integrate ui module

To integrate the anybill ui module to your app, simply instatiate the AnybillSDKApp View Controller and present it in your desired position in your app. You can use the load method of the AnybillSDKApp to add your app color to the anybill ui module.

Modal fullscreen instantiation:

        let sdkUI = AnybillSDKApp(nibName: "AnybillSDKApp", bundle: Bundle(identifier: "com.anybill.AnybillUI"))
        sdkUI.title = "Anybill"
        sdkUI.modalPresentationStyle = .fullScreen
        sdkUI.load(color: UIColor.blue)
        self.present(sdkUI, animated: true, completion: nil)

Example integration to a UITabBarController:

    public override func viewDidLoad() {
        super.viewDidLoad()

        let yourVC = YourViewController()
        let icon1 = UITabBarItem(title: "YourIcon", image: Image(named: "yourIcon"), tag: 0)
        yourVC.tabBarItem = icon1

        ...

        let anybillVC = AnybillSDKApp(nibName: "AnybillSDKApp", bundle: Bundle(identifier: "com.anybill.AnybillUI"))
        anybillVC.load(color: UIColor.orange)
        let icon2 = UITabBarItem(title: "Anybill", image: Image(named: "anybill"), tag: 0)
        anybillVC.tabBarItem = icon2

        let controllers = [yourVC, ..., anybillVC]
        self.viewControllers = controllers
        
    }

Note: The ui module needs multiple permissions to work properly. Therefore the following permissions have to be added to your application's plist.info file:

# Update data on appstart

Optionally you can add SDKData().updateAllData() to your application(_:didFinishLaunchingWithOptions:) method in the AppDelegate file to update the anybill data on the appstart.