I am using XMPPFramework. My XMPP connection is always closed when I locked the iPhone screen.
I need to reconnect to my Openfire server when I unlock the screen.
Here is how I used XMPPReconnect (in Swift):
func xmppStreamDidAuthenticate(sender: XMPPStream) {
let xmppReconnect = XMPPReconnect()
xmppReconnect.activate(sender)
xmppReconnect.addDelegate(self, delegateQueue: dispatch_get_main_queue())
}
However, it seems it never reconnects when I unlock the screen.
Am I using XMPPReconnect correctly?
How do I achieve my target?
You have to write code in your appdelegate in applicationDidBecomeActive. becuase when you unlock the screen this method will call, and in this method you have to call connect method to openfire....
func applicationDidBecomeActive(application: UIApplication) {
self.connect()
}
func connect() -> Bool {
println("connecting")
setupStream()
var jabberID: String? = "YOUR_JID"
var myPassword: String? = "YOUR_PASSWORD"
var server: String? = "YOUR_HOST"
xmppStream!.hostName = "YOURHOST_NAME"
xmppStream!.hostPort = 5222
if let stream = xmppStream {
if !stream.isDisconnected() {
return true
}
if jabberID == nil || myPassword == nil {
println("no jabberID set:" + "\(jabberID)")
println("no password set:" + "\(myPassword)")
return false
}
stream.myJID = XMPPJID.jidWithString(jabberID)
password = myPassword
var error: NSError?
if !stream.connectWithTimeout(XMPPStreamTimeoutNone, error: &error) {
var alertView: UIAlertView? = UIAlertView(title:"Error", message: "Cannot connect to \(error!.localizedDescription)", delegate: nil, cancelButtonTitle: "Ok")
alertView!.show()
return false
}
}
return true
}
Hope this will help!
Related
I m trying to implement webservice call, when user takes screenshot, and I have successfully implemented it with the bellow method.
//this method get called when screenshot captured
func detectScreenShot() {
let mainQueue = OperationQueue.main
NotificationCenter.default.addObserver(forName: NSNotification.Name.UIApplicationUserDidTakeScreenshot,
object: nil,
queue: mainQueue,
using: { notification in
WEBSERVICE_INTERFACE.webServiceWithPostJSONParameters(param: nil, methodName: Constants.URLs.screenshot, headers: Constants.Headers.urlEncoded, showProgress: false, completion: { (response) in
if let response = response{
let response = BaseResponse(JSONString : response)
if let message = response?.message{
LIMITUtils.showAlertMessage(message: message)
}
}
})
})
}
But now I have the scenario, suppose user takes the screenshot by making internet connection off, then no webservice call will happen and user will get the benefits out of it. Now I wanted to have some sort of solution where I can save the webservice call if no connection is available and make the same call when internet connection becomes available. Can anyone please suggest me how I can proceed for this?
Use Reachability for check Internet available or not write bellow code to check internet
//MARK:- check internet connection
func checkInternetStatus() {
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(reachabilityChanged(_:)), name:ReachabilityChangedNotification, object: nil)
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) {
//Check for Internet Status
do {
self.reachability = try Reachability.reachabilityForInternetConnection()
try self.reachability?.startNotifier()
} catch {
jprint("Errore")
return
}
}
}
func reachabilityChanged(note:NSNotification) {
let Status = reachability?.currentReachabilityStatus //note.object as! Reachability
if Status != .NotReachable {
jprint("isNetworkAvailable = true")
isNetworkAvailable = true
} else {
jprint("isNetworkAvailable = false")
ShowFlinntAlert("No internet", description: "Your internet connection seems to be down")
isNetworkAvailable = false
}
}
Before API call just check
guard isInternetAvailable() else {
return
}
I am currently using Ashley Mill's Reachability Class. If the application launches with network connectivity then I am able to toggle between connectivity availability without any issues and able to display a network connectivity Alert Controller properly. However if the application is launched when the app starts without internet connection/on airplane mode it abruptly crashes.
override func viewDidLoad()
{
super.viewDidLoad()
setUpReachability (nil)
}
func setUpReachability(hostName: String?)
{
do
{
let reachability = try hostName == nil ? Reachability.reachabilityForInternetConnection() : Reachability(hostname: hostName!)
self.reachability = reachability
try! self.reachability?.startNotifier()
}
catch ReachabilityError.FailedToCreateWithAddress(let address)
{
print("\(address)")
return
} catch {}
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ViewController.reachabilityChanged(_:)), name: ReachabilityChangedNotification, object: reachability)
}
func reachabilityChanged(notification: NSNotification)
{
let reachability = notification.object as! Reachability
if reachability.isReachable()
{
if reachability.isReachableViaWiFi()
{
connected = true
}
else
{
connected = true
}
}
else
{
let alert = UIAlertController( title: "No Network Connection Available", message:"Try Again", preferredStyle: .Alert)
alert.addAction(UIAlertAction( title: "Will Do!" , style: .Default) { _ in } )
presentViewController ( alert, animated: true ) {}
connected = false
}
}
What can be done to allow the iPhone application to launch and display an alert saying there is no network connection rather than abruptly crash?
Error Message:
fatal error: unexpectedly found nil while unwrapping an Optional value
But I would think that reachability changed would catch this in the else statement and pop the error message up?
Shouldn't the else in the reachability.isReachableViaWiFi() if statement be:connected = false ?
The error was that I was in fact trying to download data at the launch of the app instead of first allowing the initialization of the app to finish to then send a request to the server to access information.
I'm implementing the login possibility with touchID using Swift.
Following: when the App is started, there is a login screen and a touchID popup - that's working fine. The problem occurs, when the app is loaded from background: I want the touchID popup appear over a login screen if a specific timespan hasn't been exceeded yet - but this time I want the touchID to go to the last shown view before the app entered background. (i.e. if the user wants to cancel the touchID, there is a login screen underneath where he then can authenticate via password, which leads him to the last shown view OR if the touchID authentication succeeded, the login screen should be dismissed and the last shown view presented.)
I really tried everything on my own, and searched for answers - nothing did help me. Here is my code:
override func viewDidLoad() {
super.viewDidLoad()
//notify when foreground or background have been entered -> in that case there are two methods that will be invoked: willEnterForeground and didEnterBackground
let notificationCenter = NSNotificationCenter.defaultCenter()
notificationCenter.addObserver(self, selector: "willEnterForeground", name:UIApplicationWillEnterForegroundNotification, object: nil)
notificationCenter.addObserver(self, selector: "didEnterBackground", name: UIApplicationDidEnterBackgroundNotification, object: nil)
password.secureTextEntry = true
if (username != nil) {
username.text = "bucketFit"
}
username.delegate = self
password.delegate = self
if let alreadyShown : AnyObject? = def.objectForKey("alreadyShown") {
if (alreadyShown == nil){
authenticateWithTouchID()
}
}
}
willEnterForeground:
func willEnterForeground() {
//save locally that the guide already logged in once and the application is just entering foreground
//the variable alreadyShown is used for presenting the touchID, see viewDidAppear method
def.setObject(true, forKey: "alreadyShown")
if let backgroundEntered : AnyObject? = def.objectForKey("backgroundEntered") {
let startTime = backgroundEntered as! NSDate
//number of seconds the app was in the background
let inactivityDuration = NSDate().timeIntervalSinceDate(startTime)
//if the app was longer than 3 minutes inactiv, ask the guide to input his password
if (inactivityDuration > 2) {
showLoginView()
} else {
def.removeObjectForKey("alreadyShown")
showLoginView()
}
}
}
authenticateWithTouchID():
func authenticateWithTouchID() {
let context : LAContext = LAContext()
context.localizedFallbackTitle = ""
var error : NSError?
let myLocalizedReasonString : NSString = "Authentication is required"
//check whether the iphone has the touchID possibility at all
if context.canEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, error: &error) {
//if yes then execute the touchID and see whether the finger print matches
context.evaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, localizedReason: myLocalizedReasonString as String, reply: { (success : Bool, evaluationError : NSError?) -> Void in
//touchID succeded -> go to students list page
if success {
NSOperationQueue.mainQueue().addOperationWithBlock({ () -> Void in
self.performSegueWithIdentifier("studentsList", sender: self)
})
} else {
// Authentification failed
print(evaluationError?.description)
//print out the specific error
switch evaluationError!.code {
case LAError.SystemCancel.rawValue:
print("Authentication cancelled by the system")
case LAError.UserCancel.rawValue:
print("Authentication cancelled by the user")
default:
print("Authentication failed")
}
}
})
}
}
shouldPerformSegueWithIdentifier:
override func shouldPerformSegueWithIdentifier(identifier: String, sender: AnyObject?) -> Bool {
if (false) { //TODO -> username.text!.isEmpty || password.text!.isEmpty
notify("Login failed", message: "Please enter your username and password to proceed")
return false
} else if (false) { //TODO when backend ready! -> !login("bucketFit", password: "test")
notify("Incorrect username or password", message: "Please try again")
return false
//if the login page is loaded after background, dont proceed (then we need to present the last presented view on the stack before the app leaved to background)
} else if let alreadyShown : AnyObject? = def.objectForKey("alreadyShown") {
if (alreadyShown != nil){
//TODO check whether login data is correct
dismissLoginView()
return false
}
}
return true
}
Thank you in advance.
What you could do is create a AuthenticationManager. This manager would be a shared instance which keep track of whether authentication needs to be renewed. You may also want this to contain all of the auth methods.
class AuthenticationManager {
static let sharedInstance = AuthenticationManager()
var needsAuthentication = false
}
In AppDelegate:
func willEnterForeground() {
def.setObject(true, forKey: "alreadyShown")
if let backgroundEntered : AnyObject? = def.objectForKey("backgroundEntered") {
let startTime = backgroundEntered as! NSDate
//number of seconds the app was in the background
let inactivityDuration = NSDate().timeIntervalSinceDate(startTime)
//if the app was longer than 3 minutes inactiv, ask the guide to input his password
if (inactivityDuration > 2) {
AuthenticationManager.sharedInstance.needsAuthentication = true
}
}
}
Then, subclass UIViewController with a view controller named SecureViewController. Override viewDidLoad() in this subclass
override fun viewDidLoad() {
super.viewDidLoad()
if (AuthenticationManager.sharedInstance().needsAuthentication) {
// call authentication methods
}
}
Now, make all your View Controllers that require authentication subclasses of SecureViewController.
I'm using the Pusher client for iOS, installing it via CocoaPods (pod 'libPusher', '~> 1.5').
It all sets up fine and the events come through fine. However if the device (iPhone 6s running iOS 9.0.2) loses the internet connection (caused by me going to airplane mode) then regains it a minute later (me coming out of airplane mode), Pusher never regains a connection.
I've added a some UIAlertViews to test out what it's doing based on it's delegate method.
Initially connectionWillConnect and connectionDidConnect are shown.
When airplane mode is turned on connectionWillConnect then connectionWillAutomaticallyReconnection afterDelay of 0.0 are shown.
(Then left for a minute or so without the internet.)
Then nothing, even after connection back to the internet. And the events are no longer received properly.
-
Here is the class I use for all the Pusher things (written in Swift 2.0), which works well until connection is lost.
class PusherInterface: NSObject, PTPusherDelegate {
// MARK: - PusherInterface Shared Instance
/// Singleton instance of the PusherInterface class.
private static let sharedInstance = PusherInterface()
// MARK: - Pusher Credentials
private static let pusherAppId = "MY_APP_ID"
private static let pusherKey = "MY_KEY"
private static let pusherSecret = "MY_SECRET"
/// The connected client used by Pusher to connect to event channels
private static var client: PTPusher = {
let pusherClient = PTPusher.pusherWithKey(pusherKey, delegate: PusherInterface.sharedInstance)
pusherClient.connect()
return pusherClient as! PTPusher
}()
// MARK: - Setup Pusher
static func startListening() {
client.subscribeToChannelNamed("MY_CHANNEL")
client.bindToEventNamed("MY_EVENT") { pusherEvent in
// Does some stuff with the data back
}
}
// MARK: - Pusher Delegate
func pusher(pusher: PTPusher!, connectionDidConnect connection: PTPusherConnection!) {
NSOperationQueue.mainQueue().addOperationWithBlock {
UIAlertView(title: "connectionDidConnect", message: "", delegate: nil, cancelButtonTitle: "Dismiss").show()
}
}
func pusher(pusher: PTPusher!, connectionWillAutomaticallyReconnect connection: PTPusherConnection!, afterDelay delay: NSTimeInterval) -> Bool {
NSOperationQueue.mainQueue().addOperationWithBlock {
UIAlertView(title: "connectionWillAutomaticallyReconnect", message: "afterDelay \(delay)", delegate: nil, cancelButtonTitle: "Dismiss").show()
}
return true
}
func pusher(pusher: PTPusher!, connectionWillConnect connection: PTPusherConnection!) -> Bool {
NSOperationQueue.mainQueue().addOperationWithBlock {
UIAlertView(title: "connectionWillConnect", message: "", delegate: nil, cancelButtonTitle: "Dismiss").show()
}
return true
}
}
Any ideas on why it's not working?
All ideas & theories would be very much appreciated! Thank you :)
It wasn't working for me either with Swift. I installed a swift implementation of Pusher and is working fine now.
let pusher = Pusher(key: "c984997151153c177dc2")
pusher.connect()
let channel = pusher.subscribe("test_channel")
channel.bind("my_event") { (event: AnyObject?) -> Void in
print("my_event push received ")
}
Background:
I'm using GCDAsyncSocket on a project successfully with Swift 1.2 (via a bridging header).
The challenge right now is that it needs some sort of queue because the system it's connecting to can only process and return one command at a time.
So if it calls methods back to back, for example:
getSystemInfo()
getSystemStatus()
Only getSystemInfo() is returned via the delegate callback because the system was busy processing it, however, the getSystemStatus() was sent asynchronously successfully but not processed by the controller. I'd like it to be able to make the calls back to back and have them queue and processed once the controller is done processing and returning back the previous response -- basically making the process synchronous.
Question:
As you can see below in the example code under, didConnectToHost delegate callback, when it connects to the controller, it calls getSystemInfo() then getSystemStatus() back to back, it should call getSystemStatus() after it gets the results from the system info.
I have been looking at NSCondition, NSOperation, even GCD, but I'm not sure what the most elegant way to approach this is. I don't want to put yet another queue processor in the mix since there already is a queue setup for the GCDAsyncSocket. What is the best, most elegant way to handle this?
Pseudo Class Code:
public class SendNet: NSObject, GCDAsyncSocketDelegate {
var socket:GCDAsyncSocket! = nil
func setupConnection(){
var error : NSError?
if (socket == nil) {
socket = GCDAsyncSocket(delegate: self, delegateQueue: dispatch_get_main_queue())
} else {
socket.disconnect()
}
if (!socket.connectToHost(host, onPort: port, withTimeout: 5.0, error: &error)){
println("Error: \(error)")
} else {
println("Connecting...")
}
}
public func socket(socket : GCDAsyncSocket, didConnectToHost host:String, port p:UInt16) {
println("Connected to \(host) on port \(p).")
self.socket = socket
getSystemInfo()
getSystemStatus()
}
func send(msgBytes: [UInt8]) {
var msgData = NSData(bytes: msgBytes, length: msgBytes)
socket.writeData(msgData, withTimeout: -1.0, tag: 0)
socket.readDataWithTimeout(-1.0, tag: 0)
}
func getSystemInfo() {
var sendBytes:[UInt8] = [0x0, 0x1, 0x2, 0x3]
send(sendBytes)
}
func getSystemStatus() {
var sendBytes:[UInt8] = [0x4, 0x5, 0x6, 0x7]
send(sendBytes)
}
public func socket(socket : GCDAsyncSocket!, didReadData data:NSData!, withTag tag:Int){
var msgData = NSMutableData()
msgData.setData(data)
var msgType:UInt16 = 0
msgData.getBytes(&msgType, range: NSRange(location: 2,length: 1))
println(msgType)
}
}
Any suggestions would be great -- thanks!
So I decided to use NSOperation for this.
Created a class file called SyncRequest.swift with the following code:
import Foundation
class SyncRequest : NSOperation {
var socket:GCDAsyncSocket! = nil
var msgData:NSData! = nil
override var concurrent: Bool {
return false
}
override var asynchronous: Bool {
return false
}
private var _executing: Bool = false
override var executing: Bool {
get {
return _executing
}
set {
if (_executing != newValue) {
self.willChangeValueForKey("isExecuting")
_executing = newValue
self.didChangeValueForKey("isExecuting")
}
}
}
private var _finished: Bool = false;
override var finished: Bool {
get {
return _finished
}
set {
if (_finished != newValue) {
self.willChangeValueForKey("isFinished")
_finished = newValue
self.didChangeValueForKey("isFinished")
}
}
}
/// Complete the operation
func completeOperation() {
executing = false
finished = true
}
override func start() {
if (cancelled) {
finished = true
return
}
executing = true
main()
}
override func main() -> (){
println("starting...")
NSNotificationCenter.defaultCenter().addObserver(self, selector: "didReadData:", name: "DidReadData", object: nil)
sendData()
}
func sendData() {
socket.writeData(msgData, withTimeout: -1.0, tag: 0)
println("Sending: \(msgData)")
socket.readDataWithTimeout(-1.0, tag: 0)
}
func didReadData(notif: NSNotification) {
println("Data Received!")
NSNotificationCenter.defaultCenter().removeObserver(self, name: "DidReadData", object: nil)
completeOperation()
}
}
Then, when I need to send something out to the controller I do the following:
// sync the request to the controller
let queue = NSOperationQueue() // sync request queue
let requestOperation = SyncRequest()
requestOperation.socket = socket // pass the socket to send through
requestOperation.msgData = msgData // pass the msgData to send
queue.maxConcurrentOperationCount = 1
queue.addOperation(requestOperation)
Don't forget to send the NSNotification when data comes in from where you are handling the GCDAsyncSocket's "didReadData" delegate.
public func socket(socket : GCDAsyncSocket!, didReadData data:NSData!, withTag tag:Int){
...
NSNotificationCenter.defaultCenter().postNotificationName("DidReadData", object: data)
...
}