Swift中修改JSON字段值实现用户相册选图更新头像的方案咨询
iOS端个人资料头像修改功能实现方案
现有代码核心问题
- 选图回调逻辑重复冗余,没有对选中图片做压缩处理,大图上传失败概率极高
- 上传回调中存在严重的类型转换错误:直接将服务器返回的字符串类型的
profileImage字段强转为UIImage类型,会直接触发崩溃 - 上传成功后没有更新本地存储的用户信息里的
profileImage字段,下次打开APP头像还是会显示旧的 - 所有UI更新和数据存储操作没有切到主线程执行,
URLSession回调默认在后台队列,直接操作UI会出现异常 - 没有错误处理逻辑,上传失败、接口鉴权失败、返回数据格式错误等场景没有任何提示和降级处理
- 直接使用相册返回的本地文件路径作为上传文件名,无实际意义且可能出现命名冲突
修正后的完整实现代码
1. 选图回调优化(去除重复逻辑)
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { // 统一获取选中图片,优先取编辑后版本 guard let selectedImage = info[.editedImage] as? UIImage ?? info[.originalImage] as? UIImage else { dismiss(animated: true) return } // 先更新本地UI,即时给用户反馈 profileImage.image = selectedImage // 生成随机文件名避免重名 let fileName = "profile_\(UUID().uuidString).jpeg" // 调用上传逻辑 uploadImage(paramName: "image", fileName: fileName, image: selectedImage) dismiss(animated: true) }
2. 图片上传逻辑修正
func uploadImage(paramName: String, fileName: String, image: UIImage) { guard let url = URL(string: "https://api.offernews.co/api/user") else { return } let boundary = UUID().uuidString var urlRequest = URLRequest(url: url) urlRequest.httpMethod = "PATCH" // 接口需要鉴权的话打开下方注释,替换成你自己的用户token // urlRequest.setValue("Bearer 你的用户登录token", forHTTPHeaderField: "Authorization") urlRequest.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type") var data = Data() // 拼接表单参数 data.append("\r\n--\(boundary)\r\n".data(using: .utf8)!) data.append("Content-Disposition: form-data; name=\"\(paramName)\"; filename=\"\(fileName)\"\r\n".data(using: .utf8)!) data.append("Content-Type: image/jpeg\r\n\r\n".data(using: .utf8)!) // 压缩图片到0.5质量,可根据需求调整,头像场景0.3-0.7都足够清晰 if let compressedImageData = image.jpegData(compressionQuality: 0.5) { data.append(compressedImageData) } data.append("\r\n--\(boundary)--\r\n".data(using: .utf8)!) URLSession.shared.uploadTask(with: urlRequest, from: data) { [weak self] responseData, response, error in // 切到主线程处理UI和本地数据更新 DispatchQueue.main.async { guard let self = self, error == nil, let responseData = responseData else { // 这里可自定义上传失败的提示,比如弹toast print("上传失败: \(error?.localizedDescription ?? "未知错误")") return } do { guard let userJson = try JSONSerialization.jsonObject(with: responseData) as? [String: Any] else { print("接口返回数据格式错误") return } // 取服务器返回的新头像路径 guard let newProfileImagePath = userJson["profileImage"] as? String else { print("返回数据无头像字段") return } // 更新本地存储的用户信息中的profileImage字段 // 下方的currentUser替换为你自己存储用户信息的key if var localUser = UserDefaults.standard.dictionary(forKey: "currentUser") { localUser["profileImage"] = newProfileImagePath UserDefaults.standard.set(localUser, forKey: "currentUser") } print("头像更新成功,新路径:\(newProfileImagePath)") } catch { print("JSON解析失败:\(error.localizedDescription)") } } }.resume() }
额外注意事项
- 如果服务器返回的
profileImage是相对路径,需要拼接上你的服务器域名前缀才能正常加载显示 - 图片压缩率可以根据业务需求调整,JPEG格式比PNG格式体积小很多,头像场景优先用JPEG
- 建议给上传过程加loading提示,避免用户重复点击提交
内容的提问来源于stack exchange,提问作者Ali kazemi
相关产品推荐
相关产品推荐

