Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add no-internet connection monitoring #52

Open
wants to merge 10 commits into
base: develop
Choose a base branch
from

Conversation

jjorn
Copy link

@jjorn jjorn commented Nov 16, 2020

Description

This PR implements a monitoring concept for availalbe internet connection.

Related Issue

This PR should also solve #43

Changes:

  • Add a link to built-in SystemConfiguration framework
  • Implement NetworkReachabilityManager acting as a monitor of current internet connection
  • Add Client.Task as a wrapper for the client operations - request, upload and download
  • Add ClientTaskExecutor including network connection monitoring

Notes

The feature of reachability has been built on SystemConfiguration framework. As an alternative, we can also use NWPathMonitor in Network framework which is available from iOS 12.0.

)
}

final class ClientTaskExecutor: NSObject {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need another Executer? The Client is now working Client.post -> Executer -> Executer. Maybe it is possible to integrate the reachability into the RequestExecuter by composition to reduce the complexity?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Eventually you forget UploadExecuter and DownloadExecuter. I just didn't want to add the same code in those components.

@@ -9,11 +9,21 @@ enum APIError: Error {

public final class Client {
public typealias RequestCompletion<ResponseType> = (HTTPURLResponse?, Result<ResponseType, Error>) -> Void

internal enum Task {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need the additional task abstraction?

}

final class ClientTaskExecutor: NSObject {
internal static let `default` = ClientTaskExecutor()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Type definitions should be on the left Side of the declaration.

do {
try self.reachabilityManager?.startListening(on: .main) { _ in }
} catch {
NSLog("[WARNING] Faild to start network monitor. Error: \(error)")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do have a Logger implementation in the framework.

super.init()

do {
try self.reachabilityManager?.startListening(on: .main) { _ in }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whats happening in the completion? Do we need a log here as well?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The closure is not about completion but callback on state update. We do not really observe the reachability all the time. In this implementation we just check that a connection can be established before we perform a network request.

}

switch (executor, task) {
case (is RequestExecutor, let .dataTask(request, completionHandler)) :
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For casting in switch cases please take a look at the example of the swift documentation:

 switch thing {
    case 0 as Int:
        print("zero as an Int")
    case 0 as Double:
        print("zero as a Double")
    case let someInt as Int:
        print("an integer value of \(someInt)")
    case let someDouble as Double where someDouble > 0:
        print("a positive double value of \(someDouble)")
    case is Double:
        print("some other double value that I don't want to print")
    case let someString as String:
        print("a string value of \"\(someString)\"")
    case let (x, y) as (Double, Double):
        print("an (x, y) point at \(x), \(y)")
    case let movie as Movie:
        print("a movie called \(movie.name), dir. \(movie.director)")
    case let stringConverter as (String) -> String:
        print(stringConverter("Michael"))
    default:
        print("something else")
    }

source: https://docs.swift.org/swift-book/LanguageGuide/TypeCasting.html

throw ClientTaskError.connectionUnavailable
}

switch (executor, task) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we use composition and we add this functionality to the request executer we do not need the cast.

/// if you can connect to a particular host,
/// only that an interface is available that might allow a connection,
/// and whether that interface is the WWAN.
open class NetworkReachabilityManager: NetworkReachabilityMonitor {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add source links and description to the implementations which you used as "inspiration".

/// network reachability status.
public typealias NetworkReachabilityStateCallback = (NetworkReachabilityState) -> Void

public protocol NetworkReachabilityMonitor {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe you could rename the protocol to NetworkReachabilityMonitoring

@@ -123,6 +134,7 @@ public final class Client {
completion: completion
)
}
return try taskExecutor.perform(task, on: requestExecutor)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if this was the intention of @SimonNumberTwo that the client is throwing an error if there is no internet connection. I guess he meant that the completion of the function should be called with an error.

@SimonNumberTwo can you give some more details about the usage in our apps? What should be the expected behavior?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes exactly. I would prefer to have an error case for no internet and let the app itself using Jetworking decide what to do with it rather than implicitly throwing an error.

@JensK611
Copy link
Contributor

@jjorn What's the actual state of this PR?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants