Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
308 changes: 24 additions & 284 deletions .swiftformat

Large diffs are not rendered by default.

36 changes: 0 additions & 36 deletions AI_CONTEXT.md

This file was deleted.

26 changes: 0 additions & 26 deletions CHANGELOG.md

This file was deleted.

84 changes: 84 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# CLAUDE.md

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

## 常用指令

```bash
# 執行所有測試(必須先進入 WebParser/ 子目錄)
cd WebParser && swift test --parallel

# 執行單一測試
cd WebParser && swift test --filter "testJSONMapping"

# 提交前格式化程式碼
swiftformat .
```

CI 從 `WebParser/` 子目錄執行 `swift test --parallel`(見 `.github/workflows/test.yml`),本地執行時務必先 `cd WebParser`。

## 架構

專案包含兩個頂層目錄:

- **`WebParser/`** — Swift Package(函式庫本體),所有 Public API 位於此處。
- **`WebParserDemo/`** — Xcode App 專案,示範如何使用函式庫。

### 函式庫內部結構(`WebParser/Sources/WebParser/`)

解析流程由四個組件依序串接:

```
WebParser(入口)
→ WebParserSession(Cookie / 資料存儲單例)
→ WebParserEngine(WKWebView 離屏渲染引擎)
→ WebParserMapper(資料轉換器)
```

**`WebParser`**(`@MainActor class`)— 協調完整生命週期:Cookie 同步 → Engine 執行 → 重試迴圈 → Mapper 映射。呼叫端提供 `WebParserConfig` 與任意 `WebParserMapper`,回傳類型為泛型 `M.T`。

**`WebParserEngine`**(`@MainActor class`)— 將 `WKWebView` 包裝在 `CheckedContinuation<Data, Error>` 中。頁面載入完成後進入輪詢迴圈,持續呼叫 `evaluateJavaScript` 直到結果非空或逾時。結果透過 `JSONSerialization` 序列化為 `Data` 再回傳,這是維持 `Sendable` 合規的關鍵橋接點。

**`WebParserMapper`**(protocol)— 負責 `Data → T` 的轉換,內建兩個實作:
- `WebParserJSJSONMapper<T: Decodable>` — 使用 `JSONDecoder` 解碼。適用於 `executionJS` 回傳 JS 物件或陣列的情境。
- `WebParserRegexMapper<T>` — 將轉換邏輯委派給呼叫端提供的 `(Data) throws -> T` 閉包,回傳型別不限 `Codable`。適用於 `executionJS` 回傳純文字(如 `document.title` 或完整 HTML)的情境。

**`WebParserSession`**(`@MainActor` 單例)— 持有共用的 `WKWebsiteDataStore`,確保 Cookie 與快取在不同解析任務之間正確共享。

**`WebParserState`**(enum)— 狀態機,透過 `WebParserProgressDelegate` 發送:`started → loading(progress:) → executingJavaScript → completed | retrying | failed`。

### Demo App 結構(`WebParserDemo/WebParserDemo/`)

Demo 採用 UIKit SceneDelegate 架構,目錄分為三組:

- **`App/`** — `AppDelegate`(`@main`)、`SceneDelegate`(建立 `UITabBarController` + 兩個 `UINavigationController`)、`ViewModel` protocol 定義。
- **`TitleTest/`** — 輸入 URL 抓取網頁標題的功能,使用 `WebParserRegexMapper`。
- **`ComicList/`** — 解析漫畫更新列表的功能,使用 `WebParserJSJSONMapper` 搭配 `WebParserProgressDelegate` 顯示載入進度。

每個 Feature 遵循 `HostController → ViewModel → View` 三層架構,ViewModel 使用 `@Observable`。

### 關鍵設計決策

- 所有 WebKit 操作均標記 `@MainActor`,因為 `WKWebView` 必須在主執行緒操作。
- Engine 回傳 `Data`(而非 `Any`),以滿足跨並發邊界的 `Sendable` 規範。
- JavaScript 腳本應使用 Swift Raw String(`#"""..."""#`),避免跳脫字元問題。
- `blockMedia = true`(預設值)透過 `mediaTypesRequiringUserActionForPlayback` 阻擋影片與音訊載入,建議維持開啟以提升效能。

## 程式碼風格

- **格式化工具**:每次提交前執行 `swiftformat .`,配置檔為根目錄 `.swiftformat`。
- **縮排**:2 個空格。
- **縮寫詞**:全大寫,例如 `URL`, `ID`, `JS`, `JSON`, `UUID`。
- **尾隨逗號**:集合型別結尾總是加逗號。
- **MARK 區塊**:使用 `MARK: -` 分隔程式碼區塊,成員依可見性排序。
- **DocC 文件**:所有 Public API 必須附帶中文 DocC 註釋,標點符號使用英文半形(逗號 `,`、句點 `.`)。
- **測試框架**:使用 Swift Testing(`@Test`、`#expect`),不使用 XCTest。
- **Swift 版本**:6.2,啟用 Strict Concurrency 檢查。

## Skill Directory Map

> 所有路徑基於 `~/.claude/skills/`
> 載入目錄即載入其下所有檔案(SKILL.md + references/)

### 所有任務必須載入
- `swift-concurrency/`
62 changes: 24 additions & 38 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,17 @@

一個基於 WebKit 離屏渲染的現代 Swift 網頁解析框架. 專為處理需要 JavaScript 執行、智慧輪詢 (Polling) 以及動態 DOM 加載的複雜網頁而設計.

[Changelog](CHANGELOG.md)

## ✨ 特色

- **Swift 6 原生支持**: 全面採用 `async/await` 架構, 並針對執行緒安全 (Concurrency Safety) 進行優化.
- **動態網頁渲染**: 內建 `WKWebView` 引擎, 支持執行自定義 JavaScript, 輕鬆應對 SPA 或加密數據網站.
- **類型安全映射 (Mapper)**:
- `WebParserJSJSONMapper`: 直接將 JS 回傳的 JSON 轉換為 Swift `Codable` 模型.
- `WebParserRegexMapper`: 利用自定義邏輯或正規表達式從原始數據中擷取內容.
- `WebParserJSJSONMapper`: 直接將 JS 回傳的 JSON 轉換為 Swift `Decodable` 模型.
- `WebParserRegexMapper`: 透過自訂閉包從原始數據中擷取任意型別的內容, 不限於 `Codable`.
- **智慧進度監控**: 透過 `WebParserProgressDelegate` 即時追蹤網頁載入與解析狀態機變化.
- **效能優化**: 支持阻擋多媒體資源 (Block Media), 顯著提升渲染速度並節省流量.
- **開發友善**: 支持 Swift `#"""` 語法, 讓 JavaScript 腳本維護不再受轉義字元困擾.



## 🎨 代碼風格 (Code Style)

本專案由 **Joe Pan** 發起並維護, 強制執行嚴格的格式化規範:

- **縮排**: 使用 2 個空格.
- **命名規範**: 縮寫詞全大寫 (如 `URL`, `ID`).
- **Swift 6 兼容**: 針對 Swift 6.2 語法優化, 確保 Strict Concurrency 檢查全數通過.
- **尾隨逗號**: 集合型別結尾總是添加逗號, 優化 Git Diff 體驗.

如果你提交 Pull Request, 請確保已執行過 `swiftformat .`.

## 🛠️ 安裝方式

透過 **Swift Package Manager** 加入你的專案:
Expand All @@ -45,14 +30,15 @@ dependencies: [

## 🚀 快速上手

`WebParser` 透過不同的 `Mapper` 來決定解析邏輯, 你可以根據網頁回傳的數據類型靈活選擇
`WebParser` 透過不同的 `Mapper` 來決定解析邏輯, 你可以根據網頁回傳的數據類型靈活選擇.

### 1. 使用 `WebParserJSJSONMapper` (推薦)
當你的 `executionJS` 回傳的是 JavaScript 對象或陣列時, 這是最高效的方式。引擎會自動處理序列化, 你只需定義 `Codable` 模型。

當你的 `executionJS` 回傳的是 JavaScript 對象或陣列時, 這是最高效的方式. 引擎會自動處理序列化, 你只需定義 `Decodable` 模型.

```swift
// 定義模型
struct Comic: Codable {
struct Comic: Decodable {
let title: String
let updateDate: String
}
Expand All @@ -66,10 +52,10 @@ print("抓取到 \(results.count) 本漫畫")
```

### 2. 使用 `WebParserRegexMapper`
當你只需要從網頁原始碼 (HTML) 中利用正規表達式或字串處理來擷取特定文字時使用。

當你需要從回傳的原始字串中利用自定義邏輯擷取特定內容時使用.

```swift
// 假設 executionJS 回傳的是整個網頁的 document.title 或特定 HTML 區塊
let config = WebParserConfig(
url: url,
executionJS: "document.title"
Expand All @@ -89,34 +75,34 @@ print("網頁標題是: \(webTitle)")
## 🧩 進階用法

### 監聽解析狀態

讓你的 ViewModel 遵守 `WebParserProgressDelegate`:

```swift
func webParser(_ parser: WebParser, didUpdateState state: WebParserState) {
if case .loading(let progress) = state {
self.uiProgress = progress // 更新 SwiftUI 進度條
}
if case let .loading(progress) = state {
self.uiProgress = progress
}
}
```

## 🎨 程式碼風格

- **縮排**: 使用 2 個空格.
- **命名規範**: 縮寫詞全大寫 (如 `URL`, `ID`, `JS`, `JSON`).
- **Swift 6 兼容**: 針對 Swift 6.2 語法優化, 確保 Strict Concurrency 檢查全數通過.
- **尾隨逗號**: 集合型別結尾總是添加逗號, 優化 Git Diff 體驗.

如果你提交 Pull Request, 請確保已執行過 `swiftformat .`.

## 🧪 測試

本專案全面導入 **Swift Testing** 框架, 確保核心邏輯與 WebKit 渲染的穩定性.

```bash
# 在終端機執行測試
swift test
cd WebParser && swift test --parallel
```

## 🤖 協作致謝 (Co-creation)

本專案由 **Joe Pan (開發者)** 與 **Gemini 1.5 Flash (AI)** 深度協作完成.

在開發過程中, 我們共同解決了以下挑戰:
- **執行緒隔離**: 處理 `WKWebView` 在 Swift 6 嚴格檢查下的隔離問題.
- **抽象化設計**: 實作了 `WebParserMapper` 協定, 讓解析邏輯具備極佳的擴充性.

> 本專案利用 **Gemini 1.5 Flash** 的長文本推理與 Swift 6 語法理解能力, 優化了框架的併發安全架構.
> 「這不只是一段程式碼, 更是人類創意與 AI 邏輯交織的成果.」

---

Released under the MIT License by Joe Pan.
Loading
Loading