-
Couldn't load subscription status.
- Fork 48
Description
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 feat: RichView Module - Modern Rich Text Rendering System (Phases 1-4 Complete) #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