Files
AFFiNE-Mirror/packages/frontend/apps/ios/AGENTS.md
Lakr 8df7353722 chore(ios): iap paywall update (#13669)
This pull request introduces several improvements and refactors to the
iOS frontend, with a focus on the paywall system, configuration, and
developer experience. The most significant changes include dynamic
pricing updates for subscription packages, the introduction of a
centralized pricing configuration, and enhanced developer documentation
and settings for Claude Code. There are also minor fixes and
improvements to restore purchase flows, App Store syncing, and protocol
usage guidance.

**Paywall System Improvements**

* Subscription package pricing and display is now dynamically updated
based on App Store data, ensuring users see accurate, localized pricing
and descriptions. This includes new logic for calculating monthly prices
and updating package button text. (`ViewModel.swift`,
`ViewModel+Action.swift`, `SKUnit+Pro.swift`, `SKUnit+AI.swift`)
[[1]](diffhunk://#diff-cb192a424400265435cb06d86b204aa17b4e8195d9dd811580f51faeda211ff0R83-R160)
[[2]](diffhunk://#diff-cb192a424400265435cb06d86b204aa17b4e8195d9dd811580f51faeda211ff0L102-R199)
[[3]](diffhunk://#diff-df2cb61867b4ff10dee98d534cf3c94fe8d48ebaef3f219450a9fba26725fdcbL58-R73)
[[4]](diffhunk://#diff-df2cb61867b4ff10dee98d534cf3c94fe8d48ebaef3f219450a9fba26725fdcbL74-R94)
[[5]](diffhunk://#diff-ea535c02550f727587e74521da8fd90dec23cbe3c685f9c4aa4923ce0bbdb363L19-R35)
[[6]](diffhunk://#diff-a5fef660f959bbb52ce3f19bba8bfbd0bb00d66c9f18a20a998101b5df6c8f60L18-R22)
* Introduced a new `PricingConfiguration.swift` file to centralize
product identifiers, default selections, and display strings for
subscription products, improving maintainability and consistency.
(`PricingConfiguration.swift`, `SKUnit+Pro.swift`, `SKUnit+AI.swift`)
[[1]](diffhunk://#diff-de4566ecd5bd29f36737ae5e5904345bd1a5c8f0a73140c3ebba41856bae3e86R1-R54)
[[2]](diffhunk://#diff-ea535c02550f727587e74521da8fd90dec23cbe3c685f9c4aa4923ce0bbdb363L19-R35)
[[3]](diffhunk://#diff-a5fef660f959bbb52ce3f19bba8bfbd0bb00d66c9f18a20a998101b5df6c8f60L18-R22)

**Developer Experience and Documentation**

* Added `AGENTS.md` to provide comprehensive guidance for Claude Code
and developers, including project overview, build commands,
architecture, native bridge APIs, Swift code style, and dependencies.
(`AGENTS.md`)
* Added a local settings file (`settings.local.json`) to configure
permissions for Claude Code, allowing specific Bash commands for iOS
builds. (`settings.local.json`)
* Updated Swift architecture guidelines to discourage protocol-oriented
design unless necessary, favoring dependency injection and composition.
(`AGENTS.md`)

**User Experience Improvements**

* The purchase footer now includes an underline for "Restore Purchase"
and a clear message about subscription auto-renewal and cancellation
flexibility. (`PurchaseFooterView.swift`)
* Improved restore purchase and App Store sync logic to better handle
user sign-in prompts and error handling. (`ViewModel+Action.swift`,
`Store.swift`)
[[1]](diffhunk://#diff-df2cb61867b4ff10dee98d534cf3c94fe8d48ebaef3f219450a9fba26725fdcbL45-R49)
[[2]](diffhunk://#diff-df2cb61867b4ff10dee98d534cf3c94fe8d48ebaef3f219450a9fba26725fdcbL58-R73)
[[3]](diffhunk://#diff-9f18fbbf15591c56380ce46358089c663ce4440f596db8577de76dc6cd306b54R26-R28)

**Minor Fixes and Refactoring**

* Made `docId` in `DeleteSessionInput` optional to match GraphQL schema
expectations. (`DeleteSessionInput.graphql.swift`)
[[1]](diffhunk://#diff-347e5828e46f435d7d7090a3e3eb7445af8c616f663e8711cd832f385f870a9bL14-R14)
[[2]](diffhunk://#diff-347e5828e46f435d7d7090a3e3eb7445af8c616f663e8711cd832f385f870a9bL25-R25)
* Minor formatting and dependency list updates in `Package.swift`.
(`Package.swift`)
* Fixed concurrency usage in event streaming for chat manager.
(`ChatManager+Stream.swift`)

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

## Summary by CodeRabbit

* New Features
* Paywall options now dynamically reflect product data with clearer
labels and monthly price calculations.
* Added an auto‑renewal note (“cancel anytime”) and underlined “Restore
Purchase” for better clarity.

* Refactor
* Improved purchase/restore flow reliability and UI updates for a
smoother experience.

* Documentation
* Added a comprehensive development guide and updated architecture/style
guidance for iOS.

* Chores
* Introduced local build permissions configuration for iOS development.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-29 09:18:47 +00:00

4.6 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

This is the AFFiNE iOS application built with Capacitor, React, and TypeScript. It's a hybrid mobile app that wraps a React web application in a native iOS shell.

Development Commands

Build and Development

  • yarn dev - Start development server with live reload
  • yarn build - Build the web application
  • yarn sync - Sync web assets with Capacitor iOS project
  • yarn sync:dev - Sync with development server (CAP_SERVER_URL=http://localhost:8080)
  • yarn xcode - Open Xcode project
  • yarn codegen - Generate GraphQL and Rust bindings
  • xcodebuild -workspace App.xcworkspace -scheme App -destination 'platform=iOS Simulator,name=iPhone 15' build | xcbeautify - Build iOS project with xcbeautify

iOS Build Process

  1. BUILD_TYPE=canary PUBLIC_PATH="/" yarn affine @affine/ios build - Build web assets
  2. yarn affine @affine/ios cap sync - Sync with iOS project
  3. yarn affine @affine/ios cap open ios - Open in Xcode

Live Reload Setup

  1. Run yarn dev and select ios for Distribution option
  2. Run yarn affine @affine/ios sync:dev
  3. Run yarn affine @affine/ios cap open ios

Architecture

Core Technologies

  • Capacitor 7.x - Native iOS bridge
  • React 19 - UI framework
  • TypeScript - Language
  • Blocksuite - Document editor
  • DI Framework - Dependency injection via @toeverything/infra

Key Directories

  • src/ - React application source
  • App/ - Native iOS Swift code
  • dist/ - Built web assets
  • capacitor-cordova-ios-plugins/ - Capacitor plugins

Native Bridge Integration

The app exposes JavaScript APIs to native iOS code through window object:

  • getCurrentServerBaseUrl() - Get current server URL
  • getCurrentI18nLocale() - Get current locale
  • getAiButtonFeatureFlag() - Check AI button feature flag
  • getCurrentWorkspaceId() - Get current workspace ID
  • getCurrentDocId() - Get current document ID
  • getCurrentDocContentInMarkdown() - Export current doc as markdown
  • createNewDocByMarkdownInCurrentWorkspace() - Import markdown as new doc

Swift Code Style

Follow the guidelines in AGENTS.md:

  • 2-space indentation
  • PascalCase for types, camelCase for properties/methods
  • Modern Swift features: @Observable, async/await, actor
  • Protocol-oriented design, dependency injection
  • Early returns, guard statements for optional unwrapping

Build Configuration

  • TypeScript config extends ../../../../tsconfig.web.json
  • Webpack bundling via @affine-tools/cli
  • Capacitor config in capacitor.config.ts
  • GraphQL codegen via Apollo
  • Rust bindings generated via Uniffi

Dependencies

  • Workspace packages: @affine/core, @affine/component, @affine/env
  • Capacitor plugins: App, Browser, Haptics, Keyboard
  • React ecosystem: React Router, Next Themes
  • Storage: IDB, Yjs for collaborative editing

Testing and Quality

  • TypeScript strict mode enabled
  • ESLint/Prettier configuration from workspace root
  • No specific test commands in this package (tests likely in workspace root)

Swift Code Style Guidelines

Core Style

  • Indentation: 2 spaces
  • Braces: Opening brace on same line
  • Spacing: Single space around operators and commas
  • Naming: PascalCase for types, camelCase for properties/methods

File Organization

  • Logical directory grouping
  • PascalCase files for types, + for extensions
  • Modular design with extensions

Modern Swift Features

  • @Observable macro: Replace ObservableObject/@Published
  • Swift concurrency: async/await, Task, actor, @MainActor
  • Result builders: Declarative APIs
  • Property wrappers: Use line breaks for long declarations
  • Opaque types: some for protocol returns

Code Structure

  • Early returns to reduce nesting
  • Guard statements for optional unwrapping
  • Single responsibility per type/extension
  • Value types over reference types

Error Handling

  • Result enum for typed errors
  • throws/try for propagation
  • Optional chaining with guard let/if let
  • Typed error definitions

Architecture

  • Avoid using protocol-oriented design unless necessary
  • Dependency injection over singletons
  • Composition over inheritance
  • Factory/Repository patterns

Debug Assertions

  • Use assert() for development-time invariant checking
  • Use assertionFailure() for unreachable code paths
  • Assertions removed in release builds for performance
  • Precondition checking with precondition() for fatal errors

Memory Management

  • weak references for cycles
  • unowned when guaranteed non-nil
  • Capture lists in closures
  • deinit for cleanup