TJHanaSDK is an iOS SDK for TJLabs Hana indoor services.
It provides authentication, Warp-based ward interaction UI, Venus-based indoor result delivery, and Jupiter-based indoor positioning and navigation features in a single SDK.
- 🔐 SDK authentication
- 🫧 Warp ward detection and click interaction
- 📍 Venus indoor result delivery
- 🧭 Jupiter indoor positioning and navigation
- 🔄 Real-time delegate-based event handling
- iOS 16.0+
- Swift 5.0+
- Info.plist
- Privacy - Motion Usage Description
- Privacy - Bluetooth Peripheral Usage Description
- Privacy - Bluetooth Always Usage Description
- Privacy - Location When In Use Usage Description
- Recommended device capabilities
- Accelerometer
- Gyroscope
- Magnetometer
- Bluetooth Low Energy
- Recommended background modes when continuous indoor service is required
- App communicates using CoreBluetooth
- App registers for location updates
pod 'TJHanaSDK'If you cannot find TJHanaSDK in CocoaPods, add the source below to your Podfile.
source 'https://github.com/CocoaPods/Specs.git'- If you need a more detailed guide, please refer to the links below.
- SDK Guide: https://tjlabs.notion.site/Hana-API-Reference-335aef6d5b728045813bfb783b945940?source=copy_link
- Authentication Guide: https://www.notion.so/tjlabs/SDK-Authorization-33eaef6d5b728034856ddc23489588f0?source=copy_link
- Demo Application : https://github.com/tjlabs/TJHana-demo-ios.git
import TJHanaSDK- You must authenticate before using Warp, Venus, or Jupiter.
TJHanaAuth.shared.auth(
accessKey: "YOUR_ACCESS_KEY",
secretAccessKey: "YOUR_SECRET_ACCESS_KEY"
) { code, success in
print("Auth:", success, "code:", code)
}TJWarpView provides a ward interaction view that can be attached to your screen and started after initialization.
final class WarpViewController: UIViewController, TJWarpViewDelegate {
private let warpView = TJWarpView()
override func viewDidLoad() {
super.viewDidLoad()
warpView.delegate = self
warpView.initialize(id: "USER_ID", sectorId: 1, forceUpdate: true)
}
func onInitSuccess(_ view: TJWarpView, _ isSuccess: Bool, _ code: WarpInitErrorCode?) {
guard isSuccess else { return }
warpView.startService()
warpView.configureFrame(to: self.view, warpImage: UIImage(named: "ic_warp"))
}
}warpView.stopService()extension ViewController: TJWarpViewDelegate {
func onInitSuccess(_ view: TJWarpView, _ isSuccess: Bool, _ code: WarpInitErrorCode?) {}
func onWarpSuccess(_ view: TJWarpView, _ isSuccess: Bool, _ code: WarpErrorCode?) {}
func onClick(_ view: TJWarpView, warpWards: [WarpWard]) {}
}public struct WarpWard: Codable {
public let id: Int
public let name: String
public let rssi: Int
public let contents: [WardContents]
}
public struct WardContents: Codable {
public let id: Int
public let name: String
public let url: URL
}TJVenusManager provides indoor result delivery through delegate callbacks.
final class VenusViewController: UIViewController, TJVenuseManagerDelegate {
private var venusManager: TJVenusManager?
override func viewDidLoad() {
super.viewDidLoad()
let manager = TJVenusManager(id: "USER_ID", sectorId: 1, forceUpdate: true)
manager.delegate = self
venusManager = manager
}
func onInitSuccess(_ manager: TJVenusManager, _ isSuccess: Bool, _ code: VenusInitErrorCode?) {
guard isSuccess else { return }
manager.startService()
}
}venusManager?.stopService()extension ViewController: TJVenuseManagerDelegate {
func onInitSuccess(_ manager: TJVenusManager, _ isSuccess: Bool, _ code: VenusInitErrorCode?) {}
func onVenusSuccess(_ manager: TJVenusManager, _ isSuccess: Bool, _ code: VenusErrorCode?) {}
func onVenusResult(_ manager: TJVenusManager, _ result: VenusResult) {}
}public struct VenusResult: Codable {
public let mobile_time: Int
public let building_id: Int
public let building_name: String
public let level_id: Int
public let level_name: String
public let x: Int
public let y: Int
}TJJupiterManager provides indoor positioning, in/out state changes, destination updates, and routing requests.
let manager = TJJupiterManager(id: "USER_ID", sectorId: 1, debugOption: false)
manager.delegate = selfmanager.startService(mode: .MODE_AUTO)manager.stopService { success, message in
print("Stopped:", success, "message:", message)
}manager.setNavigationDestination(
dest: Point(level_id: 2, x: 120, y: 240)
)manager.requestRouting(
start: RoutingStart(level_id: 2, x: 100, y: 200, absolute_heading: 90),
end: Point(level_id: 2, x: 120, y: 240),
waypoints: []
)extension ViewController: TJJupiterManagerDelegate {
func onInitSuccess(_ manager: TJJupiterManager, _ isSuccess: Bool, _ code: JupiterInitErrorCode?) {}
func onJupiterSuccess(_ manager: TJJupiterManager, _ isSuccess: Bool, _ code: JupiterErrorCode?) {}
func onJupiterReport(_ manager: TJJupiterManager, _ code: JupiterServiceCode, _ msg: String) {}
func onJupiterResult(_ manager: TJJupiterManager, _ result: JupiterResult) {}
func isJupiterInOutStateChanged(_ manager: TJJupiterManager, _ state: InOutState) {}
func isUserGuidanceOut() {}
func isNavigationRouteChanged(_ manager: TJJupiterManager, _ routes: [(String, String, Int, Float, Float)]) {}
func isNavigationRouteFailed() {}
func isWaypointChanged(_ manager: TJJupiterManager, _ waypoints: [[Double]]) {}
}public struct JupiterResult: Codable {
public var mobile_time: Int
public var index: Int
public var building_name: String
public var level_name: String
public var jupiter_pos: Position
public var navi_pos: Position?
public var llh: LLH?
public var velocity: Float
public var is_vehicle: Bool
public var is_indoor: Bool
public var validity_flag: Int
}public struct Position: Codable {
public var x: Float
public var y: Float
public var heading: Float
}
public struct LLH: Codable {
public var lat: Double
public var lon: Double
public var azimuth: Double
}
public struct RoutingStart: Codable {
public let level_id: Int
public let x: Int
public let y: Int
public var absolute_heading: Int
}
public struct Point: Codable {
public let level_id: Int
public let x: Int
public let y: Int
}public enum UserMode: String {
case MODE_PEDESTRIAN = "PDR"
case MODE_VEHICLE = "DR"
case MODE_AUTO = "AUTO"
}
public enum InOutState: Int {
case UNKNOWN = -1
case OUT_TO_IN = 0
case INDOOR = 1
case IN_TO_OUT = 2
case OUTDOOR = 3
}
public enum JupiterInitErrorCode: Int {
case UNKNOWN = -1
case INVALID_ID = 0
case INVALID_MODE = 1
case NETWORK_DISCONNECT = 2
case DUPLICATED_SERVICE = 3
case LOGIN_FAIL = 4
case CALC_INIT_FAIL = 5
}
public enum JupiterErrorCode: Int {
case UNKNOWN = -1
case INVALID_ID = 0
case INVALID_MODE = 1
case NETWORK_DISCONNECT = 2
case DUPLICATED_SERVICE = 3
case LOGIN_FAIL = 4
case GENERATOR_FAIL = 5
case CALC_INIT_FAIL = 6
}
public enum JupiterServiceCode: Int {
case UNKNOWN = -1
case SERVICE_FAIL = 0
case SERVICE_SUCCESS = 1
case BECOME_BACKGROUND = 2
case BECOME_FOREGROUND = 3
case BLUETOOTH_UNAVAILABLE = 4
case BLUETOOTH_OFF = 5
case BLUETOOTH_SCAN_STOP = 6
case NETWORK_DISCONNECT = 7
}- For a more detailed example, please refer to the demo project at the link below.
- https://github.com/tjlabs/TJJupiterVM-demo-ios
See the LICENSE file for details.