iOS icon indicating copy to clipboard operation
iOS copied to clipboard

Refactor post detail rendering with native RichView module

Open graycreate opened this issue 3 months ago • 0 comments

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.swift public 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 .task modifier (structured concurrency)
    • [ ] Priority control (.userInitiated)
  • [ ] 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 HtmlView with RichView
    • [ ] Remove imgs parameter (auto-detect from HTML)
    • [ ] Use .default configuration
    • [ ] Implement event handlers (links, images, @mentions)
    • [ ] Maintain rendered state binding
  • [ ] 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 RichText with RichView
    • [ ] Use .compact configuration (smaller fonts)
    • [ ] Implement event handlers
    • [ ] Adapt to reply list layout
  • [ ] 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.swift if no longer needed
    • [ ] Remove old RichText.swift (Atributika version)
    • [ ] Remove Atributika dependency if unused elsewhere
    • [ ] Update documentation

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

graycreate avatar Oct 19 '25 10:10 graycreate