Skip to content

iOS 接入指南

旧版本迁移指引:

为区分洞见SDK,1.2.0版本起,Unity SDK 主要做了重命名工作,默认会将前缀 OasisSDK更名为 OasisUnitySDK, ARO更名为 AROasisUnity 如:AROLandmark,1.2.0 之后会更名为 AROasisUnityLandmark,仅名称差异,不涉及数据模型改动。迁移过程中如报错,请按上述方法做对应修改,或者直接参考demo重新接入。

简介

OasisUnitySDK 是基于 Unity3D 引擎的 SDK,集成应用验证、资源下载、算法跟踪、沉浸AR体验等功能

示例 Demo:(gitlab 如无权限,请备注合作信息后,邮件联系: zhenghongsheng@ezxr.comDemo,请直接搜索**【接入】**标签,按照注释提示接入SDK。

Demo测试识别图:

SDK 集成

一、pod 集成

  1. 选择版本 Tag:iOS Release Note
  2. 静态库版本:在 podfile 中添加
bash
# 已发布至CocoaPods;私有依赖库会整体打包,不再需要显式引入
pod 'OasisUnitySDK', :git => 'https://gitlab.com/InsightAR/ARWorldSDK-iOS.git', :tag => '1.9.6'

1.9.4版本及之前

bash
# 主要SDK及相关公共依赖库,1.8.2表示示意的tag版本,请修改为实际接入的版本
  pod 'OasisUnitySDK', :git => 'https://gitlab.com/InsightAR/ARWorldSDK-iOS.git', :tag => '1.8.2'
  pod 'opencv2',  :git => 'https://gitlab.com/InsightAR/OasisSDKLibs.git', :tag => '1.0.5'
  pod 'Mapbox',   :git => 'https://gitlab.com/InsightAR/OasisSDKLibs.git', :tag => '1.0.5'

  
  # 💡 1.8.0及其之后的版本,需要对应添加以下依赖
  pod 'BeaconLib', :git => 'https://gitlab.com/InsightAR/OasisSDKLibs.git', :tag => '1.0.5'
  1. 终端执行 pod install 或 pod update 命令,拉取对应依赖仓库
  2. AppName.plist 中,添加相机等隐私权限:

必要权限

  • 相机权限:Privacy - Camera Usage Description

按需添加

  • 相册权限:Privacy - Photo Library Additions Usage Description

1.8.0 版本及以后,需要添加以下权限

  • GPS权限:Privacy - Location Always and When In Use Usage Description
  • GPS权限:Privacy - Location When In Use Usage Description
  • 蓝牙权限:Privacy - Bluetooth Always Usage Description
  • 蓝牙权限:Privacy - Bluetooth Peripheral Usage Description

二、手动集成

不推荐,请尽量使用 pod 方式集成。

如有特殊需求,请联系 kelexin@ezxr.com,zhenghongsheng@ezxr.com

Issues

  • 需要手动添加定制过的openCV版本
  • a lot of warnings

接口

简单可记为四个功能:注册SDK、下载资源、AR体验和关闭、AR交互。

更细致可分以下功能块

  1. SDK 注册:用于应用鉴权(必须)
  2. 兼容性检查:用于检查设备是否在兼容性名单(必须)
  3. 数据获取:获取周围可体验的场景列表,(非必须**)**
  4. 资源下载:下载可以用于AR 体验的资源(也可以通过本地方式加载),(必须)
  5. 生命周期:管理AR生命周期,包括启动 SDK 引擎加载响应资源,添加 ARView,关闭 SDK。(必须)
  6. 资源管理:判断资源是否已下载、是否有更新、删除等(非必须)
  7. AR Script:与 AR 通信使用,需要处理 AR 界面上的按键回调,比如关闭按键的关闭回调。(非必须但是重要
  8. 工具:SDK 版本号获取、logLevel 设置。

SDK 注册

1. 申请你的AppKey及AppSecret

先注册登录 ARWorld 创作者平台,在【我的应用】模块【创建应用】。

目前仅开放移动端 SDK,如有接入 SIM、Nreal、EZXR AR Glasses 的需求请联系易现工作人员或产品kelexin@ezxr.com

关键填写项目:

  • 适配设备,指应用适配何种设备,我们会生成对应平台的授权信息;

  • 存储方式,指应用的内容存储在何处,我们在平台提供存储服务,你也可使用阿里云的 OSS 服务,填写 阿里云OSS 的相关配置即可。

    完成应用创建后,再进入应用详情页,填写你要接入的 SDK 版本。

使用AppKey\Secret注册

(特别注意:这里需要传入 app 的启动参数,即 APP_KEYAPP_SECRET,需要替换为自己的)

swift
AROasisUnityManager.shared()?.registerAppKey(APP_KEY, 
                                            appSecret: APP_SECRET, 
                                        launchOptions: launchOptions)

二、 兼容性检查

SDK 需要需要 A10 及以上处理器的机型(iPhone 7 及以上);系统 iOS 12.0 及以上; 不支持 x86 架构;建议 A11 及以上(iPhone 8、iPhone X)

swift
if AROasisUnityManager.isSDKSupported() == false {
     /// SDK 不支持当前机型,最好关闭、隐藏入口
}

三、 列表获取

如果需要展示数据列表(比如获取周围可体验AR数据),可以调用相关的接口。

比如,获取当前GPS位置附近可体验AR事件列表:

swift
/// 默认:获取附近的
func getNearbyLandmarkers(showFinish: Bool) {
    let landmarkersOption = AROasisUnityNearbyLandmarkersPrepareOption.init(longitude: longitude, latitude: latitude)
    AROasisUnityManager.shared()?.queryLandmarkersNearby(landmarkersOption, onSuc: { (landmarkers: [AROasisUnityLandmarker]?) in
        SwiftyBeaver.debug(landmarkers as Any)
        if let landmarkers = landmarkers {
            // 数据展示
        } else {
            // 获取为空,服务端未配置?请联系产品
        }
    }, failed: { (error: Error?) in
        // 数据获取失败
    })
}

或者,获取当前位置为中心、指定半径范围内可体验的AR场景列表。

swift
let landmarkersOption = AROasisUnityLandmarkersPrepareOption.init(coordiType: .WGS84,
                                                         longitude: longitude,
                                                         latitude: latitude,
                                                         maxDistance: Double(range), tagIds: "")
AROasisUnityManager.shared()?.queryLandmarkers(landmarkersOption, onSuc: { (landmarkers:[AROasisUnityLandmarker]?) in
    if let landmarkers = landmarkers {
        // 数据展示
    } else {
        // 获取为空,服务端未配置?请联系产品
    }
}, failed: { (error:Error?) in
    // 数据获取失败
})

四、 资源下载

需要根据 cid下载对应的 AR 资源。

该方法会自动检测更新,无需更新会直接返回本地已下载的资源。

swift
// 初始化 landmarkerOption:AROasisUnityPrepareOption
let landmarkerOption:AROasisUnityPrepareOption.init(cid:"cid")

// 如果是通过扫码二维码获取的 ARORespLandmarker
// let landmarkerOption:AROasisUnityPrepareOption.init(qrCodeLandmaker: respLandmarker)

// 下载资源,会自动判断是否有更新
AROasisUnityManager.shared()?.prepareLandmarker(withOptions: landmarkerOption, downloadProgress: { (progress:Progress?) in
    if let progress = progress {
        let complected = progress.fractionCompleted
        let showString = String(format: "下载进度 = %.0f %%", complected*100)
        // 进度显示
    }
}, completion: { (error:Error?, result:AROasisUnityLandmarker?) in
    if let error = error {
        // 处理error
        return
    }
    if let result = result {
       // 下载成功,加载AR,参考生命周期
    }
})

停止下载

swift
AROasisUnityManager.shared()?.stopPrepare(landmarker.cid)

/// 或者 

AROasisUnityManager.shared()?.stopPrepare(respLandmarker.content.cid)

五、 生命周期(最重要)

生命周期处理是最重要的环节,此时需要完成

  • SDK 是否初始化完成:如果 App 启动后,不会立即加载 AR,可跳过此步骤
swift
/**
SDK初始化需要一定的时间,期间不可以调用AR相关的加载方法及事件交互
此方法用于安全性检测,如果返回false,请避免继续操作SDK
*/
AROasisUnityManager.shared().isReadyToUse()
  • SDK启动:包含算法启动、渲染引擎启动
  • OasisView:View获取和添加
  • Error 处理:比如初始化失败错误
  • 回调事件处理:比如ARView上的关闭按键点击
  • 生命周期事件处理:比如退出OasisView,资源加载出错等
swift
func startOasisRunning(result: AROasisUnityLandmarker) {
    AROasisUnityManager.shared()?.startRunning(result, stateCallback: { (result:AROasisStartResult?) in
        guard let result = result else {
            return
        }
        if result.state == .error {
            return
        }
        if self.oasisView == nil {
             if let oasisView = AROasisUnityManager.shared()?.getOasisView() {
                 self.oasisView = oasisView
                 self.view.insertSubview(oasisView, at: 0)
             } else {
                 print("nil Oasis View")
             }
         }
    }, eventCallback: { (event: AROasisEvent?) in
        guard let event = event else {
            print("nil event")
            return
        }
        switch event.type{
        case .function_RunScript:
            let data = try? JSONSerialization.data(withJSONObject: event.params ?? "", options: [])
            let str = String(data: data!, encoding: String.Encoding.utf8)
            AROasisUnityManager.shared().runOasisScript("test", param: str) { event in
                print("ARScript脚本执行完成")
            }
        default:
            return
        }
    }, loadCallback: { (type: UInt) in
        let eventType = IAREventType.init(UInt32(type))
        switch eventType{
        case IAREventTypeExitMainContent:
            self.oasisView?.removeFromSuperview()
            // 退出相关事件处理
            self.navigationController?.popViewController(animated: true)
        default:
            return
        }
    })
}

六、 资源管理

判断一个资源本地是否已经存在

swift
if let isLocalResource = AROasisUnityManager.shared()?.isDownloadedLandmarker(landmarker.cid){
    if isLocalResource {
        // 已存在,可以继续判断是否有新版本更新,或者操作删除
    } else {
        // 不存在,可以执行下载
    }
}

检查资源是否有更新

swift
AROasisUnityManager.shared().checkUpdate(landmarker.cid) { shouldUpdate, _ in
    if shouldUpdate {
        // 执行更新操作
    } else {
        // 不需要更新,直接打开本地资源即可
    }
} failed: { error in
    print("check update failed." + error.debugDescription)
}

删除单个资源

swift
AROasisUnityManager.shared()?.removeResource(landmarker.cid)

删除所有资源

swift
AROasisUnityManager.shared()?.removeAllResources()

七、 与SDK通信

  1. 与 AR 内容通信

通常用于定制类 AR 内容的事件交互,需要自定义事件名称及具体参数,通过以下方法告知 SDK,由 SDK 中转到内容层。

scriptName:事件名称,约定名称,无特殊限制,同一 case 请避免重复

params:事件参数,通常携带一些业务参数,如 {"id":"123"} 等

swift
AROasisUnityManager.shared()?.runOasisScript("scriptName", param: "params", callback: nil)

从 SDK 获取回调:参考 AROasisEvent

  1. 与 SDK 通信

内容层已经有能力调起 SDK 的相关功能,但需要内容层来触发,比如截屏、录屏等操作。

接入方如有主动截屏当前帧的需求,则需要直接和 SDK 通信。

目前已经支持主动截屏

swift
/**
截屏事件对应
*/
AROasisUnityManager.shared().screenshot()

八、 生命周期管理/建议

AR 内容可以发起若干事件,接入方可自行选择监听并做出对应响应,各事件对应说明如下:

IAREventTypeValueDescription
IAREventTypeLoadMainContentSuccess201

AR 内容成功加载,表示AR已经可以正常加载

**使用场景:**此时可以隐藏接入方自己的相关UI

IAREventTypeExitMainContent202

AR 内容发起了关闭操作,引擎此时也会停止渲染

**使用场景:**接入方需要监听此事件,退出 AR 体验流程

IAREventTypeExitMainContentError203

AR 内容加载出错(网络、内容不兼容等原因)

**使用场景:**接入方需要监听此事件,处理打开失败的场景

IAREventTypeContentLoading204

AR场景正在加载,进度值需要读取“progress”字段,推荐样式类型需要读取“loading_style”字段

**使用场景:**接入方需要监听此事件,定制app风格的loading样式,示例用法详见demo

IAREventTypeContentLoadingCompleted205

AR场景加载完成,推荐样式类型需要读取“loading_style”字段,隐藏自定的loading视图

**使用场景:**接入方需要监听此事件,隐藏自定义风格的loading视图,完成场景过渡

九、 自定义场景过渡效果

AR 初次加载,或进行场景切换时,都需要一段时间进行加载内容,此期间内相机会出现黑屏。为了更好的交互体验,需要接入方按照实际需求,获取 SDK 的加载进度参数后,定制对应的加载动画完成过渡。(可以参考 Demo 示例)

请参考第八项表格中的:

IAREventTypeContentLoading、 IAREventTypeContentLoadingCompleted

完成相关过渡效果的开始、结束等动画效果。

swift
  // loading 动画,需要接入方定制
var loadingView: ARLoadingView?
// 子场景加载动画,需要接入方定制
var poiLoadingView: ARPOILoadingView?

AROasisUnityManager.shared()?.startRunning(result, stateCallback: { [unowned self] (result:AROasisUnityStartResult?) in
    // 处理AR运行状态,加载相机视图等
}, eventCallback: { [unowned self] (event:AROasisUnityEvent?) in
    // 处理AR交互事件
}, loadCallback: { (event: AROasisUnityLoadEvent?) in
    guard let event = event else {
        return
    }
    switch event.type {
    case IAREventTypeContentLoading: // 加载进度
        if let progress = event.params["progress"] as? NSNumber {
            if let style = event.params["loading_style"] as? Int {
                let type = IARSceneLoadingType.init(rawValue: style)
                switch type {
                case .fullScreen:
                    // 全屏样式,用于下载完成后,内容加载到展示的阶段
                    if self.loadingView == nil { // loadingView 为动画浮层,请参考demo实现
                        self.loadingView = ARLoadingView.init(frame: self.view.bounds)
                        self.view.addSubview(self.loadingView!)
                    }
                    self.loadingView?.updateProgress(progress: progress.floatValue)
                case .fullMask:
                    // 用于子场景切换
                    if self.poiLoadingView == nil { // poiLoadingView 为动画浮层,请参考demo实现
                        self.poiLoadingView = ARPOILoadingView.init(frame: self.view.bounds, type: type!)
                        self.view.addSubview(self.poiLoadingView!)
                        self.poiLoadingView?.performAnimations()
                    }
                case .unloading:
                    // 用于场景卸载
                    if self.poiLoadingView == nil { // poiLoadingView 为动画浮层,请参考demo实现
                        self.poiLoadingView = ARPOILoadingView.init(frame: self.view.bounds, type: type!)
                        self.view.addSubview(self.poiLoadingView!)
                        self.poiLoadingView?.performAnimations()
                    }
                default:
                    _ = "未知的类型"
                }
                print("正在打开:\(progress.floatValue)")
            }
        }
    case IAREventTypeContentLoadingCompleted:
        if let style = event.params["loading_style"] as? Int {
            switch IARSceneLoadingType.init(rawValue: style) {
            case .fullScreen:
                // 全屏样式,用于下载完成后,内容加载到展示的阶段
                self.loadingView?.removeFromSuperview()
                self.loadingView = nil
            case .fullMask:
                // 用于子场景切换
                self.poiLoadingView?.removeFromSuperview()
                self.poiLoadingView = nil
            case .unloading:
                // 用于场景卸载
                self.poiLoadingView?.removeFromSuperview()
                self.poiLoadingView = nil
            default:
                _ = "未知的类型"
            }
        }
    default:
        return
    }
})

十、 辅助/调试/工具类功能

获取 SDK 版本

swift
// 获取当前接入SDK的版本号
let sdkVersion = AROasisUnityManager.getSDKVersion()

设置LogLevel

swift
// 初期接入,建议打开log,方便定位问题
AROasisUnityManager.setLogLevel(.DEBUG)

十一、 拍照与视频录制

  1. 拍照

SDK 可以主动发起拍照,拍照结果会在对应的事件回调中返回,包含了已封装的 UIImage,以及未处理过的图片路径,如下:

swift
// SDK 发起拍照,API 位于 AROasisUnityManager.h
AROasisUnityManager.shared().screenshot()

// ------------------------------------------

// 监听拍照结果
AROasisUnityManager.shared()?.startRunning(result, stateCallback: { [unowned self] (result:AROasisUnityStartResult?) in
            // 添加AR视图相关,忽略
        }, eventCallback: { [unowned self] (event:AROasisUnityEvent?) in
            guard let event = event else {
                return
            }
            // 截屏回调,包含:[image:UIImage, imagepath: String]
            case .function_ScreenShot:
                // 💡 以下代码仅作为结果演示,具体处理方式请根据实际需求调整。 
                if let image = event.params["image"] as? UIImage {
                    let imageView = UIImageView.init(image: image)
                    let displayWidth = UIScreen.main.bounds.width / 2
                    let displayHeight = displayWidth * image.size.height / image.size.width
                    imageView.frame = CGRect.init(origin: .zero, size: CGSize.init(width: displayWidth, height: displayHeight))
                    imageView.center = self.view.center
                    self.view.addSubview(imageView)
                    DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
                        imageView.removeFromSuperview()
                    }
                }
            default:
                return
            }
        }, loadCallback: { (event: AROasisUnityLoadEvent?) in
            // 声明周期相关,忽略
        })
  1. 录屏(1.9 及更早版本)

SDK 可以主动发起录屏以及结束录屏,拍照结果会以通知的形式返回,代码示例如下:

swift
// SDK 主动开始录屏,API 位于 AROasisUnityManager.h
AROasisUnityManager.shared().startRecord()
// SDK 主动结束录屏,API 位于 AROasisUnityManager.h
AROasisUnityManager.shared().stopRecord()

// ------------------------------------------

// 注册录屏结果通知,监听录屏结果(kIARRecordStart、kIARRecordSuccess、kIARRecordFailed 在SDK中已定义)
NotificationCenter.default.addObserver(self, selector: #selector(handleRecordStart(noti:)), name: NSNotification.Name.init(kIARRecordStart), object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(handleRecordSuccess(noti:)), name: NSNotification.Name.init(kIARRecordSuccess), object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(handleRecordFailed(noti:)), name: NSNotification.Name.init(kIARRecordFailed), object: nil)

/// 处理录屏结果
@objc func handleRecordSuccess(noti: Notification) {
    if let url = noti.userInfo?["url"] as? URL {
        print("video url:" + url.absoluteString)
        // todo: 视频结果需要接入方自行处理
    }
} 
/// 处理录屏开始
@objc func handleRecordStart(noti: Notification) {
    print("录屏开始")
}
/// 处理录屏失败
@objc func handleRecordFailed(noti: Notification) {
    print("录屏失败")
}
  1. 录屏(2.0+ 版本)

2.0 版本调整了录屏相关的代码结构,整合后的录屏,和截屏处理方式保持一致,作为一个回调消息来处理。

swift
// SDK 主动开始录屏,API 位于 AROasisUnityManager.h
AROasisUnityManager.shared().startRecord()
// SDK 主动结束录屏,API 位于 AROasisUnityManager.h
AROasisUnityManager.shared().stopRecord()

// ------------------------------------------

AROasisUnityManager.shared()?.startRunning(result, stateCallback: { [unowned self] (result:AROasisUnityStartResult?) in
            // 略
        }, eventCallback: { [unowned self] (event:AROasisUnityEvent?) in
            guard let event = event else {
                return
            }
            switch event.type{
            // 录屏回调,包含:[videopath: String]
            case .stopScreenRecord:
                if let videoUrl = event.params["videopath"] as? String {
                    debugPrint("录屏结束,录屏沙盒路径: " + videoUrl)
                    // 拿到沙盒录屏后,即可处理视频文件
                } else {
                    debugPrint("录屏结果异常。")
                }
            default:
                return
            }
        }, loadCallback: { (event: AROasisUnityLoadEvent?) in
            // 略
        })