Using NSNetService and NSNetServiceBrowser on a Swift application - swift

I'd like to make an app that is able to discover and connect to peers on the local network, so i've decided to implement it in swift, using the bonjour framework.
However, i can't make Bonjour work using Swift, and I can't figure out why. Here is the code I use to test this service :
import Foundation
let BM_DOMAIN = "local"
let BM_TYPE = "_helloworld._tcp."
let BM_NAME = "hello"
let BM_PORT : CInt = 6543
/// Netservice
let nsns = NSNetService(domain: BM_DOMAIN,
type: BM_TYPE, name: BM_NAME, port: BM_PORT)
let nsnsdel = BMNSDelegate() //see bellow
nsns.delegate = nsnsdel
nsns.publish()
/// Net service browser.
let nsb = NSNetServiceBrowser()
let nsbdel = BMBrowserDelegate() //see bellow
nsb.delegate = nsbdel
nsb.searchForServicesOfType(BM_TYPE, inDomain: BM_DOMAIN)
println("press enter")
// this prevents the app from quitting instantly.
NSFileHandle.fileHandleWithStandardInput().availableData
The delegates are glue code that simply prints every call to the console.
class BMNSDelegate : NSObject, NSNetServiceDelegate {
func netServiceWillPublish(sender: NSNetService!) {
println("netServiceWillPublish:sender");
}
// .....and so on for the 8 other methods.....
}
class BMBrowserDelegate : NSObject, NSNetServiceBrowserDelegate {
func netServiceBrowserWillSearch(aNetServiceBrowser: NSNetServiceBrowser!){
println("netServiceBrowserWillSearch")
}
// .....and so on for the 6 other methods.....
}
Here is the output of this sample code :
netServiceWillPublish:sender
netServiceBrowserWillSearch
press enter
If I use Bonjour browser, I can see that the service is correctly published. However the callbacks in both delegates are not called, beside the **WillPublish ones :-(
After intense debugging (and reading on stackoverflow), I can't figure why it does not work. Any ideas ?
(I'm using Mac OS X 10.9.3, and xcode 6.0 beta build 6A215l)

Without your full code, it may be difficult to know, for sure, what your issue is. I suspect that you declared your variables/constants local to a function. When they went out of scope, the references to the service went out of scope. That's why you tried a blocking call requesting input from STDIN (to keep things stuck there). According to Apple documentation, netService and netServiceBrowser both implicitly associate with the default run loop, so you don't need to explicitly do that either. Explicitly associating with a run loop causes the program to get stuck, which is not what you want. This code creates the following output
netServiceWillPublish:<NSNetService 0x14522e00> local _helloworld._tcp. hello
netServiceBrowserWillSearch
netServiceDidPublish:<NSNetService 0x14522e00> local. _helloworld._tcp. hello
netServiceDidFindService
and without being blocked or in a run loop that prevents the program from proceeding normally. In AppDelegate.swift
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var nsns:NSNetService?
var nsnsdel:BMNSDelegate?
var nsb:NSNetServiceBrowser?
var nsbdel:BMBrowserDelegate?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
let BM_DOMAIN = "local"
let BM_TYPE = "_helloworld._tcp."
let BM_NAME = "hello"
let BM_PORT : CInt = 6543
/// Netservice
nsns = NSNetService(domain: BM_DOMAIN,
type: BM_TYPE, name: BM_NAME, port: BM_PORT)
nsnsdel = BMNSDelegate() //see bellow
nsns?.delegate = nsnsdel
nsns?.publish()
/// Net service browser.
nsb = NSNetServiceBrowser()
nsbdel = BMBrowserDelegate() //see bellow
nsb?.delegate = nsbdel
nsb?.searchForServicesOfType(BM_TYPE, inDomain: BM_DOMAIN)
//println("press enter")
// this prevents the app from quitting instantly.
// NSRunLoop.currentRunLoop().run()
// NSFileHandle.fileHandleWithStandardInput().availableData
return true
}
and the delegate callbacks elsewhere...
class BMNSDelegate : NSObject, NSNetServiceDelegate {
func netServiceWillPublish(sender: NSNetService!) {
println("netServiceWillPublish:\(sender)");
}
func netService(sender: NSNetService, didNotPublish errorDict: [NSObject : AnyObject]) {
println("didNotPublish:\(sender)");
}
func netServiceDidPublish(sender: NSNetService) {
println("netServiceDidPublish:\(sender)");
}
func netServiceWillResolve(sender: NSNetService) {
println("netServiceWillResolve:\(sender)");
}
func netService(sender: NSNetService, didNotResolve errorDict: [NSObject : AnyObject]) {
println("netServiceDidNotResolve:\(sender)");
}
func netServiceDidResolveAddress(sender: NSNetService) {
println("netServiceDidResolve:\(sender)");
}
func netService(sender: NSNetService, didUpdateTXTRecordData data: NSData) {
println("netServiceDidUpdateTXTRecordData:\(sender)");
}
func netServiceDidStop(sender: NSNetService) {
println("netServiceDidStopService:\(sender)");
}
func netService(sender: NSNetService,
didAcceptConnectionWithInputStream inputStream: NSInputStream,
outputStream stream: NSOutputStream) {
println("netServiceDidAcceptConnection:\(sender)");
}
}
class BMBrowserDelegate : NSObject, NSNetServiceBrowserDelegate {
func netServiceBrowser(netServiceBrowser: NSNetServiceBrowser,
didFindDomain domainName: String,
moreComing moreDomainsComing: Bool) {
println("netServiceDidFindDomain")
}
func netServiceBrowser(netServiceBrowser: NSNetServiceBrowser,
didRemoveDomain domainName: String,
moreComing moreDomainsComing: Bool) {
println("netServiceDidRemoveDomain")
}
func netServiceBrowser(netServiceBrowser: NSNetServiceBrowser,
didFindService netService: NSNetService,
moreComing moreServicesComing: Bool) {
println("netServiceDidFindService")
}
func netServiceBrowser(netServiceBrowser: NSNetServiceBrowser,
didRemoveService netService: NSNetService,
moreComing moreServicesComing: Bool) {
println("netServiceDidRemoveService")
}
func netServiceBrowserWillSearch(aNetServiceBrowser: NSNetServiceBrowser!){
println("netServiceBrowserWillSearch")
}
func netServiceBrowser(netServiceBrowser: NSNetServiceBrowser,
didNotSearch errorInfo: [NSObject : AnyObject]) {
println("netServiceDidNotSearch")
}
func netServiceBrowserDidStopSearch(netServiceBrowser: NSNetServiceBrowser) {
println("netServiceDidStopSearch")
}
}

NSNetServiceBrowser needs a runloop to execute. Instead of reading from stdin, call NSRunLoop.currentRunLoop().run().

Related

How i can send message to the Injected Script on page from SafariExtensionHandler

I can send message when received message in override func messageReceived with from page prop and dispatchMessageToScript method, but I don't understand how can I do it in injectOpenWelcomeTab method. Thanks
import SafariServices
class SafariExtensionHandler: SFSafariExtensionHandler {
override func messageReceived(withName messageName: String, from page: SFSafariPage, userInfo: [String : Any]?) {
// This method will be called when a content script provided by your extension calls safari.extension.dispatchMessage("message").
page.getPropertiesWithCompletionHandler { properties in
NSLog("The extension received a message (\(messageName)) from a script injected into (\(String(describing: properties?.url))) with userInfo (\(userInfo ?? [:]))")
//Вот таким образом можем дёргать методы из контроллера
SafariExtensionViewController.shared.popupChangeBackgorundColor()
}
page.dispatchMessageToScript(withName: "openWelcomePage")
}
public func injectOpenWelcomeTab(){
//Here I want send message to injected script
}
}
SFSafariApplication.getActiveWindow is your best friend here.
Just make sure you import SafariServices
func injectOpenWelcomeTab() {
getActivePage {
$0?.dispatchMessageToScript(withName: "openWelcomePage", userInfo: ["foo": bar as Any])
}
}
func getActivePage(completionHandler: #escaping (SFSafariPage?) -> Void) {
SFSafariApplication.getActiveWindow {$0?.getActiveTab {$0?.getActivePage(completionHandler: completionHandler)}}
}

0Auth2 Stuck on Permission Page

Currently having an issue with 0auth2 permissions. It keeps on getting stuck to this page: Stuck101. It doesn't crash, just remains completely dormant.
The debug console shows this:
[Debug] OAuth2: Initialization finished
[Debug] OAuth2: Looking for items in keychain
[Debug] OAuth2: Starting authorization
[Debug] OAuth2: No access token, checking if a refresh token is available
[Debug] OAuth2: Error refreshing token: I don't have a refresh token, not
trying to refresh
[Debug] OAuth2: Opening authorize URL embedded: https://login.microsoftonline.com/common/oauth2/v2.0/authorize?state=7A3CEE60&response_type=code&scope=openid+profile+offline_access+User.Read+Mail.Read+Calendars.Read&redirect_uri=KTracker2%3A%2F%2Foauth2%2Fcallback&client_id=584bb6db-5b71-4d7c-9015-fab8a9dfae4c
2018-05-09 04:49:47.198828+0100 KTracker2[8281:180974] [App] if we're in the real pre-commit handler we can't actually add any new fences due to CA restriction
My App delegate file houses this code:
// AppDelegate.swift
// KTracker2
//
// Created by CaudyMac on 07/05/2018.
// Copyright © 2018 Kallum Caudwell. All rights reserved.
//
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey: Any] = [:]) -> Bool {
if url.scheme == "KTracker2" {
let service = OutlookService.shared()
service.handleOAuthCallback(url: url)
return true
}
else {
return false
}
}
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
func applicationWillResignActive(_ application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
}
func applicationDidEnterBackground(_ application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(_ application: UIApplication) {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
}
Service Code is here:
// OutlookService.swift
// KTracker2
// Created by CaudyMac on 09/05/2018.
// Copyright © 2018 Kallum Caudwell. All rights reserved.
import Foundation
import p2_OAuth2
import SwiftyJSON
class OutlookService {
// Configure the OAuth2 framework for Azure
private static let oauth2Settings = [
"client_id" : "584bb6db-5b71-4d7c-9015-fab8a9dfae4c",
"authorize_uri": "https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
"token_uri": "https://login.microsoftonline.com/common/oauth2/v2.0/token",
"scope": "openid profile offline_access User.Read Mail.Read Calendars.Read",
"redirect_uris": ["KTracker2://oauth2/callback"],
"verbose": true,
] as OAuth2JSON
private static var sharedService: OutlookService = {
let service = OutlookService()
return service
}()
private let oauth2: OAuth2CodeGrant
private init() {
oauth2 = OAuth2CodeGrant(settings: OutlookService.oauth2Settings)
oauth2.authConfig.authorizeEmbedded = true
//oauth2.authConfig.ui.useSafariView = false
userEmail = ""
}
class func shared() -> OutlookService {
return sharedService
}
var isLoggedIn: Bool {
get {
return oauth2.hasUnexpiredAccessToken() || oauth2.refreshToken != nil
}
}
func handleOAuthCallback(url: URL) -> Void {
oauth2.handleRedirectURL(url)
}
func login(from: AnyObject, callback: #escaping (String?) -> Void) -> Void {
oauth2.authorizeEmbedded(from: from) {
result, error in
if let unwrappedError = error {
callback(unwrappedError.description)
} else {
if let unwrappedResult = result, let token = unwrappedResult["access_token"] as? String {
// Print the access token to debug log
NSLog("Access token: \(token)")
callback(nil)
}
}
}
}
func logout() -> Void {
oauth2.forgetTokens()
}
func makeApiCall(api: String, params: [String: String]? = nil, callback: #escaping (JSON?) -> Void) -> Void {
// Build the request URL
var urlBuilder = URLComponents(string: "https://graph.microsoft.com")!
urlBuilder.path = api
if let unwrappedParams = params {
// Add query parameters to URL
urlBuilder.queryItems = [URLQueryItem]()
for (paramName, paramValue) in unwrappedParams {
urlBuilder.queryItems?.append(
URLQueryItem(name: paramName, value: paramValue))
}
}
let apiUrl = urlBuilder.url!
NSLog("Making request to \(apiUrl)")
var req = oauth2.request(forURL: apiUrl)
req.addValue("application/json", forHTTPHeaderField: "Accept")
let loader = OAuth2DataLoader(oauth2: oauth2)
// Uncomment this line to get verbose request/response info in
// Xcode output window
//loader.logger = OAuth2DebugLogger(.trace)
loader.perform(request: req) {
response in
do {
let dict = try response.responseJSON()
DispatchQueue.main.async {
let result = JSON(dict)
callback(result)
}
}
catch let error {
DispatchQueue.main.async {
let result = JSON(error)
callback(result)
}
}
}
}
private var userEmail: String
func getUserEmail(callback: #escaping (String?) -> Void) -> Void {
// If we don't have the user's email, get it from
// the API
if (userEmail.isEmpty) {
makeApiCall(api: "/v1.0/me") {
result in
if let unwrappedResult = result {
let email = unwrappedResult["mail"].stringValue
self.userEmail = email
callback(email)
} else {
callback(nil)
}
}
} else {
callback(userEmail)
}
}
func getInboxMessages(callback: #escaping (JSON?) -> Void) -> Void {
let apiParams = [
"$select": "subject,receivedDateTime,from",
"$orderby": "receivedDateTime DESC",
"$top": "10"
]
makeApiCall(api: "/v1.0/me/mailfolders/inbox/messages", params: apiParams) {
result in
callback(result)
}
}
func getEvents(callback: #escaping (JSON?) -> Void) -> Void {
let apiParams = [
"$select": "subject,start,end",
"$orderby": "start/dateTime ASC",
"$top": "10"
]
makeApiCall(api: "/v1.0/me/events", params: apiParams) {
result in
callback(result)
}
}
func getContacts(callback: #escaping (JSON?) -> Void) -> Void {
let apiParams = [
"$select": "givenName,surname,emailAddresses",
"$orderby": "givenName ASC",
"$top": "10"
]
makeApiCall(api: "/v1.0/me/contacts", params: apiParams) {
result in
callback(result)
}
}
}
I also have KTracker2 added in the plist files
Any ideas to why my app lies dormant would be great. Thanks a bunch.

Alamofire background working on simulator but not on device

As the title mentions I've set up a backgroundURL with Alamofire. It works like a charm in simulator but on my device doesn't. I'm sure I'm missing something here since I'm not that experienced with URL.
Here's the code I have so far:
class NetworkManager {
static let shared = NetworkManager()
private lazy var backgroundManager: Alamofire.SessionManager = {
let bundleIdentifier = MyStruct.identifier
return Alamofire.SessionManager(configuration: URLSessionConfiguration.background(withIdentifier: bundleIdentifier))
}()
var backgroundCompletionHandler: (() -> Void)? {
get{
return backgroundManager.backgroundCompletionHandler
}
set{
backgroundManager.backgroundCompletionHandler = newValue
}
}
}
func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: #escaping () -> Void) {
NetworkManager.shared.backgroundCompletionHandler = completionHandler
}
In my ViewController:
func populateArrays(){
Alamofire.request("http://www.aps.anl.gov/Accelerator_Systems_Division/Accelerator_Operations_Physics/sddsStatus/mainStatus.sdds.gz").responseData { response in
switch response.result{
case .success:
print("Validation Successful")
case .failure(let error):
print(error.localizedDescription)
}
if let data = response.result.value{
Solved it. For anyone else that has this problem you need to add the following code to your appDelegate.
func applicationDidEnterBackground(_ application: UIApplication) {
var bgTask = 0
var app = UIApplication.shared
bgTask = app.beginBackgroundTask(expirationHandler: {() -> Void in
app.endBackgroundTask(bgTask)
})
It seems to me that you are not using the background manager you've created. Instead of
Alamofire.request("http://www.aps.anl.gov...")
which calls the default (not background) session manager, you should use:
backgroundManager.request("http://www.aps.anl.gov...")
Which Jon Shier mentioned in the comments by the way.

XMPP Stream Delegate Not Called? Swift 3

I am trying to connect to an XMPP server in my iOS Application. I am using the XMPPFrameworks and for some reason the XMPP Stream delegate is not being called after I try to connect to the server. I have double checked the login information using a third party XMPP application on my computer so I do not believe it is that. Am I not setting this delegate up correctly? Am I using the wrong syntax? Do I need to set this in the app delegate instead of my view controller? Any help would be much appreciated. Below is my code
import UIKit
import XMPPFramework
class ViewController: UIViewController, XMPPStreamDelegate {
override func viewDidLoad() {
super.viewDidLoad()
connect()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func connect() {
let stream = XMPPStream()
stream?.addDelegate(self, delegateQueue: DispatchQueue.main)
stream?.myJID = XMPPJID.init(string: "XXXXXXXXXXX")
stream?.hostName = "XXXXXXXXX"
stream?.hostPort = 5222
do {
try stream?.connect(withTimeout: XMPPStreamTimeoutNone)
} catch {
print("error connecting")
}
}
func xmppStreamDidConnect(sender: XMPPStream) {
print("connected!")
do {
try sender.authenticate(withPassword: "XXXXXXXXXX")
} catch {
print("error registering")
}
}
}
I think that your delegate method is not right. You can try with the delegate method given below:
#objc func xmppStreamDidConnect(_ sender: XMPPStream!) {
//write your code here.
}
try this
do {
try self.xmppController = XMPPController(hostName: server,
userJIDString: userJID,
password: userPassword)
self.xmppController.xmppStream.addDelegate(self, delegateQueue: DispatchQueue.main)
self.xmppController.connect()
} catch {
sender.showErrorMessage(message: "Something went wrong")
}
and XMPPController
class XMPPController: NSObject {
var xmppStream: XMPPStream
let hostName: String
let userJID: XMPPJID
let hostPort: UInt16
let password: String
init(hostName: String, userJIDString: String, hostPort: UInt16 = 5222, password: String) throws {
guard let userJID = XMPPJID(string: userJIDString) else {
throw XMPPControllerError.wrongUserJID
}
self.hostName = hostName
self.userJID = userJID
self.hostPort = hostPort
self.password = password
// Stream Configuration
self.xmppStream = XMPPStream()
self.xmppStream.hostName = hostName
self.xmppStream.hostPort = hostPort
self.xmppStream.startTLSPolicy = XMPPStreamStartTLSPolicy.allowed
self.xmppStream.myJID = userJID
super.init()
self.xmppStream.addDelegate(self, delegateQueue: DispatchQueue.main)
}
func connect() {
if !self.xmppStream.isDisconnected() {
return
}
try! self.xmppStream.connect(withTimeout: XMPPStreamTimeoutNone)
}}
it works for me. required your attention this line
try self.xmppController = XMPPController(hostName: server,
userJIDString: userJID,
password: userPassword)
I had the same issue. In my case (as I followed some tutorial) the object was not global and the delegate became nil. That's why it was not called. You have to store the object which implements XMPPStreamDelegate globally.

setObjectForKey method with custom objects doesn't work NSUserDefaults swift

I use the setObjectForKey method when I try to save a custom object, but when I try to retrieve it for the same key, it returns nil. I have been researching and I know that there is some issue with set custom objects to NSUserDefaults so I tried using NSKeyedArchiver class, but it still returns nil.
//
// VirtualRewardsClient.swift
// VirtualRewardsNew
//
// Created by Dhruv Mangtani on 3/14/15.
// Copyright (c) 2015 dhruv.mangtani. All rights reserved.
//
import UIKit
class VirtualRewardsClient{
class var sharedInstance: VirtualRewardsClient{
struct Static{
static var instance = VirtualRewardsClient()
}
return Static.instance
}
func getClass() -> Class{
var defaults = NSUserDefaults.standardUserDefaults()
println(defaults.objectForKey(classKey))
if let data = defaults.objectForKey(classKey) as? NSData{
//let unarc = NSKeyedUnarchiver(forReadingWithData: data)
//unarc.setClass(Class.self, forClassName: "Class")
let currentClass = NSKeyedUnarchiver.unarchiveObjectWithData(data) as Class
println("entering")
Class.sharedInstance.students = currentClass.students
Class.sharedInstance.teacher = currentClass.teacher
return currentClass
} else {
var newClass = Class()
newClass.students = [Student]()
var encodedObject: NSData = NSKeyedArchiver.archivedDataWithRootObject(newClass)
defaults.setObject(encodedObject, forKey: classKey)
defaults.synchronize()
return newClass
}
}
}
Class.swift
import Foundation
import UIKit
let classKey = "CLASS_KEY"
class Class: NSObject{
let defaults = NSUserDefaults.standardUserDefaults()
class var sharedInstance: Class{
struct Static{
static var instance: Class = VirtualRewardsClient.sharedInstance.getClass()
}
return Static.instance
}
var students:[Student] = [Student]()
var teacher = Teacher(currentClass: sharedInstance)
func addStudent(name: String, value: Int){
students.append(Student(name: name, startingPoints: value))
defaults.setObject(Class.sharedInstance, forKey: classKey)
VirtualRewardsClient.sharedInstance.getClass()
}
func addStudent(name: String){
students.append(Student(name: name))
defaults.setObject(Class.sharedInstance, forKey: classKey)
VirtualRewardsClient.sharedInstance.getClass()
}
func printClass(){
for i in students{
println("Student: \(i.name), Points: \(i.points)")
}
}
}
App Delegate:
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
/*var storyboard = UIStoryboard(name: "Main", bundle: nil)
var vc = storyboard.instantiateViewControllerWithIdentifier("StudentsNavigationController") as UIViewController
window?.rootViewController = vc
println("didFinishLaunchingWithOptions\(window?.rootViewController)")
*/
return true
}
func applicationWillResignActive(application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
func applicationDidEnterBackground(application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
println("applicationDidEnterBackground")
}
func applicationWillEnterForeground(application: UIApplication) {
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
println("applicationWillEnterForeground")
}
func applicationDidBecomeActive(application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
println("applicationDidBecomeActive")
}
func applicationWillTerminate(application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
}
change Class to VirtualRewardsClient