Refactor post detail rendering with native RichView module
Overview
Refactor V2er's content rendering by replacing two different implementations with a unified, high-performance RichView module.
Current State (Two Implementations)
1. Topic Content (NewsContentView.swift)
Implementation: HtmlView - WKWebView-based
Location: V2er/View/FeedDetail/NewsContentView.swift:23
HtmlView(html: contentInfo?.html, imgs: contentInfo?.imgs ?? [], rendered: $rendered)
Problems:
- High memory usage (~20MB per WebView)
- Slow initialization and rendering
- Async height calculation causes UI jank
- JavaScript bridge complexity
- No caching mechanism
2. Reply Content (ReplyItemView.swift)
Implementation: RichText - NSAttributedString HTML parser
Location: V2er/View/FeedDetail/ReplyItemView.swift:48
RichText { info.content }
Problems:
- No code syntax highlighting
- No @mention detection/navigation
- No image preview support
- Inconsistent with topic rendering
- No caching mechanism
Unified Issues
- Maintaining two different implementations
- Inconsistent user experience
- Both lack modern features (@mention, code highlighting)
- Both lack performance optimizations (caching)
Proposed Solution
Implement a unified RichView module that replaces both:
Architecture
V2EX HTML → HTMLToMarkdownConverter → Markdown
→ swift-markdown Parser → AST
→ V2EXMarkupVisitor → AttributedString
→ UITextView (via UIViewRepresentable)
Module Structure
V2er/View/RichView/
├── RichView.swift # Public interface
├── Components/ # RichTextView, AsyncImageAttachment
├── Rendering/ # HTML→Markdown, MarkdownRenderer, V2EXMarkupVisitor
├── Support/ # RenderCache, DegradationChecker, PerformanceBenchmark
├── Models/ # RichViewEvent, RenderConfiguration, RenderMetadata
└── Extensions/ # AttributedString+RichView, String+Markdown
Implementation Plan
Phase 1: Basic Infrastructure (2-3 days)
- [ ] Create RichView module directory structure
- [ ] Integrate SPM dependencies (swift-markdown, Highlightr)
- [ ] Implement
RichView.swiftpublic interface - [ ] Implement
Rendering/HTMLToMarkdownConverter.swift(basic tags) - [ ] Implement
Rendering/MarkdownRenderer.swift - [ ] Implement
Rendering/V2EXMarkupVisitor.swift(basic) - [ ] Implement
Components/RichTextView.swift(UITextView wrapper)
Deliverable: Can render simple V2EX posts with text formatting and links
Phase 2: Complete Features (3-4 days)
- [ ] Complete
Rendering/HTMLToMarkdownConverter.swift- [ ] All HTML tags (img, blockquote, ul, ol, li, hr, h1-h6)
- [ ] @mention detection and conversion
- [ ] Image link wrapping handling
- [ ] Complete
Rendering/V2EXMarkupVisitor.swift- [ ] Image rendering with placeholders
- [ ] List and quote rendering
- [ ] Code syntax highlighting (Highlightr integration)
- [ ] Implement
Components/AsyncImageAttachment.swift- [ ] Kingfisher integration for async loading
- [ ] Placeholder and error handling
- [ ] Implement
Models/RichViewEvent.swift(event types) - [ ] Implement
Models/RenderConfiguration.swift(config options) - [ ] Complete event handling in
RichTextView- [ ] Link taps, image taps, @mention taps
- [ ] Text selection support
Deliverable: All V2EX content features working (images, code, @mentions)
Phase 3: Performance Optimization (2-3 days)
- [ ] Implement
Support/RenderCache.swift- [ ] NSCache with NSObject wrapper for AttributedString
- [ ] MD5-based cache keys
- [ ] LRU eviction policy
- [ ] Implement
Support/DegradationChecker.swift- [ ] HTML size detection (>100KB → WebView)
- [ ] Unsupported tag detection
- [ ] Error detection and logging
- [ ] Implement
Support/PerformanceBenchmark.swift- [ ] Render time measurement
- [ ] Memory usage tracking
- [ ] Cache hit rate statistics
- [ ] Implement
Models/RenderMetadata.swift- [ ] Performance metrics recording
- [ ] Optimize async rendering
- [ ] Use
.taskmodifier (structured concurrency) - [ ] Priority control (.userInitiated)
- [ ] Use
- [ ] Performance testing
- [ ] Render speed < 50ms (single post)
- [ ] Memory reduction > 70%
- [ ] Scrolling at 60fps
Deliverable: Production-ready performance with caching
Phase 4: Integration (2-3 days)
4.1 Replace Topic Content (NewsContentView)
- [ ] Replace
HtmlViewwithRichView- [ ] Remove
imgsparameter (auto-detect from HTML) - [ ] Use
.defaultconfiguration - [ ] Implement event handlers (links, images, @mentions)
- [ ] Maintain
renderedstate binding
- [ ] Remove
- [ ] Add feature flag
useRichViewForTopic- [ ] Fallback to HtmlView on error
- [ ] Test with various topic types
- [ ] Text-only, images, code, @mentions, mixed
4.2 Replace Reply Content (ReplyItemView)
- [ ] Replace
RichTextwithRichView- [ ] Use
.compactconfiguration (smaller fonts) - [ ] Implement event handlers
- [ ] Adapt to reply list layout
- [ ] Use
- [ ] Add feature flag
useRichViewForReply- [ ] Fallback to RichText on error
- [ ] Performance testing
- [ ] Reply list scrolling (60fps)
- [ ] Cache hit rate > 80%
- [ ] Memory usage comparison
- [ ] Test with various reply types
- [ ] Short/long, code, @mentions, list performance (100+ replies)
4.3 UI & Interaction Testing
- [ ] Font size adaptation (topic: 16pt, reply: 14pt)
- [ ] Dark mode support (colors, code themes)
- [ ] Line/paragraph spacing matching existing UI
- [ ] Link clicking (external/internal navigation)
- [ ] Image preview (viewer, gestures, close)
- [ ] @mention navigation (user profile)
- [ ] Text selection (long-press, copy)
4.4 Degradation Testing
- [ ] Large content (>100KB) → WebView fallback
- [ ] Unsupported tags → WebView fallback
- [ ] Render errors → fallback with proper error handling
- [ ] Verify fallback functionality
Deliverable: Both topic and reply content using RichView, ready for testing
Phase 5: Rollout (1-2 days)
- [ ] Gradual rollout strategy
- [ ] 10% users (monitor metrics)
- [ ] 50% users (verify stability)
- [ ] 100% users (full rollout)
- [ ] Performance monitoring
- [ ] Render time tracking
- [ ] Memory usage monitoring
- [ ] Cache hit rate analytics
- [ ] Degradation rate tracking
- [ ] User feedback collection
- [ ] Bug fixes and refinements
- [ ] Final cleanup
- [ ] Remove
HtmlView.swiftif no longer needed - [ ] Remove old
RichText.swift(Atributika version) - [ ] Remove Atributika dependency if unused elsewhere
- [ ] Update documentation
- [ ] Remove
Deliverable: Full production rollout with monitoring
Success Metrics
Performance Targets
- Topic Content: 10x faster rendering (< 100ms vs ~1s)
- Reply Content: 3-5x faster with caching
- Memory: 70%+ reduction (eliminate WebView overhead)
- Scrolling: Stable 60fps in reply lists with 100+ items
- Cache Hit Rate: > 80% in reply lists
Feature Completeness
- ✅ Code syntax highlighting (185+ languages)
- ✅ @mention detection and navigation
- ✅ Image preview with built-in viewer
- ✅ Consistent rendering (topic & reply)
- ✅ Text selection and copy
Quality Metrics
- Degradation Rate: < 1% of content requiring WebView fallback
- Crash Rate: No increase from baseline
- User Complaints: No significant increase
Dependencies
- swift-markdown (Apple official, iOS 15+) - CommonMark/GFM parser
- Highlightr (v2.1.0+) - Syntax highlighting
- SwiftSoup (already in project) - HTML parsing
- Kingfisher (already in project) - Image loading
Migration Comparison
| Feature | HtmlView (Topic) | RichText (Reply) | RichView (New) |
|---|---|---|---|
| Render Engine | WKWebView | NSAttributedString HTML | AttributedString + Markdown |
| Performance | Slow (~1s) | Medium (~200ms) | Fast (<50ms) |
| Memory Usage | High (~20MB) | Low (~1MB) | Low (~1MB) |
| Code Highlighting | ❌ | ❌ | ✅ (185+ languages) |
| @Mention Navigation | Manual | ❌ | ✅ Built-in |
| Image Preview | Manual | ❌ | ✅ Built-in |
| Height Calculation | Async (jank) | Sync | Sync |
| Caching | ❌ | ❌ | ✅ Automatic (80%+ hit rate) |
| Maintainability | Complex (JS bridge) | Simple | Simple (Swift native) |
Risks & Mitigation
| Risk | Mitigation |
|---|---|
| Complex HTML edge cases | WebView degradation fallback |
| Performance not meeting targets | Staged KPIs (200ms→100ms→50ms) |
| Breaking existing functionality | Feature flags + gradual rollout |
| Memory leaks in image loading | Structured concurrency + lifecycle management |
| User complaints about changes | Monitor feedback, quick rollback capability |
Related
- PR #69: Technical design and API specification documentation
-
Files:
-
.plan/richtext_plan.md- Complete technical design (1,141 lines) -
.plan/richview_api.md- Full API specification (997 lines)
-
Timeline
Estimated: 10-12 days total
Priority: High (major performance improvement + feature unification)
Breakdown:
- Phase 1: 2-3 days
- Phase 2: 3-4 days
- Phase 3: 2-3 days
- Phase 4: 2-3 days
- Phase 5: 1-2 days