我正在开发一个应用程序,需要仅通过移动数据而不是 wifi 来执行特定的 API,从而允许使用可用的网络类型(无论是蜂窝数据还是 wifi 或 ....)来完成其他 API 调用。
有没有办法强制使用移动数据使用,即使在此 API 调用上 WIFI 已打开。向苹果开发者开票后,这是他们的回复:
Yes. When using Network Framework, take a look at requiredInterfaceType on NWConnection. For higher level APIs like URLSession, this would be an Enhancement Request.
allowsConstrainedNetworkAccess
上的
allowsExpensiveNetworkAccess
或
URLRequest
来获得一些控制权。
allowsConstrainedNetworkAccess
:
在 iOS 13 及更高版本中,用户可以将设备设置为使用低数据模式作为“设置”应用中的蜂窝数据选项之一。用户可以打开低数据模式来减少应用程序的网络数据使用量。当用户打开低数据模式时,此属性控制请求的行为。如果没有可用的非约束网络接口并且请求的 allowedConstrainedNetworkAccess 属性为 false,则从请求创建的任何连接都会失败。在这种情况下,连接失败时提供的错误有一个 networkUnavailableReason 属性,其值为 NSURLErrorNetworkUnavailableReason.constrained。
在请求上设置此属性会覆盖 URLSessionConfiguration 的 allowedConstrainedNetworkAccess 属性。例如,如果会话配置的 allowedConstrainedNetworkAccess 值为 false,并且您根据 allowedConstrainedNetworkAccess 为 true 的请求创建任务,则该任务会将该值视为 true。
限制您的应用程序对用户启动的任务使用受限网络访问,并推迟酌情任务,直到非受限界面可用。
allowsExpensiveNetworkAccess
:
系统根据网络接口的性质和其他因素来确定什么构成“昂贵”。 iOS 13 认为大多数蜂窝网络和个人热点都很昂贵。如果没有可用的廉价网络接口并且请求的 allowedExpectiveNetworkAccess 属性为 false,则从请求创建的任何任务都会失败。在这种情况下,任务失败时提供的错误有一个 networkUnavailableReason 属性,其值为 NSURLErrorNetworkUnavailableReason.expective。
在请求上设置此属性会覆盖 URLSessionConfiguration 的 allowedExpectiveNetworkAccess 属性。例如,如果会话配置的 allowedExpectiveNetworkAccess 值为 false,并且您根据 allowedExpectiveNetworkAccess 为 true 的请求创建任务,则该任务会将该值视为 true。
限制您的应用程序对用户启动的任务使用昂贵的网络访问,并推迟可自由支配的任务,直到有便宜的界面可用。
import Foundation
import Network
class CellularRequest {
static func execute(url: URL,
httpMethod: String,
postBody: String? = nil,
completion: @escaping (_ error: NWError?, _ httpBody: String?, _ httpHeaders: String?)
-> Void) {
guard let urlHost = url.host else { return }
let host = NWEndpoint.Host(urlHost)
let tlsOptions = NWProtocolTLS.Options()
let tcpOptions = NWProtocolTCP.Options()
let parameters = NWParameters(tls: tlsOptions, tcp: tcpOptions)
parameters.prohibitExpensivePaths = false
#if targetEnvironment(simulator)
parameters.requiredInterfaceType = .loopback
#else
parameters.requiredInterfaceType = .cellular
#endif
let connection = NWConnection(host: host, port: .https, using: parameters)
connection.stateUpdateHandler = { newState in
switch newState {
case .ready:
// The connection is ready; you can now send an HTTP request
var httpRequest = "\(httpMethod) \(url.path) HTTP/1.1\r\nHost: \(urlHost)\r\n"
if let postBody = postBody {
httpRequest += "Content-Type: application/json\r\n"
httpRequest += "Content-Length: \(postBody.utf8.count)\r\n\r\n"
httpRequest += postBody
} else {
httpRequest += "\r\n"
}
let requestData = httpRequest.data(using: .utf8)
connection.send(content: requestData, completion: .contentProcessed { error in
if let error = error {
completion(error, nil, nil)
} else {
connection.receive(minimumIncompleteLength: 1, maximumLength: .max) { data, _, _, error in
if let data = data {
if let response = String(data: data, encoding: .utf8),
response.contains("\r\n\r\n") {
// The headers are complete, and we have the full response
let headersAndBody = response.components(separatedBy: "\r\n\r\n")
completion(nil, headersAndBody.first, headersAndBody.last)
}
} else if let error = error {
completion(error, nil, nil)
}
}
}
})
case let .failed(error), let .waiting(error):
completion(error, nil, nil)
default:
break
}
}
// Start the connection
connection.start(queue: DispatchQueue.global())
}
}