avatar

mdo

Hello

  • 首页
  • 知识库
  • 归档
  • 标签
  • 关于
主页 开发一个博客文章发布 App
文章

开发一个博客文章发布 App

发表于 2026-06-19 更新于 2026-06- 19
作者 mdo
27~34 分钟 阅读

针对 iOS 平台开发一个博客文章发布 App(支持写文章、编辑、管理和一键发布),需要兼顾极致的富文本编辑体验与高能的网络通信同步。

以下是为您量身定制的完整开发技术栈、核心架构和功能实现方案:


🎨 核心功能模块

  • 富文本编辑器:支持 Markdown 解析、实时预览、图片/视频插入。

  • 本地草稿箱:支持断网离线编辑,自动保存,防止内容丢失。

  • 媒体库管理:异步上传图片到云存储(如 OSS/S3),获取 URL 插入文章。

  • 文章发布/管理:分类、标签、状态管理(草稿、已发布、私密)。

  • 全双工同步(选配):配合您之前提到的 Golang RPC WebSocket,实现多端多设备文章状态实时同步。


🛠️ 推荐技术栈 (iOS 端)

模块 [1]

推荐方案

说明

UI 框架

SwiftUI

苹果主流,声明式 UI,极其适合做列表和表单展示。

编辑器核心

TextKit 2 / MarkdownUI

TextKit 2 适合定制原生富文本;MarkdownUI 适合渲染排版。

网络层

Alamofire / NWWebSocket

Alamofire 处理标准 RESTful API;NWWebSocket 处理 RPC 通信。

本地数据库

Core Data / GRDB.swift

存储本地草稿、离线文章列表,GRDB 性能极佳。

图片加载

Kingfisher

缓存和异步加载文章内的插图。


💻 核心代码实现:Markdown 编辑器与发布

以下是一个使用 SwiftUI 编写的简单文章编辑器界面,并演示如何将其转化为 JSON 数据准备发布。

swift

import SwiftUI

// 1. 定义文章数据模型
struct BlogPost: Codable {
    var title: String
    var content: String
    var tags: [String]
    var status: String // "draft" 或 "published"
}

struct EditorView: View {
    @State private var title: String = ""
    @State private var content: String = ""
    @State private var tags: String = ""
    @State private var isPublishing = false
    
    var body: some View {
        NavigationView {
            VStack(spacing: 16) {
                // 文章标题输入
                TextField("请输入文章标题...", text: $title)
                    .font(.title2)
                    .bold()
                    .padding(.horizontal)
                
                Divider()
                
                // 标签输入
                TextField("标签 (用逗号分隔)", text: $tags)
                    .font(.subheadline)
                    .foregroundColor(.gray)
                    .padding(.horizontal)
                
                Divider()
                
                // 文章正文编辑器 (支持多行)
                TextEditor(text: $content)
                    .font(.body)
                    .padding(.horizontal)
                    .frame(maxHeight: .infinity)
            }
            .navigationTitle("写文章")
            .navigationBarTitleDisplayMode(.inline)
            .toolbar {
                ToolbarItem(placement: .navigationBarTrailing) {
                    Button(action: publishPost) {
                        if isPublishing {
                            ProgressView()
                        } else {
                            Text("发布")
                                .bold()
                                .foregroundColor(.blue)
                        }
                    }
                    .disabled(title.isEmpty || content.isEmpty || isPublishing)
                }
            }
        }
    }
    
    // 2. 发布文章逻辑
    func publishPost() {
        isPublishing = true
        
        let tagArray = tags.components(separatedBy: ",").map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
        let post = BlogPost(title: title, content: content, tags: tagArray, status: "published")
        
        // 序列化为 JSON
        guard let jsonData = try? JSONEncoder().encode(post) else { return }
        
        // 发送网络请求 (此处可对接您的 Golang 后端 HTTP API 或 WebSocket RPC)
        sendToServer(data: jsonData)
    }
    
    func sendToServer(data: Data) {
        guard let url = URL(string: "https://yourblogapi.com") else { return }
        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        request.httpBody = data
        
        URLSession.shared.dataTask(with: request) { _, response, error in
            DispatchQueue.main.MainActor.run {
                self.isPublishing = false
                if let error = error {
                    print("发布失败: \(error.localizedDescription)")
                } else {
                    print("发布成功!")
                    // 清空当前输入或返回上一级
                }
            }
        }.resume()
    }
}

请谨慎使用此类代码。


🔗 进阶:如何与 Golang WebSocket RPC 对接?

如果您希望利用上一条聊到的 Golang WebSocket RPC 来发布文章,iOS 端可以采用以下策略:

  1. 消息封装:在 iOS 端将 BlogPost 作为 params,封装进标准的 JSON-RPC 格式:

    json

    {
      "jsonrpc": "2.0",
      "method": "Blog.Publish",
      "params": {
        "title": "我的第一篇iOS文章",
        "content": "内容...",
        "tags": ["Swift", "Go"]
      },
      "id": 101
    }
    

    请谨慎使用此类代码。

  2. 长连接维护:使用原生 URLSessionWebSocketTask 或 Starscream 库保持与 Golang 后端的连接,在用户点击发布时,直接通过 WebSocket 帧(Frame)发送该 JSON 字符串。

  3. 实时状态反馈:后端处理完毕后,通过相同连接返回 id: 101 的 Response,iOS 监听到该消息后更新 UI 为“发布成功”。

技术
许可协议:  CC BY 4.0
分享

相关文章

7月 26, 2026

完美地解决 TP3 老系统数据的平滑读取

为了彻底、完美地解决 TP3 老系统数据的平滑读取,必须解决两个核心痛点: TP3 的盲猜反序列化机制(标量存原样,数组/对象存原生 serialize,同时兼容 json)。 TP3 与 TP6 的缓存前缀(Prefix)不一致。 以下是为你量身定制的终极落地解决方案,通

7月 26, 2026

thinkphp3 redis序列化和反序列化

在老系统重构迁移至 ThinkPHP 6 (TP6) 的过程中,ThinkPHP 3 (TP3) 的 Redis 序列化历史遗留问题是最核心的连环坑之一。 TP3 的 S('key', $value) 缓存方法和底层 Redis 驱动,在存储非字符串(如数组、对象)时,有一套独特的序列化行为。如果不

7月 24, 2026

Table 空间极易发生哈希冲突并溢出

既然你没有在业务中主动调用 Room,只使用了全局广播,那么这个问题就非常明确了:即使你不用房间功能,think-swoole 只要开启了 WebSocket,底层就会强行初始化并注入一个名为 Room 的驱动组件。 [1] 框架底层默认使用 table 类型来作为 Room 的存储介质。当进行全局

下一篇

Golang 中,结合使用 RPC(远程过程调用)与 WebSocket

上一篇

Redis 缓存余额 + 前端异步局部刷新 + 提供手动刷新按钮

最近更新

  • 完美地解决 TP3 老系统数据的平滑读取
  • thinkphp3 redis序列化和反序列化
  • Table 空间极易发生哈希冲突并溢出
  • 将监控程序直接跑在云端
  • AI 驱动型 Facebook 群组关键词监控 Chrome 浏览器插件

热门标签

API CodeGeex Gitkraken Management Manticore Premiere Sublime Swoole ThinkPHP ThinkPHP5

目录

©2026 mdo. 保留部分权利。