The Mobile Close Channel SDK allows you to integrate the Close communication platform in your own iOS app.
We are developers ourselves and we know how frustrating it can be to integrate an SDK. But not this time: we will do our best to make it a fun exercise instead! Pinky promise, this won't be an emotional roller coaster: buckles are not needed!
So, take a coffee ☕️, tea 🫖, or havermelk 🥛 and take my hand to guide you through this in a few easy steps. If you still run into problems don't hesitate to contact us via https://sdk.thecloseapp.com
To make an easy start, be sure you have this Xcode versions:
- Xcode 14.0 or 14.1
Newer versions could work / should work, but we have verified it to work with the above tool versions.
These older versions of Xcode are verified to work with SDK version 1.2.4:
- Xcode 13.2.1
- Xcode 13.4.2
- To get access to our private repository with our SDK framework, you'll need:
- a GitHub account
- a GitHub personal access token, how to create one is explained here
- To get access to the Close platform you'll need:
- A CloseChannel-Info.plist file
- That contains an API base URL and API access token (values to access our sandbox environment can be found below)
- A Close Code of the flow that needs to be presented in one of the channels (or use
SDKDEMO
to start testing)
- A CloseChannel-Info.plist file
iOS Deployment target
in the project settings of your app is set to 12.0 or higher.
Both Arm and x86 processor architectures are supported, thus the SDK will also work in the simulator.
⚠️ iPad targets are not fully supported, there could be cosmetic issues⚠️ SwiftUI is not specifically supported
The SDK supports en_GB
, en_US
, nl
and de
localisations. Please note that to use these localisations your app also must support these. The default localisation is en_GB
.
Please note that the SDK only support portrait mode for showing the chat messages and info messages views. Take special care for not allowing the screen to rotate in landscape.
It is strongly recommended to first start with the Mobile Close Channel SDK Sample which showcases how to use the SDK and the different features it provides. It talks to a test environment that can be used to test with.
When you've done that you can come back to this page to continue the wonderful journey of integrating Close in your own app.
To add the SDK to your project follow the steps in this section.
⛔️ IMPORTANT: The SDK framework binary is in a private repository. Read this to see how to get access.
When you have arranged that, then add Close to your Podfile.
- In your Podfile add the Close CocoaPods specs repository:
source 'https://github.com/close-dev-team/close-cocoapods-specs.git'
- Then add the Close Channel framework:
pod 'CloseChannel'
Also add to the end of your Podfile:
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
This is needed, otherwise you will get 'dyld: Symbol not found' errors during compilation. See CocoaPods/CocoaPods#9775 for more info.
- Next, run:
pod install --repo-update
- You are asked for your E-mail address and password. Make sure that you enter the personal access token you created before as your password. If you did not create it yet, check this
- When you have done all the above you can start using the CloseChannel framework in your code by simply importing it:
import CloseChannel
Example of a minimal `Podfile`
source 'https://github.com/CocoaPods/Specs.git'
source 'https://github.com/close-dev-team/close-cocoapods-specs.git'
platform :ios, '12.0'
target 'Close Channel Sample' do
use_frameworks!
pod 'CloseChannel'
end
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'
if target.name == 'SwiftSocket'
config.build_settings['SWIFT_VERSION'] = '5.0'
end
end
end
end
You can also add the framework manually. Make sure you first clone the aforementioned repository. Then copy the framework into your project.
Show me how
The CloseChannelController instance is the one you're going to talk to. Let's first create it.
import CloseChannel
class AppDelegate: UIResponder, UIApplicationDelegate {
let closeChannelController = CloseChannelController.sharedInstance
}
Note: or simplicity sake, in the examples we use create the CloseChannelController in the AppDelegate. This is a bad practice and we advise you to create it somewhere else.
As it is a singleton instance, you can create and use it in any of your classes.
When you run this, in the console you will see the message: The API base URL is not set
. That's because the SDK does not know to which Close endpoint it should to talk to. This URL needs to be configured first.
You can configure this by following these steps:
- Add a plist file named
CloseChannel-Info.plist
to your project, don't forget to add it to the correct target(s) - Add a string property named
api_base_url
with the API URL as the value. - Add a string property named
api_access_token
with the API access token (see below) as the value.
https://api.sdk-sandbox.closetest.nl:16443/
, and the api_access_token sdk-sandbox-access-token
but this should be replaced later with the URL that Close provides to your company.
Example `CloseChannel-Info.plist` file
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>api_base_url</key>
<string>https://api.sdk-sandbox.closetest.nl:16443/</string>
<key>api_socket_url</key>
<string>sdk-sandbox-access-token</string>
</dict>
</plist>
A sample file can be found here.
When the SDK is correctly set up we can continue connecting to the Close platform. This starts with registering a user on our platform.
import UIKit
import CloseChannel
class YourUserProvider {
static func appUser() -> AppUser {
return AppUser()
}
}
struct AppUser {
let id = "a_unique_id"
let name = "mieke"
}
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
let closeChannelController = CloseChannelController.sharedInstance
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let appUser = YourUserProvider.appUser()
closeChannelController.registerUser(uniqueId: appUser.id,
nickname: appUser.name) { closeUserId in
// The returned closeUserId is a unique identifier that Close uses to identify users. You can store
// it for later use.
} failure: { error in
print("Failed to register user: \(error.code) \(error.message)")
}
return true
}
}
A uniqueId
is the id for a user in Close. Please make sure this is something a user cannot change him/herself, because then it would not be possible to link to the same data. So don't use a phone number or E-mail address. Instead, for example, use an UUID. If the value is null a uniqueId will be generated
The nickname
is the nickname, first name or full name of the user. It is optional so does not have to be specified.
When registerUser is called multiple times, it will return the already existing user. It is not possible to change the uniqueId or to change the nickname with the registerUser call.
Please make sure to read the SDK reference documentation for more information
The YourUserProvider
class in this code snippet provides a way to get user data. Replace this with your own implementation. The most important thing here is that you retrieve something that is unique to the user.
⚠️ uniqueId
is optional. If the value is nil a unique ID will be generated. Please make sure to read the SDK reference documentation for all details.
After the user is registered you only should add a channel.
Please contact Close for the correct Close code for your app. For now you can use SDKDEMO
closeChannelController.addChannel(closeCode: "SDKDEMO") { channel in
print("Channel succesfully added")
} failure: { error in
print("Could not add channel: \(error.code) \(error.message)")
}
⚠️ Note that you should only add a channel once. When you try to add another channel with the same Close code, you'll receive an error. You can use thegetCloseChannels
function to check if a channel is already available, before adding it. See the section Tying it all together below for an example.
To show a channel, you can simple call the function below to open the last added channel. Be sure to do this on the main thread.
DispatchQueue.main.async {
closeChannelController.openChannelMessagesView()
}
There are 2 ways of showing the channel:
- Channel messages view. This shows messages in a chat-like way with text balloons, ordered by date/time of sending
- Channel info view. This shows informational messages, tickets and bought products, ordered by pre defined order
Showing the channel info view<
DispatchQueue.main.async {
closeChannelController.openChannelInfoView()
}
Opening a specific channel
Alternatively you can retrieve a list of channels and use the channel ID to open a specific channels
closeChannelController.getChannels { channels in
if channels.count > 0 {
let channel = channels[0]
DispatchQueue.main.async {
self.closeChannelController.openChannelMessagesView(channelId: channel.id)
}
}
} failure: { error in
print("Failed to get channels: \(error.code) \(error.message)")
}
So far you have learned how you show a channel in the easiest way by opening it fullscreen. If you want to have more control, like adding your own navigation bar or make the channel part of a tab bar, you should use getChannelMessagesViewController
(or the alternative getChannelInfoViewController
):
closeChannelController.getChannelMessagesViewController(success: { channelMessagesViewController in
// Set the modalPresentationStyle property
// and present the viewcontroller
})
Please note that not all modal presentation styles are currently supported. Check the changelog for known issues.
Also note that you should always reserve a large part (80% or more) of your screen to display the close channel correctly
In this section some examples, make sure you do the calls on the main thread:
Show without a navigation bar
With openChannelMessagesView
a navigation bar is shown in the colors defined in the Close builder. If you want to show the channel without a navigationbar you could do this:
closeChannelController.getChannelMessagesViewController(success: { channelMessagesViewController in
channelMessagesViewController.modalPresentationStyle = .fullScreen
yourViewController.present(channelMessagesViewController, animated: true)
})
Show with a custom navigation bar
If you want to have your own navigation bar, you can do something like:
closeChannelController.getChannelMessagesViewController(success: { channelMessagesViewController in
let navigationController = UINavigationController(rootViewController: channelMessagesViewController)
channelMessagesViewController.title = "Messages"
// (Do any navigation bar appearance customizations here)
channelMessagesViewController.modalPresentationStyle = .fullScreen
yourViewController.present(channelMessagesViewController, animated: true)
})
Show in a tab
If you want to show the channel as part of a tab, you could do this:
closeChannelController.getChannelMessagesViewController(success: { channelMessagesViewController in
yourTabBarController.viewControllers = [channelMessagesViewController]
})
Show in a tab with a navigationbar
To use this combination, you can simply add a navigation controller in between. Then instead of adding the channel viewcontroller, add the navigation controller to the tabbar:
closeChannelController.getChannelMessagesViewController(success: { channelMessagesViewController in
let navigationController = UINavigationController(rootViewController: channelMessagesViewController)
channelMessagesViewController.title = "Messages"
yourTabBarController.viewControllers = [navigationController]
})
As closures are used to return wether a call succeeded or failed, it is easy to tie everything together:
- We register a user
- When that succeeds, we check if there are channels available
- If so, we open the last added channel
- If not, we create a channel and open that one
import UIKit
import CloseChannel
class YourUserProvider {
static func appUser() -> AppUser {
return AppUser()
}
}
struct AppUser {
let id = "a_unique_id"
let name = "mieke"
}
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
let closeChannelController = CloseChannelController.sharedInstance
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let appUser = YourUserProvider.appUser()
closeChannelController.registerUser(uniqueId: appUser.id,
nickname: appUser.name) { closeUserId in
// The returned closeUserId is a unique identifier that Close uses to identify users. You can store
// it for later use.
} failure: { error in
print("Failed to register user: \(error.code) \(error.message)")
}
showChannel()
return true
}
func showChannel() {
let appUser = YourUserProvider.appUser()
closeChannelController.registerUser(uniqueId: appUser.id,
nickname: appUser.name) { closeUserId in
self.closeChannelController.getChannels { channels in
if channels.count == 0 {
self.closeChannelController.addChannel(closeCode: "SDKDEMO") { channel in
DispatchQueue.main.async {
self.closeChannelController.openChannelMessagesView()
}
} failure: { error in
print("Could not add channel: \(error.code) \(error.message)")
}
} else {
DispatchQueue.main.async {
self.closeChannelController.openChannelMessagesView()
}
}
} failure: { error in
print("Failed to get channels: \(error.code) \(error.message)")
}
} failure: { error in
print("Failed to register user: \(error.code) \(error.message)")
}
}
}
Your app is now integrated with the Close platform! To improve and make it ready for production please check out the sections below.
Change the api_base_url
and api_access_token
to the correct ones you received from Close. Also make sure
to use the right Close code. It can happen that the one for a Testing or Staging environment
differs from the one on the Production environment.
Before being able to upload the app to Apple, add permission strings for any of the permissions not already supported by your app.
The code samples in the sections above have been simplified by not showing all parameters. Make sure you read the SDK reference documentation for all details. Also if you want more control over the views, check the reference documentation as there are interface methods to suit all your needs.
Start here for an overview of the documentation.
Additionally, in the code samples above, error handling has been greatly simplified. You should improve on error handling and add retries, which is explained in detail here.
Messages are meaning nothing if your users don't read them. Be sure you connect the Close push notifications. It's not hard, we do most of the work for you. Continue reading here.
If you want to integrate with multiple channels and want to implement an overview of channels, be sure to checkout this document
With the Close Builder you can create your own flows to send to your users. Use the account provided by Close to login and start building!
When running into problems, please check out this document first.
- Report any issues found in the documentation or sample code here
- Report any issues found in the SDK here
Note that references to CL-#### in commit messages refer to issues in our private issue tracker.