A full-featured iOS communication infrastructure for the BMLT Meeting List Toolbox system
BMLTiOSLib
The BMLTiOSLib fits between the BMLT Root Server's Semantic Interface and your iOS app:
This project is an iOS Shared Framework, designed to ease integration of a BMLT Root Server into an iOS client (it may also work for MacOS, but we're not there yet). The BMLTiOSLib is a Swift-only framework. It won't support Objective-C. The BMLTiOSLib/Framework directory has the relevant exported classes. The BMLTiOSLib/Test Harness Project directory implements a fairly complex program that we've written to exercise the library. Because Apple now requires that iOS apps only interact with SSL servers, it's a bit complex to test. We don't want to compromise security by allowing connections to self-signed certs, so we can't test with localhost.
PROTIP: We use Charles Proxy to examine the interaction between the simulator and the server. It is possible to set up Charles as a "man in the middle" to decrypt SSL interactions.
For easy testing, you can connect to the BMLT Test Server, which uses SSL. This database may change a lot, as it is our main test bed, but it will always have the ability to be accessed via SSL. The BMLTiOSLib Tester app is not pretty. It's not meant to be. It's a space shuttle cockpit program that's designed to let us press all the various buttons. It also has a couple of issues with operation that, quite frankly, aren't a priority to fix, as we now have several "real world" implementations that stress the BMLTiOSLib a great deal more than this ugly little duckling. This is the documentation page for the BMLTiOSLib.
INSTALLATION
As A Swift Package:
You can include the BMLTiOSLib, using the Swift Package Manager, simply by referring to its GitHub Repository URI (SSH: git@github.com:bmlt-enabled/BMLTiOSLib.git or HTTPS: https://github.com/bmlt-enabled/BMLTiOSLib.git).
You will then need to import the module, by adding the following to the source files that will be accessing the library:
import BMLTiOSLib
Using Carthage:
To use this from Carthage, simply add the following to your Cartfile:
github "bmlt-enabled/BMLTiOSLib"
You then cd
to the project directory, and execute carthage update
on the command line.
You will then need to import the module, by adding the following to the source files that will be accessing the library:
import BMLTiOSLib
It should be noted that Carthage can have issues with App Store provisioning, and you may want to build the framework, yourself, to avoid these issues.
Directly:
You can also directly access this project from its location as a GitHub repo, and include it into your project.
UNDER THE HOOD
The BMLTiOSLib communicates with the Root Server using the BMLT JSON Semantic Interface. Administration is done using the BMLT Administrative Semantic Interface (Also JSON). The whole idea is to completely abstract the communication layer from the app development process. BMLTiOSLib provides a simple, error-checked functional interface to the BMLT. Interaction with the BMLTiOSLib is done via Apple's Delegation Pattern. When you instantiate an instance of BMLTiOSLib, you register your app as a BMLTiOSLibDelegate delegate instance, and it will receive events as they come in. You then use the functional interface to operate the connection.
BASIC USAGE:
The BMLTiOSLib class represents the public interface to the BMLTiOSLib framework. This class needs to be instantiated with a URI to a valid Root Server (the same URI used to log in), and a BMLTiOSLibDelegate delegate instance. Instantiation immediately starts a communication process, and the result will be reflected in the delegate's bmltLibInstance(_ inLibInstance: BMLTiOSLib, serverIsValid: Bool) callback. If this instance fails to connect to a valid Root Server, it should be deleted, and reinstantiated for a new connection. Once a connection is established, the HTTP session is maintained until the instance is deinstantiated. The session is required to be maintained for Semantic Administration. You cannot share a session across instances of BMLTiOSLib.
BASIC SERVER INFORMATION:
Once you have successfully connected (established a session) to the Root Server, this instance will have some fundamental information available about that server. Your delegate's bmltLibInstance(_ inLibInstance: BMLTiOSLib, serverIsValid: Bool) method is called when the connection is successful (serverIsValid
is true). This information can be accessed by calling the following BMLTiOSLib instance properties:
-
distanceUnits and distanceUnitsString (This is the distance unit used for the server -Km or Mi).
-
availableMeetingValueKeys (This contains the Dictionary key strings that can access various meeting properties).
-
emailMeetingContactsEnabled (True, if the Server is set to allow emails to be sent to meeting contacts).
-
emailServiceBodyAdminsEnabled (True, if these emails will CC the Service Body Admin for that Service body, as well as the meeting contact -They may be the same email address).
-
changeDepth (The number of changes saved per meeting).
-
googleAPIKey (The Google Maps API key for the Root Server -May not be useful for most other Servers).
-
delegate (That will be the object -implementing the BMLTiOSLibDelegate protocol that was passed in as delegate when the instance was created).
-
versionAsString and versionAsInt (The server version)
-
isAdminAvailable (True, if Semantic Administration is available).
-
defaultLocation (The Root Server's default central location).
-
serviceBodies (This is a "flat" Array of the Service bodies, with no hierarchy).
-
hierarchicalServiceBodies (This maps out the Service bodies in the hierarchy they occupy on the Root Server, and this is just one node with children only -no parents or Service body).
-
allPossibleFormats (an Array of format objects available -May not all be used by the meetings).
-
availableServerLanguages (an Array of language objects).
You determine if the connection was successful by examining the value of the serverIsValid
parameter in the required bmltLibInstance(_ inLibInstance: BMLTiOSLib, serverIsValid: Bool) call:
public func bmltLibInstance(_ inLibInstance: BMLTiOSLib, serverIsValid: Bool) {
if serverIsValid {
•
•
•
•
}
}
If any errors occurred, the required bmltLibInstance(_ : BMLTiOSLib, errorOccurred: Error) call is made:
public func bmltLibInstance(_ inLibInstance: BMLTiOSLib, errorOccurred: Error) {
•
•
•
•
}
Here's a brief image, showing how the BMLTiOSLib acts when setting up the connection:
MEETING SEARCHES:
The way that you do a meeting search with this class, is to acquire the instance's searchCriteria object, and use its various properties to set up your desired search. Once that is done, you call this class instance's performMeetingSearch(_:BMLTiOSLibSearchCriteria.SearchCriteriaExtent) method, indicating whether you want just meetings, just the formats used by the meetings in the search results, or both. Once the search is complete, this class will call your delegate routines:
-
bmltLibInstance(_:BMLTiOSLib,meetingSearchResults:[BMLTiOSLibMeetingNode]) is called with the results of the meeting search.
-
bmltLibInstance(_:BMLTiOSLib,formatSearchResults:[BMLTiOSLibFormatNode],isAllUsedFormats:Bool) is called with the results of the format search.
Either or both may be called, depending on what you requested when you called performMeetingSearch(_:BMLTiOSLibSearchCriteria.SearchCriteriaExtent). If there are no results, they will be called with empty Arrays. You can get meeting search results, the formats used for the given meeting search, or both. These results are returned in these two BMLTiOSLibDelegate calls:
public func bmltLibInstance(_ inLibInstance: BMLTiOSLib, meetingSearchResults: [BMLTiOSLibMeetingNode]) {
•
•
•
•
}
public func bmltLibInstance(_ inLibInstance: BMLTiOSLib, formatSearchResults: [BMLTiOSLibFormatNode], isAllUsedFormats: Bool) {
•
•
•
•
}
If isAllUsedFormats is false, then the formats returned are ONLY those used in the set of meetings specified in the search. If isAllUsedFormats
is true, then the format set is every available format on the server; regardless of whether ot not it is used by any of the meetings (basically the same as the contents of the allPossibleFormats instance property).
MEETING CHANGES:
You can query for meeting changes, including deleted meetings (and you can restore deleted meetings if you are an authorized administrator). You do this by calling one of these methods:
-
getAllMeetingChanges(fromDate:Date?,toDate:Date?,meetingID:Int?)
-
getAllMeetingChanges(fromDate:Date?,toDate:Date?,serviceBodyID:Int?)
-
getAllMeetingChanges(fromDate:Date?,toDate:Date?,serviceBodyID:Int?,meetingID:Int?)
-
getAllMeetingChanges(fromDate:Date?,toDate:Date?,serviceBodyID:Int?,meetingID:Int?,userID:Int?)
-
getDeletedMeetingChanges(fromDate:Date?,toDate:Date?,serviceBodyID:Int?)
-
getDeletedMeetingChanges(fromDate:Date?,toDate:Date?,serviceBodyIDs:[Int]?)
After calling one of the above methods, your delegate is called back with the bmltLibInstance(_:BMLTiOSLib,changeListResults:[BMLTiOSLibChangeNode]) method; which will have an Array of the requested change objects. You can then use these objects to revert meetings, or restore deleted meetings:
public func bmltLibInstance(_ inLibInstance: BMLTiOSLib, changeListResults: [BMLTiOSLibChangeNode]) {
•
•
•
•
}
SENDING MESSAGES TO MEETING CONTACTS:
In some Root Servers, the administrator can choose to enable the ability for users of the site to send messages to designated contacts for meetings (or the Service Body Administrator responsible for the meeting). In these cases, the message is received as an email, but the sender does not send an email. Instead, they use a method of the BMLTiOSLibMeetingNode class, called sendMessageToMeetingContact(fromAddress:String,messageBody:String). The message is sent in the background. When the message has been sent, your delegate is called with the bmltLibInstance(_:BMLTiOSLib,sendMessageSuccessful:Bool) method.
ADMINISTRATION:
In order to perform administration on the Root Server, you need to log in with the adminLogin(loginID:String,password:String) method. The login will remain valid for the lifetime of this object (and its connection session), or until the adminLogout() method is called. Results of meeting searches may return the meeting objects as instances of BMLTiOSLibEditableMeetingNode instead of BMLTiOSLibMeetingNode (as long as you are logged in as an administrator, and have sufficient rights to edit the meeting). It will depend on the edit rights that the login has for the given meeting. If you cannot edit the meeting, then the instance will be of BMLTiOSLibMeetingNode, instead of BMLTiOSLibEditableMeetingNode. If the instance is BMLTiOSLibEditableMeetingNode, the isEditable property will return true. If the instance is of the BMLTiOSLibEditableMeetingNode class, you can cast it to that class, and manipulate the public properties. Once the properties have been set, you can then call the saveChanges() method for that instance, and the meeting will be saved. Until the saveChanges() method is called, the meeting changes are not sent to the server. Once the meeting has been saved, your delegate will receive a call to its bmltLibInstance(:BMLTiOSLib,adminMeetingChangeComplete:BMLTiOSLibChangedMeeting!) method with an object that will contain whatever fields of the meeting changed, with the "before" and "after" values (always Strings). You can also delete a meeting, by calling the delete() method (The deletion happens immediately). If you delete the meeting, your delegate is called with the bmltLibInstance(:BMLTiOSLib,deleteMeetingSuccessful:Bool) method. If you call the restoreToOriginal() method, any changes that you made to the meeting object will be reverted to the state of the meeting on the server. Nothing will be sent to the server. You can also revert a meeting to the state it was in before a given change record for that meeting, using the revertMeetingToBeforeThisChange(_:BMLTiOSLibChangeNode) method. Nothing will be sent to the server. If the change was inappropriate for the meeting, the call will return false. If it was successful, the meeting's state will be reverted to that in the change record, but will not yet be sent to the server. You still need to call saveChanges().
ROLLING BACK AND UNDELETING MEETINGS:
Selecting the saveMeetingToBeforeThisChange() of a change or editable meeting object will use the restore deleted or rollback function of the Semantic Admin interface (as long as you are logged in as an administrator, and have sufficient rights to edit the meeting). We do allow you to take the "before" record of the meeting (found in the json_data
JSON response, or the beforeObject property of the change record object), and save that. This allows you to add new changes (as opposed to simply accepting the whole change in a rollback, you can choose to only take certain changes). It also gives a better change record in the meeting history. Instead of a curt "Meeting was rolled back to a previous version.", you now have a list of the exact fields that were changed. Remember that the beforeObject and afterObject properties are fully-qualified meeting objects, and, if editable, can be saved, which overwrites whatever is currently in the database (It's exactly like saving a changed meeting record). You revert a meeting by calling the revertMeetingToBeforeThisChange() method of the change record object concerned. It's quite simple.
NEW MEETINGS:
Creating new meetings is easy (as long as you are logged in as an administrator, and have sufficient rights to create a meeting). You create an instance of BMLTiOSLibEditableMeetingNode with an ID of 0 (the default). Then, when you call saveChanges(), it will create a new meeting. When you create a new meeting, or restore a deleted meeting, your delegate is called with the bmltLibInstance(_:BMLTiOSLib,newMeetingAdded:BMLTiOSLibEditableMeetingNode) method. The newMeetingAdded
parameter will contain an object that models the newly-created meeting (including the new ID, if it was a brand-new meeting).
As of December, 2017:
TO DO
-
Make tasks interruptable without terminating the session. Currently, the way to terminate a task is to terminate the session. This works great for search apps, but not so good for admin apps, as the session carries the login. If you terminate the session, you force the user to log back in.
-
Make the library multi-tasking. Currently, the library works in a single-threaded manner. It should be able to handle multiple simultaneous tasks.
NICE TO HAVE
-
Test against a simple TVOS Swift app
-
Test against a simple MacOS Swift app