You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift开发:Gmail预填充邮件URL未移除百分编码问题

问题描述

在iOS应用中实现意见反馈功能,需打开邮件应用并预填充收件人、主题及正文内容。目前功能基本正常,但Gmail应用里主题和正文中的空格会显示为百分编码(例如显示成%2520),怀疑是iOS 15的问题,但不确定。

实现代码如下:

private func createEmailUrl(to: String, subject: String, body: String) -> URL? {
    let subjectEncoded = subject.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
    let bodyEncoded = body.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
    
    
    let gmailUrl = URL(string: "googlegmail://co?to=\(to)&subject=\(subjectEncoded)&body=\(bodyEncoded)")
    
    let outlookUrl = URL(string: "ms-outlook://compose?to=\(to)&subject=\(subjectEncoded)")
    let yahooMail = URL(string: "ymail://mail/compose?to=\(to)&subject=\(subjectEncoded)&body=\(bodyEncoded)")
    let sparkUrl = URL(string: "readdle-spark://compose?recipient=\(to)&subject=\(subjectEncoded)&body=\(bodyEncoded)")
    let defaultUrl = URL(string: "mailto:\(to)?subject=\(subjectEncoded)&body=\(bodyEncoded)")
    
    if let gmailUrl = gmailUrl, UIApplication.shared.canOpenURL(gmailUrl) {
        return gmailUrl
    } else if let outlookUrl = outlookUrl, UIApplication.shared.canOpenURL(outlookUrl) {
        return outlookUrl
    } else if let yahooMail = yahooMail, UIApplication.shared.canOpenURL(yahooMail) {
        return yahooMail
    } else if let sparkUrl = sparkUrl, UIApplication.shared.canOpenURL(sparkUrl) {
        return sparkUrl
    }
    
    return defaultUrl
}

调用方式:

UIApplication.shared.open(url)

Gmail中显示的主题示例:

Reg:%2520Suggestions/Feedback%2520About%2520iOS%2520App

解决方案

出现%2520是因为双重URL编码:你先对主题和正文做了一次编码,而Gmail的URL Scheme在处理参数时又自动执行了一次编码,导致原本的空格编码%20被再次编码为%2520。

方案1:单独处理Gmail参数

针对Gmail不做预编码,其他邮件应用保持原有逻辑:

private func createEmailUrl(to: String, subject: String, body: String) -> URL? {
    // 通用编码后的参数,用于非Gmail应用
    let subjectEncoded = subject.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
    let bodyEncoded = body.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
    
    // Gmail单独处理,不预编码参数
    let gmailUrl = URL(string: "googlegmail://co?to=\(to)&subject=\(subject)&body=\(body)")
    
    let outlookUrl = URL(string: "ms-outlook://compose?to=\(to)&subject=\(subjectEncoded)")
    let yahooMail = URL(string: "ymail://mail/compose?to=\(to)&subject=\(subjectEncoded)&body=\(bodyEncoded)")
    let sparkUrl = URL(string: "readdle-spark://compose?recipient=\(to)&subject=\(subjectEncoded)&body=\(bodyEncoded)")
    let defaultUrl = URL(string: "mailto:\(to)?subject=\(subjectEncoded)&body=\(bodyEncoded)")
    
    if let gmailUrl = gmailUrl, UIApplication.shared.canOpenURL(gmailUrl) {
        return gmailUrl
    } else if let outlookUrl = outlookUrl, UIApplication.shared.canOpenURL(outlookUrl) {
        return outlookUrl
    } else if let yahooMail = yahooMail, UIApplication.shared.canOpenURL(yahooMail) {
        return yahooMail
    } else if let sparkUrl = sparkUrl, UIApplication.shared.canOpenURL(sparkUrl) {
        return sparkUrl
    }
    
    return defaultUrl
}

方案2:使用URLComponents统一构建(更可靠)

URLComponents会自动处理编码逻辑,避免手动编码导致的重复问题,适配所有邮件应用:

private func createEmailUrl(to: String, subject: String, body: String) -> URL? {
    // 构建Gmail URL
    var gmailComponents = URLComponents(string: "googlegmail://co")!
    gmailComponents.queryItems = [
        URLQueryItem(name: "to", value: to),
        URLQueryItem(name: "subject", value: subject),
        URLQueryItem(name: "body", value: body)
    ]
    let gmailUrl = gmailComponents.url
    
    // 构建Outlook URL
    var outlookComponents = URLComponents(string: "ms-outlook://compose")!
    outlookComponents.queryItems = [
        URLQueryItem(name: "to", value: to),
        URLQueryItem(name: "subject", value: subject)
    ]
    let outlookUrl = outlookComponents.url
    
    // 构建Yahoo Mail URL
    var yahooComponents = URLComponents(string: "ymail://mail/compose")!
    yahooComponents.queryItems = [
        URLQueryItem(name: "to", value: to),
        URLQueryItem(name: "subject", value: subject),
        URLQueryItem(name: "body", value: body)
    ]
    let yahooMail = yahooComponents.url
    
    // 构建Spark URL
    var sparkComponents = URLComponents(string: "readdle-spark://compose")!
    sparkComponents.queryItems = [
        URLQueryItem(name: "recipient", value: to),
        URLQueryItem(name: "subject", value: subject),
        URLQueryItem(name: "body", value: body)
    ]
    let sparkUrl = sparkComponents.url
    
    // 构建默认mailto URL
    var mailtoComponents = URLComponents(string: "mailto:\(to)")!
    mailtoComponents.queryItems = [
        URLQueryItem(name: "subject", value: subject),
        URLQueryItem(name: "body", value: body)
    ]
    let defaultUrl = mailtoComponents.url
    
    if let gmailUrl = gmailUrl, UIApplication.shared.canOpenURL(gmailUrl) {
        return gmailUrl
    } else if let outlookUrl = outlookUrl, UIApplication.shared.canOpenURL(outlookUrl) {
        return outlookUrl
    } else if let yahooMail = yahooMail, UIApplication.shared.canOpenURL(yahooMail) {
        return yahooMail
    } else if let sparkUrl = sparkUrl, UIApplication.shared.canOpenURL(sparkUrl) {
        return sparkUrl
    }
    
    return defaultUrl
}

内容的提问来源于stack exchange,提问作者Shunan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 09:10:53