How to use ObjC in Swift Package Manager

Issue #575

Create Objc target

Check runtime

Check for example _runtime(_ObjC) or os(macOS if you plan to use platform specific feature

For example, in test we use XCTest which is run via Xcode and is a macOS framework, so we need to check for os(macOS)

Note that in Objc framework, the header files must be in include folder

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
targets: {
var targets: [Target] = [
.testTarget(
name: "QuickTests",
dependencies: [ "Quick", "Nimble" ],
exclude: [
"QuickAfterSuiteTests/AfterSuiteTests+ObjC.m",
"QuickFocusedTests/FocusedTests+ObjC.m",
"QuickTests/FunctionalTests/ObjC",
"QuickTests/Helpers/QCKSpecRunner.h",
"QuickTests/Helpers/QCKSpecRunner.m",
"QuickTests/Helpers/QuickTestsBridgingHeader.h",
"QuickTests/QuickConfigurationTests.m",
]
),
]
#if os(macOS)
targets.append(contentsOf: [
.target(name: "QuickSpecBase", dependencies: []),
.target(name: "Quick", dependencies: [ "QuickSpecBase" ]),
])
#else
targets.append(contentsOf: [
.target(name: "Quick", dependencies: []),
])
#endif
return targets
}(),

How to fix library not found with SPM and CocoaPods in Xcode

Issue #572

After migrating a few pods to use SPM, some libraries fail to load. This is because the workspace now uses both SPM and cocoapods

code signature in … not valid for use in process using Library Validation: mapped file has no Team ID and is not a platform binary (signed with custom identity or adhoc?)

The workaround is to disable Library validation

Screenshot 2020-01-08 at 22 48 28

How to test a developing package with Swift Package Manager

Issue #525

Use macOS Command Line project

Example Puma

  • Create a new macOS project, select Command Line ToolScreenshot 2019-11-30 at 22 40 35
  • Drag Puma.xcodeproj as a sub project of our test project
  • Go to our TestPuma target, under Link Binary with Libraries, select Puma framework
Screenshot 2019-11-30 at 22 41 18
  • Puma has dependencies on PumaCore and PumaiOS, but in Xcode we only need to select Puma framework

  • In code, we need to explicitly import PumaiOS framework if we use any of its classes

1
2
3
4
5
6
7
8
9
10
11
import Foundation
import Puma
import PumaiOS

func testDrive() {
run {
SetVersionNumber {
$0.buildNumberForAllTarget("1.1")
}
}
}
  • As our Puma.xcodeproj is inside this test project, we can drill down into our Puma.xcodeproj and update the code.

Workspace

Instead of dragging Puma as a subproject of TestPuma, we can use workspace, and link Puma frameworks

Screenshot 2019-12-14 at 21 36 32

Troubleshooting

Code signing for frameworks

To avoid signing issue, we need to select a Team for all frameworks

not valid for use in process using Library Validation: mapped file has no Team ID and is not a platform binary (signed with custom identity or adhoc?

Library not loaded

Need to set runpath search path, read https://stackoverflow.com/questions/28577692/macos-command-line-tool-with-swift-cocoa-framework-library-not-loaded

Specify LD_RUNPATH_SEARCH_PATHS = @executable_path in Build Settings

missing required module ‘clibc’

Take a look at Puma -> SPMLibc, there’s header search path

1
$(SRCROOT)/.build/checkouts/swift-package-manager/Sources/clibc/include

which is at the .build folder inside root

Screenshot 2019-12-14 at 21 55 30

So for our TestPuma target, we need to add this header search path with the correct path

1
$(SRCROOT)/../../.build/checkouts/swift-package-manager/Sources/clibc/include
Screenshot 2019-12-14 at 21 55 55

Read more

How to organize dependencies in Swift Package Manager

Issue #523

In Puma I want to make build tools for iOS and Android, which should share some common infrastructure. So we can organize dependencies like.

Puma -> PumaAndroid, PumaiOS -> PumaCore -> xcbeautify, Files, Colorizer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
// swift-tools-version:5.1
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription

let package = Package(
name: "Puma",
platforms: [.macOS("10.15")],
products: [
.library(name: "Puma", targets: ["Puma"])
],
dependencies: [
.package(
url: "https://github.com/thii/xcbeautify.git",
.upToNextMajor(from: "0.4.1")
),
.package(
url: "https://github.com/getGuaka/Colorizer",
.upToNextMajor(from: "0.2.0")
),
.package(
url: "https://github.com/JohnSundell/Files.git",
.upToNextMajor(from : "3.1.0")
)
],
targets: [
.target(
name: "Puma",
dependencies: [
"PumaiOS",
"PumaAndroid",
],
path: "Sources/Puma"
),
.target(
name: "PumaCore",
dependencies: [
"XcbeautifyLib",
"Colorizer",
"Files"
],
path: "Sources/Core"
),
.target(
name: "PumaiOS",
dependencies: [
"PumaCore"
],
path: "Sources/iOS"
),
.target(
name: "PumaAndroid",
dependencies: [
"PumaCore"
],
path: "Sources/Android"
),
.testTarget(
name: "PumaTests",
dependencies: ["Puma"
]
)
]
)

How to make Swift Package Manager package for multiple platforms

Issue #504

https://twitter.com/NeoNacho/status/1181245484867801088?s=20

There’s no way to have platform specific sources or targets today, so you’ll have to take a different approach. I would recommend wrapping all OS specific files in #if os and just having one target. For tests, you could do something similar, one test target, but conditional tests

Every files are in Sources folder, so we can use platform and version checks. For example Omnia is a Swift Package Manager that supports iOS, tvOS, watchOS, macOS and Catalyst.

For macOS only code, need to check for AppKit and Catalyst

https://github.com/onmyway133/Omnia/blob/master/Sources/macOS/ClickedCollectionView.swift

1
#if canImport(AppKit) && !targetEnvironment(macCatalyst)

For SwiftUI feature, need to check for iOS 13 and macOS 10.15

https://github.com/onmyway133/Omnia/blob/master/Sources/SwiftUI/Utils/ImageLoader.swift

1
@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)