TESOBE Ltd.
OBPKit allows you to easily connect your existing iOS and OSX apps to servers offering the Open Bank Project API.
It takes care of the authorisation process, and once the user has given your app permission to access his/her resources, it provides you with a helper to marshal resources through the API, or if you want to roll your own, you can use OBPKit to add authorisation headers to the requests you form yourself.
You can look at the HelloOBP-iOS and HelloOBP-Mac sample applications to see OBPKit in use.
You can use either Carthage or CocoaPods (more). (If you are sitting on the fence, there is a way to support both until you are ready to decide based on experience. Link coming soon; ask for details if interested.)
-
Install the latest Carthage — in a nutshell: install homebrew, run
brew update
and runbrew install carthage
. -
If you don't already have one, create a file named
Cartfile
at the root level of your project folder. To your Cartfile add...github "OpenBankProject/OBPKit-iOSX"
-
Run
carthage update --platform iOS,Mac
(omitting the platforms you do not need). This will add to your project's root directory, the directories Carthage/Checkouts, containing all the needed projects, and Carthage/Build/{iOS,Mac}, containing the frameworks built from the projects. It will also add a Cartfile.resolved file at root level. -
In Xcode, open the project for your app, select your app target, go to the build phases tab and expand the
Embed Frameworks
phase, and drag in the OBPKit, OAuthCore, STHTTPRequest and UICKeyChainStore frameworks from the Carthage/Build/{iOS,Mac} subfolder for your app platform. -
You will also need to add the Security framework to your
Link Binary With Libraries
build phase. -
Add
Carthage/
to your.gitignore
file before you commit this change.
-
If you already have a Podfile, add…
pod 'OAuthCore', :git => 'https://github.com/t0rst/OAuthCore.git' # ...OBPKit currently requires the t0rst fork of OAuthCore pod 'OBPKit', :git => 'https://github.com/OpenBankProject/OBPKit-iOSX.git'
…to your target dependencies. Otherwise, create a file named
Podfile
at the root level of your project folder, with contents like…platform :ios, '8.0' # delete if inappropriate platform :osx, '10.9' # delete if inappropriate target 'your-app-target-name' do pod 'OAuthCore', :git => 'https://github.com/t0rst/OAuthCore.git' # ...OBPKit currently requires the t0rst fork of OAuthCore pod 'OBPKit', :git => 'https://github.com/OpenBankProject/OBPKit-iOSX.git' end
-
Run
pod install
. This will modify your yourproj.xcproj, add yourproj.xcworkspace and Podfile.lock, and add a Pods folder at the root level of your project folder. Use yourproj.xcworkspace from now on. -
Add
Pods/
to your.gitignore
file before you commit this change.
There are three main classes to use, one protocol to adopt and some helpers.
An OBPServerInfo
instance records the data necessary to access an OBP server. It stores sensitive credentials securely in the key chain.
The OBPServerInfo
class keeps a persistent record of all complete instances. An instance is complete once its client key and secret have been set. You can typically obtain these for your app from https://host-serving-OBP-API/consumer-registration.
You can use the OBPServerInfo
class to keep a record of all the OBP servers for which you support a connection; usually you will just have one, but more are possible. OBPServerInfo
instances are reloaded automatically when your app is launched.
A default instance of the helper class OBPServerInfoStorage
handles the actual save and restore. You can customise your storage approach by nominating that your override class be used. You can configure this, as well as other security details, by passing a dictionary of customisation options to the function OBPServerInfoCustomise
before the OBPServerInfo
class initialize has been called.
You request an OBPSession
instance for the OBP server you want to connect to, and use it to handle the authorisation sequence, and once access is gained, use the session's marshall object to help you marshal resources through the API.
By default, OBPSession
uses OAuth for authorisation, which is the correct way for your app to gain your user's permission to access his/her resources, and OBPKit makes this very easy to use. However, during initial development, you can also set the authMethod
of your instance to use Direct Login if you wish. (You can also set the authMethod
to none in order to restrict access to just the public resources on an OBP server; or do this as needed for individual calls to OBPMarshall.)
The OBPSession
class keeps track of the instances that are currently alive, and will create or retrieve an OBPSession
instance for an OBPServerInfo
instance identifying a server you want to talk to. Both OBPServerInfo
and OBPSession
allow you to access default instances for when you only want to deal with singletons.
For OAuth, OBPSession
needs some part of your app to act as an OBPWebViewProvider
protocol adopter in order to show the user a web page when it is time to get authorisation to access his/her resources.
If you don't provide an OBPWebViewProvider
protocol adopter, then the OBPDefaultWebViewProvider
class singleton will be used. It provides basic support, and you can choose whether an in-app or external web view is brought up by calling configuring with the class member configureToUseExternalWebViewer:withCallbackSchemeName:andInstallCallbackHook:
. There are advantages and disadvantages to both, as set out in the Xcode quick help for the class.
See Callback Schemes below for important additional considerations.
A default OBPMarshal
instance is available from your OBPSession
object, and will take care of creating, fetching, updating and deleting resources from the API, with response data and corresponding deserialised object delivered to your completion blocks. You can easily override the default behaviour by invoking a range of options. You can also supply extra headers via the options to, for example, limit by ordinal or date the range of transactions you retrieve.
REST API Verb | OBPMarshal Call |
---|---|
GET | -getResourceAtAPIPath:withOptions:forResultHandler:orErrorHandler: |
POST | -createResource:atAPIPath:withOptions:forResultHandler:orErrorHandler: |
PUT | -updateResource:atAPIPath:withOptions:forResultHandler:orErrorHandler: |
DELETE | -deleteResourceAtAPIPath:withOptions:forResultHandler:orErrorHandler: |
You can use the OBPDateFormatter
helper to convert back and forth between NSDate
instances and the string representation used when sending and recieving OBP API resources.
The HelloOBP-iOS and HelloOBP-Mac sample apps demonstrate simple use of the OBPKit classes.
In your app delegate after start-up, check for and create the OBPServerInfo
instance for the main server you will connect to (if it hasn't already been restored from a previous run):
if (nil == [OBPServerInfo firstEntryForAPIServer: kDefaultServer_APIBase])
{
OBPServerInfo* serverInfo;
serverInfo = [OBPServerInfo addEntryForAPIServer: kDefaultServer_APIBase];
serverInfo.data = DefaultServerDetails();
}
Here the details of the default server are fetched from a simple header (DefaultServerDetails.h), which is insecure, but in production, you might want to give the API keys some stronger protection. While getting up and running, this kind of thing is sufficient:
static NSString* const kDefaultServer_APIBase = @"https://apisandbox.openbankproject.com/obp/v2.0.0/";
NS_INLINE NSDictionary* DefaultServerDetails() {
return @{
OBPServerInfo_APIBase : kDefaultServer_APIBase,
OBPServerInfo_AuthServerBase : @"https://apisandbox.openbankproject.com/",
OBPServerInfo_ClientKey : @"0iz1zuscashkoyd3ztzb3i5whuubzetfihfc52ve",
OBPServerInfo_ClientSecret : @"p4li5mklyt1h42w5u0tx4w2evtn2yz3gmntyn2ty",
};
}
In your main or starting view, create the OBPSession instances that you want to work with:
if (_session == nil)
{
OBPServerInfo* serverInfo = [OBPServerInfo firstEntryForAPIServer: chosenServer_APIBase];
_session = [OBPSession sessionWithServerInfo: serverInfo];
}
When the user requests to log in, ask the default session instance to validate, i.e. get authorisation for accessing resources on behalf of the client:
- (void)viewDidLoad
{
...
// Kick off session authentication
[[OBPSession currentSession] validate:
^(NSError* error)
{
if (error == nil) // success, do stuff...
[self fetchAccounts];
}
];
}
After this, you can use the marshal property of the current session instance to retrieve resources from the API:
OBPMarshal* marshal = [OBPSession currentSession].marshal;
NSString* path = [NSString stringWithFormat: @"banks/%@/accounts/private", bankID];
HandleOBPMarshalData responseHandler =
^(id deserializedObject, NSString* responseBody)
{
_accountsDict = deserializedObject;
[self loadAccounts];
};
[marshal getResourceAtAPIPath: path
withOptions: nil
forResponseHandler: responseHandler
orErrorHandler: nil]; // nil ==> use default error handler
As part of the OAuth handshake, the API auth server will redirect to a callback URL that your app specifies. OBPKit handles this for you by using a default callback at x-${PRODUCT_BUNDLE_IDENTIFIER:lower}://callback
. For example. the HelloOBP-iOS bundle identifier is com.tesobe.HelloOBP-iOS
and it therefore use a default callback of x-com.tesobe.helloobp-ios://callback
. You need only take further action if you require a different callback. However, there are two important points to note:
- The bundle identifier of your app should use characters that are legal in RFC3986-Schemes, i.e.
[- .A-Za-z0-9]
; if this is not possible or desirable, then use a custom callback scheme. - For extra security, the API auth server requires that the callback URL your app specifies for redirect is the same as the one you gave when you registered your app as an API Consumer and got API keys in return. You can change the registered redirect URL at a later time (keep a note of the consumer id for this), but to save you the trouble, consider the bundle id you will use before registering and construct your callback URL accordingly.
When using an external web view (as mentioned above in OBPWebViewProvider), iOS and macOS use the scheme to uniquely identify the app that should receive the redirect URL. iOS and macOS detect these schemes by looking in your app bundle's info dictionary, hence you should add a section to your info.plist
file as follows (viewing as source):
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>callback</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>CFBundleURLSchemes</key>
<array>
<string>x-${PRODUCT_BUNDLE_IDENTIFIER:lower}</string>
</array>
</dict>
</array>
You can replace the bundle-derived scheme with your own custom scheme if need be, and likewise use a different callback name if necessary. OBPDefaultWebViewProvider
helper function callbackSchemeWithName:
retrieves the scheme from the bundle info dictionary for you.
Most of the time, the default behaviour of OBPMarshal
is what you will want, but for special situations you can easily override the default OBPMarshal
behaviour by passing an options dictionary in with your calls:
To… | add the key… | with value… |
---|---|---|
…access only public resources | OBPMarshalOptionOnlyPublicResources |
@YES |
…add extra headers | OBPMarshalOptionExtraHeaders |
@{ @"obp_limit" : @(chunkSize), @"obp_offset" : @(nextChunkOffset) } (…for example) |
…expect a response body that isn't JSON | OBPMarshalOptionDeserializeJSON |
@NO |
…expect a JSON container object that is not a dictionary | OBPMarshalOptionExpectClass |
[NSArray class] (…for example) |
…expect a JSON container object that could be any class | OBPMarshalOptionExpectClass |
[NSNull null] |
…expect a non-default HTTP status code | OBPMarshalOptionExpectStatus |
@201 |
…accept several HTTP status codes | OBPMarshalOptionExpectStatus |
@[@201, @212] |
…send a form instead of JSON | OBPMarshalOptionSendDictAsForm |
@YES |