将Objective-c框架导入Swift框架(Google Analytics + Cocoapod)

时间:2021-04-15 15:22:46

I'm trying to centralize my commonly used Swift code into a framework, and part of that code uses Google Analytics. I brought in Google Analytics as a Cocoapod, but I can't access it from the new framework like I did from the original project because it's Objective-C and there's no bridging header support in frameworks [I'm using Swift 1.2].

我正在尝试将常用的Swift代码集中到框架中,部分代码使用Google Analytics。我将Google Analytics作为Cocoapod引入,但是我无法像在原始项目中那样从新框架访问它,因为它是Objective-C并且框架中没有桥接头支持[我正在使用Swift 1.2]。

The line of code I normally have in the bridging header that makes all of this work is:

我通常在桥接头中使用的代码行使所有这些工作:

#import <Google/Analytics.h>

Where exactly do I put this in my project to make this all work like it did before in the bridging header?

我在这个项目中的确切位置,使其全部像以前在桥接标题中那样工作?

What I found in Apple's documentation about mixing Swift and Objective-C is this:

我在Apple关于混合Swift和Objective-C的文档中发现的是:

Importing Code from Within the Same Framework Target

从同一框架目标中导入代码

If you’re writing a mixed-language framework, you may need to access your Objective-C code from Swift and your Swift code from Objective-C.

如果您正在编写混合语言框架,则可能需要从Swift访问Objective-C代码并从Objective-C访问Swift代码。

Importing Objective-C into Swift

将Objective-C导入Swift

To import a set of Objective-C files in the same framework target as your Swift code, you’ll need to import those files into the Objective-C umbrella header for the framework.

要在与Swift代码相同的框架目标中导入一组Objective-C文件,您需要将这些文件导入到框架的Objective-C总体标头中。

To import Objective-C code into Swift from the same framework

从同一框架将Objective-C代码导入Swift

Under Build Settings, in Packaging, make sure the Defines Module setting for that framework target is set to Yes. In your umbrella header file, import every Objective-C header you want to expose to Swift. For example: OBJECTIVE-C

在“构建设置”下的“打包”中,确保将该框架目标的“定义模块”设置设置为“是”。在您的伞头文件中,导入要向Swift公开的每个Objective-C头。例如:OBJECTIVE-C

import <XYZ/XYZCustomCell.h>

import <XYZ/XYZCustomView.h>

import <XYZ/XYZCustomViewController.h>

The phrase I take to be most relevant is where it says:

我认为最相关的短语是:

you’ll need to import those files into the Objective-C umbrella header for the framework

您需要将这些文件导入到框架的Objective-C伞形头中

But what is this file and how to you create it?

但是这个文件是什么以及如何创建它?

Apple's documentation mentions earlier (in a table):

Apple的文档较早提到(在表中):

Objective-C code

Import into Swift

导入Swift

#import "Header.h"

Well, I tried just creating a file "Header.h" and importing it, but that doesn't work. I don't know what they're trying to say. I can't find an "umbrella" anything in the build settings.

好吧,我尝试创建一个文件“Header.h”并导入它,但这不起作用。我不知道他们想说什么。我无法在构建设置中找到任何“伞”。

So my question is, how can I import this file (#import <Google/Analytics.h>) in my Swift project so that it can see the Google Analytics cocoapod framework like I would normally do in the bridging header of a normal project?

所以我的问题是,如何在我的Swift项目中导入此文件(#import ),以便它可以像普通项目的桥接标题中那样看到Google Analytics cocoapod框架?

Update:

I've come to believe that perhaps the objective-c bridging header is the .h file of the same name as the project. I've now tried adding the import statement there, and the error I get is:

我开始相信,Objective-c桥接头可能是与项目同名的.h文件。我现在尝试在那里添加import语句,我得到的错误是:

! Include of non-modular header inside framework module 'JBS'

!在框架模块'JBS'中包含非模块化头

3 个解决方案

#1


2  

The solution is not as straightforward as for an app. We have to create a modulemap.

解决方案并不像应用程序那么简单。我们必须创建一个模块图。

Have a look at this example repo.

看看这个例子repo。

Inside Swift code we can only import so called modules. The trick is to define a module which in turn contains all the ObjC headers that we need the Swift code to access.

在Swift代码中,我们只能导入所谓的模块。诀窍是定义一个模块,该模块又包含我们需要Swift代码访问的所有ObjC头。

The module map section of this article may also help you.

本文的模块映射部分也可能对您有所帮助。

As convenient as the bridging header is, it has one key limitation—you can’t use it inside a framework project. The alternative is to use a module.

与桥接头一样方便,它有一个键限制 - 你不能在框架项目中使用它。另一种方法是使用模块。

To do this, create a directory in your project directory named after the library you want to use. I did this in the shell, outside Xcode’s auspices, naming it CommonCrypto. Inside the directory, create a module.map file that encapsulates library settings. For CommonCrypto, module.map looks like this:

为此,请在项目目录中创建一个以要使用的库命名的目录。我在Xcode的赞助之外的shell中做了这个,命名为CommonCrypto。在目录中,创建一个封装库设置的module.map文件。对于CommonCrypto,module.map如下所示:

module CommonCrypto [system] { header "/usr/include/CommonCrypto/CommonCrypto.h" export * }

模块CommonCrypto [system] {header“/usr/include/CommonCrypto/CommonCrypto.h”export *}

Now add the new module to Import Paths under Swift Compiler – Search Paths in your project settings. Use ${SRCROOT} in the module path (e.g. ${SRCROOT}/CommonCrypto) to insure that the project works no matter where it’s checked out.

现在将新模块添加到项目设置中的Swift Compiler - Search Paths下的Import Paths。在模块路径中使用$ {SRCROOT}(例如$ {SRCROOT} / CommonCrypto)以确保项目无论在何处检出都能正常工作。

This makes it possible to just import CommonCrypto in your Swift files. Note that consumers of any frameworks you build using this technique are also going to have to add the module to their Swift search paths.

这使得可以在Swift文件中导入CommonCrypto。请注意,使用此技术构建的任何框架的使用者也必须将模块添加到其Swift搜索路径中。

I followed the procedure in the article above and made it work for my need this way:

我按照上面文章中的步骤进行操作,并以此方式满足我的需要:

module Muse { header "Muse.framework/Headers/Muse.h" export * }

模块Muse {header“Muse.framework / Headers / Muse.h”export *}

I removed the [system] lexon for safety (as it removes warning) and put the framework inside the same folder as the module.map file.

为了安全起见,我删除了[system] lexon(因为它删除了警告)并将框架放在与module.map文件相同的文件夹中。

Also, don't forget to include libc++.tbd and other required dependencies in your framework target (in the Linked Frameworks and Libraries section of the General tab) if you need them.

另外,如果需要,请不要忘记在框架目标中包含libc ++。tbd和其他必需的依赖项(在“常规”选项卡的“链接的框架和库”部分中)。

#2


1  

For those who have continued to ask if I had solved this problem, the following is how I accomplished this task importing the KochavaTracker SDK Cocoapod into a framework. That SDK is currently written in Objective-C. This answer is based on the answer provided by nuKs, with these specific steps.

对于那些继续问我是否已经解决了这个问题的人,以下是我如何完成将KochavaTracker SDK Cocoapod导入框架的任务。该SDK目前是用Objective-C编写的。这个答案是基于nuKs提供的答案,并提供了这些具体步骤。

1) Create a folder named KochavaTracker under your project folder, i.e. MyFramework/MyFramework/KochavaTracker.

1)在项目文件夹下创建一个名为KochavaTracker的文件夹,即MyFramework / MyFramework / KochavaTracker。

2) Within that folder create a file named module.modulemap and insert the following content:

2)在该文件夹中创建名为module.modulemap的文件并插入以下内容:

/*
 This is the private module which is used to make private ObjC headers available to Swift code.
 Note how all header files need to be specified with paths relative to this file.

 This file lives inside a folder, and that folder is the actual module. In Xcode the SWIFT_INCLUDE_PATHS needs to include the parent directory to that folder.
 */
module KochavaTracker {
    header "../../Pods/KochavaTrackeriOS/KochavaTrackeriOS/Classes/KochavaTracker.h"
    export *
}

This effectively creates a module which is a wrapper for interface of the SDK, which Swift can later import like it does other Swift modules. Note that it relies on a relative path to the Pods folder.

这有效地创建了一个模块,它是SDK接口的包装器,Swift稍后可以像其他Swift模块一样导入。请注意,它依赖于Pods文件夹的相对路径。

3) As found in build settings, modify your SWIFT_INCLUDE_PATHS to include:

3)如在构建设置中找到,修改您的SWIFT_INCLUDE_PATHS以包括:

$(SRCROOT)/MyFramework

This makes it so that you will be able to import the module and it will locate it in/as the KochavaTracker folder which you created under it.

这使得您可以导入模块,并将其定位在/作为您在其下创建的KochavaTracker文件夹中。

4) Add to your Swift code, where appropriate:

4)在适当的情况下添加到您的Swift代码:

import KochavaTracker

From there you should be able to reference the classes in the KochavaTracker module.

从那里你应该能够引用KochavaTracker模块中的类。

#3


0  

The bridging header file is a specific header file to use Objective-C file into a Swift framework.

桥接头文件是一个特定的头文件,用于将Objective-C文件用于Swift框架。

You can find more about bridging in the Apple doc :

您可以在Apple doc中找到有关桥接的更多信息:

Xcode creates the header file along with the file you were creating, and names it by your product module name followed by adding "-Bridging-Header.h". (You’ll learn more about the product module name later, in Naming Your Product Module.)

Xcode创建头文件以及您创建的文件,并按产品模块名称命名,然后添加“-Bridging-Header.h”。 (稍后您将在命名产品模块中了解有关产品模块名称的更多信息。)

So the only thing you have to make is create that file manually by choosing

因此,您唯一需要做的就是通过选择手动创建该文件

File > New > File > (iOS, watchOS, tvOS, or OS X) > Source > Header File.

文件>新建>文件>(iOS,watchOS,tvOS或OS X)>源>头文件。

If the name of your framework is ABC,then the name of the header file should be :

如果框架的名称是ABC,那么头文件的名称应该是:

ex : ABC-Bridging-Header.h

例如:ABC-Bridging-Header.h

You can put it where you want in your framework project.

您可以将它放在框架项目中的所需位置。

Hope this can help someone !

希望这可以帮助别人!

#1


2  

The solution is not as straightforward as for an app. We have to create a modulemap.

解决方案并不像应用程序那么简单。我们必须创建一个模块图。

Have a look at this example repo.

看看这个例子repo。

Inside Swift code we can only import so called modules. The trick is to define a module which in turn contains all the ObjC headers that we need the Swift code to access.

在Swift代码中,我们只能导入所谓的模块。诀窍是定义一个模块,该模块又包含我们需要Swift代码访问的所有ObjC头。

The module map section of this article may also help you.

本文的模块映射部分也可能对您有所帮助。

As convenient as the bridging header is, it has one key limitation—you can’t use it inside a framework project. The alternative is to use a module.

与桥接头一样方便,它有一个键限制 - 你不能在框架项目中使用它。另一种方法是使用模块。

To do this, create a directory in your project directory named after the library you want to use. I did this in the shell, outside Xcode’s auspices, naming it CommonCrypto. Inside the directory, create a module.map file that encapsulates library settings. For CommonCrypto, module.map looks like this:

为此,请在项目目录中创建一个以要使用的库命名的目录。我在Xcode的赞助之外的shell中做了这个,命名为CommonCrypto。在目录中,创建一个封装库设置的module.map文件。对于CommonCrypto,module.map如下所示:

module CommonCrypto [system] { header "/usr/include/CommonCrypto/CommonCrypto.h" export * }

模块CommonCrypto [system] {header“/usr/include/CommonCrypto/CommonCrypto.h”export *}

Now add the new module to Import Paths under Swift Compiler – Search Paths in your project settings. Use ${SRCROOT} in the module path (e.g. ${SRCROOT}/CommonCrypto) to insure that the project works no matter where it’s checked out.

现在将新模块添加到项目设置中的Swift Compiler - Search Paths下的Import Paths。在模块路径中使用$ {SRCROOT}(例如$ {SRCROOT} / CommonCrypto)以确保项目无论在何处检出都能正常工作。

This makes it possible to just import CommonCrypto in your Swift files. Note that consumers of any frameworks you build using this technique are also going to have to add the module to their Swift search paths.

这使得可以在Swift文件中导入CommonCrypto。请注意,使用此技术构建的任何框架的使用者也必须将模块添加到其Swift搜索路径中。

I followed the procedure in the article above and made it work for my need this way:

我按照上面文章中的步骤进行操作,并以此方式满足我的需要:

module Muse { header "Muse.framework/Headers/Muse.h" export * }

模块Muse {header“Muse.framework / Headers / Muse.h”export *}

I removed the [system] lexon for safety (as it removes warning) and put the framework inside the same folder as the module.map file.

为了安全起见,我删除了[system] lexon(因为它删除了警告)并将框架放在与module.map文件相同的文件夹中。

Also, don't forget to include libc++.tbd and other required dependencies in your framework target (in the Linked Frameworks and Libraries section of the General tab) if you need them.

另外,如果需要,请不要忘记在框架目标中包含libc ++。tbd和其他必需的依赖项(在“常规”选项卡的“链接的框架和库”部分中)。

#2


1  

For those who have continued to ask if I had solved this problem, the following is how I accomplished this task importing the KochavaTracker SDK Cocoapod into a framework. That SDK is currently written in Objective-C. This answer is based on the answer provided by nuKs, with these specific steps.

对于那些继续问我是否已经解决了这个问题的人,以下是我如何完成将KochavaTracker SDK Cocoapod导入框架的任务。该SDK目前是用Objective-C编写的。这个答案是基于nuKs提供的答案,并提供了这些具体步骤。

1) Create a folder named KochavaTracker under your project folder, i.e. MyFramework/MyFramework/KochavaTracker.

1)在项目文件夹下创建一个名为KochavaTracker的文件夹,即MyFramework / MyFramework / KochavaTracker。

2) Within that folder create a file named module.modulemap and insert the following content:

2)在该文件夹中创建名为module.modulemap的文件并插入以下内容:

/*
 This is the private module which is used to make private ObjC headers available to Swift code.
 Note how all header files need to be specified with paths relative to this file.

 This file lives inside a folder, and that folder is the actual module. In Xcode the SWIFT_INCLUDE_PATHS needs to include the parent directory to that folder.
 */
module KochavaTracker {
    header "../../Pods/KochavaTrackeriOS/KochavaTrackeriOS/Classes/KochavaTracker.h"
    export *
}

This effectively creates a module which is a wrapper for interface of the SDK, which Swift can later import like it does other Swift modules. Note that it relies on a relative path to the Pods folder.

这有效地创建了一个模块,它是SDK接口的包装器,Swift稍后可以像其他Swift模块一样导入。请注意,它依赖于Pods文件夹的相对路径。

3) As found in build settings, modify your SWIFT_INCLUDE_PATHS to include:

3)如在构建设置中找到,修改您的SWIFT_INCLUDE_PATHS以包括:

$(SRCROOT)/MyFramework

This makes it so that you will be able to import the module and it will locate it in/as the KochavaTracker folder which you created under it.

这使得您可以导入模块,并将其定位在/作为您在其下创建的KochavaTracker文件夹中。

4) Add to your Swift code, where appropriate:

4)在适当的情况下添加到您的Swift代码:

import KochavaTracker

From there you should be able to reference the classes in the KochavaTracker module.

从那里你应该能够引用KochavaTracker模块中的类。

#3


0  

The bridging header file is a specific header file to use Objective-C file into a Swift framework.

桥接头文件是一个特定的头文件,用于将Objective-C文件用于Swift框架。

You can find more about bridging in the Apple doc :

您可以在Apple doc中找到有关桥接的更多信息:

Xcode creates the header file along with the file you were creating, and names it by your product module name followed by adding "-Bridging-Header.h". (You’ll learn more about the product module name later, in Naming Your Product Module.)

Xcode创建头文件以及您创建的文件,并按产品模块名称命名,然后添加“-Bridging-Header.h”。 (稍后您将在命名产品模块中了解有关产品模块名称的更多信息。)

So the only thing you have to make is create that file manually by choosing

因此,您唯一需要做的就是通过选择手动创建该文件

File > New > File > (iOS, watchOS, tvOS, or OS X) > Source > Header File.

文件>新建>文件>(iOS,watchOS,tvOS或OS X)>源>头文件。

If the name of your framework is ABC,then the name of the header file should be :

如果框架的名称是ABC,那么头文件的名称应该是:

ex : ABC-Bridging-Header.h

例如:ABC-Bridging-Header.h

You can put it where you want in your framework project.

您可以将它放在框架项目中的所需位置。

Hope this can help someone !

希望这可以帮助别人!