制作Swift Framework

Creating your first iOS Framework

 January 07, 2016

If you’ve ever tried to create your own iOS framework, you know that it’s not for the faint of heart – managing dependencies and writing tests doesn’t make it any easier. This tutorial will walk you through creating your first iOS framework from start to finish so that you can go out and create your own.

We’re going to build a framework that exposes a function calledRGBUIColor(red:green:blue) that returns a new UIColor created from those values. We’ll build it using Swift, with Carthage as our dependency manager. Our framework will be consumable using Carthage, CocoaPods, or git submodules.

Let’s begin!

Setting up the Xcode Project

  • Select File → New → Project.
  • Select iOS → Framework & Library from the left sidebar and select “Cocoa Touch Library” on the right.
  • Click “Next” and fill in the option prompts. Make sure to select the “Include Unit Tests” check box.

  • Select where you’d like to save your project.
  • Uncheck “Create Git repository on My Mac”, we’ll manually set it up later.
  • Click “Create” and the project will open in Xcode.
  • Go to File → Save As Workspace and save it in the same directory as your Xcode project with the same name. We put the project in a workspace because we’ll be adding our Carthage dependencies as submodules; they must be in a workspace for Xcode to build them.
  • Close the Xcode project with File → Close Project.
  • Open the workspace with File → Open.
  • Click on the scheme on the top left of Xcode and select “Manage Schemes”. We need to mark our scheme as “shared” so that the project can be built with Carthage.
  • Locate the “RGB” scheme and check the “Shared” check box and click “Close”.

Let’s jump over to the terminal.

Initializing Git

First, navigate to the directory you saved your project in.

  • Run git init to initialize an empty repository.
  • Create a .gitignore which will keep out some pesky Xcode and dependency files we don’t want to track in git.

Here’s a standard one for Swift projects with a few modifications. We added .DS_Storeand removed fastlane and extra comments.

## OS X Finder
.DS_Store

## Build generated
build/
DerivedData

## Various settings
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata

## Other
*.xccheckout
*.moved-aside
*.xcuserstate
*.xcscmblueprint

## Obj-C/Swift specific
*.hmap
*.ipa

# Swift Package Manager
.build/

# Carthage
Carthage/Build

Adding Carthage and Dependencies

  • Create a file in your project directory named Cartfile and the runtime dependencies to it. We’ll add Curry.
github "thoughtbot/Curry"
  • Create a Cartfile.private. It will house private dependencies like our test frameworks. We’ll use Quick and Nimble.
github "Quick/Quick"
github "Quick/Nimble"
  • Create a bin/setup script. It’s used to give our contributors (and us) a simple way to set up the project and the dependencies.
mkdir bin
touch bin/setup
chmod +x bin/setup
  • Open up bin/setup and fill it with:
#!/usr/bin/env sh

if ! command -v carthage > /dev/null; then
  printf 'Carthage is not installed.\n'
  printf 'See https://github.com/Carthage/Carthage for install instructions.\n'
  exit 1
fi

carthage update --platform iOS --use-submodules --no-use-binaries

In this script, we make sure the user has Carthage installed and run its update command to install the iOS dependencies.

We’re using --use-submodules so that our dependencies are added as submodules. This allows users to consume our framework outside of Carthage if they want. We use--no-use-binaries so that our dependencies are built on our system.

With bin/setup created, let’s run it so that Carthage will download our dependencies.

  • In the terminal, run bin/setup.

Now we need to set up our project to build and link the new dependencies.

Adding Dependencies to the Workspace

Since our dependencies are submodules, we need to add them to our workspace.

  • Open up Carthage/Checkouts and add each dependency’s .xcodeproj to the root of the workspace. They can be dragged from Finder into the navigator of the Xcode project.

When you’re done it should look like:

  • With “RGB” selected in the Navigator and the “RGB” target selected on the middle sidebar, select the “Build Phases” tab and expand the “Link binary with libraries” section.
  • Click the “+” icon and select the Curry.framework from the Curry-iOS target.
  • Click “Add”.

  • Select the “RGBTests” target from the middle sidebar.
  • Using the same process as before, add the Quick and Nimble frameworks to the “Link binary with libraries” section for this target.

When adding dependencies to each target, Xcode will automatically add them to the “Framework Search Paths” under the “Build Settings” tab. We can remove these from the “RGB” and “RGBTests” target because Xcode treats them as implicit dependencies due to them being in the same workspace.

  • Select the target, locate the “Framework Search Paths” setting, highlight it, and press “backspace” on your keyboard.

  • Next, look in the “RGB” project in the Navigator; you’ll see there are three new frameworks at the root level. To keep this area organized, highlight all three, right click and select “New group from selection” to place them in a named group. I’ll call mine “Frameworks”.

Now that Carthage is set up, let’s add CocoaPods.

Adding CocoaPods support

To add CocoaPods support, we need to create a .podspec at the root of our project and fill in our project info.

  • Create a file named RGB.podspec.
  • Copy and paste the sample below into the file.
  • Fill in the options with your project’s details. There are a lot more options available to you, but these are what’s needed for this project.
Pod::Spec.new do |spec|
  spec.name = "RGB"
  spec.version = "1.0.0"
  spec.summary = "Sample framework from blog post, not for real world use."
  spec.homepage = "https://github.com/jakecraige/RGB"
  spec.license = { type: 'MIT', file: 'LICENSE' }
  spec.authors = { "Your Name" => 'your-email@example.com' }
  spec.social_media_url = "http://twitter.com/thoughtbot"

  spec.platform = :ios, "9.1"
  spec.requires_arc = true
  spec.source = { git: "https://github.com/jakecraige/RGB.git", tag: "v#{spec.version}", submodules: true }
  spec.source_files = "RGB/**/*.{h,swift}"

  spec.dependency "Curry", "~> 1.4.0"
end

One line to pay attention to is spec.dependency "Curry", '~> 1.4.0'. Because we’re supporting CocoaPods, we expect the consumers of our framework to be using it instead of Carthage, so we have to specify dependencies here and in the Cartfile.

Once this is set up we can run the pod lib lint command to test that everything is configured properly. If all goes well, we’ll see something like this:

With the project and dependencies set up, we’re almost ready to write some code. Before we do that, let’s create our first commit.

git commit -am "Project and dependencies set up"

Writing the First Test

Open RGBTests/RGBTests.swift so that we can take a look at the default template. It uses @testable and XCTest, but we’ll be changing both of these.

We’ll remove @testable because we want to test the public API that consumers of the framework will use. As our framework grows, we may need @testable to test parts that are not exposed publicly; generally we want to avoid that so we are testing what’s exposed to the consumer. This feature is most useful in testing applications rather than frameworks.

From the Apple Docs on Testability:

With testability, you are now able to write tests of Swift 2.0 frameworks and apps without having to make all of your internal routines public. Use @testable import {ModuleName} in your test source code to make all public and internal routines usable by XCTest targets, but not by other framework and app targets.

We’ll use Quick and Nimble for testing. Quick provides a nicer testing interface with a behavior-driven style that is very similar to RSpec and Specta; Nimble gives us many powerful assertions and the ability to write asynchronous code with less boilerplate.

Once those changes are made, the test file should look like:

import Quick
import Nimble
import RGB

class RGBTests: QuickSpec {
    override func spec() {
        describe("RGB") {
            it("works") {
                expect(true).to(beTrue())
            }
        }
    }
}

Run the tests with ⌘U or Product → Test and they should be green.

And… we’re done!

Just kidding. Let’s write some real tests.

We expect that calling RGBUIColor(red: 195, green: 47, blue: 52) will return a beautiful “thoughtbot red” UIColor.

In code, this looks like:

describe("RGBUIColor") {
    it("is a correct representation of the values") {
        let thoughtbotRed = UIColor(
            red: CGFloat(195/255),
            green: CGFloat(47/255),
            blue: CGFloat(52/255),
            alpha: 1
        )
        let color = RGBUIColor(red: 195, green: 47, blue: 52)

        expect(color).to(equal(thoughtbotRed))
    }
}

If we run the tests, now they will fail as we expect. Swift’s type checking will prevent us from running the tests because we never defined the RGBUIColor function.

Let’s do that.

Writing the Implementation

Right click on the “RGB” group in the Navigator and select “New File”.

Create a Swift file called RGBUIColor.swift and save it. Fill it with this implementation:

import Curry

func RGBUIColor(red red: Int, green: Int, blue: Int) -> UIColor {
    return curry(createColor)(red)(green)(blue)
}

private func createColor(red: Int, green: Int, blue: Int) -> UIColor {
    return UIColor(
        red: CGFloat(red/255),
        green: CGFloat(green/255),
        blue: CGFloat(blue/155),
        alpha: 1
    )
}

The use of curry here is used as an example of using a runtime dependency. This is a non-standard use and doesn’t provide any value here.

Now let’s run the tests!

At first glance, this error may seem a bit odd. We clearly defined the RGBUIColorfunction, right?

We did, but it’s not marked as public.

What this means is that if someone tries to use our framework, they won’t be able to see this function. If you want to see the difference in action, add @testable back and your tests will pass.

Experiencing this error is why we removed the @testable from the import at the beginning. It helps us to catch these kinds of errors earlier, before we release our framework to others.

To fix this, let’s mark the function as public like so:

public func RGBUIColor(red red: Int, green: Int, blue: Int) -> UIColor {
    return curry(createColor)(red)(green)(blue)
}

Let’s run the tests!

We’re Green!

Let’s commit this bad boy.

git commit -am "Completed my first iOS framework!"

That’s all folks!

That’s it. There were a lot of steps but we’ve successfully created a marginally usefulframework that could be published to GitHub. As a matter of fact, we published thesource for this framework on GitHub.

We can’t wait to see what kinds of awesome open-source projects you’ll create.

<think>我们正在回答用户关于“如何使用Xcode创建框架”的问题。根据提供的引用和系统指令,我们需要构建一个清晰的步骤指南,并确保使用正确的中文和LaTeX格式。同时,在回答末尾生成相关问题。 步骤: 1. 理解框架:在Xcode中,框架(Framework)是一种将代码、资源(如图片、本地化字符串)和头文件打包在一起的方式,以便于在多个项目中共享和重用。 2. 创建框架项目:在Xcode中,我们可以通过选择“Framework & Library”模板来创建框架。 3. 配置框架:设置框架的版本、支持的平台(iOS、macOS等)以及公开的头文件。 4. 编写代码:在框架中添加类和功能。 5. 构建框架:编译框架,生成.framework文件。 6. 使用框架:在其他项目中导入并使用该框架。 详细步骤: ### 1. 创建新项目 - 打开Xcode,选择“File” > “New” > “Project...”。 - 在模板选择器中,选择“Framework” (位于iOS或macOS的“Framework & Library”部分)。 - 输入产品名称(例如:MyFramework),选择语言(Objective-C或Swift),然后点击“Next”。 - 选择保存位置,点击“Create”。 ### 2. 配置框架 - 在项目设置中,可以设置框架的版本号(Current Project Version)和营销版本(Marketing Version)。 - 在“Build Phases”中,确保将需要公开的头文件添加到“Headers”的“Public”部分(对于Swift,则使用`public`或`open`修饰符)。 - 如果需要支持多平台,可以在“General”的“Deployment Info”中调整目标平台。 ### 3. 添加代码和资源 - 在项目中添加新的类或文件。例如,创建一个Swift类`MyClass.swift`: ```swift public class MyClass { public init() { } public func sayHello() { print("Hello from MyFramework!") } } ``` 注意:使用`public`修饰符使类和方法对外可见。 - 如果需要添加资源(如图片、音频等),可以创建一个资源包(Bundle)或者直接放在框架中(对于Swift框架,可以使用Asset Catalog)。 ### 4. 构建框架 - 选择目标设备(Generic iOS Device或模拟器),然后按Command+B构建。 - 构建成功后,可以在Products目录下找到生成的.framework文件(右键点击,选择“Show in Finder”)。 ### 5. 使用框架 - 将.framework文件拖到其他项目中。 - 在项目设置的“General”选项卡中,将框架添加到“Embedded Binaries”和“Linked Frameworks and Libraries”。 - 在代码中导入框架并使用: ```swift import MyFramework let myObject = MyClass() myObject.sayHello() ``` ### 注意事项 - 如果框架包含资源(如图片),需要确保资源被正确打包。一种推荐的方式是创建资源包(Bundle)[^2]。 - 对于Swift框架,确保在导入到其他项目时,目标项目的Swift版本兼容。 - 如果框架需要支持模拟器和真机(即通用框架),可以使用“lipo”工具合并架构[^1]。 ### 示例:创建通用框架 有时我们需要一个同时支持模拟器和真机的框架(称为Fat Framework),可以通过以下步骤: 1. 分别构建模拟器和真机版本。 2. 使用终端命令合并: ```bash lipo -create path/to/simulator_framework path/to/device_framework -output path/to/universal_framework ``` 但是注意,Xcode 12及以上版本已经支持使用xcframework来更好地管理多平台框架[^3]。 ### 引用说明 根据提供的引用,我们参考了以下内容: - 引用[1]提到了在Xcode 4上创建通用静态库的方法,我们借鉴了合并架构的思想[^1]。 - 引用[2]提到构建静态库项目并将其转换为框架,以及如何打包资源[^2]。 - 引用[3]提醒不要同时在多个Xcode窗口中打开同一个项目[^3]。 - 引用[4]提到了在Qt项目中转到Xcode进行打包的经验,但我们这里主要关注纯Xcode框架创建[^4]。 - 引用[5]强调了框架在代码重用中的优势[^5]。 §§相关问题§§ 1. 如何为框架添加资源文件(如图片、本地化字符串)? 2. 如何创建支持多个平台(如iOS和macOS)的XCFramework? 3. 如何对框架进行单元测试? 4. 框架与静态库(Static Library)有什么区别?</think>### 如何使用 Xcode 创建框架 在 Xcode 中创建框架(Framework)是将可重用代码模块化的标准方法。以下是详细步骤: --- #### 步骤 1:创建框架项目 1. 打开 Xcode → 选择 **File > New > Project...** 2. 在模板选择器中: - 选择 **Framework**(位于 *iOS/macOS > Framework & Library* 下) - 输入产品名称(如 `MyFramework`) - 选择语言(Swift/Objective-C) - 取消勾选 *Include Tests*(可选) 3. 点击 **Create** 生成项目模板 > 框架项目会自动配置正确的构建设置和目录结构[^2]。 --- #### 步骤 2:添加公共代码 1. 在项目导航器中添加新文件(类/结构体/工具函数) 2. **关键设置**: - Swift:使用 `public` 或 `open` 修饰符暴露接口 ```swift public class DataProcessor { public static func process() { ... } } ``` - Objective-C:在 **Headers** 的 **Public** 区域添加头文件 ![](https://docs-assets.developer.apple.com/published/9ea5c0d3b6/rendered2x-1611672002.png) --- #### 步骤 3:配置构建设置 1. 在 **Build Settings** 中: - 设置 `Mach-O Type` 为 **Dynamic Library** - 调整 `iOS Deployment Target` 兼容最低系统版本 - 启用 `Build Libraries for Distribution`(支持二进制兼容) 2. 在 **Build Phases** 中: - 添加资源文件(图片/本地化文件等)到 **Copy Bundle Resources** - 确保公开头文件在 **Headers > Public** 区域 --- #### 步骤 4:构建框架 1. 选择目标设备: - 模拟器 → 生成 **x86_64** 架构 - 真机 → 生成 **arm64** 架构 2. 按 **⌘ + B** 编译 3. 生成的文件位置: - 右键 **Products > MyFramework.framework** → **Show in Finder** - 路径示例:`Build/Products/Debug-iphoneos/` > 如需通用框架(Fat Framework),使用 `lipo` 合并架构: > ```bash > lipo -create path/to/simulator_framework path/to/device_framework -output universal_framework > ``` --- #### 步骤 5:在其他项目中使用 1. 将 `.framework` 文件拖入目标工程 2. 在项目设置中: - **General** → 添加到 **Frameworks, Libraries, and Embedded Content** - **Build Settings** → 添加框架路径到 **Framework Search Paths** 3. 代码中导入: ```swift import MyFramework DataProcessor.process() ``` --- #### 高级技巧 1. **资源打包**: - 创建独立资源包(Bundle)存放图片/本地化文件[^2] - 通过 `Bundle(for: Self.self)` 加载资源 2. **XCFramework**(跨平台): ```bash xcodebuild -create-xcframework \ -framework device_framework_path \ -framework simulator_framework_path \ -output MyFramework.xcframework ``` 3. **模块稳定性**: - 在 `Package.swift` 添加 `// swift-tools-version:5.5` - 启用 `BUILD_LIBRARY_FOR_DISTRIBUTION`[^5] > 框架可显著提升代码复用率,特别适合组件化架构[^5]。避免同一项目在多个 Xcode 窗口打开以防配置冲突[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值