跳到主要内容
极客日志极客日志
首页博客AI提示词GitHub精选代理工具
搜索
|注册
博客列表
SwiftAI大前端

基于 SwiftUI 开发 iOS Ollama 客户端实践

介绍使用 Swift 和 SwiftUI 构建 iOS 客户端连接本地 Ollama 服务的方案。内容包括数据模型定义、流式 API 调用实现、会话管理及界面设计。通过开源项目演示如何在移动端安全访问私有化部署的大语言模型,满足敏感数据处理需求。

二进制发布于 2025/2/6更新于 2026/5/511 浏览
基于 SwiftUI 开发 iOS Ollama 客户端实践

基于 SwiftUI 开发 iOS Ollama 客户端实践

一、背景与动机

因行业特殊性,许多敏感数据需要使用大模型进行处理和分析,无法使用公有云上的各种模型。因此,在公司内部部署了 Ollama 来跑本地模型解决问题。

此时面临一个问题:手机端需要访问 Ollama API,但市面上缺乏趁手的 App。为了解决这一痛点,决定使用 Swift + SwiftUI 开发一个原生客户端。

二、功能设计

2.1 Ollama 服务配置

支持以下配置项:

  • 服务地址(URL)
  • 使用的模型名称
  • 系统 Prompt(System Prompt)
  • 模型参数(Temperature, TopP 等,预留扩展)

2.2 Ollama API 流式调用

为了提升用户体验,采用流式调用方式。每次调用只返回一部分结果,客户端不断读取并实时渲染,直到全部返回。

2.3 会话管理

  • 支持创建新会话
  • 支持重开历史会话
  • 支持选择会话列表中的历史记录继续对话

三、数据模型建模

为了方便调用和解析 JSON,首先定义核心数据模型。

3.1 聊天请求对象

struct OllamaChatRequest: Codable {
    var model: String = ""
    var messages: [OllamaMessage] = []
    var stream: Bool = true
}

3.2 聊天响应对象

struct OllamaChatResponse: Codable {
    var message: OllamaMessage
}

3.3 消息流对象

struct OllamaChatStream: Codable {
    var done: Bool
    var message: OllamaMessage
}

3.4 消息对象

struct OllamaMessage: Codable, Identifiable {
    public static let ROLE_SYSTEM = "system"
    public static let ROLE_ASSISTANT = "assistant"
    public static let ROLE_USER = "user"
    
    let id = UUID()
    var role: String
    var content: String
}

3.5 模型列表对象

struct OllamaModelResponse: Codable {
    var models: [OllamaModel]
}

struct OllamaModel: Identifiable, Codable {
    let id = UUID()
    var name: String
}

3.6 服务配置对象

struct OllamaConfig: Codable {
    public static let CONFIG_PATH = "config.json"
    
    var url: String
    var prompt: String
    var model: String
    var models: [OllamaModel]
}

四、网络层实现

iOS 端推荐使用 URLSession 配合 async/await 进行网络请求。针对流式输出,需要处理 SSE (Server-Sent Events) 格式的数据。

4.1 网络服务类

class OllamaNetworkService {
    private let session: URLSession
    
    init() {
        self.session = URLSession(configuration: .default)
    }
    
    func chat(request: OllamaChatRequest, baseURL: String) async throws -> AsyncThrowingStream<String, Error> {
        var components = URLComponents(string: "\(baseURL)/api/chat")!
        guard let url = components.url else { throw URLError(.badURL) }
        
        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        request.httpBody = try JSONEncoder().encode(request)
        
        let (bytes, response) = try await session.bytes(for: request)
        guard let httpResponse = response as? HTTPURLResponse,
              httpResponse.statusCode == 200 else {
            throw URLError(.badServerResponse)
        }
        
        return bytes.lines.asyncThrowingMap { line in
            // 解析 SSE 格式数据
            if line.isEmpty || line == "\n" { return nil }
            // 实际项目中需解析 JSON 提取 content 字段
            return line
        }.filter { $0 != nil }.compactMap { $0 }
    }
}

4.2 错误处理

在网络请求中应包含完善的错误捕获机制,包括网络超时、JSON 解析失败、API 返回错误码等情况。

五、视图模型与 UI 实现

5.1 ViewModel

使用 ObservableObject 管理状态,监听流式数据更新。

class ChatViewModel: ObservableObject {
    @Published var messages: [OllamaMessage] = []
    @Published var isLoading = false
    
    func sendMessage(_ text: String) async {
        isLoading = true
        defer { isLoading = false }
        
        let userMsg = OllamaMessage(role: .ROLE_USER, content: text)
        messages.append(userMsg)
        
        do {
            let assistantMsg = OllamaMessage(role: .ROLE_ASSISTANT, content: "")
            messages.append(assistantMsg)
            
            let service = OllamaNetworkService()
            let request = OllamaChatRequest(model: "llama2", messages: messages)
            
            for try await chunk in try await service.chat(request: request, baseURL: "http://localhost:11434") {
                assistantMsg.content += chunk
                Task { @MainActor in
                    messages[messages.count - 1] = assistantMsg
                }
            }
        } catch {
            print(error.localizedDescription)
        }
    }
}

5.2 界面布局

主界面采用 NavigationView 或 TabView 结构,左侧为会话列表,右侧为聊天内容区域。输入框固定在底部。

六、会话持久化

建议使用 CoreData 或 UserDefaults 存储会话历史。对于长文本,可考虑本地数据库存储;对于配置信息,可使用 Codable 序列化到文件。

七、安全与部署

  1. HTTPS: 生产环境建议配置 HTTPS,防止中间人攻击。
  2. 鉴权: 如果 Ollama 服务端开启了认证,需在 Header 中添加 Token。
  3. 构建: 由于涉及备案问题,可选择本地构建安装至 iPhone 进行测试,暂不上架 App Store。

八、总结

本方案展示了如何使用 Swift 和 SwiftUI 快速构建连接本地 Ollama 服务的移动端应用。通过流式接口优化体验,结合合理的架构设计,实现了敏感数据的本地化处理需求。后续可进一步扩展多模态支持及更复杂的提示词工程功能。

目录

  1. 基于 SwiftUI 开发 iOS Ollama 客户端实践
  2. 一、背景与动机
  3. 二、功能设计
  4. 2.1 Ollama 服务配置
  5. 2.2 Ollama API 流式调用
  6. 2.3 会话管理
  7. 三、数据模型建模
  8. 3.1 聊天请求对象
  9. 3.2 聊天响应对象
  10. 3.3 消息流对象
  11. 3.4 消息对象
  12. 3.5 模型列表对象
  13. 3.6 服务配置对象
  14. 四、网络层实现
  15. 4.1 网络服务类
  16. 4.2 错误处理
  17. 五、视图模型与 UI 实现
  18. 5.1 ViewModel
  19. 5.2 界面布局
  20. 六、会话持久化
  21. 七、安全与部署
  22. 八、总结
  • 💰 8折买阿里云服务器限时8折了解详情
  • GPT-5.5 超高智商模型1元抵1刀ChatGPT中转购买
  • 代充Chatgpt Plus/pro 帐号了解详情
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • Linux 安装与使用 3x-ui 面板
  • Windows 11 与 Ubuntu 22.04 双系统安装指南
  • AI 大模型原理、应用与未来趋势
  • 深入理解 Linux 信号机制:从 task_struct 到信号递达全过程
  • Agentic Workflow 解析:技术挑战与未来趋势
  • OpenClaw 接入飞书机器人与 Ollama 本地大模型部署指南
  • 户外机器人 GNSS 仿真测试:双天线定向与 RTK 高精度定位实战
  • 大模型应用开发极简入门:从原理到实战指南
  • Python 协同过滤算法 Django 餐厅推荐系统
  • OpenClaw 多 Agent 模式:构建 AI 助手团队
  • Java 双向链表实现与 LinkedList 源码解析
  • Kiro AI 助手完整使用指南
  • Office 区域不支持 Copilot 的解决方案
  • FMC 与 FMC+ 标准详解
  • Python3.8 图像生成应用:Stable Diffusion 轻量化部署
  • 算法兵法全略
  • 从 vw/vh 到 clamp():前端响应式设计的痛点与进化
  • 云开发 Copilot 功能介绍与低代码生成实战
  • MCP 实战:利用 Figma AI Bridge 生成前端代码
  • C++信奥模拟算法习题专项训练

相关免费在线工具

  • RSA密钥对生成器

    生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online

  • Mermaid 预览与可视化编辑

    基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online

  • 随机西班牙地址生成器

    随机生成西班牙地址(支持马德里、加泰罗尼亚、安达卢西亚、瓦伦西亚筛选),支持数量快捷选择、显示全部与下载。 在线工具,随机西班牙地址生成器在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online