返回博客首页
← 所有帖子

将不可访问的 Swift 代码与 NativeScript 集成

2022 年 5 月 26 日 — 作者 Jason Cassidy

使用 NativeScript,我们可以完全访问 Java、Kotlin、Objective-C 和 Swift 中的原生平台 API。所有平台语言都为其带来了独特的结构。使用 Swift,您可以使用所有内容,只要您想要使用的结构将其类型暴露给 Objective-C。

那么,如果 Swift 结构没有暴露给 Objective-C,我们如何将第三方 Swift 代码集成到我们的 NativeScript 应用程序中呢?

我们要集成的示例 Pod

这是我们将使用的示例 Swift pod,https://github.com/SwiftKickMobile/SwiftMessages,它允许您以多种方式显示漂亮的邮件卡片。

它也不适用于 Objective-C。

设置我们的项目并添加 Pod

让我们创建一个示例项目

ns create SwiftSample --template @NativeScript/template-hello-world-ts
cd SwiftSample
ns run ios

我们得到了可爱的点击按钮屏幕。

添加 Pod

现在我们在我们的应用程序中添加对 pod 的引用

  • App_Resources/iOS/PodFile 中添加一个文件
  • 添加文本
    pod 'SwiftMessages'
    

类型在哪里?

此时,我们可以生成类型,这样我们就可以对 Pod 中的内容进行强类型访问。

ns typings ios

这将在 typings/ios/<architecture> 下生成一个文件。

我们确实有一个 objc!SwiftMessages.d.ts 文件,但有一个问题。

如果我查看 SwiftMessages 的文档,一个简单的用例是 SwiftMessages.show(view: myView),但我们的类型文件中在哪里没有 SwiftMessages 类?

这里的问题是 SwiftMessages 类没有暴露给 Objective-c,因此无法从 NativeScript 访问。更多信息请访问 这里

包装它!

那么解决方案是什么?凭借 NativeScript 的魔力,我们可以

  • 将我们自己的 swift 文件放入我们的项目中
  • 使其可供 Objective-C 访问
  • 从那里调用 Pod 中的类方法。

首先,我们在项目中 App_Resources/iOS/src/NSCSwiftMessages.swift 中添加一个新的 Swift 文件。

让我们添加一些代码(从 SwiftMessages github 上的示例复制),以便我们能够从 NativeScript 中调用它。

**注意**:您可以在此处使用任何类前缀,但我们建议使用 NSC 前缀以确保您的类不会与 NS 前缀类冲突,这些类 在整个 iOS 代码中很常见,这是由于 NeXTStep 的整个 iOS 平台的基础。您可以将 NSC 视为“NativeScript”或“NativeScript Compiler”的缩写,如果这能让您更容易记住。这也有助于简化您以这种方式公开的任何平台代码的命名约定,仅供 NativeScript 使用。

import Foundation
import SwiftMessages

@objcMembers
@objc(NSCSwiftMessages)
public class NSCSwiftMessages: NSObject {

  // We can assign a callback to this from TypeScript.
  @objc public var onDoneCallBack: ((String)-> Void)? = nil;

  public func showMessage(title: String, body: String) {

    // Instantiate a message view from the provided card view layout. SwiftMessages searches for nib
    // files in the main bundle first, so you can easily copy them into your project and make changes.
    let view = MessageView.viewFromNib(layout: .cardView)

    // Theme message elements with the warning style.
    view.configureTheme(.success)
    view.button?.isHidden=true;
    // Add a drop shadow.
    view.configureDropShadow()
    view.button?.isHidden=true;
    // Set message title, body, and icon. Here, we're overriding the default warning
    // image with an emoji character.
    let iconText = ["🤔", "😳", "🙄", "😶"].randomElement()!
    view.configureContent(title: title, body: body, iconText: iconText)

    // Increase the external margin around the card. In general, the effect of this setting
    // depends on how the given layout is constrained to the layout margins.
    view.layoutMarginAdditions = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20)

    // Reduce the corner radius (applicable to layouts featuring rounded corners).
    (view.backgroundView as? CornerRoundingView)?.cornerRadius = 10

    var config = SwiftMessages.Config()

    // Specify one or more event listeners to respond to show and hide events.
    config.eventListeners.append() { event in
        if case .didHide = event {
            // If the callback is setup call it.
            self.onDoneCallBack?("Message Alert Hidden");
            
        }
    }

    // Show the message.
    SwiftMessages.show(config: config, view: view)
  }
}

这里重要的元素是我们通过添加注释 @objcMembers@objc 将我们的 swift 暴露给了 Objective-C。

现在,我们可以再次运行类型生成,并将有一个新的文件 objc!nsswiftsupport.d

在这个新文件中,我们将看到我们新类的类型

declare class NSCSwiftMessages extends NSObject {

	static alloc(): NSCSwiftMessages; // inherited from NSObject

	static new(): NSCSwiftMessages; // inherited from NSObject

	onDoneCallBack: (p1: string) => void;

	showMessageWithTitleBody(title: string, body: string): void;
}

我们将此文件复制到项目的根目录(或任何有意义的地方),并在 ./references.d.ts 文件中添加对它的引用。

/// <reference path="./node_modules/@nativescript/types/index.d.ts" />
/// <reference path="./objc!nsswiftsupport.d.ts" />

调用我们的新代码

现在,我们可以调用我们的代码,因此在 app/main-view-model.ts 中将点击方法更改为

onTap() {
  // Create an instance of the Swift class we created.
  const message = NSCSwiftMessages.new();
  // Assign a callback to get notified when the message closes
  message.onDoneCallBack = (message: string) => {this.message = message;};
  // Show the actual message
  message.showMessageWithTitleBody("This is the Title", "Hello There!");
}

现在,当我们运行应用程序并点击按钮时,我们漂亮的全新消息就会显示出来!

当它隐藏时,我们会得到一个回调,该回调会更改标签。

示例项目在 GitHub 上可用