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
相关产品推荐
相关产品推荐

