English
This article aims to help iOS developers get started with UTS quickly.
Readers are required to have iOS native application development experience.
UTS 插件是 uni-app 新型插件形式 详情
For iOS developers, what we need to know is:
framework
dependent libraryframework
dependent library will be added to the packaging project to generate the final ipa package因为 UTS 语法与 swift 较类似,建议快速阅读后,在实践中掌握 UTS 语法。uts语法介绍。
objective-c
languageAlthough it is not required to master swift to develop UTS plug-ins, since UTS is currently on the iOS platform, it will be compiled into swift source code and master the swift language, which is convenient for troubleshooting and complex function implementation.
Therefore, it is recommended to learn swift syntax, and it is recommended to read
UTS and swift are basically consistent in terms of data types, but in some scenarios, there are still differences, which are specifically explained here
In principle:
**The data type is subject to the built-in type of UTS, and each native platform will automatically adapt to it. **
**When the API parameters of a specific platform cannot be compatible with the UTS type, the data type explicitly required by the other party is allowed to prevail. **
Int, Float, and Double types do not exist in UTS Developers should use the Number type to cover the scenarios where Int, Float, and Double are used on the iOS platform during the development process
However, when it is necessary to rewrite the system method or implement the protocol method (delegate method) of the third-party dependent library during development, for example, when the API explicitly requires the parameter to be Int, you need to write the native type Int
Let's take a protocol method as an example. It is necessary to implement a protocol method defined in a three-party dependency library.
// swift
// This protocol is defined in other third-party SDKs
protocol TestProtocol {
func addTwoInts(_ a: Int, _ b: Int) -> Int
}
When we need to implement the protocol method in the above three-party library in UTS, since the parameter and return value types are required to be of type Int, in order to adapt to this situation, UTS allows developers to use the data type Int of the native platform to meet the requirements of the native API. Data type requirements:
// implement the protocol method in UTS
class TestClass implements TestProtocol {
addTwoInts(a: Int, b: Int): Int {
return a + b
}
}
Note: The implements
keyword is used in UTS to indicate compliance with a protocol, which will be described in detail below
For iOS projects, in addition to source code, it also involves common issues such as dependencies, resources, and project configuration
This chapter will introduce how to configure these properties in the UTS plug-in development environment
Notice:
When the plugin needs to add configuration items in the native project Info.plist, it needs to create the Info.plist file in the plugin's app-ios directory
Take the configuration file in the uts-tencentgeolocation Tencent location plugin in hello uts as an example:
Example file location in hello uts:
~/uni_modules/uts-tencentgeolocation/utssdk/app-ios/Info.plist
This example indicates that you need to configure APIKey in Info.plist and enable background location permission
Info.plist example:
<?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>TencentLBSAPIKey</key>
<string>您申请的APIKey</string>
<key>UIBackgroundModes</key>
<array>
<string>location</string>
</array>
</dict>
</plist>
The Info.plist format and configuration rules are consistent with the iOS project, and the configuration information will be merged into the native project's Info.plist when packaging in the cloud
HBuilder X 3.6.11+ version support
When the plugin needs to enable related services in capabilities, it needs to create a UTS.entitlements file in the plugin's app-ios directory
For example, you need to check the Access WiFi Information item in capabilities, and the corresponding configuration example of UTS.entitlements:
<?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>com.apple.developer.networking.wifi-info</key>
<true/>
</dict>
</plist>
The UTS.entitlements format and configuration rules are consistent with those in the iOS project, and the configuration information will be merged into the entitlements configuration file of the native project when packaging in the cloud
If your plug-in needs to rely on resource files such as pictures, audio, etc., you can put the resource files in the path ~/utssdk/app-ios/Resources/
under the plug-in directory
All files in this directory will be added to the main bundle of the application when packaging in the cloud. It is recommended to save only the built-in resource files of the uts plugin.
uts 插件支持依赖三方库,目前支持 framework、xcframework(仅云打包支持)、.a库
需要将依赖的framework或者xcframework文件存放到插件目录下 ~/utssdk/app-ios/Frameworks/
路径中
When packaging in the cloud, all dependent libraries in this directory will be added to the project. It is recommended to store only dependent libraries related to plug-ins
Take the uts-tencentgeolocation Tencent positioning plug-in in hello uts as an example. This plug-in needs to rely on the Tencent positioning library TencentLBS.framework
, then put the dependent inventory in ~/uni_modules/uts-tencentgeolocation/utssdk/app-ios/Framework/ TencentLBS.framework
location is enough
└─Libs // .a库存放目录
├─MyStaticLibA //A静态库(该库所有文件放在此文件夹内,OC库)
│ ├─libMyStaticLib.a //.a文件,必须
│ ├─MyStaticLib.h //A.a库对应的头文件,必须
│ ├─MyClassA.h //需要暴露的头文件A,可选
│ └─MyClassB.h //需要暴露的头文件B,可选
└─TestSwiftLibrary //B静态库(该库所有文件放在此文件夹内,Swift库)
├─libTestSwiftLibrary.a //.a文件,必须
└─TestSwiftLibrary.swiftmodule //.swiftmodule文件夹,必须
注意:
// uts
const aResult = ToolA.toolAMethod();
const bResult = ToolB.toolBMethod();
const libResult = TestLib.testLib();
const res = {
aResult: aResult,
bResult: bResult,
libResult: libResult
};
options.success?.(res);
// uts
import { Tool, Manager, TestLibraryExa } from 'TestLibraryExa';
Manager.testManager();
Tool.testTool()
let lib = TestLibraryExa();
lib.test()
console.log(lib.version);
有些第三方的 SDK 使用 OC 语言开发,且产物 .framework 文件里不包含 Moudules 文件夹。这就造成该 SDK 不支持 use module 模式,不能直接在 Swift 文件中导入。 这样的 SDK 不能直接被 uts 插件引用,需要做以下处理:
如果你有 SDK 的源码,那么有以下几种方法可以生成 Modules。
以名称为 TestSDK
的 framework 为例:
通过创建和 SDK 的 target 同名的头文件方式:
.h
文件,如 TestSDK.h
;TestSDK.h
中,如 #import <TestSDK/TestA.h>
;target
-> Build Phases
-> Headers
中将刚创建的 TestSDK.h
设置为 public
;通过自定义 Module Map 的方式:
TestSDK
SDK 源码目录下创建 module.map.modulemap
文件;TestSDK
为例,实践时需要改为自己的 SDK 和文件名;target
-> Build Settings
-> Module Map File
设置 Module Map File
为 $(PROJECT_DIR)/TestSDK/module.map.modulemap
;framework module TestSDK {
header "TestA.h" //需要对外暴露的头文件,需要为 plubic 的文件
header "TestB.h" //需要对外暴露的头文件,需要为 plubic 的文件
export *
}
如果使用的是第三方非开源的 SDK, 那么可以使用下面的方式来生成 Modules:
以 TestSDK
为例:
TestSDK.framework
文件夹下创建 Modules
文件夹;Modules
文件夹下创建 module.modulemap
文件;注意: 实践时要将
TestSDK
改成你要操作的 SDK 名称,.h 文件也要改成你要暴露的头文件名字。
framework module TestSDK {
// 下面的.h 文件都要是 TestSDK.framework -> Headers 文件夹下的头文件
header "AClass.h"
header "BClass.h"
header "CClass.h"
header "DClass.h"
export *
}
HBuilder X 3.6.11+ version support
DCloudUTSFoundation is a built-in library for the framework, and all uts plugins will depend on this basic library
DCloudUTSFoundation will encapsulate some common methods for developers to call directly
When using it, you need to import the UTSIOS class in the uts file first, and all methods are called through the UTSIOS class
// Import UTSIOS class from DCloudUTSFoundation dependent library
import { UTSiOS } from "DCloudUTSFoundation"
HBuilder X 3.6.11+ version support
Get the UIViewController instance displayed by the current app
Take uts-alert in hello uts as an example:
Example file location in hello uts:
~/uni_modules/uts-alert/utssdk/app-ios/index.uts
export function showAlert(title: string|null, message: string|null, result: (index: Number) => void) {
// The uts method will be executed in the sub-thread by default, and the UI operations involved must run in the main thread, and the code can be run in the main thread through the DispatchQueue.main.async method
DispatchQueue.main.async(execute=():void => {
// Initialize UIAlertController instance object alert
let alert = new UIAlertController(title=title,message=message,preferredStyle=UIAlertController.Style.alert)
// create UIAlertAction button
let okAction = new UIAlertAction(title="确认", style=UIAlertAction.Style.default, handler=(action: UIAlertAction):void => {
// callback method for clicking the button
result(0)
})
// create UIAlertAction button
let cancelAction = new UIAlertAction(title="取消", style=UIAlertAction.Style.cancel, handler=(action: UIAlertAction):void => {
// callback method for clicking the button
result(1)
})
// Add UIAlertAction to alert
alert.addAction(okAction)
alert.addAction(cancelAction)
// Open the alert popup
UTSiOS.getCurrentViewController().present(alert, animated= true)
})
}
HBuilder X 3.6.11+ version support
Convert string color value to UIColor
format support
example
let bgColor = UTSiOS.colorWithString("#000000")
view.backgroundColor = bgColor
HBuilder X 3.6.11+ version support
Get the runtime absolute path of the specified plugin resource
For the resource path of the socket, please pass the absolute path of the resource in the project directory
example
const imagePath = UTSiOS.getResourcePath("/static/logo.png")
console.log(imagePath)
const image = new UIImage(contentsOfFile = imagePath)
/* imagePath: "/var/mobile/Containers/Data/Application/FA7080BA-3EF7-4C4E-B7C5-0332539B2964/Documents/Pandora/apps/__UNI__FB95CAB/www/static/logo.png" */
Continuously updating
Read through the chapters above.
So far we think you have mastered UTS syntax, basic swift syntax, and UTS support for iOS resources.
But for a swift language user who is familiar with iOS development, there are many common habits that have changed, and we will specifically point out in this chapter, so that developers can deepen their understanding.
In swift, let
is used to declare constants, and var
is used to declare variables
// swift
var str = "abc" // 声明一个字符串变量
let str1 = "abc" // 声明一个字符串常量
In uts
, use const
to declare constants, and let
to declare variables
// swift
let str = "abc" // 声明一个字符串变量
const str1 = "abc" // 声明一个字符串常量
Optional types in swift are defined as type?
// swift
var user: String? = nil
Optional types in uts are defined as type | null
// uts
let user: string | null = null
There is no need to use the new
keyword when calling the constructor to create an instance object in swift
var alert = UIAlertController()
When calling the constructor in uts to instantiate an object, you need to add the new
keyword before the constructor
var alert = new UIAlertController()
在 swift 中参数名称使用 :
连接参数值,在 uts 中需要使用 =
连接
example
// swift
var alert = UIAlertController(title: "提示", message: "提示内容", preferredStyle: .alert);
// written in uts
let alert = new UIAlertController(title="提示", message="提示内容", preferredStyle=UIAlertController.Style.alert)
Enumerations can ignore the enumeration type and directly abbreviate .enumeration value
in swift. UTS does not support abbreviation. You need to write enumeration type.enumeration value
in its entirety
In the above example, the value of the last parameter preferredStyle in swift can be abbreviated as
.alert
in uts requires full write out
UIAlertController.Style.alert
枚举在 swift 中可以定义关联类型,可在关联值中传递信息,但是在 ts 中没有这种语法,所以目前 uts 还暂不支持此种用法。
// 定义带关联值的枚举
enum Barcode {
case upc(Int, Int, Int, Int)
case qrCode(String)
}
// 定义枚举值
var productBarcode = Barcode.upc(8, 85909, 51226, 3)
productBarcode = .qrCode("ABCDEFGHIJKLMNOP")
// 匹配
switch productBarcode {
case let .upc(numberSystem, manufacturer, product, check):
print("UPC : \(numberSystem), \(manufacturer), \(product), \(check).")
case let .qrCode(productCode):
print("QR code: \(productCode).")
}
如果遇到上述类型的枚举,且在三方库中无法改动的,可以在 Swift 文件中进行调用,然后把 该 Swift 文件打包在 framework 中供 uts 插件使用; 如果上述类型的枚举定义在有源码的 swift 中时,可以将其定义成不包含关联值的枚举,然后使用合适的数据结构来表示关联值携带的信息。
When defining a subclass in swift to inherit from the parent class, you need to add the parent class name after the subclass name, separated by a colon :
// swift
class Son: Father {
}
The extends
keyword needs to be used instead of the colon :
in uts
// uts
class Son extends Father {
}
To make a custom type conform to a certain protocol in swift, when defining the type, you need to add the protocol name after the type name, separated by a colon :
. When following multiple protocols, separate each protocol with a comma ,
:
class SomeClass: FirstProtocol, AnotherProtocol {
}
The implements
keyword needs to be used instead of the colon :
in uts
class SomeClass implements FirstProtocol, AnotherProtocol {
}
The method of judging the system version in swift
// swift
if #available(iOS 10.0, *) {
}
This syntax is not supported in uts and can be replaced by the following
if (UTSiOS.available("iOS 10.0, *")) {
}
Closures in swift can be shortened
// If the last parameter in swift is a closure, it is called a trailing closure, which can be abbreviated as follows, regardless of the parameter label type
let action = UIAlertAction(title: "确认", style: .default) { action in
}
Shorthand syntax is not supported in uts, and the closure function needs to be written completely
// The closure function corresponding to the handler in uts must be written completely
let action = new UIAlertAction(title="确认", style=UIAlertAction.Style.default, handler=(action: UIAlertAction):void => {
})
原生中有些方法的闭包参数是逃逸闭包,此时就要在闭包前面添加 @escaping
标记:
// 在闭包参数前添加@escaping
function requestLocationPromise(@escaping completion: (res: boolean)=>void) {
}
When calling native methods involving target-action in uts, such as adding click event methods to UIButton
and registering notification center event methods, precautions,
The following is an example of listening to screen capture events:
Example file location in hello uts:
~/uni_modules/uts-screenshot-listener/utssdk/app-ios/index.uts
// Register to monitor screen capture events and callback methods
// The target-action callback method needs to be constructed by Selector("method name")
const method = Selector("userDidTakeScreenshot")
NotificationCenter.default.addObserver(this, selector = method, name = UIApplication.userDidTakeScreenshotNotification, object = null)
// method to capture screenshot callback
// The method of target-action needs to be prefixed with @objc
@objc static userDidTakeScreenshot() {
const obj = new UTSJSONObject()
// callback
this.listener?.(obj)
}
Dictionary type in swift, use Map type in uts instead
// swift
var value: Dictionary<String,Any> = Dictionary()
value["name"] = "uts"
// uts
let map: Map<string,any> = new Map()
map.set("name","uts")
HBuilder X 3.6.11+ version support
When overriding the system method or implementing the protocol method of the third-party SDK, some methods may have parameter labels
Taking Tencent positioning in hello uts as an example, the protocol method needs to be implemented when monitoring location changes:
tencentLBSLocationManager(_ manager: TencentLBSLocationManager, didUpdate location: TencentLBSLocation)
The second parameter of this method has didUpdate
parameter label
The implementation in native swift is
// swift
func tencentLBSLocationManager(_ manager: TencentLBSLocationManager, didUpdate location: TencentLBSLocation) {
var response = LocationResponse();
response.name = location.name;
response.address = location.address;
response.latitude = NSNumber(location.location.coordinate.latitude);
response.longitude = NSNumber(location.location.coordinate.longitude);
self.locationOptions?.success(response);
}
In uts, the annotation syntax @argumentLabel("didUpdate") needs to be used to represent the parameter label
// uts
// The delegate method that implements location updates
tencentLBSLocationManager(manager: TencentLBSLocationManager, @argumentLabel("didUpdate") location: TencentLBSLocation) {
let response = new LocationResponse();
response.name = location.name;
response.address = location.address;
response.latitude = Number(location.location.coordinate.latitude);
response.longitude = Number(location.location.coordinate.longitude);
this.locationOptions?.success(response)
}
Example file location in hello uts:
~/uni_modules/uts-tencentgeolocation/utssdk/app-ios/index.uts
只写参数名称的参数,编译后会在参数前默认增加 _
来忽略参数标签(如上面的示例,第一个参数 manager,这种方式能兼容绝大多数方法,尤其是Swift 调用 OC 方法),但是有些参数没有参数标签,默认添加 _
的行为会和原生方法定义不一致,这种情况需要定义一个空的参数标签来解决 @argumentLabel("didUpdate")
以高德定位 SDK 的代理方法为例:第三个参数 reGeocode 只有参数名称,没有参数标签
// swift
func amapLocationManager(_ manager: AMapLocationManager!, didUpdate location: CLLocation!, reGeocode: AMapLocationReGeocode!)
uts 实现此方法时,需要给 reGeocode 参数添加一个空的参数标签
// uts
amapLocationManager(manager : AMapLocationManager, @argumentLabel("didUpdate") location : CLLocation, @argumentLabel("") reGeocode : AMapLocationReGeocode) {
}
Swift marks a function or method as asynchronous, you can add the async
keyword after the parameter list in its declaration
// swift
@available(iOS 13.0.0, *)
func testAsync(_ opts: AsyncOptions) async -> UTSJSONObject {
if (opts.type == "success") {
opts.success("success");
}
else {
opts.fail("fail");
}
opts.complete("complete");
return UTSJSONObject([
"name": "testAsync"
]);
}
The definition of an asynchronous method in uts is to add the async
keyword at the beginning of the method
// uts
async function testAsync(opts: AsyncOptions) {
if (opts.type == "success") {
opts.success("success");
} else {
opts.fail("fail");
}
opts.complete("complete");
return { name: "testAsync" };
}
Note: Using async to define an asynchronous method is only supported by iOS 13+ versions, and errors will be reported when calling in lower versions
swift中try有以下三种方式:
以JSON反序列化为例
// swift
do{
let dict = try JSONSerialization.jsonObject(with: d, options: [])
print(dict)
}catch{
// catch 中默认提供error信息, 当序列化不成功是, 返回error
print(error)
}
// swift
// 注意:dict是个可选值
let dict = try? JSONSerialization.jsonObject(with: data, options: [])
// swift
// 注意:dict是个可选值
let dict = try! JSONSerialization.jsonObject(with: data, options: [])
为了满足Swift上述语法,UTS使用特殊语法来支持,以上三种写法分别对应为:
// uts
try {
let dict = UTSiOS.try(JSONSerialization.jsonObject(with = data, options = []))
}catch (e) {
console.log(e)
}
// uts
UTSiOS.try(JSONSerialization.jsonObject(with = data, options = []), "?" )
// uts
UTSiOS.try(JSONSerialization.jsonObject(with = data, options = []), "!" )
Refer to the uts-alert plugin in the Hello UTS project
path:
~/uni_modules/uts-alert/utssdk/app-ios/index.uts
DispatchQueue.main.async(execute=():void => {
// The UI can be operated in the main thread
})
Refer to the uts-toast plugin in the Hello UTS project
path:
~/uni_modules/uts-toast/utssdk/app-ios/index.uts
When HBuilderX currently writes iOS uts plugins, some grammatical hints will be missing and the parameter types will be inaccurate, for example:
These issues will be optimized in subsequent versions