Reputation: 161
I have a very strange situation. My server is currently down and getting 503 http status code.
Based on the given code as follows, code gets into the if condition and but when I put debug point to the let error = self?.decodeErrorMessage(data: data, statusCode: response.statusCode)
. then it is jumping from if condition and executes the else if condition.
ClientViewModel.swift
let networkRequest = CustomNetworkRequest(headers: headers, httpMethod: .get, httpBody: nil, parameters: nil, url: url)
let customNetworker = CustomNetworker(urlSession: URLSession(configuration: config))
customNetworker.dataRequest(networkRequest, successHandler: {[weak self] data in
self?.parseData(data)
completion(nil)
}, failureHandler: { [weak self] error in
completion(error)
})
CustomNetworker.swift
final class CustomNetworker {
private let urlSession: URLSession
public init(urlSession: URLSession) {
self.urlSession = urlSession
}
func dataRequest(input parameters here) {
let task = urlSession.dataTask(with: urlRequest) {[weak self] data, response, error in
if let response = response as? HTTPURLResponse , 300...599 ~= response.statusCode,
let data = data,
let error = self?.decodeErrorMessage(data: data, statusCode: response.statusCode) {
failureHandler(error)
} else if let error = error {
failureHandler(error)
} else if let data = data {
successHandler(data)
} else {
failureHandler(error)
}
}
task.resume()
}
}
However, If I remove [weak self]
then it works as expected, it calls that method. How should I resolve the issue properly?
let task = urlSession.dataTask(with: urlRequest) {data, response, error in
if let response = response as? HTTPURLResponse , 300...599 ~= response.statusCode,
let data = data,
let error = self.decodeErrorMessage(data: data, statusCode: response.statusCode) {
failureHandler(error)
}
else if let error = error {
failureHandler(error)
} else if let data = data {
successHandler(data)
} else {
failureHandler(error)
)
}
}
task.resume()
decodeErrorMessage method
private func decodeErrorMessage(data: Data, statusCode: Int) -> CustomError? {
// not coming into this method
if let errorData = try? JSONDecoder.convertFromSnakeCaseDecoder.decode(CustomServiceError.self, from: data) as CustomServiceError? {
if let errorData = errorData {
return formCustomError(something here)
}
return CustomError.genericError(debugMessage: debugMessage, sourceError: nil)
} else if let errorMessage = StatusCode(rawValue: statusCode) {
let debugMessage = String(data:data, encoding: .utf8)
return formCustomError(something here)
} else {
let debugMessage = String(data:data, encoding: .utf8)
return formCustomError(something here)
}
}
Upvotes: 2
Views: 1089
Reputation: 437977
The issue is that you’ve made customNetworker
, the CustomNetworker
, a local variable and you lose your strong reference to it as soon as this local variable falls out of scope. When the dataRequest(input:)
method uses a [weak self]
reference, it’s saying that you don’t want dataRequest(input:)
to keep a strong reference to this CustomNetworker
instance, either. As a result, with no strong references, the CustomNetworker
will be deallocated.
You have a few alternatives:
If you remove the [weak self]
reference in the closure inside dataRequest(input:)
, then it will retain the CustomNetworker
until the closure runs. As long as you don’t save this failureHandler
(and other closures) as stored properties, you should be fine. But don’t just throw in a [weak self]
because you read somewhere that you should always use weak references. Use weak references where you need to avoid strong reference cycles, which would not appear to be the case here.
The other alternative is to refactor this code, moving this network request routine, dataRequest(input:)
to some more long-lived object.
E.g., we often have a long-lived network manager object that maintains the URLSession
and has all these network related methods. Then, assuming you’re keeping a strong reference to this network manager instance elsewhere, you don’t need to worry about the object being deallocated when it falls out of scope. This also saves you from having to pass in the URLSession
object for every request. A caller should not be dealing with these URLSession
instances, anyway.
For example, I often use a singleton network layer (kind of equivalent to URLSession.shared
pattern):
final class NetworkManager {
static let shared = NetworkManager()
private var session: URLSession = .shared // or create and configure this session as you need; but one session object is all you need
private init() { }
@discardableResult
func performRequest(...) -> URLSessionTask {
...
let task = session.dataTask(...) { [weak self] data, response, error in
...
}
task.resume()
return task
}
}
Then you can do things like:
NetworkManager.shared.performRequest(...) { [weak self] ... in
...
}
The other alternative is to just keep a strong reference to this customNetworker
variable (e.g. make it a property rather than a local variable). That will prevent the CustomNetworker
object from falling out of scope and becoming nil
because there are no more strong references to it.
Somewhat unrelated, but it would appear that you are creating a URLSession
object when you instantiate your CustomNetworker
object, and subsequently never invalidate it. This will leak. You should create a single URLSession
object and use it for all subsequent network requests.
If you do create multiple URLSession
objects, you must invalidate each one when you’re done with it (e.g., finishTasksAndInvalidate
), or else you will leak. But if you just have one session that you keep alive for all future network requests (which is preferred), then that is not necessary.
Upvotes: 5
Reputation: 535851
The implication is that whatever self
is, it is some object that you have not retained properly, so it goes out of existence before the networking can happen. When you remove weak self
you create a sort of retain cycle that keeps this object alive, but this merely masks the problem. You should be concentrating on how self
is created and retained and why it is going out of existence so quickly.
Upvotes: 1
Reputation: 498
I don't think it's possible to set break point inside expression. You have if statement with a complex expression and trying to set break point inside it and trace if one of sub expression is true / or false. If you would like to debug it then you need to divide it in several if statements.
Upvotes: 0