PhoenixSdk is an indoor positioning, mobile-ready, offline, Sdk, designed to be integrated into an existing mobile application effortlessly
- Ready to use
- Modular Features
- iOS Optimized
- Localize user into indoor/outdoor area thanks to low-signal Bluetooth beacons and GPS
- Calculate user realtime position
- Monitoring indoor/outdoor switching and change floor
- Elaborate custom road to point of interest
You can also:
- Integrate this module into an existing app to customize UX
- Manipulate indoor/outdoor user's position coordinate freely
- Swift 5
- iOS 13.2
- xcode 12
CocoaPods is a dependency manager for Cocoa projects. For usage and installation instructions, visit their website. To integrate PhoenixSdk into your Xcode project using CocoaPods, open your terminal:
pod repo add NextomeSdk https://gitlab.com/Nextome/nextome-ios-sdk
Open your Podfile and add these lines:
After add this pod:
pod 'PhoenixSdk', :git => 'https://github.com/Nextome/POD-Nextome-Sdk'
pod 'NextomeLegacy', :git => 'https://github.com/Nextome/POD-Nextome-Sdk'
Then, into general tab, add this capabilities:
- Background Modes
- Uses Bluetooth LE accessories
- Location Updates
Finally, add this line to your Info.plist
- Privacy - Location Always and When In Use Usage Description
- Privacy - Location Always Usage Description
- Privacy - Location When In Use Usage Description
DONE
If you want only to test map, you can use x86 framework version. Remove previous integration lines and replace with these, into your podfile:
pod 'PhoenixSdk_x86', :git => 'https://github.com/Nextome/POD-Nextome-Sdk'
pod 'NextomeLegacy_x86', :git => 'https://github.com/Nextome/POD-Nextome-Sdk'
##Notice
-
Into iOS simulator, init Nextome Sdk with override mode; specifying venue and map (you will read more details as you go along), because Simulator doesn't support Bluetooth.
-
Replace x86 pod with arm64 before send to store, because Apple rejects all type of project which contains third party library that integrates simulator architecture.
PhoenixSdk is currently design with public async observer
Info | Observer |
---|---|
User position | POSITION_STREAM |
Floor change | FLOOR_CHANGE |
Indoor/outdoor switch | OUTDOOR_STREAM |
General Log | LOG_WRITER |
Error | ERROR |
- Create Nextome.plist file and insert it, into your main's app Target -> (You can see here: Nextome.plist) Insert bundle data and you can adjust settings* for sdk. Settings Detail
Setting | Detail |
---|---|
Particle Engine | Correction of user position trajectory |
Position Log | Send last calculate user position to Nextome server, to improve sdk |
Mode | "BLE" localize user venue through beacon monitoring. |
"GPS" localize user venue through gps | |
Binaries | -Not implemented yet- |
You can modify these settings, easily calling NextomeSdk, public interface:
- sdkMode = "GPS" or "BLE"
- enableParticleEngine = true/false
- enableSendLog = true/false
- enableBinaries = true/false
- Import and initialize Sdk, into your AppDelegate
import PhoenixSdk
var sdk: NextomeSdk?
After didFinishLaunchingWithOptions
self.sdk = NextomeSdk(device: String? = nil, logTimer: Int? = nil, venueId: Int? = nil, floorId: Int? = nil)
Optional override parameter | Detail |
---|---|
device | Set custom device data |
logTimer | Customize send log to Nextome server timer |
venueId | Ovveride venue id and skipping localization |
floorId | Ovveride current map |
If you specificy VenueId and FloorId, Sdk localization's feature will turn off. NextomeSdk will only download maps and display theme.
Otherwise, if you want to init NextomeSdk with localization enabled; without customizations, you can easily do this:
self.sdk = NextomeSdk()
DONE
If you need to show, user position on virtual map, follow this step to integrate compiled flutter framework into your project:
- open xworkspace and import our compiled flutter module, which you can find here
- Download and open flutter_map.zip
- Choose configuration: Debug, Release, Profile
- drag & drop frameworks, into main workspace
- check "Copy items if needed" into alert windows
- click "general" tab of workspace -> "Frameworks, Library section"
- set "Embed & Sign in" of whole of them, except for FlutterPluginRegistant
By default, Sdk use Compiled Nextome Flutter Map cross-plattform module, developed to display user position and indoor map. If you want to customize it, with different features or design, follow this step to integrate, Nextome Flutter Map Source files.
-
Configure Flutter Sdk and prepare Android Studio with flutter plugins Flutter Get Started
-
Clone into your main project's folder, Nextome Flutter Map respository Repository
-
Open flutter_mapview module with Android Studio and run it on iOS simulator. After finish, you're now ready to integrate modules into iOS main App.
-
Add to your PodFile
flutter_application_path = '{path}/flutter_mapview'
load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
install_all_flutter_pods(flutter_application_path)
Run pod install
- Into your AppDelegate import and init Flutter engine
import Flutter
import FlutterPluginRegistrant
var flutterEngine : FlutterEngine?
After didFinishLaunchingWithOptions
self.flutterEngine = FlutterEngine(name: "io.flutter", project: nil)
self.flutterEngine?.run(withEntrypoint: nil)
GeneratedPluginRegistrant.register(with: self.flutterEngine!)
- Into your Main View Controller prepare flutter channel, to connect iOS and Flutter Engine.
import Flutter
var flutterViewController: FlutterViewController?
var flutterEngine = (UIApplication.shared.delegate as? AppDelegate)?.flutterEngine
var mapChannel = FlutterMethodChannel()
flutterViewController = FlutterViewController(engine: flutterEngine!, nibName: nil, bundle: nil)
self.mapChannel = FlutterMethodChannel(name: "net.nextome.phoenix", binaryMessenger: flutterViewController!.binaryMessenger)
DONE
Open your Main View Controller and into viewDidLoad, attach to main's observers
//position observer
NotificationCenter.default.addObserver(self, selector: #selector(onDidReceivePosition(_:)), name: NSNotification.Name(rawValue: "POSITION_STREAM"), object: nil)
//position observer
NotificationCenter.default.addObserver(self, selector: #selector(onDidReceiveFloorChange(_:)), name: NSNotification.Name(rawValue: "FLOOR_CHANGE"), object: nil)
//path observer
NotificationCenter.default.addObserver(self, selector: #selector(onDidReceivePath(_:)), name: NSNotification.Name(rawValue: "PATH_STREAM"), object: nil)
//outdoor observer
NotificationCenter.default.addObserver(self, selector: #selector(onDidReceiveOutdoor(_:)), name: NSNotification.Name(rawValue: "OUTDOOR_STREAM"), object: nil)
(optional)
//log observer
NotificationCenter.default.addObserver(self, selector: #selector(onDidReceiveLogs(_:)), name: NSNotification.Name(rawValue: "LOG_WRITER"), object: nil)
//error observer
NotificationCenter.default.addObserver(self, selector: #selector(onDidReceiveError(_:)), name: NSNotification.Name(rawValue: "ERROR"), object: nil)
//Refresh Poi observer
NotificationCenter.default.addObserver(self, selector: #selector(onRefreshPoiComplete(_:)), name: NSNotification.Name(rawValue: "REFRESHPOI_COMPLETE"), object: nil)
Prepares for use, onDidReceive functions, to manage Sdk data.
@objc func onDidReceivePosition(_ notification: Notification){
//receive position
let positionData = notification.userInfo as? [String : [String: Any]]
//get data
let x = positionData!["positionData"]!["x"] as! String
let y = positionData!["positionData"]!["y"] as! String
let floor = positionData!["positionData"]!["floor"] as! String
//send to flutter engine, new position's data
self.mapChannel.invokeMethod("position", arguments: x + "," + y)
}
//MARK: MAP Path observer
@objc func onDidReceivePath(_ notification: Notification){
//receive path
let pathData = notification.userInfo as? [String : String]
if(pathData!["pathData"] != nil){
if(pathData!["pathData"] == "[]"){
//advise ui
PKHUD.sharedHUD.contentView = PKHUDTextView(text: "Shortest path not found. Please try again")
PKHUD.sharedHUD.show()
PKHUD.sharedHUD.dimsBackground = false
PKHUD.sharedHUD.hide(afterDelay: 2) { success in
}
}else{
self.mapChannel.invokeMethod("path", arguments: pathData!["pathData"]!)
}
}
}
@objc func onDidReceiveOutdoor(_ notification: Notification){
//receive position
let positionData = notification.userInfo as? [String : Double]
//get data
let lat = positionData!["lat"]!
let lng = positionData!["lng"]!
}
@objc func onDidReceiveFloorChange(_ notification: Notification){
//receive position
let floorData = notification.userInfo as? [String : [String: Any]]
//update poi
let poiData = self.sdk?.getPOIData()
mapChannel.invokeMethod("POI", arguments: poiData)
//update map's package
let data = self.sdk?.getVenueData()
mapChannel.invokeMethod("localPackageUrl", arguments: data)
}
(optional)
@objc func onDidReceiveError(_ notification: Notification){
//advise user
print("Errore. Riavvia l'sdk ")
}
@objc func onDidReceiveLogs(_ notification: Notification){
//get data
let logInfo = notification.userInfo as? [String: String]
var log = logInfo!["log"]!
}
@objc func onRefreshPoiComplete(_ notification: Notification){
//get new data and send to flutter engine
let poiData = self.sdk?.getPOIData()
mapChannel.invokeMethod("POI", arguments: poiData)
}
Start Sdk
sdk?.start()
Stop Sdk
sdk?.stop()
Get current venue package data to easily send it, to flutter engine
sdk?.getVenueData() -> String?
Get current venue POI data to easily send it, to flutter engine
sdk?.getPOIData() -> String?
Force Refresh current venue POI data to easily send it, to flutter engine
sdk?.refreshPOI()
Force Refresh current displayed map Keep Attention: if you force mapId during localization, sdk locks forced map and not switchs anymore
sdk?.forceRefreshMap(mapId: Int)
To restore map's switch, according to user position
sdk?.setLiveMap()
Calculate shortest route:
- from user position to selected map's POI
- from custom position A to custom position B and send it to flutter engine
sdk?.calculatePath_ToPOI(poi: String)
sdk?.calculatePath_ToCustomPoint(point: String)
Get current user position data
sdk?.getPositionData() -> [String: Any]
send SDK Report to Nextome admin
sdk?.sendLogReport -> Void
Easily show flutter_map module with current venue (localized place) data
//push flutter controller
self.navigationController?.pushViewController(flutterViewController!, animated: true)
//get map's poi
let poiData = self.sdk?.getPOIData()
mapChannel.invokeMethod("POI", arguments: poiData)
//get map's resources
let data = self.sdk?.getVenueData()
mapChannel.invokeMethod("localPackageUrl", arguments: data)
//flutter callbacks
mapChannel.setMethodCallHandler{(call: FlutterMethodCall, result: FlutterResult) -> Void in
// Handle poi json
if call.method == "poiData"{
//calculate path
self.sdk?.calculatePath(poi: call.arguments as? String)
}
// Handle long press
if(call.method == "customPosition"){
let customPosition = call.arguments as! String
//advise user
if(self.sdk?.customPositionCheck == 0){
//advise ui
PKHUD.sharedHUD.contentView = PKHUDTextView(text: "Start point is set")
PKHUD.sharedHUD.show()
PKHUD.sharedHUD.hide(afterDelay: 1.0) { success in
}
}
self.sdk?.calculateCustomPath(point: customPosition)
}
}
You can clone this repository and explore our complete Example project, to see how to manage all Sdk's data.
If you have problem with app export from xcode or AppStore distribution, follow this steps:
- Open your Xcode project's setting
- Click on Build Phase tab
- Add new Run Script and paste this code, which remove all x86 files and architecture from project
# Type a script or drag a script file from your workspace to insert its path.
APP_PATH="${TARGET_BUILD_DIR}/${WRAPPER_NAME}"
find "$APP_PATH" -name '*.framework' -type d | while read -r FRAMEWORK
do
FRAMEWORK_EXECUTABLE_NAME=$(defaults read "$FRAMEWORK/Info.plist" CFBundleExecutable)
FRAMEWORK_EXECUTABLE_PATH="$FRAMEWORK/$FRAMEWORK_EXECUTABLE_NAME"
echo "Executable is $FRAMEWORK_EXECUTABLE_PATH"
echo $(lipo -info "$FRAMEWORK_EXECUTABLE_PATH")
FRAMEWORK_TMP_PATH="$FRAMEWORK_EXECUTABLE_PATH-tmp"
case "${TARGET_BUILD_DIR}" in
*"iphonesimulator")
echo "No need to remove archs"
;;
*)
if $(lipo "$FRAMEWORK_EXECUTABLE_PATH" -verify_arch "i386") ; then
lipo -output "$FRAMEWORK_TMP_PATH" -remove "i386" "$FRAMEWORK_EXECUTABLE_PATH"
echo "i386 architecture removed"
rm "$FRAMEWORK_EXECUTABLE_PATH"
mv "$FRAMEWORK_TMP_PATH" "$FRAMEWORK_EXECUTABLE_PATH"
fi
if $(lipo "$FRAMEWORK_EXECUTABLE_PATH" -verify_arch "x86_64") ; then
lipo -output "$FRAMEWORK_TMP_PATH" -remove "x86_64" "$FRAMEWORK_EXECUTABLE_PATH"
echo "x86_64 architecture removed"
rm "$FRAMEWORK_EXECUTABLE_PATH"
mv "$FRAMEWORK_TMP_PATH" "$FRAMEWORK_EXECUTABLE_PATH"
fi
;;
esac
echo "Completed for executable $FRAMEWORK_EXECUTABLE_PATH"
echo $(lipo -info "$FRAMEWORK_EXECUTABLE_PATH")
done
MIT
© 2020 Nextome srl | All Rights Reserved.