LogoLogo
CMSGitHubSupportBook a demo
  • Documentation
  • Academy
  • Help Center
  • Welcome
  • SDKS & Frameworks
    • Web
      • Getting Started
        • Prerequisites
          • MapsIndoors
          • Map Engine Provider
            • Option 1: Get your Mapbox Access Token
            • Option 2: Get your Google Maps API Keys​
          • Map Engine Setup
        • Getting Started: MapsIndoors
      • Map Visualization
        • Highlight, Hover and Select
        • Remove Labels from Buildings and Venues
        • Change Building Outline
        • Managing Collisions Based on Zoom Level
        • 3D Maps
          • Managing your 3D Maps
        • Base Map Styling - Google Maps
        • Managing feature visibility for Mapbox
      • Wayfinding
        • Directions
        • Directions Service
          • Tailoring the directions to your specific needs
        • Directions Renderer
          • Customizing the Route Animation
        • Multi-stop navigation
          • Custom Icons
        • User's Location as Point of Origin
      • Search
        • Search Operations
        • Searching
        • Using External ID, Geospatial Joins
        • Utilizing MapsIndoors Web Components and Other Searches
      • Map Management
      • Data Visualization
        • Display Heatmap Overlay
      • Other guides
        • Authentication
          • Single Sign-On
            • SSO Configuration
            • SSO Authorisation
          • 2-Factor Authentication
          • Password Reset
        • Application User Roles
        • Custom Properties
        • Display Language
        • Language
        • User Positioning
          • Show User's Location aka. Blue Dot
          • Using Cisco DNA Spaces
        • Working with Events
        • Turn Off Collisions Based on Zoom Level
        • Remove Labels from Buildings and Venues for Web
        • Synchronizing data for a subset of venues
        • Custom Floor Selector
      • Display Rules in Practice
      • Offline Data
      • Managing map visibility
    • Android
      • Getting Started
        • Prerequisites
        • Create a New Project
        • Show a Map
        • Create a Search Experience
        • Getting Directions
        • Enable Live Data
        • Integrating MapsIndoors into your own App
        • Migrating from V3 to V4
          • Migrating to Mapbox V11
      • Directions
        • Directions Service
        • Directions Renderer
          • User's Location as Point of Origin
        • Wayfinding Instructions
          • See Route Element Details
        • Using multi-stop navigation
      • Searching
        • Searching on a Map
        • Creating a Search Experience
      • Switching Solutions
      • Caching & Offline Data
      • Display Language
      • Displaying Objects
        • Application User Roles
        • Getting a Polygon from a Location
        • Location Clustering
        • Location Data Sources
        • Location Details
        • Turn Off Collisions Based on Zoom Level
        • Enabling and Disabling features on the map
      • Change Building Outline Color
      • Event Logging
      • Configuring a menu with AppConfig
      • Display Heatmap Overlay
      • Custom Properties
      • Custom Floor Selector
      • External IDs
      • User Positioning
        • Show User's Location aka. Blue Dot
        • Using Cisco DNA Spaces
        • Using Google Fused Location Provider
        • Using Indoor Atlas
      • Authentication
        • Single Sign-On
          • SSO Configuration
          • SSO Authorisation
        • 2-Factor Authentication
        • Password Reset
      • Display Rules in Practice
        • Label styling through Display Rules
      • Highlight and Select
    • iOS
      • Getting Started
        • Prerequisites
        • Set Up Your Environment
        • Display a Map
        • Search
        • Getting Directions
        • Migrating from v3 to v4
      • Directions
        • Directions Renderer
          • User's Location as Point of Origin
        • Wayfinding Instructions
          • See Route Element Details
        • Directions Service
        • Using multi-stop navigation
      • Searching
        • Searching on a Map
        • Creating a Search Experience
      • Caching & Offline Data
      • Displaying Objects
        • Application User Roles
        • Getting a Polygon from a Location
        • Location Details
        • Turn Off Collisions Based on Zoom Level
        • Enabling and Disabling features on the map
      • Custom Floor Selector
      • Change Building Outline Color
      • Custom Map Padding
      • Custom Properties
      • Display Rules in Practice
        • Label styling through Display Rules
      • Switching Solutions
      • Show User's Location aka. Blue Dot
        • Using Indoor Atlas
        • Using Cisco DNA Spaces
      • Highlight and Select
      • Display Language
    • React Native
      • Getting Started
        • Prerequisites
        • Project Setup
        • Displaying a Map
        • Creating a Search Experience
        • Getting Directions
        • Enabling Live Data
      • Showing Blue Dot
    • Flutter
      • Getting Started
        • Prerequisites
        • Create a New Project
        • Show a Map
        • Create a Search Experience
        • Getting Directions
      • Migration Guide
    • Integration API
      • Integration API Access
        • Access with Swagger
        • Access with Postman
        • Access with Python
        • Client credentials flow
      • Data Description
      • Reverse Geocoding
      • Route Access
      • OpenAPI Specification
    • Built-In Map Edits
      • Getting started
      • Authentication
      • Release notes
      • Reference docs
  • Products
    • Product Overview
    • CMS
      • Interface Overview
      • Display Rules
      • Media Library
        • 2D Models and Icons
        • 3D Models
      • Editing Data
      • Solution Settings
      • Settings
      • Data Concepts
      • User Roles
      • Route Network
        • Barrier Route Element
        • Door Route Element
      • Additional Location Details
    • Map Template
      • Getting Started
        • Web Component
        • React Component
      • Configuration
        • Query Parameters
      • Customization
      • Deploying Map Template to a cloud storage provider
      • 2D/3D Visibility Switch
      • External customization of the Map Template
      • Location Details configuration
      • Kiosk
        • QR code configuration
  • Other
    • Design
      • Standard MapsIndoors Map Style
      • Using a Custom Mapbox MapStyle
    • Changelog
      • Web SDK
        • V4
        • V3
      • Android SDK
        • V4
        • V3
      • iOS SDK
        • V4
        • V3
      • React Native SDK
      • Flutter SDK
      • MI Components
      • Map Template
    • Glossary
  • Legacy Docs
    • Android SDK V3
      • Getting Started
        • Prerequisites
        • Create a New Project
        • Show a Map
        • Create a Search Experience
        • Getting Directions
        • Enable Live Data
        • Integrating MapsIndoors into your own App
    • iOS SDK V3
      • Getting Started
        • Prerequisites
        • Set Up Your Environment
        • Display a Map
        • Search
        • Directions
        • Live Data
        • Integrating MapsIndoors into your own App
      • Inspect Route Element for iOS v3
      • Using Cisco DNA Spaces
      • Using Indoor Atlas
      • Switching Solutions
      • Show User's Location aka. Blue Dot
      • Application User Roles
      • Getting a Polygon from a Location
      • Location Details
  • MapsIndoors SDK Firewall
  • Google Analytics & Logging
  • Reference Docs
    • Web SDK
    • Android SDK
    • iOS SDK
    • React Native SDK
    • Flutter SDK
Powered by GitBook
On this page
  • Bookable Locations​
  • Booking in Practice​
  • Listing Bookable Locations​
  • Listing Bookings for a Location​
  • Editing, Performing and Cancelling Bookings​

Was this helpful?

Export as PDF
  1. SDKS & Frameworks
  2. iOS

Booking

iOS v4

Last updated 1 year ago

Was this helpful?

This guide covers the different aspects of Booking in the MapsIndoors iOS SDK. The concept of Booking in MapsIndoors implies that specific Locations in your MapsIndoors dataset is treated as Bookable resources. Typical bookable resources could be meeting rooms and workspaces.

A MapsIndoors dataset can only have bookable resources if an integration with a Booking provider exists. Current examples of Booking providers are Google Calendar and Microsoft Office 365. These providers and more can be added and integrated to your MapsIndoors project by request.

The central service in the SDK managing Bookings is the Booking Service.

MPMapsIndoors.shared.bookingService

The Booking Service can help with the following tasks:

By default, the MPBookingService performs anonymous Bookings using a service account known to MapsIndoors. However, it is also possible to list, perform and cancel Bookings .

Bookable Locations

To determine whether or not a Location is bookable can be looked up using the MPMapsIndoors.shared.bookingService. bookableLocationsUsing(query: MPBookableQuery) method. Below is an example of querying for bookable Locations:

let startTime = Date()
let endTime = startTime.advanced(by: 60*60)
let bookableQuery = MPBookableQuery(startTime: startTime, endTime: endTime)
let bookingService = MPMapsIndoors.shared.bookingService

if let locations = try? await bookingService.bookableLocationsUsing(query: bookableQuery) {
    
}

The above example creates a query for Locations that are bookable for a timespan between now and 1 hour ahead.

It is also possible to check a location statically using MPLocation.isBookable, but please note that this information is not a dynamic property reflecting the current bookable state from the Booking Service. If MPLocation.isBookable is true it means that the Location has a potentially bookable resource known by the integrated Booking provider, but still it might be booked for a specific time.

It is possible execute a booking creation request using the MPMapsIndoors.shared.bookingService.performBooking(MPBooking) method which takes a booking object as input. If the booking is successfully performed, the booking will return in the block with an assigned bookingId.

do {
    let booking = MPBooking()
    booking.location = bookableLocation
    booking.title = "Meeting"
    booking.startTime = Date()
    booking.participantIds = ["myemail@email.com"]
    booking.endTime = booking.startTime!.addingTimeInterval(60*60)
    try await MPMapsIndoors.shared.bookingService.performBooking(booking) 
} catch {
    let alert = UIAlertController(title: "Ooops!", message: "\(error)", preferredStyle: .alert)
    alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
    self.present(alert, animated: true)
}

In the above example a Booking object is created and several properties are assigned:

  • The related Location object

  • A Title for the Booking

  • Participants for the Event being created through the Booking

  • Start and end time

Depending on the Booking provider, the participants will receive invites for an event created by this Booking request.

It is possible to cancel a created Booking using the MPBookingService.cancel() method which takes an existing Booking object as input.

do {
    if (try await MPMapsIndoors.shared.bookingService.cancelBooking(booking)) != nil {
        let alert = UIAlertController(title: "Booking was cancelled!", message: "Booking was successfully cancelled!", preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
        present(alert, animated: true)
    }
} catch {
    let alert = UIAlertController(title: "Ooops!", message: "\(error)", preferredStyle: .alert)
    alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
    present(alert, animated: true)
}

In this tutorial we will create a Booking experience using the Booking Service in MapsIndoors. You will learn how to list bookable Locations, list Bookings for a Location and perform new Bookings using the Booking Service, MPBookingService.

Please note that a MapsIndoors dataset can only have bookable resources if an integration with a booking provider exists. Current examples of booking providers are Google Calendar and Microsoft Office 365. These providers, and more, can be added and integrated to your MapsIndoors project by request. It is a prerequisite for this tutorial that the API key used refers to a dataset containing bookable Locations.

We will start by listing bookable Locations. Create a class BookableLocationsController inheriting from UITableViewController.

class BookableLocationsController: UITableViewController {

Create a private property that should hold our bookable Locations.

private var bookableLocations = [MPLocation]()

In your viewDidAppear() method,

  1. Initialise a MPBookableQuery object with a timespan for your potential Booking.

  2. Call getBookableLocations() on the MPBookingService instance.

  3. Assign the returned locations to your bookableLocations property.

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Auth", style: .plain, target: self, action: #selector(openAuthConfig))
    self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Login", style: .plain, target: self, action: #selector(openLogin))

    Task {
        await refresh()
    }
}

add a refresh function:

func refresh() async {
    let startTime = Date()
    let endTime = startTime.advanced(by: 60*60)
    let bookableQuery = MPBookableQuery(startTime: startTime, endTime: endTime)

    let bookingService = MPMapsIndoors.shared.bookingService

    if let locations = try? await bookingService.locationsConfiguredForBooking(bookableQuery) {
        locationsConfiguredForBooking = locations
        tableView.reloadData()
    }

    if let locations = try? await bookingService.bookableLocationsUsing(query: bookableQuery) {
        bookableLocations = locations
        tableView.reloadData()
    }
}

In numberOfSections(in:) add:

override func numberOfSections(in tableView: UITableView) -> Int {
    return 2
}

In tableView(_:numberOfRowsInSection:) , return the size of bookableLocations.

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    if section == 0 {
        return bookableLocations.count
    } else {
        return locationsConfiguredForBooking.count
    }
}

In tableView(_:cellForRowAt:), create a UITableViewCell with the current Locations name as the text for the label.

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = UITableViewCell()
    var list = locationsConfiguredForBooking
    if indexPath.section == 0 {
        list = bookableLocations
    }
    let location = list[indexPath.row]
    cell.textLabel?.text = location.name
    return cell
}

In tableView(_:didSelectRowAt:), get the relevant MPLocation for the indexPath. Initialise a BookingsController which we will implement next. Assign the selected Location to a bookableLocation property on BookingsController and push the controller to the navigation stack.

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    let bookingsVC = BookingsController()
    var list = locationsConfiguredForBooking
    if indexPath.section == 0 {
        list = bookableLocations
    }
    let location = list[indexPath.row]
    bookingsVC.bookableLocation = location
    navigationController?.pushViewController(bookingsVC, animated: true)
}

Create a new controller, BookingsController inheriting again from UITableViewController. This controller will list the Bookings for a locations within a timespan, as well as give access to creating new and editing bookings.

class BookingsController: UITableViewController {

Create a public property bookableLocation that will hold the Location we want to book.

var bookableLocation : MPLocation?

Create a private property bookings that can hold the Location's bookings.

private var bookings = [MPBooking]()

In your viewDidLoad() method, initialise a UIBarButtonItem with the title Booktargeting newBooking which we will create later. Add the button to the navigationItem.

let button = UIBarButtonItem(title: "Book", style: .plain, target: self, action: #selector(newBooking))
self.navigationItem.rightBarButtonItem = button

Also in your viewDidLoad() method, initialise a MPBookingsQuerywith the MPLocation stored in bookableLocation and a timespan, in this example 24 hours starting one hour ago.

let bookingsQuery = MPBookingsQuery()
bookingsQuery.location = bookableLocation
bookingsQuery.startTime = Date().advanced(by: -60*60)
bookingsQuery.endTime = bookingsQuery.startTime?.advanced(by: 24*60*60)

Lastly in your viewDidLoad() method, call bookingsUsing(query: bookingsQuery)with the bookingsQuery we just created. Store the returned Bookings in our bookings property.

Task {
    if let bookings = try await MPMapsIndoors.shared.bookingService.bookingsUsing(query: bookingsQuery) {
        self.bookings = bookings
        tableView.reloadData()
    }
}

In numberOfSections(in:), return 1 since we only need one section.

override func numberOfSections(in tableView: UITableView) -> Int {
    return 1
}

In tableView(_:numberOfRowsInSection:), return the size of bookings.

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return bookings.count
}

In tableView(_:cellForRowAt:), create a UITableViewCell with the current Booking title as the text for the label.

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = UITableViewCell()
    let booking = bookings[indexPath.row]
    cell.textLabel?.text = booking.title
    return cell
}

In tableView(_:didSelectRowAt:), get the relevant MPBooking for the indexPath and call editBooking() with that Booking.

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    let booking = bookings[indexPath.row]
    editBooking(booking: booking)
}

Create a method editBooking(booking:). In this mehod, initialise a BookingController which we will implement next. Assign the selected booking to the BookingController and push the controller to the navigation stack.

func editBooking(booking:MPBooking) {
    let bookingVC = BookingController()
    bookingVC.booking = booking
    navigationController?.pushViewController(bookingVC, animated: true)
}

Create an Objective-C exposed method newBooking() which will be use by our UIBarButtonItem created in viewDidLoad(). In the newBooking() mehod, initialise a new MPBooking instance and provide some default values for the Booking. Call the newly created editBooking(booking:) with the Booking instance.

@objc func newBooking() {
    let booking = MPBooking()
    booking.location = bookableLocation
    booking.title = "Meeting"
    booking.startTime = Date()
    booking.participantIds = ["myemail@email.com"]
    booking.endTime = booking.startTime!.addingTimeInterval(60*60)
    editBooking(booking: booking)
}

We need a third controller to display, edit and perform an actual Booking.

We will create an enum model to keep track on the different parts of the MPBooking model displayed through the view controller.

enum BookingRow : Int {
    case title = 0
    case description = 1
    case start = 2
    case end = 3
    case id = 4
    case count = 5
}

Create BookingController inheriting once again from UITableViewController.

class BookingController: UITableViewController {

Create a public property booking that will hold our Booking.

var booking = MPBooking()

Create a method updateButtons() that will either place a book button if there is no bookingId on the Booking, which means it was created locally, or place a cancel button if a bookingId exist for the Booking, which means it was selected from a list of Bookings fetched from the MPBookingService.

private func updateButtons() {
    if booking.bookingId != nil {
        self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Cancel Booking", style: .plain, target: self, action: #selector(cancel))
    } else {
        self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Create Booking", style: .plain, target: self, action: #selector(book))
    }
}

In your viewDidLoad() method, just call the updateButtons() method.

override func viewDidLoad() {
    updateButtons()
}

Create an Objective-C exposed method book() which will be use by our UIBarButtonItem inserted in viewDidLoad(). In the book() mehod, call perform(booking) on the MPBookingService instance with our Booking object as input. If all goes well and we have a Booking returned in the block, we assign this new Booking to our booking propery and refresh our views. If not, we assume that we have an error, and show this in an alert controller.

@objc private func book() async {
    do {
        if let booking = try await MPMapsIndoors.shared.bookingService.performBooking(booking) {
            self.booking = booking
            updateButtons()
            tableView.reloadData()
        }
    } catch {
        let alert = UIAlertController(title: "Ooops!", message: "\(error)", preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
        self.present(alert, animated: true)
    }
}

Create another Objective-C exposed method cancel() which will be use by our UIBarButtonItem inserted in viewDidLoad(). In the cancel() mehod, call cancel(booking) on the MPBookingService instance with our Booking object as input. If all goes well and we have a Booking returned in the block, we assign this new Booking to our booking propery and refresh our views. If not, we assume that we have an error, and show this in an alert controller.

@objc private func cancel() async {
        do {
            if (try await MPMapsIndoors.shared.bookingService.cancelBooking(booking)) != nil {
                let alert = UIAlertController(title: "Booking was cancelled!", message: "Booking was successfully cancelled!", preferredStyle: .alert)
                alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
                present(alert, animated: true)
            }
        } catch {
            let alert = UIAlertController(title: "Ooops!", message: "\(error)", preferredStyle: .alert)
            alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
            present(alert, animated: true)
        }
    }

In numberOfSections(in:), return 1 since we only need one section.

override func numberOfSections(in tableView: UITableView) -> Int {
    return 1
}

In tableView(_:numberOfRowsInSection:), return the value of BookingRow.count.rawValue.

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return BookingRow.count.rawValue
}

In tableView(:cellForRowAt indexPath:), create a UITableViewCell and create a switch control structure by initialising a BookingRow enum value from indexPath.row. Based on the cases, populate the textLabel with title, bookingDescription, startTime, endTime and bookingId from your MPBooking instance.

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = UITableViewCell.init(style: .subtitle, reuseIdentifier: nil)
    switch BookingRow.init(rawValue: indexPath.row)  {
    case .title:
        cell.textLabel?.text = booking.title ?? ""
        cell.detailTextLabel?.text = "Title"
    case .description:
        cell.textLabel?.text = booking.bookingDescription ?? ""
        cell.detailTextLabel?.text = "Description"
    case .start:
        cell.textLabel?.text = "\(booking.startTime ?? Date())"
        cell.detailTextLabel?.text = "Start time"
    case .end:
        cell.textLabel?.text = "\(booking.endTime ?? Date().addingTimeInterval(60*60))"
        cell.detailTextLabel?.text = "End time"
    case .id:
        cell.textLabel?.text = booking.bookingId ?? ""
        cell.detailTextLabel?.text = "Booking id"
    default : ()
    }
    return cell
}

In tableView(_:cellForRowAt:), create a switch control structure again by initialising a BookingRow enum value from indexPath.row. Based on the cases, either initialise and present FieldEditController or DatePickerController which we will implement next.

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    weak var wself = self
    switch BookingRow.init(rawValue: indexPath.row) {
    case .title:
        let fieldEditVC = FieldEditController()
        fieldEditVC.title = "Edit Title"
        fieldEditVC.beginEdit(initialValue: booking.title) { (value) in
            wself?.booking.title = value
            wself?.tableView.reloadData()
        }
        present(fieldEditVC, animated: true, completion: nil)
    case .description:
        let fieldEditVC = FieldEditController()
        fieldEditVC.title = "Edit Description"
        fieldEditVC.beginEdit(initialValue: booking.bookingDescription) { (value) in
            wself?.booking.bookingDescription = value
            wself?.tableView.reloadData()
        }
        present(fieldEditVC, animated: true, completion: nil)
    case .start:
        let dateEditVC = DatePickerController()
        dateEditVC.title = "Edit Start Date"
        dateEditVC.beginEdit(initialValue: booking.startTime) { (value) in
            wself?.booking.startTime = value
            wself?.tableView.reloadData()
        }
        present(dateEditVC, animated: true, completion: nil)
    case .end:
        let dateEditVC = DatePickerController()
        dateEditVC.title = "Edit End Date"
        dateEditVC.beginEdit(initialValue: booking.endTime) { (value) in
            wself?.booking.endTime = value
            wself?.tableView.reloadData()
        }
        present(dateEditVC, animated: true, completion: nil)
    default : ()
    }
}

Creating UI for editing text and dates is outside the scope of this tutorial. But since we need it for creating the actual Booking, you are advised to just insert the following 3 controllers into your code.

First a controller EditController for arranging the presented editing view skeleton.

class EditController: UIViewController {
    let doneButton = UIButton()
    let titleLabel = UILabel()
    override var title: String? { didSet { titleLabel.text = title } }
    let sw = UIStackView()
    func spacer() -> UIView {
        return UIView()
    }
    override func viewDidLoad() {
        sw.addArrangedSubview(doneButton)
        sw.addArrangedSubview(titleLabel)
        sw.spacing = 40
        sw.alignment = .center
        sw.axis = .vertical
        let backgroundView = UIView(frame: CGRect(x: 0,y: 0,width: 3000,height: 3000))
        backgroundView.backgroundColor = UIColor.systemBackground
        backgroundView.translatesAutoresizingMaskIntoConstraints = false
        sw.insertSubview(backgroundView, at: 0)
        view = sw

        doneButton.setTitle("Done", for: .normal)
        doneButton.setTitleColor(.link, for: .normal)
        doneButton.addTarget(self, action: #selector(close), for: .touchUpInside)
    }
    @objc func close() {
        self.dismiss(animated: true, completion: nil)
    }
}

Secondly, a controller DatePickerController inheriting EditController presenting and managing the date picker.

class DatePickerController: EditController {

    private let datePicker = UIDatePicker()
    var didEdit : ((_ value:Date) -> Void)?
    func beginEdit(initialValue:Date?, didEdit: @escaping ((Date) -> Void)) {
        datePicker.date = initialValue ?? Date()
        self.didEdit = didEdit
    }

    override func viewDidLoad() {

        super.viewDidLoad()

        datePicker.addTarget(self, action: #selector(didEditDate), for: .allEvents)

        sw.addArrangedSubview(datePicker)
        sw.addArrangedSubview(spacer())

    }

    @objc func didEditDate() {
        didEdit?(datePicker.date)
    }
}

Lastly, a controller FieldEditController inheriting EditController presenting and managing the text field.

class FieldEditController: EditController, UITextFieldDelegate {

    private let textField = UITextField()
    private var didEdit: ((_ value:String) -> Void)?
    func beginEdit(initialValue:String?, didEdit: @escaping ((String) -> Void)) {
        textField.text = initialValue ?? ""
        self.didEdit = didEdit
    }

    override func viewDidLoad() {

        super.viewDidLoad()

        sw.addArrangedSubview(textField)
        sw.addArrangedSubview(spacer())

        textField.becomeFirstResponder()
        textField.delegate = self

    }

    func textFieldDidEndEditing(_ textField: UITextField) {
        didEdit?(textField.text ?? "")
    }
}

This concludes the tutorial about Booking in the MapsIndoors iOS SDK. Depending on your dataset you should not be far from a working Booking experience where you can list Bookable Locations, list Bookings and create new Bookings.

Performing a Booking of a Location

Cancelling a Booking of a Location

Booking in Practice

Listing Bookable Locations

Listing Bookings for a Location

Editing, Performing and Cancelling Bookings

List Bookable Locations
Work with Bookings
Listing Bookings for a Location
Performing a Booking of a Location
Cancelling a Booking of a Location
on behalf of a user
​
​
​
​
​
​
​