feat(ios): [IAP] Paywall Initial Commit (#13609)

Requires https://github.com/toeverything/AFFiNE/pull/13606 to be merged.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- New Features
- Introduced an in-app Paywall with Pro, AI, and Believer plans, feature
previews, paging dots, and selectable pricing options.
- Added purchase and restore actions, plus a unified, polished UI using
new color/icon resources.

- Documentation
  - Added Swift Code Style Guidelines.

- Chores
- Updated dependencies (including MarkdownView 3.4.2), added new
resource packages, and removed an unused dependency.
  - Raised iOS deployment target to 16.5 and refreshed project settings.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: DarkSky <25152247+darkskygit@users.noreply.github.com>
This commit is contained in:
Lakr
2025-09-19 19:01:46 +08:00
committed by GitHub
parent 1f228382c2
commit 360c9545f4
53 changed files with 1718 additions and 8 deletions

View File

@@ -32,8 +32,8 @@ class AFFiNEViewController: CAPBridgeViewController {
CookiePlugin(),
HashcashPlugin(),
NavigationGesturePlugin(),
// IntelligentsPlugin(representController: self), // no longer put in use
NbStorePlugin(),
PayWallPlugin(associatedController: self),
]
plugins.forEach { bridge?.registerPluginInstance($0) }
}

View File

@@ -1,8 +1,18 @@
import AffinePaywall
import Capacitor
import Foundation
import SwiftUI
import UIKit
@objc(PayWallPlugin)
public class PayWallPlugin: CAPPlugin, CAPBridgedPlugin {
init(associatedController: UIViewController? = nil) {
controller = associatedController
super.init()
}
weak var controller: UIViewController?
public let identifier = "PayWallPlugin"
public let jsName = "PayWall"
public let pluginMethods: [CAPPluginMethod] = [
@@ -12,14 +22,17 @@ public class PayWallPlugin: CAPPlugin, CAPBridgedPlugin {
@objc func showPayWall(_ call: CAPPluginCall) {
do {
let type = try call.getStringEnsure("type")
let controller = try controller.get()
// TODO: Implement actual paywall logic here
// For now, just log the type and resolve
print("PayWall: Showing paywall of type: \(type)")
// TODO: GET TO KNOW THE PAYWALL TYPE
print("[*] showing paywall of type: \(type)")
DispatchQueue.main.async {
Paywall.presentWall(toController: controller, type: type)
}
call.resolve(["success": true, "type": type])
} catch {
call.reject("Failed to show paywall", nil, error)
call.reject("failed to show paywall", nil, error)
}
}
}
}

View File

@@ -0,0 +1,21 @@
//
// Tools.swift
// AFFiNE
//
// Created by qaq on 9/18/25.
//
import Foundation
extension Optional {
func get(_ failure: String? = nil) throws -> Wrapped {
guard let self else {
if let failure {
throw NSError(domain: #function, code: -1, userInfo: [NSLocalizedDescriptionKey: failure])
} else {
throw NSError(domain: #function, code: -1)
}
}
return self
}
}