Asset Tracking Extended

This detailed example walks you through the following steps:

  • Creating a New Asset Based on User Input: Learn how to generate a new asset within your application, leveraging user input to create assets dynamically.

  • Binding User-Created Asset IDs to the Current Device: Discover the process of associating user-generated asset IDs with the device you're currently using, facilitating tracking and management of these assets.

  • Starting and Stopping Tracking Based on User Operations: Gain insights into how to initiate and halt tracking operations, giving users control over the tracking process.

  • Switching Between Tracking Modes Based on User Operations: Learn how to transition between different tracking modes, providing flexibility and adaptability to user preferences.

  • Receiving AssetTrackingCallbacks and Displaying Results in the User Interface: Explore how to handle AssetTrackingCallbacks and effectively communicate tracking results to the user interface, ensuring a seamless and informative user experience.

For all code examples, refer to iOS Tracking Android Code Examples

SetProfileViewController view source

1
2
class SetProfileViewController: UIViewController, UIGestureRecognizerDelegate {
3
@IBOutlet weak var editCustomId: UITextField!
4
@IBOutlet weak var editAssetName: UITextField!
5
@IBOutlet weak var editAssetDescription: UITextField!
6
@IBOutlet weak var editAssetAttributes: UITextField!
7
@IBOutlet weak var lastAssetId: UITextField!
8
@IBOutlet weak var createAsset: UIButton!
9
10
@IBOutlet weak var editAssetId: UITextField!
11
@IBOutlet weak var bindAsset: UIButton!
12
13
var customId: String = ""
14
var assetName: String = ""
15
var assetDescription: String = ""
16
var assetAttributes: String = ""
17
var assetId: String = ""
18
19
let userDefaults: UserDefaults = UserDefaults.standard
20
21
override func viewDidLoad() {
22
super.viewDidLoad()
23
self.navigationController?.interactivePopGestureRecognizer?.delegate = self
24
self.hideKeyboardWhenTappedAround()
25
initData()
26
setUpInitialView()
27
addGestureEvents()
28
}
29
30
override func viewWillAppear(_ animated: Bool) {
31
self.navigationController?.setNavigationBarHidden(true, animated: true)
32
}
33
34
private func initData() {
35
customId = userDefaults.string(forKey: Constants.CUSTOM_ID_KEY) ?? UUID().uuidString.lowercased()
36
assetName = userDefaults.string(forKey: Constants.ASSET_NAME_KEY) ?? "my car"
37
assetDescription = userDefaults.string(forKey: Constants.ASSET_DESCRIPTION_KEY) ?? "a luxury BMW"
38
assetAttributes = userDefaults.string(forKey: Constants.ASSET_ATTRIBUTES_KEY) ?? "a test attribute"
39
40
assetId = userDefaults.string(forKey: Constants.ASSET_ID_KEY) ?? ""
41
}
42
43
private func setUpInitialView(){
44
editCustomId.delegate = self
45
editAssetName.delegate = self
46
editAssetDescription.delegate = self
47
editAssetAttributes.delegate = self
48
editAssetId.delegate = self
49
50
editCustomId.text = customId
51
editAssetName.text = assetName
52
editAssetDescription.text = assetDescription
53
editAssetAttributes.text = assetAttributes
54
editAssetId.text = assetId
55
lastAssetId.text = userDefaults.string(forKey: Constants.LAST_BIND_ASSET_ID_KEY) ?? ""
56
57
createAsset.setTitleColor(.white, for: .normal)
58
createAsset.layer.cornerRadius = 10
59
createAsset.layer.backgroundColor = UIColor.systemBlue.cgColor
60
61
bindAsset.setTitleColor(.systemBlue, for: .normal)
62
bindAsset.layer.cornerRadius = 10
63
bindAsset.layer.borderWidth = 1.0
64
bindAsset.layer.borderColor = UIColor.systemBlue.cgColor
65
}
66
67
private func addGestureEvents() {
68
createAsset.addTarget(self, action: #selector(onCreateAssetTapped), for: .touchUpInside)
69
bindAsset.addTarget(self, action: #selector(onBindAssetTapped), for: .touchUpInside)
70
}
71
72
func isLauncherScreen() -> Bool {
73
return self.navigationController?.viewControllers.count == 1
74
}
75
76
func lanchToHomeView() {
77
let viewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewController")
78
self.view.window?.rootViewController = UINavigationController(rootViewController: viewController)
79
}
80
81
private func saveAssetProfile(assetId: String){
82
userDefaults.set(customId, forKey: Constants.CUSTOM_ID_KEY)
83
userDefaults.set(assetName, forKey: Constants.ASSET_NAME_KEY)
84
userDefaults.set(assetDescription, forKey: Constants.ASSET_DESCRIPTION_KEY)
85
userDefaults.set(assetAttributes, forKey: Constants.ASSET_ATTRIBUTES_KEY)
86
userDefaults.set(assetId, forKey: Constants.ASSET_ID_KEY)
87
}
88
89
@objc private func onCreateAssetTapped() {
90
if (assetName.isEmpty) {
91
let toastView = ToastView(message: "Please enter asset name")
92
toastView.show()
93
return
94
}
95
96
if(AssetTracking.shared.isRunning()){
97
let toastView = ToastView(message: "Asset tracking is ON, please turn off tracking before creating new asset!")
98
toastView.show()
99
return
100
}
101
102
let assetProfile: AssetProfile = AssetProfile.init(customId: customId, assetDescription: assetDescription, name: assetName, attributes: ["test": assetAttributes])
103
104
AssetTracking.shared.createAsset(assetProfile: assetProfile) { assetCreationResponse in
105
let assetId = assetCreationResponse.data.id
106
self.editAssetId.text = assetId
107
self.saveAssetProfile(assetId: assetId)
108
} errorHandler: { error in
109
let errorMessage = error.localizedDescription
110
let toastView = ToastView(message: "Create asset failed: " + errorMessage)
111
toastView.show()
112
}
113
}
114
115
private func showForceBindDialog(assetId: String, warningMessage: String) {
116
// Create an alert controller
117
let alertController = UIAlertController(title: "", message: warningMessage + ", do you want to clear local data and force bind to new asset id?", preferredStyle: .alert)
118
119
let okAction = UIAlertAction(title: "Proceed", style: .default) { (_) in
120
self.dismiss(animated: true, completion: nil)
121
AssetTracking.shared.forceBindAsset(assetId: assetId) { responseCode in
122
let toastView = ToastView(message: "Force bind new asset successfully with assetId: " + assetId)
123
toastView.show()
124
125
self.userDefaults.set(true, forKey: Constants.IS_NOT_FIRST_INSTALLATION_KEY)
126
self.userDefaults.set(assetId, forKey: Constants.LAST_BIND_ASSET_ID_KEY)
127
128
if self.isLauncherScreen() {
129
self.lanchToHomeView()
130
} else {
131
self.navigationController?.popViewController(animated: true)
132
}
133
} errorHandler: { error in
134
let errorMessage = error.localizedDescription
135
let toastView = ToastView(message: "Bind asset failed: " + errorMessage)
136
toastView.show()
137
}
138
}
139
alertController.addAction(okAction)
140
141
// Add "Cancel" button
142
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { (_) in
143
self.dismiss(animated: true, completion: nil)
144
}
145
alertController.addAction(cancelAction)
146
147
// Show the alert
148
present(alertController, animated: true, completion: nil)
149
}
150
151
@objc private func onBindAssetTapped() {
152
guard let assetId = editAssetId.text else {
153
let toastView = ToastView(message: "Please enter asset id")
154
toastView.show()
155
return
156
}
157
158
AssetTracking.shared.bindAsset(assetId: assetId) { responseCode in
159
let toastView = ToastView(message: "Bind asset successfully with id: " + assetId)
160
toastView.show()
161
162
self.userDefaults.set(true, forKey: Constants.IS_NOT_FIRST_INSTALLATION_KEY)
163
self.userDefaults.set(assetId, forKey: Constants.LAST_BIND_ASSET_ID_KEY)
164
165
if self.isLauncherScreen() {
166
self.lanchToHomeView()
167
} else {
168
self.navigationController?.popViewController(animated: true)
169
}
170
} errorHandler: { error in
171
let errorMessage = error.localizedDescription
172
173
if (errorMessage.contains(AssetTrackingApiExceptionType.UN_UPLOADED_LOCATION_DATA.rawValue)) {
174
self.showForceBindDialog(assetId: assetId, warningMessage: errorMessage)
175
} else {
176
let toastView = ToastView(message: "Bind asset failed: " + errorMessage)
177
toastView.show()
178
}
179
}
180
181
}
182
183
}
184
extension SetProfileViewController{
185
func hideKeyboardWhenTappedAround() {
186
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
187
tap.cancelsTouchesInView = false
188
view.addGestureRecognizer(tap)
189
}
190
191
@objc private func dismissKeyboard() {
192
view.endEditing(true)
193
}
194
195
}
196
extension SetProfileViewController: UITextFieldDelegate {
197
public func textFieldDidChangeSelection(_ textField: UITextField) {
198
let text = textField.text?.trimmingCharacters(in: CharacterSet.whitespaces) ?? ""
199
if textField == self.editCustomId {
200
customId = text
201
} else if textField == self.editAssetName {
202
assetName = text
203
} else if textField == self.editAssetDescription {
204
assetDescription = text
205
} else if textField == self.editAssetAttributes {
206
assetAttributes = text
207
} else if textField == self.editAssetId {
208
assetId = text
209
}
210
}
211
212
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
213
textField.resignFirstResponder()
214
return true
215
}
216
}

ExtendedTrackingViewController view source

1
class ExtendedTrackingViewController: UIViewController, AssetTrackingCallback {
2
@IBOutlet weak var locationInfo: UILabel!
3
@IBOutlet weak var trackingStatus: UILabel!
4
@IBOutlet weak var trackingModeSelector: UISegmentedControl!
5
@IBOutlet weak var startTracking: UIButton!
6
@IBOutlet weak var stopTracking: UIButton!
7
@IBOutlet weak var createAsset: UIButton!
8
@IBOutlet weak var viewDataLogs: UIButton!
9
var assetTracking: AssetTracking = AssetTracking.shared
10
11
override func viewDidLoad() {
12
super.viewDidLoad()
13
assetTracking.initialize(apiKey: "YOUR_API_KEY")
14
15
assetTracking.delegate = self
16
17
startTracking.setTitle("Start Tracking", for: .normal)
18
startTracking.setTitleColor(.white, for: .normal)
19
startTracking.layer.cornerRadius = 10
20
startTracking.layer.backgroundColor = UIColor.systemBlue.cgColor
21
22
stopTracking.setTitle("Stop Tracking", for: .normal)
23
stopTracking.setTitleColor(.white, for: .normal)
24
stopTracking.layer.cornerRadius = 10
25
stopTracking.layer.backgroundColor = UIColor.systemBlue.cgColor
26
27
bindExistingAssetId()
28
setUpButtons()
29
updateTrackingStatus()
30
}
31
32
override func viewWillAppear(_ animated: Bool) {
33
self.navigationController?.setNavigationBarHidden(true, animated: true)
34
}
35
36
private func bindExistingAssetId(){
37
let assetId = UserDefaults.standard.string(forKey: Constants.LAST_BIND_ASSET_ID_KEY) ?? ""
38
39
if(!assetId.isEmpty) {
40
AssetTracking.shared.bindAsset(assetId: assetId) { responseCode in
41
let toastView = ToastView(message: "Bind asset successfully with id: " + assetId)
42
toastView.show()
43
} errorHandler: { error in
44
let errorMessage = error.localizedDescription
45
let toastView = ToastView(message: "Bind asset failed: " + errorMessage)
46
toastView.show()
47
}
48
}
49
}
50
51
private func setUpButtons(){
52
createAsset.setTitleColor(.white, for: .normal)
53
createAsset.layer.cornerRadius = 10
54
createAsset.layer.backgroundColor = UIColor.systemBlue.cgColor
55
56
viewDataLogs.setTitleColor(.systemBlue, for: .normal)
57
viewDataLogs.layer.cornerRadius = 10
58
viewDataLogs.layer.borderWidth = 1.0
59
viewDataLogs.layer.borderColor = UIColor.systemBlue.cgColor
60
61
createAsset.addTarget(self, action: #selector(onCreateAssetTapped), for: .touchUpInside)
62
63
viewDataLogs.addTarget(self, action: #selector(onViewDataLogsTapped), for: .touchUpInside)
64
}
65
66
@objc private func onCreateAssetTapped() {
67
if assetTracking.isRunning() {
68
let toastView = ToastView(message: "please stop tracking before editing asset profile")
69
toastView.show()
70
return
71
}
72
73
let setProfileViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "SetProfileViewController") as! SetProfileViewController
74
self.navigationController?.pushViewController(setProfileViewController, animated: true)
75
}
76
77
@objc private func onViewDataLogsTapped() {
78
let dataUploadLogVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "DataUploadLogViewController") as! DataUploadLogViewController
79
self.navigationController?.pushViewController(dataUploadLogVC, animated: true)
80
}
81
82
83
@IBAction func startTracking(_ sender: Any) {
84
let assetId = assetTracking.getAssetId()
85
86
if (assetId.isEmpty){
87
let toastView = ToastView(message: "Please bind asset first before start tracking!")
88
toastView.show()
89
return
90
}
91
92
assetTracking.startTracking()
93
}
94
95
@IBAction func stopTracking(_ sender: Any) {
96
locationInfo.text = ""
97
assetTracking.stopTracking()
98
}
99
100
101
@IBAction func onTrackingModeChanged(_ sender: Any) {
102
var trackingMode: TrackingMode = TrackingMode.ACTIVE
103
switch trackingModeSelector.selectedSegmentIndex {
104
case 0:
105
trackingMode = .ACTIVE
106
case 1:
107
trackingMode = .BALANCED
108
case 2:
109
trackingMode = .PASSIVE
110
default:
111
break
112
}
113
114
let locationConfig = LocationConfig(trackingMode: trackingMode)
115
assetTracking.updateLocationConfig(config: locationConfig)
116
}
117
118
func onTrackingStart(assetId: String) {
119
updateTrackingStatus()
120
}
121
122
func onTrackingStop(assetId: String, trackingDisableType: TrackingDisableType) {
123
updateTrackingStatus()
124
}
125
126
func onLocationSuccess(location: CLLocation) {
127
locationInfo.text = """
128
--------- Location Info ---------
129
Latitude: \(location.coordinate.latitude)
130
Longitude: \(location.coordinate.longitude)
131
Altitude: \(location.altitude)
132
Accuracy: \(location.horizontalAccuracy)
133
Speed: \(location.speed)
134
Bearing: \(location.course)
135
Time: \(location.timestamp)
136
"""
137
}
138
139
func showLocationAlert() {
140
let alert = UIAlertController(title: "Location Services Disabled", message: "To enable location services, please go to Settings > Privacy > Location Services.", preferredStyle: .alert)
141
alert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil))
142
143
present(alert, animated: true, completion: nil)
144
}
145
146
func onLocationFailure(error: Error) {
147
}
148
149
func onLocationServiceOff() {
150
showLocationAlert()
151
}
152
153
154
func updateTrackingStatus() {
155
trackingStatus.text = "Tracking Status: \(assetTracking.isRunning() ? "ON" : "OFF")"
156
if !assetTracking.isRunning() {
157
locationInfo.text = ""
158
}
159
}
160
161
}

Upon executing the code example provided above, your app's appearance will resemble the following snippet:

documentation imagedocumentation image
Asset Profile PageAsset Tracking Page

Code Highlights

The SetProfileViewController class inherits from the UIViewController class, which is the base class for all view controllers in iOS.

  • The class has several outlet properties: editCustomId, editAssetName, editAssetDescription, editAssetAttributes, lastAssetId, createAsset, and bindAsset. These properties are linked to the corresponding UI elements in the view controller's storyboard.

  • createAsset(): This method creates a new asset with the specified custom ID, asset name, description, and attributes. The method calls the AssetTracking.shared.createAsset() method to make the API request.

  • bindAsset(): This method binds the current view controller to the specified asset ID. The method calls the AssetTracking.shared.bindAsset() method to make the API request.

  • saveAssetProfile(): This method saves the asset profile to the user's device. The method updates the values of the customId, assetName, assetDescription, assetAttributes, and assetId properties, and then calls the userDefaults.set() method to save the values to the user's device.

  • onCreateAssetTapped(): This method is called when the user taps on the create asset button. The method checks if the asset name is empty, and if it is, it displays a toast message. Otherwise, it creates a new asset and saves the asset profile.

  • onBindAssetTapped(): This method is called when the user taps on the bind asset button. The method checks if the asset ID is empty, and if it is, it displays a toast message. Otherwise, it binds the current view controller to the asset and saves the asset profile.

  • dismissKeyboard(): This method dismisses the keyboard. The method calls the view.endEditing(true) method to dismiss the keyboard.

Here is a more detailed explanation of some of the key concepts in the code:

  • UITextFieldDelegate: This protocol is adopted by classes that want to receive notifications when a text field changes.

  • UIGestureRecognizerDelegate: This protocol is adopted by classes that want to receive notifications when a gesture recognizer is triggered.

  • AssetTrackingApiExceptionType: This enum defines the possible types of errors that can occur when calling the Asset Tracking API.

The ViewController class inherits from the UIViewController class, which is the base class for all view controllers in iOS.

  • The class has several outlet properties: locationInfo, trackingStatus, trackingModeSelector, startTracking, stopTracking, createAsset, and vie**wDataLogs. These properties are linked to the corresponding UI elements in the view controller's storyboard.

  • startTracking(): This method starts tracking the asset. The method calls the assetTracking.startTracking() method.

  • stopTracking(): This method stops tracking the asset. The method calls the assetTracking.stopTracking() method.

  • onTrackingStart(): This method is called when tracking starts. The method updates the trackingStatus label.

  • onTrackingStop(): This method is called when tracking stops. The method updates the trackingStatus label.

  • onLocationSuccess(): This method is called when a new location is received. The method updates the locationInfo label.

  • onLocationFailure(): This method is called when an error occurs while getting the location. The method does nothing in this case.

  • onLocationServiceOff(): This method is called when location services are turned off. The method displays an alert to the user.

Here is a more detailed explanation of some of the key concepts in the code:

  • AssetTracking: This class is responsible for tracking assets.

  • TrackingMode: This enum defines the three possible tracking modes: ACTIVE, BALANCED, and PASSIVE.

  • TrackingDisableType: This enum defines the possible reasons why tracking was stopped.

  • CLLocation: This class represents a location on Earth.

  • UIAlertController: This class is used to display alerts.

© 2024 NextBillion.ai all rights reserved.