Constantly check for internet connectivity in webview for IOS - ios

I already have the script for checking for an internet connection in the viewDidLoad like below which works great. If there is a connection is loads the url and if not it loads a local html page with a pretty picture telling the user that there internet is down.
override func viewDidLoad() {
super.viewDidLoad()
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.viewController = self
webView.navigationDelegate = self
ref = Database.database().reference()
if CheckInternet.Connection() {
let url = URL(string: "https://www.example.com/")
let URLrequest = URLRequest(url: url!)
self.webView.load(URLrequest)
} else {
let htmlpath = Bundle.main.path(forResource: "nointernet", ofType: "html")
let url = URL(fileURLWithPath: htmlpath!)
let request = URLRequest(url: url)
self.webView.load(request)
}
}
Problem is that if the users device loses an internet connection while they are using my app then it just hangs. I want to be able to have the app switch to the local html page automatically as soon as the connection is lost and switch back to the html (example.com) when the connection comes back online.
I have tried several different functions but none of them work. Is there an event that is triggered when the network connectivity changes on the users device that I could use my existing code?

I suggest to use Reachability
1.Install it via Pod:
use_frameworks!
pod 'ReachabilitySwift'
2.Variable declaration in your UIViewController
let reachability = Reachability()
3.Add observers and start the observer
// connected observer
reachability?.whenReachable = { reachability in
// Connected to Internet
}
// disconnected observer
reachability?.whenUnreachable = { _ in
// Not Connected to Internet
}
// start reachability observer
do {
try reachability?.startNotifier()
} catch {
print("Unable to start notifier")
}
4.Before disappering your UIViewController, stop the observer:
reachability?.stopNotifier()

Related

Start local server iOS & load it on WKWebView

I found following libraries to start a server locally.
https://github.com/httpswift/swifter
https://github.com/swisspol/GCDWebServer
For instance, I'm using swifter. It does compile for iOS.
I also added code to start a local server as follows.
class ViewController: UIViewController {
var webView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
let server = HttpServer()
server["/hello"] = { .ok(.htmlBody("You asked for \($0)")) }
let semaphore = DispatchSemaphore(value: 0)
do {
try server.start(9080, forceIPv4: true)
print("Server has started ( port = \(try server.port()) ). Try to connect now...")
let config = WKWebViewConfiguration()
webView = WKWebView(frame: self.view.bounds, configuration: config)
/////////////////////////////
// What should be url here to point to local server?
webView.load(URLRequest(url: URL(string: "http://192.168.31.70:9080/hello")!))
/////////////////////////////
view = webView
semaphore.wait()
} catch {
print("Server start error: \(error)")
semaphore.signal()
}
}
}
Also, If I submit it to apple, is there any possibility of rejection because of starting a local server?
Can you elaborate on what you are trying to achieve by starting an HTTP server in-process?
If you need control over the way that responses are made, I would recommend looking at registering a custom URL protocol handler with WKWebViewConfiguration.setURLSchemeHandler(_:forURLScheme:). Then, ask the WKWebView to load a resource from that custom URL scheme, and your handler (that conforms to WKURLSchemeHandler) will get called, and you can construct a URLResponse however you'd like.
I tried Telegraph
Step 1. Open Podfile & add a dependency - pod 'Telegraph'
Step 2. Open AppDelegate.swift & put code as follows.
// Serve the Server from Build folder
let demoBundleURL = Bundle.main.url(forResource: "webApp", withExtension: nil)!
// and handle those requests at Path
serverHTTP.serveDirectory(demoBundleURL, "/webApp")
// Setting Custom Router - OPTIONAL
serverHTTP.httpConfig.requestHandlers.insert(DRHTTPMiddleWare(), at: 0)
// Start Server
try? serverHTTP.start(port: 9000, interface: "localhost")
Step 3. In ViewController.swift, put code as follows.
func loadWebView() {
guard let url = URL(string: "http://localhost:9000/webApp") else { return }
webView = WKWebView(
frame: self.view.bounds,
configuration: WKWebViewConfiguration()
)
webView.load(URLRequest(url: url))
view = webView
}

"How to retrieve an url from Firebase Database BEFORE load WebView in Swift?"

I am building an application with webView and Firebase as database. The url that should be used to load the webView should come from the database.
I have implemented both webView and Firebase correctly but the problem is that my webView is not loading with the url from the database but the url declared in the var declaration.
It seems that viewDidLoad call in priority the methods webView load before the closure inside Database Observe method
If I look at the console, I can clearly see that everything is working BUT the url that should be used to load the webview is retrieved from the database AFTER webview load...this should be done BEFORE given that I put the retrieve method BEFORE...
class ViewController: UIViewController {
var dbRef: DatabaseReference!
var nextUrl = "https://www.google.co.uk/"
func retrieveUrl () {
dbRef = Database.database().reference().child("EXAMPLE")
dbRef.observe(.value) { (snapshot) in
let value = snapshot.value as! NSDictionary
let url = value["url"]!
self.nextUrl = (url as! String)
print (nextUrl)
}
print ("function observe is called")
}
override func viewDidLoad () {
super.viewDidLoad()
retrieveUrl()
print ("webview is about to load")
let request = URLRequest (url: URL(string: nextUrl)!)
self.webView.load(request)
self.webView.addObserver(self, forKeyPath: #keyPath(WKWebView.isLoading), options: .new, context: nil)
}
}
in the console I can see that "webview is about to load" is printed before nextUrl...which is very strange to me
You should load webView in callback of firebase observer.
Because you are loading webView right after firebase network call its like async work, so webView try to loads with nextUrl is empty or not, to prevent async like behavior you must wait for nextUrl or you should load webView in firebase callback
Do it like following
class ViewController: UIViewController {
var dbRef: DatabaseReference!
var nextUrl = "https://www.google.co.uk/"
func retrieveUrl () {
dbRef = Database.database().reference().child("EXAMPLE")
dbRef.observe(.value) {
(snapshot) in
let value = snapshot.value as! NSDictionary
let url = value["url"]!
self.nextUrl = (url as! String)
print (nextUrl)
print ("webview is about to load")
let request = URLRequest (url: URL(string: nextUrl)!)
self.webView.load(request)
self.webView.addObserver(self, forKeyPath: #keyPath(WKWebView.isLoading), options: .new, context: nil)
}
print ("function observe is called")
}
override func viewDidLoad () {
super.viewDidLoad()
retrieveUrl()

How to connect Socket in Swift 4

I want to connect my app to the socket here is the code :-
import UIKit
import SocketIO
class SocketIOManager: NSObject {
static let manager = SocketManager(socketURL: URL(string: "myURL")!)
static let socket = manager.defaultSocket
class func connectSocket() {
let token = UserDefaults.standard.getAccessToken()
self.manager.config = SocketIOClientConfiguration(
arrayLiteral: .connectParams(["token": token]), .secure(true) // problem is here in passing token value
)
socket.connect()
}
class func receiveMsg() {
socket.on("new message here") { (dataArray, ack) in
print(dataArray.count)
}
}
}
I created a new class SocketIOManager.swift and I invoke my function in view controller
SocketIOManager.connectSocket()
and I am not using the localhost url but still my app is not connected to the socket I think I followed this How to connect with Socket.io? Swift 4 and I also add App Transport Security in info.plist. Any help? The problem is when I am passing the token value it gives me error otherwise it is working properly. Should I need to pass token when using socket?
You should make a shared property in your SocketIOManager class like this:
static let shared = SocketIOManager()
and then create init() like this:
var socket: SocketIOClient!
// defaultNamespaceSocket and swiftSocket both share a single connection to the server
let manager = SocketManager(socketURL: URL(string: "http://localhost:3000")!, config: [.log(true), .compress])
override init() {
super.init()
socket = manager.defaultSocket
}
and finally write your methods like this:
func connectSocket() {
let token = UserDefaults.standard.getAccessToken()
self.manager.config = SocketIOClientConfiguration(
arrayLiteral: .connectParams(["token": token]), .secure(true)
)
socket.connect()
}
func receiveMsg() {
socket.on("new message here") { (dataArray, ack) in
print(dataArray.count)
}
}
and call your method like this:
SocketIOManager.shared.connectSocket()
The point is that you should make a strong reference to manager property in your viewController and static let shared = SocketIOManager() you do this for you!
Is your URL secured under HTTPS?
If it is not, that could be the problem, due to App Transport Security restrictions.
See Info Plist Reference
See also this post here.
If that's not the problem. I'd suggest to check if your Socket version on both sides, server and client, are the same. Recently I had a problem because of that.
Hope it helps!
try this Example:
let manager = SocketManager(socketURL: URL(string:
"http://xxxxxxxxx.com")!, config: [.log(true), .compress])
var socket = manager.defaultSocket
socket.connect()
socket.on(clientEvent: .connect) {data, ack in
print("socket connected")
self.gotConnection()
}
}
func gotConnection(){
socket.on("new message here") { (dataArray, ack) in
print(dataArray.count)
}
}

Socket.connect() is not consistent when using connectParams with JWT

I'm using https://github.com/auth0/socketio-jwt to connect the user to my node.js/socket.io server and I'm using one round trip
My problem right now is that whenever user logs in on the IOS part, the socket.connect() is not consistent, my theory is that the token is not yet ready even before the socket.connect() gets invoked.
I'm using Singleton design for my Socket.io class as many people pointed that out.
Here's the code on the SocketManager.swift part
import SocketIO
class SocketIOManager: NSObject {
static let sharedInstance = SocketIOManager()
var socket = SocketIOClient(socketURL: URL(string: mainURL)!, config: [.log(false), .compress, .connectParams(["token": getToken()])]) // getToken() I got it from other file which is Constant.Swift
func establishConnection() {
socket.connect()
}
func closeConnection() {
socket.disconnect()
}
}
I'm using KeychainAccess to store the token and Constant.Swift file store all the global variables and functions so that I could call it on any Swift files.
Constant.Swift
import Foundation
import KeychainAccess
let keychain = Keychain(server: "www.example.com", protocolType: .https)
func getToken() -> String {
if let token = keychain["token"] {
return token
}
return ""
}
LoginViewController.swift
#IBAction func facebookButtonClicked(_ sender: UIButton) {
Alamofire.request("/login", method: .post, parameters: parameters, encoding: JSONEncoding.default)
.responseJSON { response in
if let value = response.result.value {
let json = JSON(value)
self.keychain["token"] = String(describing: json["token"])
SocketIOManager.sharedInstance.establishConnection()
self.segueToAnotherVC() // Segue to another screen, to simplify things i put it in a function
}
}
}
So technically what is happening in this controller is, when the user logs in, I will store the token into KeychainAccess (it is equivalent to NSUserDefaults), then only I will make a socket connection because the socket connection needs a token beforehand.
What should I do to make the connection consistent all the time, whenever user logs in? Any methods that I could use?
I suggest you to use keychain like this:
let keychain = KeychainSwift()
keychain.set("string", forKey: "key")
keychain.get("key")
keychain.delete("key")
keychain Usage:
let saveBool: Bool = KeychainWrapper.setString("String", forKey: "key")
let retrievedString: String? = KeychainWrapper.stringForKey("key")
let removeBool: Bool = KeychainWrapper.removeObjectForKey("key")
And make sure that your token is set when calling establish connection, if not, don't try and connect.
References:
https://github.com/socketio/socket.io-client-swift/issues/788
https://github.com/marketplacer/keychain-swift
https://github.com/jrendel/SwiftKeychainWrapper
More info:
JSON Web Token is a JSON-based open standard for creating access tokens that assert some number of claims.

Load webview URL from other VC

I'm trying to make it work but nothing.
I want to make (URL textfield) on (RightVC) as (webview)-url on (LeftVC).
I saved the textfield with this code:
#IBAction func save(sender: UIButton) {
NSUserDefaults.standardUserDefaults().setObject(urltxtfield.text, forKey: "urltxtfield")
NSUserDefaults.standardUserDefaults().synchronize()
}
override func viewDidLoad() {
super.viewDidLoad()
if NSUserDefaults.standardUserDefaults().objectForKey("urltxtfield") != nil {
urltxtfield.text = NSUserDefaults.standardUserDefaults().objectForKey("urltxtfield") as? String
} else {
urltxtfield.placeholder = "urltxtfield"
}
}
And it's ok with save. But how to make URL work with LeftVC webview?
This pic to make you understand me:
In your LeftVC (webView), you first need to get the NSUserDefaults and then make a request with your webView.
let prefs = NSUserDefaults.standardUserDefaults()
if let url = prefs.stringForKey("urltxtfield"){
// You have an URL and can make the request
let requestURL = NSURL(string: url)
let request = NSURLRequest(URL: requestURL!)
webView.loadRequest(request)
}else{
// Nothing stored in NSUserDefaults
}
Update
After I got the project I updated the reference to the webView from strong to weak and I added the temporary code
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
In info.plist to allow you to browse on websites (you have to check if you want this and I can recommend you to read more about it).
here: some sample of code to call load request to UIWebview when user enter url that urlstring it pass here as parameter Instead of google.com:
UIWebView.loadRequest(webviewInstance)(NSURLRequest(URL: NSURL(string: "google.cam")!))
OR
let url = NSURL (string: "http://www.google.com");
let requestObj = NSURLRequest(URL: url!);
myWebView.loadRequest(requestObj);

Resources