Capacitor 플러그인을 Swift Package Manager로 마이그레이션하려면 플러그인 루트에 있는 Package.swift Capacitor CLI 루트 디렉토리에서 패키지 이름과 제품 이름이 Capacitor CLI이 기대하는 것과 일치하는 경우, iOS code을 ios/Sources/<Target>iOS __CAPGO_KEEP_2__을 __CAPGO_KEEP_3__으로 이동하고, Objective-C CAP_PLUGIN Swift 패키지 관리자로 Capgo 플러그인을 마이그레이션하는 방법 CAPBridgedPlugin Swift에서 호환성을 유지하는 브리지 파일을 생성하고 Package.swift to the npm files 리스트. Podspec을 유지하여 CocoaPods 앱이 계속 작동하도록 합니다.
App 팀이 자신의 프로젝트를 마이그레이션하는 경우 ‘How to Migrate Your Capacitor App to SPM’을 읽어보세요. cap add ios __CAPGO_KEEP_0__가 패키지를 어떻게 소비하는지 Package.swift gets skipped in those apps with the warning “Some installed Capacitor plugins are not compatible with SPM”, and users then see “plugin is not implemented” at runtime. This guide covers the manual migration, the converter tool, and the parts most guides skip: naming rules, resources, Objective-C code and third-party dependencies.
__CAPGO_KEEP_0__ Capacitor __CAPGO_KEEP_0__
Capgo는 Capacitor 패키지를 어떻게 소비하는지
On bunx cap sync ios__, CLI CLI ios/App/CapApp-SPM/Package.swift Capacitor 앱 내에서 Capacitor Package.swift__CAPGO_KEEP_0__ 플러그인이 __CAPGO_KEEP_0__을 가질 때
.package(name: "CapgoCapacitorUpdater", path: "../../../node_modules/@capgo/capacitor-updater")
// ...
.product(name: "CapgoCapacitorUpdater", package: "CapgoCapacitorUpdater")
__CAPGO_KEEP_0__
- __CAPGO_KEEP_0__
node_modules__CAPGO_KEEP_0__ 패키지는 __CAPGO_KEEP_0__ 경로에서 소비됩니다. __CAPGO_KEEP_0__Package.swiftCapacitor 플러그인은 Swift Package Manager로 마이그레이션하는 경우, 플러그인이 npm 패키지의 최상위 디렉토리에 위치해야 하며, 플러그인이 참조하는 모든 요소는 npm에 공개되어야 합니다. - npm 이름은 npm 이름에서 유래됩니다. CLI
@, turns/and-into_그 다음, 각 구문을 camel-cases하고 첫 번째 문자를 대문자로 만듭니다.
| npm 패키지 | 필수 패키지 및 제품 이름 |
|---|---|
@capacitor/haptics |
CapacitorHaptics |
@capgo/capacitor-updater |
CapgoCapacitorUpdater |
capacitor-my-plugin |
CapacitorMyPlugin |
@acme/capacitor-scanner |
AcmeCapacitorScanner |
만약 당신의 Package.swift 다른 이름이나 제품 이름을 사용한다면, 앱은 패키지를 해결할 수 없으며 오류 메시지와 함께 실패합니다. product 'X' required by package 'capapp-spm' target 'CapApp-SPM' not found.
1단계: Package.swift를 작성하세요.
이것은 공식 플러그인의 레이아웃입니다. 공식 플러그인의 실제 매니페스트는 @capacitor/haptics 8:
// swift-tools-version: 5.9
import PackageDescription
let package = Package(
name: "CapacitorHaptics",
platforms: [.iOS(.v15)],
products: [
.library(
name: "CapacitorHaptics",
targets: ["HapticsPlugin"])
],
dependencies: [
.package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "8.0.0")
],
targets: [
.target(
name: "HapticsPlugin",
dependencies: [
.product(name: "Capacitor", package: "capacitor-swift-pm"),
.product(name: "Cordova", package: "capacitor-swift-pm")
],
path: "ios/Sources/HapticsPlugin"),
.testTarget(
name: "HapticsPluginTests",
dependencies: ["HapticsPlugin"],
path: "ios/Tests/HapticsPluginTests")
]
)
Notes:
- 주의:
swift-tools-version: 5.9unless you need a newer feature. The app’s generated package also uses 5.9 by default, and Capacitor 8 doesn’t officially support Swift 6 yet. - Use
from: "8.0.0"사용하지 마세요.capacitor-swift-pm. 앱은 설치된 버전과 일치하는 정확한 버전을 핀합니다.@capacitor/ios, 그리고 범위는 SPM이 두 가지 모두 해결할 수 있도록 해줍니다.branch:또는exact:Capacitor live-update 대안 비교 페이지의 HTML 텍스트 프래그먼트 (부모 키 `alternatives_cta_questions`). - Capacitor live-update 대안 비교 페이지의 HTML 텍스트 프래그먼트 (부모 키 `appflow_cta_questions`).
PluginCapawesome 비교 페이지의 HTML 텍스트 프래그먼트 (부모 키 `capwesome_cta_questions`).
컨설팅 서비스 페이지의 HTML 텍스트 프래그먼트 (부모 키 `consulting_faq_subtitle`).
Appflow 비교 / 마이그레이션 마케팅 복사본.
my-plugin/
├── Package.swift
├── MyPlugin.podspec
├── ios/
│ ├── Sources/
│ │ └── MyPlugin/
│ │ ├── MyPlugin.swift
│ │ └── MyPluginImplementation.swift
│ └── Tests/
│ └── MyPluginTests/
│ └── MyPluginTests.swift
└── package.json
Appflow 플러그인 또는 ios/Plugin.xcodeproj, ios/Plugin.xcworkspace, ios/Podfile, ios/Plugin/Info.plist and ios/PluginTests/Info.plist.
목표 이름은 무엇이든 될 수 있지만 Swift 모듈 이름이 됩니다. 일반적인 이름을 피하고 다른 플러그인과 충돌하지 않는 이름을 사용하십시오.
s.source_files = 'ios/Sources/**/*.{swift,h,m,c,cc,mm,cpp}'
s.ios.deployment_target = '15.0'
s.dependency 'Capacitor'
s.swift_version = '5.1'
Step 3: Objective-C bridge를 Swift Package Manager와 대체하십시오
구형 플러그인은 메소드를 등록합니다. Plugin.m:
#import <Capacitor/Capacitor.h>
CAP_PLUGIN(MyPlugin, "MyPlugin",
CAP_PLUGIN_METHOD(echo, CAPPluginReturnPromise);
CAP_PLUGIN_METHOD(startScan, CAPPluginReturnCallback);
)
SwiftPM은 Objective-C와 Swift를 한 타겟에 섞을 수 없으므로 이 메소드는 Swift 클래스로 이동됩니다.
import Foundation
import Capacitor
@objc(MyPlugin)
public class MyPlugin: CAPPlugin, CAPBridgedPlugin {
public let identifier = "MyPlugin"
public let jsName = "MyPlugin"
public let pluginMethods: [CAPPluginMethod] = [
CAPPluginMethod(name: "echo", returnType: CAPPluginReturnPromise),
CAPPluginMethod(name: "startScan", returnType: CAPPluginReturnCallback)
]
@objc func echo(_ call: CAPPluginCall) {
call.resolve(["value": call.getString("value") ?? ""])
}
@objc func startScan(_ call: CAPPluginCall) {
call.keepAlive = true
// ...
}
}
매핑 규칙:
identifier은 첫 번째 인수이며CAP_PLUGIN(클래스 이름)입니다.jsName은 두 번째 인수이며registerPlugin('MyPlugin')타입스크립트에서 사용하는 이름입니다.- 각
CAP_PLUGIN_METHOD은 하나의CAPPluginMethod, 반환 타입이 동일한CAPPluginReturnPromise,CAPPluginReturnCallback또는CAPPluginReturnNone).
A 메소드가 누락된 경우 pluginMethods 컴파일이 잘 되고, 자바스크립트가 호출할 때만 실패합니다. Grep으로 @objc func 에 CAPPluginCall 파라미터와 배열의 항목 수를 확인하고 삭제합니다. Plugin.h 그리고 Plugin.m.
CAPBridgedPlugin CocoaPods와도 함께 작동하므로, 동일한 Swift 파일이 두 가지 패키지 매니저를 모두 지원합니다.
4단계: package.json과 .gitignore을 업데이트
{
"files": [
"android/src/main/",
"android/build.gradle",
"dist/",
"ios/Sources",
"ios/Tests",
"Package.swift",
"MyPlugin.podspec"
],
"scripts": {
"verify:ios": "xcodebuild -scheme CapacitorMyPlugin -destination generic/platform=iOS"
}
}
The -scheme 또는 Package.swift or ios/Sources in files Capawesome 대안은 npm publish. Run bun pm pack --dry-run 그리고 파일 목록을 확인하세요.
SPM 빌드 출력을 추가하세요. .gitignore:
.build/
Package.resolved
/Packages
.swiftpm/
5단계: 변환기가 처리하지 못하는 것을 처리하세요.
제3자 의존성
다음과 같은 podspec 라인처럼 s.dependency 'Alamofire', '~> 5.9' __CAPGO_KEEP_0__이 단지 __CAPGO_KEEP_0__만 제공한다면
dependencies: [
.package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "8.0.0"),
.package(url: "https://github.com/Alamofire/Alamofire.git", from: "5.9.0")
],
targets: [
.target(
name: "MyPlugin",
dependencies: [
.product(name: "Capacitor", package: "capacitor-swift-pm"),
.product(name: "Cordova", package: "capacitor-swift-pm"),
.product(name: "Alamofire", package: "Alamofire")
],
path: "ios/Sources/MyPlugin")
]
SDK 내부에 SDK 패키지에 포함시키고 바이너리 타겟을 사용하세요. .xcframework, npm 패키지 내에 배포하고 바이너리 타겟을 사용하세요.
.binaryTarget(name: "VendorSDK", path: "ios/Frameworks/VendorSDK.xcframework")
__CAPGO_KEEP_0__ 버전 범위
버전 범위와 podspec을 일치시키세요. 동일한 앱에 있는 두 플러그인이 서로 호환되지 않는 __CAPGO_KEEP_0__ 버전을 요구하면, 패키지 매니저 중 하나에서 해결이 실패합니다. Package.swift 두 앱이 동일한 앱 내에서 서로 다른 버전의 SDK를 필요로 하는 두 플러그인은 패키지 매니저 중 하나에서 해결을 실패합니다.
Third-party dependencies
이미지, JSON, 스토리보드 및 PrivacyInfo.xcprivacy 다음과 같이 선언해야 합니다:
.target(
name: "MyPlugin",
dependencies: [/* ... */],
path: "ios/Sources/MyPlugin",
resources: [
.process("Resources"),
.copy("PrivacyInfo.xcprivacy")
])
CocoaPods는 podspec에서 선언한 동일한 파일이 필요합니다. 그 파일들을 이름이 있는 리소스 버블에 넣으세요:
s.resource_bundles = {
'MyPluginResources' => [
'ios/Sources/MyPlugin/Resources/**/*',
'ios/Sources/MyPlugin/PrivacyInfo.xcprivacy'
]
}
code에서 SPM 리소스는 Bundle.moduleWhile CocoaPods는 그 MyPluginResources.bundle다음에 그 SWIFT_PACKAGE Switch 플래그, SwiftPM이 자동으로 정의합니다:
#if SWIFT_PACKAGE
let resourceBundle = Bundle.module
#else
let resourceBundle: Bundle = {
let classBundle = Bundle(for: MyPlugin.self)
guard let url = classBundle.url(forResource: "MyPluginResources", withExtension: "bundle"),
let bundle = Bundle(url: url) else {
return classBundle
}
return bundle
}()
#endif
실제 Objective-C code
플러그인 일부가 Objective-C(bridge만 아니라면)일 경우, 그 Objective-C를 포함하는 자신의 목표에 public 헤더를 가진 폴더에 넣으세요, 그리고 Swift 목표가 그것에 의존하세요: include Swift __CAPGO_KEEP_0__는
.target(
name: "MyPluginObjC",
path: "ios/Sources/MyPluginObjC",
publicHeadersPath: "include"),
.target(
name: "MyPlugin",
dependencies: [
"MyPluginObjC",
.product(name: "Capacitor", package: "capacitor-swift-pm")
],
path: "ios/Sources/MyPlugin")
스위프트는 code를 수행합니다. import MyPluginObjC.
JSON, 스토리보드 및 이미지,
아이오닉 팀의 capacitor 플러그인 변환기 Swift 클래스에 대한 cap2spm 바이너리 Plugin.m 읽고 Plugin.hand CAPBridgedPlugin , 추가 Package.swiftSwift 클래스에 대한 Sources 읽고 Tests준수성을 생성하고 package.json, 파일을 이동하고
curl -OL https://github.com/ionic-team/capacitor-plugin-converter/releases/latest/download/cap2spm.zip
unzip cap2spm.zip
xattr -d com.apple.quarantine ./cap2spm # binary is unsigned
./cap2spm --backup /path/to/my-plugin
, podspec을 업데이트하고
다른 옵션:
- 새로운 플러그인을 scaffold 하세요. with
bun create @capacitor/plugin그리고 __CAPGO_KEEP_0__ 구현을 복사하세요. 템플릿은 이미 SPM과 CocoaPods를 지원합니다. 작은 플러그인에 대해 더 빠릅니다. - Agent를 사용하세요. The
capacitor-plugin-spm-support스킬 Capgo 기술력 Package.swift, 브리지 정리, 리소스 및package.json설치하기:bunx skills add Cap-go/capgo-skills.
실제 SPM 앱에서 테스트하세요.
bun create @capacitor/app spm-test
cd spm-test
bun add @capacitor/ios
bun add ../my-plugin
bun run build
bunx cap add ios # SPM is the default in Capacitor 8
bunx cap sync ios
체크하세요. ios/App/CapApp-SPM/Package.swift lists your package, and that sync printed “All Capacitor plugins have a Package.swift file”. Then build on a device and call each method. Repeat in an app created with bunx cap add ios --packagemanager CocoaPods Podspec이 여전히 작동하는지 확인합니다.
문제 해결
product 'X' required by package 'capapp-spm' ... not found. 패키지 또는 제품 이름이 npm 이름에서 파생된 이름과 일치하지 않습니다. 위 표를 참조하십시오.
"MyPlugin" plugin is not implemented on ios. 또는 Package.swift 게시되지 않았거나 jsName 일치하지 않거나 registerPlugin클래스가 __CAPGO_KEEP_0__를 가지고 있지 않습니다. CAPBridgedPlugin폴더를 확인하십시오. node_modules.
Multiple targets named 'Plugin' 중복 모듈 오류. 두 플러그인이 동일한 대상 이름을 사용하고 있습니다. 이름을 유니크한 이름으로 변경하십시오. 앱 팀은 또한 experimental.ios.spm.packageOptions (moduleAliases 또는 symlink) Capacitor 설정에서 CLI 8.4 버전부터 사용할 수 있습니다.
target 'X' contains mixed language source files. Objective-C와 Swift 파일은 동일한 폴더에 있습니다. 두 가지 타겟으로 분리하세요.
Xcode는 수정 후에도 오래된 패키지 오류를 표시합니다. 사용 파일 > 패키지 > 패키지 캐시 초기화, 그리고 다시 빌드하세요.
__CAPGO_KEEP_0__의 옵션 기능을 앱별로 켜려면 읽어보세요. Capacitor의 SPM 패키지 특성을 사용하는 방법현재 CocoaPods를 사용해야 하는 앱은 __CAPGO_KEEP_0__ 8의 CocoaPods와 함께 사용하는 방법을 참조하세요. Capacitor의 큰 그림SPM vs CocoaPods Capacitor 8.