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

URLSession网络请求回调闭包接收数据后未执行问题

问题根因与修复方案

核心导致闭包不执行的原因

  • 系统ATS安全策略拦截:你使用的是http非加密协议请求,iOS默认的App Transport Security策略会直接拦截所有未声明例外的HTTP请求,拦截发生在系统层面,不会触发URLSession的任何回调,这是闭包完全不执行的最常见原因。
  • 代码逻辑存在异步时序错误:URLSession.dataTask是异步执行的,调用resume()后请求才会被调度发起,此时立刻同步判断登录状态、返回Bool值,请求还未收到响应,逻辑必然不成立。另外代码中判断的self.loggedInUser属性根本没有在ViewModel中声明,属于无效代码。
  • 额外逻辑错误:给JSON解码器设置了.base64的data解码策略,该策略仅用于处理接口返回的base64编码二进制数据,普通JSON响应不需要该配置,会导致后续解码失败;且网络回调默认在子线程执行,直接更新UI绑定的状态属性没有切换主线程,会引发UI异常。

分步修复

  1. 配置ATS HTTP请求例外
    打开项目的Info.plist文件,添加如下配置:

    • 根节点添加App Transport Security Settings(Dictionary类型)
    • 在该字典下可直接添加Allow Arbitrary Loads设为YES开放所有HTTP请求,或添加Exception Domains(Dictionary类型),在其内部添加www.venndor.co.uk节点,将该节点下的NSExceptionAllowsInsecureHTTPLoads设为YES,仅对接口域名放开HTTP限制。
  2. 修正ViewModel网络请求逻辑
    废弃同步返回Bool的写法,使用异步回调处理请求结果,修正解码配置,所有UI状态更新切换到主线程,参考修复代码:

import Foundation
import SwiftUI
import CoreLocation

struct DbCustomer: Codable, Identifiable
{
    var id: String
    var user_name: String
    var pass_word: String
    var fullname: String
}

class LunchboxViewModel: ObservableObject
{
    
    @AppStorage("loggedIn") var loggedIn: Bool = false
    @AppStorage("username") var username: String = ""
    @AppStorage("password") var password: String = ""
    @AppStorage("fullname") var fullname: String = ""
    @AppStorage("userid") var userId: String = ""

    // 改为逃逸闭包异步回调结果,不要同步返回
    func loginInUser(username: String, completion: @escaping (Bool) -> Void)
    {
        var urlComponents = URLComponents()
        urlComponents.scheme = "http"
        urlComponents.host = "www.venndor.co.uk"
        urlComponents.path = "/logindo.php"
        urlComponents.queryItems = [
            URLQueryItem(name: "username", value: username)
        ]
        guard let url = urlComponents.url else {
            completion(false)
            return
        }
        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        URLSession.shared.dataTask(with: request) { [weak self] data, response, error in
            // 先判断请求错误
            if let error = error {
                print("请求错误: \(error.localizedDescription)")
                DispatchQueue.main.async {
                    completion(false)
                }
                return
            }
            // 校验响应状态
            guard let httpResp = response as? HTTPURLResponse, httpResp.statusCode == 200, let data = data else {
                DispatchQueue.main.async {
                    completion(false)
                }
                return
            }
            do{
                let decoder = JSONDecoder()
                // 删除错误的base64解码策略,普通JSON不需要
                let decodedData =  try decoder.decode([DbCustomer].self, from: data)
                guard let user = decodedData.first else {
                    DispatchQueue.main.async {
                        completion(false)
                    }
                    return
                }
                // UI/状态更新必须切主线程
                DispatchQueue.main.async {
                    self?.username = user.user_name
                    self?.password = user.pass_word
                    self?.userId = user.id
                    self?.fullname = user.fullname
                    self?.loggedIn = true
                    completion(true)
                }
            }catch let error{
                print("解码错误: \(error)")
                DispatchQueue.main.async {
                    completion(false)
                }
            }
        }.resume()
    }
}
  1. 修正页面调用逻辑
    之前在handleButtonPress里直接同步判断方法返回值的写法完全不适用异步请求,改为等待回调结果后再执行页面跳转:
func handleButtonPress()
{
    if signUpProgress == 1
    {
        // 等待异步请求回调后再处理跳转
        viewModel.loginInUser(username: username) { [weak self] isSuccess in
            guard let self = self else { return }
            if isSuccess {
                self.loginSignUpOption = "logIn"
            } else {
                // 可在这里加登录失败提示
                print("登录失败")
            }
        }
        // 异步请求需要等待回调,直接return终止后续流程
        return
    }
    if companyname == "" && signUpProgress > 2
    {
        let newUser = DbCustomer(id: "0", user_name: username, pass_word: "", fullname: fullname)
        viewModel.signUpUser(user: newUser)
        loginSignUpOption = "logIn"
        return
    }
    if signUpProgress == 5
    {
        let newUser = DbCustomer(id: "0", user_name: username, pass_word: "", fullname: fullname)
        let newCompany = DbVendor(id: "0", company_name: companyname, description: companydescription, customer_id: "0")
        viewModel.signUpUserWithCompany(user: newUser, company: newCompany)
        loginSignUpOption = "logIn"
        return
    }
    else
    {
        withAnimation(.spring())
        {
            signUpProgress += 1
        }
    }
}

补充:如果配置完ATS后闭包还是不执行,可以先把请求地址临时改成可用的HTTPS测试地址验证回调逻辑,确认是ATS拦截问题后再仔细核对Info.plist的配置格式,节点类型或拼写错误会导致配置完全不生效。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 22:57:21