//
//  AppDelegate.swift
//  SampleAppSwift
//
//  Created by Piyush Joshi on 5/1/15.
//  Copyright (c) 2015 AzME. All rights reserved.
//

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.
        let reach = AEReachModule.moduleWithNotificationIcon(UIImage(named:"icon.png")) as! AEReachModule

        EngagementAgent.init("<connectionString>", modulesArray:[reach])

        if application.respondsToSelector("registerUserNotificationSettings:")
        {
            application.registerUserNotificationSettings(UIUserNotificationSettings(
                forTypes: (UIUserNotificationType.Alert | UIUserNotificationType.Badge | UIUserNotificationType.Sound),
                categories: nil))

                application.registerForRemoteNotifications()
        }
        else
        {
            application.registerForRemoteNotificationTypes(UIRemoteNotificationType.Alert | UIRemoteNotificationType.Badge | UIRemoteNotificationType.Sound)
        }

        return true
    }

    func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData)
    {
        EngagementAgent.shared().registerDeviceToken(deviceToken)
    }

    func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject])
    {
        EngagementAgent.shared().applicationDidReceiveRemoteNotification(userInfo)
    }

    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.
    }

    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.
    }

    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:.
    }

}


//
//  ViewController.swift
//  SampleAppSwift
//
//  Created by Piyush Joshi on 5/1/15.
//  Copyright (c) 2015 AzME. All rights reserved.
//

import UIKit

class ViewController: EngagementViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // 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.
    }

}


//
//  SampleAppSwiftTests.swift
//  SampleAppSwiftTests
//
//  Created by Piyush Joshi on 5/1/15.
//  Copyright (c) 2015 AzME. All rights reserved.
//

import UIKit
import XCTest

class SampleAppSwiftTests: XCTestCase {

    override func setUp() {
        super.setUp()
        // Put setup code here. This method is called before the invocation of each test method in the class.
    }

    override func tearDown() {
        // Put teardown code here. This method is called after the invocation of each test method in the class.
        super.tearDown()
    }

    func testExample() {
        // This is an example of a functional test case.
        XCTAssert(true, "Pass")
    }

    func testPerformanceExample() {
        // This is an example of a performance test case.
        self.measureBlock() {
            // Put the code you want to measure the time of here.
        }
    }

}


// ----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ----------------------------------------------------------------------------
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
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.

        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.
    }

    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.
    }

    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:.
        // Saves changes in the application's managed object context before the application terminates.
    }

    // MARK: - Core Data stack

    lazy var applicationDocumentsDirectory: NSURL = {
        // The directory the application uses to store the Core Data store file.
        let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
        return urls[urls.count-1]
    }()

    lazy var managedObjectModel: NSManagedObjectModel = {
        // The managed object model for the application. This property is not optional. It is a fatal error for the application not to be able to find and load its model.
        let modelURL = NSBundle.mainBundle().URLForResource("QSTodoDataModel", withExtension: "momd")!
        return NSManagedObjectModel(contentsOfURL: modelURL)!
    }()

    lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator? = {
        // The persistent store coordinator for the application. This implementation creates and return a coordinator, having added the store for the application to it. This property is optional since there are legitimate error conditions that could cause the creation of the store to fail.
        // Create the coordinator and store
        var coordinator: NSPersistentStoreCoordinator? = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
        let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("qstodoitem.sqlite")
        var error: NSError? = nil
        var failureReason = "There was an error creating or loading the application's saved data."
        do {
            try coordinator!.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: nil)
        } catch var error1 as NSError {
            error = error1
            coordinator = nil

            // Report any error we got.
            var dict = [String: AnyObject]()
            dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data"
            dict[NSLocalizedFailureReasonErrorKey] = failureReason
            dict[NSUnderlyingErrorKey] = error
            error = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict)

            // Replace this with code to handle the error appropriately.
            // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
            NSLog("Unresolved error \(error), \(error!.userInfo)")
            abort()
        } catch {
            fatalError()
        }

        return coordinator
    }()

    lazy var managedObjectContext: NSManagedObjectContext? = {
        // Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.) This property is optional since there are legitimate error conditions that could cause the creation of the context to fail.
        let coordinator = self.persistentStoreCoordinator
        if coordinator == nil {
            return nil
        }
        var managedObjectContext = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
        managedObjectContext.persistentStoreCoordinator = coordinator
        return managedObjectContext
    }()
}


// ----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ----------------------------------------------------------------------------
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import Foundation
import UIKit

protocol ToDoItemDelegate {
    func didSaveItem(text : String)
}

class ToDoItemViewController: UIViewController,  UIBarPositioningDelegate, UITextFieldDelegate {

    @IBOutlet weak var text: UITextField!

    var delegate : ToDoItemDelegate?

    override func viewDidLoad()
    {
        super.viewDidLoad()

        self.text.delegate = self
        self.text.becomeFirstResponder()
    }

    @IBAction func cancelPressed(sender : UIBarButtonItem) {
        self.text.resignFirstResponder()
    }

    @IBAction func savePressed(sender : UIBarButtonItem) {
        saveItem()
        self.text.resignFirstResponder()
    }

    // Textfield

    func textFieldDidEndEditing(textField: UITextField)
    {
        self.navigationController?.popViewControllerAnimated(true);
    }

    func textFieldShouldEndEditing(textField: UITextField) -> Bool
    {
        return true
    }

    func textFieldShouldReturn(textField: UITextField) -> Bool
    {
        saveItem()

        textField.resignFirstResponder()
        return true
    }

    // Delegate

    func saveItem()
    {
        if let text = self.text.text {
            self.delegate?.didSaveItem(text)
        }
    }
}


// ----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ----------------------------------------------------------------------------
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
import UIKit
import CoreData

class ToDoTableViewController: UITableViewController, NSFetchedResultsControllerDelegate, ToDoItemDelegate {

    var table : MSSyncTable?
    lazy var fetchedResultController: NSFetchedResultsController = {
        let fetchRequest = NSFetchRequest(entityName: "TodoItem")
        let managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext!

        // show only non-completed items
        fetchRequest.predicate = NSPredicate(format: "complete != true")

        // sort by item text
        fetchRequest.sortDescriptors = [NSSortDescriptor(key: "ms_createdAt", ascending: true)]

        // Note: if storing a lot of data, you should specify a cache for the last parameter
        // for more information, see Apple's documentation: http://go.microsoft.com/fwlink/?LinkId=524591&clcid=0x409
        let resultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: managedObjectContext, sectionNameKeyPath: nil, cacheName: nil)

        resultsController.delegate = self;

        return resultsController
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        let managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext!
        let client = MSClient(applicationURLString: "ZUMOAPPURL", applicationKey: "ZUMOAPPKEY")
        let store = MSCoreDataStore(managedObjectContext: managedObjectContext)
        client.syncContext = MSSyncContext(delegate: nil, dataSource: store, callback: nil)

        self.table = client.syncTableWithName("TodoItem")
        self.refreshControl?.addTarget(self, action: "onRefresh:", forControlEvents: UIControlEvents.ValueChanged)

        var error : NSError? = nil
        do {
            try self.fetchedResultController.performFetch()
        } catch let error1 as NSError {
            error = error1
            print("Unresolved error \(error), \(error?.userInfo)")
            abort()
        }

        // Refresh data on load
        self.refreshControl?.beginRefreshing()
        self.onRefresh(self.refreshControl)
    }

    func onRefresh(sender: UIRefreshControl!) {
        UIApplication.sharedApplication().networkActivityIndicatorVisible = true

        self.table!.pullWithQuery(self.table?.query(), queryId: "AllRecords") {
            (error) -> Void in

            UIApplication.sharedApplication().networkActivityIndicatorVisible = false

            if error != nil {
                // A real application would handle various errors like network conditions,
                // server conflicts, etc via the MSSyncContextDelegate
                print("Error: \(error!.description)")

                // We will just discard our changes and keep the servers copy for simplicity
                if let opErrors = error!.userInfo[MSErrorPushResultKey] as? Array<MSTableOperationError> {
                    for opError in opErrors {
                        print("Attempted operation to item \(opError.itemId)")
                        if (opError.operation == .Insert || opError.operation == .Delete) {
                            print("Insert/Delete, failed discarding changes")
                            opError.cancelOperationAndDiscardItemWithCompletion(nil)
                        } else {
                            print("Update failed, reverting to server's copy")
                            opError.cancelOperationAndUpdateItem(opError.serverItem!, completion: nil)
                        }
                    }
                }
            }

            self.refreshControl?.endRefreshing()
        }
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    // MARK: Table Controls

    override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool
    {
        return true
    }

    override func tableView(tableView: UITableView, editingStyleForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCellEditingStyle
    {
        return UITableViewCellEditingStyle.Delete
    }

    override func tableView(tableView: UITableView, titleForDeleteConfirmationButtonForRowAtIndexPath indexPath: NSIndexPath) -> String?
    {
        return "Complete"
    }

    override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath)
    {
        let record = self.fetchedResultController.objectAtIndexPath(indexPath) as! NSManagedObject
        var item = MSCoreDataStore.tableItemFromManagedObject(record)

        item["complete"] = true

        UIApplication.sharedApplication().networkActivityIndicatorVisible = true

        self.table!.update(item) { (error) -> Void in
            UIApplication.sharedApplication().networkActivityIndicatorVisible = false
            if error != nil {
                print("Error: \(error!.description)")
                return
            }
        }
    }

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
    {
        if let sections = self.fetchedResultController.sections {
            return sections[section].numberOfObjects
        }

        return 0;
    }

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let CellIdentifier = "Cell"

        var cell = tableView.dequeueReusableCellWithIdentifier(CellIdentifier, forIndexPath: indexPath)
        cell = configureCell(cell, indexPath: indexPath)

        return cell
    }

    func configureCell(cell: UITableViewCell, indexPath: NSIndexPath) -> UITableViewCell {
        let item = self.fetchedResultController.objectAtIndexPath(indexPath) as! NSManagedObject

        // Set the label on the cell and make sure the label color is black (in case this cell
        // has been reused and was previously greyed out
        cell.textLabel!.text = (item.valueForKey("text") as! String)
        cell.textLabel!.textColor = UIColor.blackColor()

        return cell
    }

    // MARK: Navigation

    @IBAction func addItem(sender : AnyObject) {
        self.performSegueWithIdentifier("addItem", sender: self)
    }

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!)
    {
        if segue.identifier == "addItem" {
            let todoController = segue.destinationViewController as! ToDoItemViewController
            todoController.delegate = self
        }
    }

    // MARK: - ToDoItemDelegate

    func didSaveItem(text: String)
    {
        if text.isEmpty {
            return
        }

        // We set created at to now, so it will sort as we expect it to post the push/pull
        let itemToInsert = ["text": text, "complete": false, "__createdAt": NSDate()]

        UIApplication.sharedApplication().networkActivityIndicatorVisible = true
        self.table!.insert(itemToInsert) {
            (item, error) in
            UIApplication.sharedApplication().networkActivityIndicatorVisible = false
            if error != nil {
                print("Error: " + error!.description)
            }
        }
    }

    // MARK: - NSFetchedResultsDelegate

    func controllerWillChangeContent(controller: NSFetchedResultsController) {
        dispatch_async(dispatch_get_main_queue(), { () -> Void in
            self.tableView.beginUpdates()
        });
    }

    func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) {

        dispatch_async(dispatch_get_main_queue(), { () -> Void in
            let indexSectionSet = NSIndexSet(index: sectionIndex)
            if type == .Insert {
                self.tableView.insertSections(indexSectionSet, withRowAnimation: .Fade)
            } else if type == .Delete {
                self.tableView.deleteSections(indexSectionSet, withRowAnimation: .Fade)
            }
        })
    }

    func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {

        dispatch_async(dispatch_get_main_queue(), { () -> Void in
            switch type {
            case .Insert:
                self.tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Fade)
            case .Delete:
                self.tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade)
            case .Move:
                self.tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade)
                self.tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Fade)
            case .Update:
                // note: Apple samples show a call to configureCell here; this is incorrect--it can result in retrieving the
                // wrong index when rows are reordered. For more information, see:
                // http://go.microsoft.com/fwlink/?LinkID=524590&clcid=0x409
                self.tableView.reloadRowsAtIndexPaths([indexPath!], withRowAnimation: .Automatic)
            }
        })
    }

    func controllerDidChangeContent(controller: NSFetchedResultsController) {
        dispatch_async(dispatch_get_main_queue(), { () -> Void in
            self.tableView.endUpdates()
        });
    }
}


// swift-tools-version:5.0

// Copyright (c) Microsoft Corporation. All rights reserved.

import PackageDescription

let package = Package(
    name: "WindowsAzureMessaging",
    platforms: [
        .iOS(.v9),
        .macOS(.v10_10),
        .tvOS(.v11)
    ],
    products: [
        .library(
            name: "WindowsAzureMessaging",
            type: .static,
            targets: ["WindowsAzureMessaging"]),
    ],
    dependencies: [],
    targets: [
        .target(
            name: "WindowsAzureMessaging",
            path: "WindowsAzureMessaging/WindowsAzureMessaging",
            exclude: ["Support"],
            cSettings: [
                .define("NH_C_VERSION", to:"\"3.1.5\""),
                .define("NH_C_BUILD", to:"\"1\""),
                .headerSearchPath("**"),
            ],
            linkerSettings: [
                .linkedFramework("Foundation"),
                .linkedFramework("SystemConfiguration"),
                .linkedFramework("UserNotifications"),
                .linkedFramework("AppKit", .when(platforms: [.macOS])),
                .linkedFramework("UIKit", .when(platforms: [.iOS, .tvOS]))
            ]
        )
    ]
)


// swift-tools-version:5.3
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

import PackageDescription

let package = Package(
    name: "WindowsAzureMessaging",
    defaultLocalization: "en",
    platforms: [
        .iOS(.v9),
        .macOS(.v10_10),
        .tvOS(.v11)
    ],
    products: [
        .library(
            name: "WindowsAzureMessaging",
            type: .static,
            targets: ["WindowsAzureMessaging"]),
    ],
    dependencies: [],
    targets: [
        .target(
            name: "WindowsAzureMessaging",
            path: "WindowsAzureMessaging/WindowsAzureMessaging",
            exclude: ["Support"],
            cSettings: [
                .define("NH_C_VERSION", to:"\"3.1.5\""),
                .define("NH_C_BUILD", to:"\"1\""),
                .headerSearchPath("**"),
            ],
            linkerSettings: [
                .linkedFramework("Foundation"),
                .linkedFramework("SystemConfiguration"),
                .linkedFramework("UserNotifications"),
                .linkedFramework("AppKit", .when(platforms: [.macOS])),
                .linkedFramework("UIKit", .when(platforms: [.iOS, .tvOS]))
            ]
        )
    ]
)


//----------------------------------------------------------------
//  Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------

import UIKit
import UserNotifications

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {

    // MARK: UIApplicationDelegate Methods

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        UNUserNotificationCenter.current().delegate = self
        return true
    }

    func application(_ application: UIApplication,
                     didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {

        let unparsedTags = UserDefaults.standard.string(forKey: Constants.NHUserDefaultTags) ?? ""
        let tagsArray = unparsedTags.split(separator: ",")

        let hub = getNotificationHub()
        hub.registerNative(withDeviceToken: deviceToken, tags: Set(tagsArray)) {
            error in
            if (error != nil) {
                print("Error registering for notifications: \(error.debugDescription)");
            } else {
                showAlert("Registered", withTitle:"Registration Status");
            }
        }
    }

    func application(_ application: UIApplication,
                     didFailToRegisterForRemoteNotificationsWithError error: Error) {
        print("Failed to register: \(error)")
    }

    func application(_ application: UIApplication,
                     didReceiveRemoteNotification userInfo: [AnyHashable: Any],
                     fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {

        print("Received remote (silent) notification");
        logNotificationDetails(userInfo);

        completionHandler(UIBackgroundFetchResult.newData);
    }

    // MARK: UNUserNotificationCenterDelegate Methods

    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                willPresent notification: UNNotification,
                                withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        print("Received notification while the application is in the foreground");

        showNotification(notification.request.content.userInfo)

        completionHandler([.sound, .badge])
    }

    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                didReceive response: UNNotificationResponse,
                                withCompletionHandler completionHandler: @escaping () -> Void) {
        print("Received notification while the application is in the background");

        showNotification(response.notification.request.content.userInfo)

        completionHandler()
    }

    // MARK: UISceneSession Lifecycle

    func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
        // Called when a new scene session is being created.
        // Use this method to select a configuration to create the new scene with.
        return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
    }

    func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
        // Called when the user discards a scene session.
        // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
        // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
    }

    func logNotificationDetails(_ userInfo: Any) {
        let state = UIApplication.shared.applicationState;
        let background = state != .active;
        print("Received \(background ? "(background)" : ""): \(userInfo)");
    }

    func showNotification(_ userInfo: Any) {
        logNotificationDetails(userInfo)

        let notificationDetail = NotificationDetailViewController(userInfo)
        UIApplication.shared.keyWindow?.rootViewController?.present(notificationDetail, animated: true, completion: nil)
    }

}


//----------------------------------------------------------------
//  Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------

import UIKit

class NotificationDetailViewController: UIViewController {
    @IBOutlet weak var titleLabel: UILabel!
    @IBOutlet weak var dismissButton: UIButton!
    @IBOutlet weak var bodyLabel: UILabel!

    var payload: [String: Any]?

    init(_ userInfo: Any) {
        super.init(nibName: "NotificationDetail", bundle: nil)
        payload = userInfo as? [String: Any]
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder);
    }

    override func viewDidLayoutSubviews() {
        titleLabel.sizeToFit();
        bodyLabel.sizeToFit();
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        var title: String?
        var body: String?

        let aps = payload?["aps"] as? [String: Any]
        let alertObject = aps?["alert"]
        if (alertObject != nil) {
            if let alertDict = alertObject as? [String: Any] {
                title = alertDict["title"] as? String
                body = alertDict["body"] as? String
            } else if let alertStr = alertObject as? String {
                body = alertStr
            } else {
                print("Unable to parse notification content. Unexpected format: \(String(describing: alertObject))");
            }
        }

        if (title == nil) {
            title = "<unset>"
        }

        if (body == nil) {
            body = "<unset>"
        }

        titleLabel.text = title
        bodyLabel.text = body
    }

    @IBAction func handleDismiss(_ sender: Any) {
        dismiss(animated: true, completion: nil)
    }
}


//----------------------------------------------------------------
//  Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------

import UIKit

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
        // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
        // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
        guard let _ = (scene as? UIWindowScene) else { return }
    }

    func sceneDidDisconnect(_ scene: UIScene) {
        // Called as the scene is being released by the system.
        // This occurs shortly after the scene enters the background, or when its session is discarded.
        // Release any resources associated with this scene that can be re-created the next time the scene connects.
        // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead).
    }

    func sceneDidBecomeActive(_ scene: UIScene) {
        // Called when the scene has moved from an inactive state to an active state.
        // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
    }

    func sceneWillResignActive(_ scene: UIScene) {
        // Called when the scene will move from an active state to an inactive state.
        // This may occur due to temporary interruptions (ex. an incoming phone call).
    }

    func sceneWillEnterForeground(_ scene: UIScene) {
        // Called as the scene transitions from the background to the foreground.
        // Use this method to undo the changes made on entering the background.
    }

    func sceneDidEnterBackground(_ scene: UIScene) {
        // Called as the scene transitions from the foreground to the background.
        // Use this method to save data, release shared resources, and store enough scene-specific state information
        // to restore the scene back to its current state.
    }

}


//----------------------------------------------------------------
//  Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------

import Foundation
import UIKit
import WindowsAzureMessaging

struct Constants {
    static let NHInfoConnectionString = "NotificationHubConnectionString";
    static let NHInfoHubName = "NotificationHubName";
    static let NHUserDefaultTags = "notification_tags";
}

func getNotificationHub() -> SBNotificationHub {
    let path = Bundle.main.path(forResource: "DevSettings", ofType: "plist")
    let configValues = NSDictionary(contentsOfFile: path!)
    let connectionString = configValues?["CONNECTION_STRING"] as? String
    let hubName = configValues?["HUB_NAME"] as? String

    return SBNotificationHub.init(connectionString: connectionString, notificationHubPath: hubName)
}

func showAlert(_ message: String, withTitle title: String = "Alert") {
    let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
    alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
    UIApplication.shared.keyWindow?.rootViewController?.present(alert, animated: true, completion: nil)
}


//----------------------------------------------------------------
//  Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------

import UIKit
import UserNotifications

class ViewController: UIViewController {
    @IBOutlet weak var tagsTextField: UITextField!
    @IBOutlet weak var registerButton: UIButton!
    @IBOutlet weak var unregisterButton: UIButton!

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        // Simple method to dismiss keyboard when user taps outside of the UITextField.
        view.endEditing(true)
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        setupUI()
    }

}

private extension ViewController {

    func setupUI() {
        tagsTextField.text = UserDefaults.standard.string(forKey: Constants.NHUserDefaultTags)
    }

    func getNotificationSettings() {
        UNUserNotificationCenter.current().getNotificationSettings { settings in
            print("Notification settings: \(settings)")
            guard settings.authorizationStatus == .authorized else { return }
            DispatchQueue.main.async {
                UIApplication.shared.registerForRemoteNotifications()
            }
        }
    }

    @IBAction func handleRegister(_ sender: Any) {
        // Save raw tags text in storage
        UserDefaults.standard.set(tagsTextField.text, forKey: Constants.NHUserDefaultTags)

        UNUserNotificationCenter.current()
            .requestAuthorization(options: [.alert, .sound, .badge]) {
                granted, error in
                if (error != nil) {
                    print("Error requesting for authorization:");
                }
                print("Permission granted: \(granted)")
                guard granted else { return }
                self.getNotificationSettings()
        }
    }

    @IBAction func handleUnregister(_ sender: Any) {
        let hub = getNotificationHub()
        hub.unregisterNative { error in
            if (error != nil) {
                print("Error unregistering for push: \(error.debugDescription)");
            } else {
                showAlert("Unregistered", withTitle: "Registration Status")
            }
        }
    }
}


//----------------------------------------------------------------
//  Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------

import Cocoa
import WindowsAzureMessaging

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate, MSNotificationHubDelegate, NSUserNotificationCenterDelegate {

    var connectionString: String?
    var hubName: String?

    func applicationDidFinishLaunching(_ aNotification: Notification) {
        if let path = Bundle.main.path(forResource: "DevSettings", ofType: "plist") {
            if let configValues = NSDictionary(contentsOfFile: path) {
                connectionString = configValues["CONNECTION_STRING"] as? String
                hubName = configValues["HUB_NAME"] as? String

                NSUserNotificationCenter.default.delegate = self
                MSNotificationHub.setDelegate(self)
                MSNotificationHub.start(connectionString: connectionString!, hubName: hubName!)

                return
            }
        }

        NSLog("Please setup CONNECTION_STRING and HUB_NAME in DevSettings.plist and restart application")

        exit(-1)
    }

    func notificationHub(_ notificationHub: MSNotificationHub!, didReceivePushNotification message: MSNotificationHubMessage!) {

        let userInfo = ["message": message!]
        NotificationCenter.default.post(name: NSNotification.Name("MessageReceived"), object: nil, userInfo: userInfo)
    }

    func userNotificationCenter(_ center: NSUserNotificationCenter, didActivate notification: NSUserNotification) {
        NSLog("Did activate notification");
    }

    func applicationWillTerminate(_ aNotification: Notification) {
        // Insert code here to tear down your application
    }

}


//----------------------------------------------------------------
//  Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------

import Cocoa
import WindowsAzureMessaging

class NotificationsTableViewController: NSObject {
    var notifications = [MSNotificationHubMessage]()

    func addNotification(_ newNotification: MSNotificationHubMessage) {
        self.notifications.append(newNotification);
    }
}

extension NotificationsTableViewController : NSTableViewDelegate {
    func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
        var cell: NSTableCellView?

        let titleCellIdentifier = NSUserInterfaceItemIdentifier(rawValue: "TitleNotificationCell")
        let bodyCellIdentifier = NSUserInterfaceItemIdentifier(rawValue: "BodyNotificationCell")

        if (tableColumn == tableView.tableColumns[0]) {
            cell = tableView.makeView(withIdentifier:titleCellIdentifier, owner: nil) as? NSTableCellView
            cell?.textField?.stringValue = notifications[row].title ?? "<no title>"
        } else if (tableColumn == tableView.tableColumns[1]) {
            cell = tableView.makeView(withIdentifier:bodyCellIdentifier, owner: nil) as? NSTableCellView
            cell?.textField?.stringValue = notifications[row].body
        }

        return cell
    }
}

extension NotificationsTableViewController : NSTableViewDataSource {
    func numberOfRows(in tableView: NSTableView) -> Int {
        return notifications.count;
    }
}


//----------------------------------------------------------------
//  Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------

import Cocoa

class TagsTableViewController: NSObject {
    var tags = [String]()

    init(data : Array<String>) {
        super.init()
        self.addTags(newTags: data)
    }

    func addTags(newTags: Array<String>) {
        self.tags = newTags
    }
}

extension TagsTableViewController : NSTableViewDelegate {
    func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
        let cellIdentifier = NSUserInterfaceItemIdentifier(rawValue: "TagsCellID")
        if let cell = tableView.makeView(withIdentifier:cellIdentifier, owner: nil) as? NSTableCellView {
            cell.textField?.stringValue = tags[row]
            return cell
        } else {
            return nil
        }
    }
}

extension TagsTableViewController : NSTableViewDataSource {
    func numberOfRows(in tableView: NSTableView) -> Int {
        return tags.count;
    }
}


//----------------------------------------------------------------
//  Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------

import Cocoa
import WindowsAzureMessaging
import Carbon.HIToolbox

class ViewController: NSViewController, NSTextFieldDelegate {

    @IBOutlet weak var deviceTokenTextField: NSTextField!
    @IBOutlet weak var installationIdTextField: NSTextField!
    @IBOutlet weak var tagsTextField: NSTextField!
    @IBOutlet weak var tagsTable: NSTableView!
    @IBOutlet weak var notificationsTable: NSTableView!
    @IBOutlet weak var userIdTextField: NSTextField!

    var tagsTableViewController: TagsTableViewController!
    var notificationsTableViewController: NotificationsTableViewController!

    override func viewDidLoad() {
        super.viewDidLoad()
        self.tagsTextField.delegate = self
        self.userIdTextField.delegate = self

        deviceTokenTextField.stringValue = MSNotificationHub.getPushChannel()
        installationIdTextField.stringValue = MSNotificationHub.getInstallationId()

        self.userIdTextField.placeholderString = MSNotificationHub.getUserId()

        self.tagsTableViewController = TagsTableViewController(data: MSNotificationHub.getTags())
        self.tagsTable.delegate = self.tagsTableViewController
        self.tagsTable.dataSource = self.tagsTableViewController

        self.notificationsTableViewController = NotificationsTableViewController()
        self.notificationsTable.delegate = self.notificationsTableViewController
        self.notificationsTable.dataSource = self.notificationsTableViewController

        self.tagsTable.reloadData()
        self.notificationsTable.reloadData()

        NotificationCenter.default.addObserver(self, selector: #selector(self.didReceivePushNotification), name: Notification.Name("MessageReceived"), object: nil)
    }

    deinit {
        NotificationCenter.default.removeObserver(self, name: Notification.Name("MessageReceived"), object: nil)
    }

    override var representedObject: Any? {
        didSet {
        // Update the view, if already loaded.
        }
    }

    func control(_ control: NSControl, textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool {
        if (commandSelector == #selector(NSResponder.insertNewline(_:))) {
            if (control == self.tagsTextField) {
                updateTags()
            }

            if (control == self.userIdTextField) {
                updateUserId()
            }

            return true
        }

        return false
    }

    func updateTags() {
        let tag = self.tagsTextField.stringValue;

        if (tag != "") {
            MSNotificationHub.addTag(tag)
            self.tagsTextField.stringValue = ""
        }

        self.tagsTableViewController.addTags(newTags: MSNotificationHub.getTags())
        self.tagsTable.reloadData()
    }

    func updateUserId() {
        if (self.userIdTextField.stringValue != "") {
            MSNotificationHub.setUserId(self.userIdTextField.stringValue)
            self.userIdTextField.stringValue = "";
            self.userIdTextField.placeholderString = MSNotificationHub.getUserId()
        }
    }

    override func keyDown(with event: NSEvent) {
        if(!event.modifierFlags.isEmpty) {
            if (event.keyCode == kVK_ForwardDelete || event.keyCode == kVK_Delete) {
                removeSelectedTags()
            }
            super.keyDown(with: event)
        }
    }

    func removeSelectedTags() {
        let selectedRow = self.tagsTable.selectedRow
        if (selectedRow >= 0) {
            if let cell = self.tagsTable.view(atColumn: 0, row: selectedRow, makeIfNecessary: true) as? NSTableCellView {
                let tagForRemoving = cell.textField?.stringValue ?? ""
                MSNotificationHub.removeTag(tagForRemoving)
                self.tagsTableViewController.addTags(newTags: MSNotificationHub.getTags())
                self.tagsTable.reloadData()
            }
        }
    }

    @objc func didReceivePushNotification(notification: Notification) {
        let message = notification.userInfo!["message"] as! MSNotificationHubMessage
        NSLog("Received notification: %@; %@", message.title ?? "<nil>", message.body)
        self.notificationsTableViewController.addNotification(message)
        self.notificationsTable.reloadData()
    }
}


//----------------------------------------------------------------
//  Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------

import Cocoa
import SwiftUI
import WindowsAzureMessaging

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate, MSNotificationHubDelegate, MSInstallationLifecycleDelegate, NSUserNotificationCenterDelegate {

    var window: NSWindow!

    var connectionString: String?
    var hubName: String?

    @ObservedObject var notifications: ObservableMessagesList = ObservableMessagesList(items: []);
    @ObservedObject var installation: ObservableInstallation = ObservableInstallation(installationId: MSNotificationHub.getInstallationId(), pushChannel: MSNotificationHub.getPushChannel())

    func applicationDidFinishLaunching(_ aNotification: Notification) {

        if let path = Bundle.main.path(forResource: "DevSettings", ofType: "plist") {
            if let configValues = NSDictionary(contentsOfFile: path) {
                connectionString = configValues["CONNECTION_STRING"] as? String
                hubName = configValues["HUB_NAME"] as? String

                if (!(connectionString ?? "").isEmpty && !(hubName ?? "").isEmpty)
                {
                    NSUserNotificationCenter.default.delegate = self
                    MSNotificationHub.start(connectionString: connectionString!, hubName: hubName!)

                    addTags()
                } else {
                    NSLog("Please setup CONNECTION_STRING and HUB_NAME in DevSettings.plist and restart application")

                    exit(-1)
                }
            }
        }

        MSNotificationHub.setDelegate(self)
        MSNotificationHub.setLifecycleDelegate(self)

        // Create the SwiftUI view that provides the window contents.
        let contentView = ContentView(notifications: notifications, installation: installation)

        // Create the window and set the content view.
        window = NSWindow(
            contentRect: NSRect(x: 0, y: 0, width: 480, height: 300),
            styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
            backing: .buffered, defer: false)
        window.center()
        window.setFrameAutosaveName("Main Window")
        window.contentView = NSHostingView(rootView: contentView)
        window.makeKeyAndOrderFront(nil)
    }

    func applicationWillTerminate(_ aNotification: Notification) {
        // Insert code here to tear down your application
    }

    // Adds some basic tags such as language and country
    func addTags() {
        // Get language and country code for common tag values
        let language = Bundle.main.preferredLocalizations.first!
        let countryCode = NSLocale.current.regionCode!

        // Create tags with type_value format
        let languageTag = "language_" + language
        let countryCodeTag = "country_" + countryCode

        MSNotificationHub.addTags([languageTag, countryCodeTag])
        MSNotificationHub.addTag("com.example.SampleNHAppMacSwiftUI:1.0.0");
    }

    func notificationHub(_ notificationHub: MSNotificationHub!, didReceivePushNotification notification: MSNotificationHubMessage!) {

        NSLog("Received notification: %@; %@", notification.title ?? "<nil>", notification.body)

        self.notifications.items.append(notification)
    }

    func notificationHub(_ notificationHub: MSNotificationHub, didSave installation: MSInstallation) {

        DispatchQueue.main.async {
            self.installation.installationId = installation.installationId
            self.installation.pushChannel = installation.pushChannel
        }
    }

}


//----------------------------------------------------------------
//  Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------

import SwiftUI
import WindowsAzureMessaging

struct ContentView: View {
    @State var tag: String = ""
    @State var tags: [String] = MSNotificationHub.getTags()
    @State var userId: String = MSNotificationHub.getUserId()

    @ObservedObject var notifications: ObservableMessagesList
    @ObservedObject var installation: ObservableInstallation

    var body: some View {
        VStack(alignment: .leading) {
            Group {
                Text("Device Token:")
                    .font(.headline)
                Text(installation.pushChannel)
                    .font(.footnote)
                    .foregroundColor(Color.gray)
                    .padding(.bottom)
            }

            Group {
                Text("Installation ID:")
                    .font(.headline)
                Text(installation.installationId)
                    .font(.footnote)
                    .foregroundColor(Color.gray)
                    .padding(.bottom)
            }

            Group {
                Text("User ID:")
                    .font(.headline)
                TextField("Set User ID", text: $userId, onEditingChanged: {focus in
                    if(!focus) {
                        MSNotificationHub.setUserId(self.userId)
                    }
                })
                    .textFieldStyle(RoundedBorderTextFieldStyle())
            }

            Group {
                Text("Tags:")
                    .font(.headline)
                TextField("Add new tag", text: $tag, onCommit: {
                    if(self.tag != "") {
                        MSNotificationHub.addTag(self.tag)
                        self.tags.append(self.tag)
                        self.tag = ""
                    }
                })
                    .textFieldStyle(RoundedBorderTextFieldStyle())

                TagsList(tags: tags, onDelete: {
                    if let idx = self.tags.firstIndex(of: $0) {
                        self.tags.remove(at: idx)
                        MSNotificationHub.removeTag($0);
                    }
                })
            }

            Group {
                Text("Notifications:")
                    .font(.headline)
                NotificationsList(notifications: notifications)
            }

            Spacer()
        }
        .padding()
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView(notifications: ObservableMessagesList(items: []), installation: ObservableInstallation(installationId: "<installationId>", pushChannel: "<pushChannel>"))
    }
}


//----------------------------------------------------------------
//  Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------

import SwiftUI
import WindowsAzureMessaging

struct NotificationsList: View {
    @ObservedObject var notifications: ObservableMessagesList

    var body: some View {
        List {
            ForEach(notifications.items, id: \.self) { notification in
                Row(title: notification.title ?? "<nil_title>", message: notification.body ?? "<nil_body>")
            }
        }
    }
}

struct NotificationsList_Previews: PreviewProvider {
    static var previews: some View {
        NotificationsList(notifications: ObservableMessagesList(items: [MSNotificationHubMessage()]))
    }
}


//----------------------------------------------------------------
//  Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------

import Foundation
import WindowsAzureMessaging

class ObservableInstallation: ObservableObject {
    @Published var installationId: String
    @Published var pushChannel: String

    init(installationId: String, pushChannel: String) {
        self.installationId = installationId
        self.pushChannel = pushChannel
    }
}


//----------------------------------------------------------------
//  Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------

import Foundation
import WindowsAzureMessaging

class ObservableMessagesList: ObservableObject {
    @Published var items = [MSNotificationHubMessage]()

    init(items: [MSNotificationHubMessage]){
        self.items = items
    }
}


//----------------------------------------------------------------
//  Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------

import SwiftUI

struct Row: View {
    var title: String = "<nil_title>"
    var message: String = ""

    var body: some View {
        HStack {
            Text(title)
            .foregroundColor(Color.gray)
            Text(message)
            .foregroundColor(Color.gray)
            Spacer()
        }
    }
}

struct Row_Previews: PreviewProvider {
    static var previews: some View {
        Row()
            .previewLayout(.fixed(width: 300, height: 70))
    }
}


//----------------------------------------------------------------
//  Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------

import SwiftUI

struct TagsList: View {
    @State private var selection: String? = nil
    var tags: [String]
    var onDelete: (String) -> Void

    var body: some View {
        List(selection: $selection) {
            ForEach(tags, id: \.self) {
                Row(title: $0);
            }
        }
        .onDeleteCommand(perform: {
            self.onDelete(self.selection!)
        })
    }
}

struct TagsList_Previews: PreviewProvider {
    static var previews: some View {
        TagsList(tags: ["tag1", "tag2"], onDelete: {_ in })
    }
}


//----------------------------------------------------------------
//  Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------

import UIKit
import UserNotifications
import WindowsAzureMessaging

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, MSNotificationHubDelegate, UNUserNotificationCenterDelegate {

    private var notificationPresentationCompletionHandler: Any?
    private var notificationResponseCompletionHandler: Any?
    private var connectionString: String?
    private var hubName: String?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

        if let path = Bundle.main.path(forResource: "DevSettings", ofType: "plist") {
            if let configValues = NSDictionary(contentsOfFile: path) {
                connectionString = configValues["CONNECTION_STRING"] as? String
                hubName = configValues["HUB_NAME"] as? String

                if (!(connectionString ?? "").isEmpty && !(hubName ?? "").isEmpty)
                {
                    UNUserNotificationCenter.current().delegate = self;
                    MSNotificationHub.setDelegate(self)
                    MSNotificationHub.start(connectionString: connectionString!, hubName: hubName!)

                    addTags()

                    return true
                }
            }
        }

        NSLog("Please setup CONNECTION_STRING and HUB_NAME in DevSettings.plist and restart application")

        exit(-1)
    }

    // Adds some basic tags such as language and country
    func addTags() {
        // Get language and country code for common tag values
        let language = Bundle.main.preferredLocalizations.first!
        let countryCode = NSLocale.current.regionCode!

        // Create tags with type_value format
        let languageTag = "language_" + language
        let countryCodeTag = "country_" + countryCode

        MSNotificationHub.addTags([languageTag, countryCodeTag])
    }

    // MARK: UISceneSession Lifecycle

    func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
        // Called when a new scene session is being created.
        // Use this method to select a configuration to create the new scene with.
        return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
    }

    func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
        // Called when the user discards a scene session.
        // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
        // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
    }

    @available(iOS 10.0, *)
    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        self.notificationPresentationCompletionHandler = completionHandler;
    }

    @available(iOS 10.0, *)
    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
        self.notificationResponseCompletionHandler = completionHandler;
    }

    func notificationHub(_ notificationHub: MSNotificationHub!, didReceivePushNotification message: MSNotificationHubMessage!) {

        let userInfo = ["message": message!]
        NotificationCenter.default.post(name: NSNotification.Name("MessageReceived"), object: nil, userInfo: userInfo)

        if (UIApplication.shared.applicationState == .background) {
            NSLog("Notification received in the background")
        }

        if (notificationResponseCompletionHandler != nil) {
            NSLog("Tapped Notification")
        } else {
            NSLog("Notification received in the foreground")
        }

        // Call notification completion handlers.
        if (notificationResponseCompletionHandler != nil) {
            (notificationResponseCompletionHandler as! () -> Void)()
            notificationResponseCompletionHandler = nil
        }
        if (notificationPresentationCompletionHandler != nil) {
            (notificationPresentationCompletionHandler as! (UNNotificationPresentationOptions) -> Void)([])
            notificationPresentationCompletionHandler = nil
        }
    }

}


//----------------------------------------------------------------
//  Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------

import UIKit

class CopyableLabel: UILabel {

    override init(frame: CGRect) {
        super.init(frame: frame)
        sharedInit()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        sharedInit()
    }

    func sharedInit() {
        isUserInteractionEnabled = true
        addGestureRecognizer(UILongPressGestureRecognizer(
            target: self,
            action: #selector(showMenu(sender:))
        ))
    }

    override public var canBecomeFirstResponder: Bool {
        get {
            return true
        }
    }

    override func copy(_ sender: Any?) {
        UIPasteboard.general.string = text
        UIMenuController.shared.setMenuVisible(false, animated: true)
    }

    @objc func showMenu(sender: Any?) {
        becomeFirstResponder()
        let menu = UIMenuController.shared

        if !menu.isMenuVisible {
            menu.setTargetRect(bounds, in: self)
            menu.setMenuVisible(true, animated: true)
        }
    }

    override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
        return action == #selector(UIResponderStandardEditActions.copy)
    }

}


//----------------------------------------------------------------
//  Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------

import UIKit

class NotificationDetailsViewController: UIViewController {
    @IBOutlet weak var summaryLabel: UILabel!
    @IBOutlet weak var detailsLabel: UILabel!

    var notification:MSNotificationHubMessage!

    override func viewDidLoad() {
        super.viewDidLoad()
        summaryLabel.text = notification.title
        detailsLabel.text = notification.body
        detailsLabel.sizeToFit()
    }
}


//----------------------------------------------------------------
//  Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------

import UIKit

class NotificationsTableViewController: UITableViewController {
    var notifications:[MSNotificationHubMessage] = [];

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    }

    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return notifications.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cellIdentifier = "NotificationCell"

        guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? NotificationTableViewCell else {
            fatalError("The dequeued cell is not an instance of NotificationCell.")
        }

        cell.notificationSummaryLabel.text = notifications[indexPath.row].title ?? notifications[indexPath.row].body

        return cell
    }

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        super.prepare(for: segue, sender: sender)
        if segue.identifier == "showNotificationDetails" {
            let cell = sender as! UITableViewCell
            guard let indexPath = tableView.indexPath(for: cell) else {
                fatalError("The selected cell is not being displayed by the table")
            }

            let detailsView = segue.destination as! NotificationDetailsViewController
            detailsView.notification = notifications[indexPath.row]
            tableView.deselectRow(at: indexPath, animated: true)
        }
    }

    func addNotification(_ notification: MSNotificationHubMessage) {
        self.notifications.append(notification)
        self.tableView.reloadData()
    }
}


//----------------------------------------------------------------
//  Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------

import UIKit

class NotificationTableViewCell: UITableViewCell {
    @IBOutlet weak var notificationSummaryLabel: UILabel!

    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)

        // Configure the view for the selected state
    }

}


//----------------------------------------------------------------
//  Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------

import UIKit

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
        // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
        // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
        guard let _ = (scene as? UIWindowScene) else { return }
    }

    func sceneDidDisconnect(_ scene: UIScene) {
        // Called as the scene is being released by the system.
        // This occurs shortly after the scene enters the background, or when its session is discarded.
        // Release any resources associated with this scene that can be re-created the next time the scene connects.
        // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead).
    }

    func sceneDidBecomeActive(_ scene: UIScene) {
        // Called when the scene has moved from an inactive state to an active state.
        // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
    }

    func sceneWillResignActive(_ scene: UIScene) {
        // Called when the scene will move from an active state to an inactive state.
        // This may occur due to temporary interruptions (ex. an incoming phone call).
    }

    func sceneWillEnterForeground(_ scene: UIScene) {
        // Called as the scene transitions from the background to the foreground.
        // Use this method to undo the changes made on entering the background.
    }

    func sceneDidEnterBackground(_ scene: UIScene) {
        // Called as the scene transitions from the foreground to the background.
        // Use this method to save data, release shared resources, and store enough scene-specific state information
        // to restore the scene back to its current state.
    }

}


//----------------------------------------------------------------
//  Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------

import UIKit

class SetupViewController: UIViewController, UITextFieldDelegate, UITableViewDataSource, UITableViewDelegate {

    @IBOutlet weak var deviceTokenLabel: UILabel!
    @IBOutlet weak var installationIdLabel: UILabel!
    @IBOutlet weak var addNewTagTextField: UITextField!
    @IBOutlet weak var tagsTable: UITableView!
    @IBOutlet weak var userId: UITextField!

    var tags = MSNotificationHub.getTags()
    var notificationsTableView:NotificationsTableViewController?

    override func viewDidLoad() {
        super.viewDidLoad()
        addNewTagTextField.delegate = self
        tagsTable.delegate = self
        tagsTable.dataSource = self
        tagsTable.reloadData()
        userId.delegate = self

        deviceTokenLabel.text = MSNotificationHub.getPushChannel()
        installationIdLabel.text = MSNotificationHub.getInstallationId()
        userId.text = MSNotificationHub.getUserId()

        notificationsTableView = (self.tabBarController?.viewControllers?[1] as! UINavigationController).viewControllers[0] as? NotificationsTableViewController

        NotificationCenter.default.addObserver(self, selector: #selector(self.didReceivePushNotification), name: Notification.Name("MessageReceived"), object: nil);
    }

    deinit {
        NotificationCenter.default.removeObserver(self, name: Notification.Name("MessageReceived"), object: nil)
    }

    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        // Hide the keyboard.
        textField.resignFirstResponder()
        return true
    }

    func textFieldDidEndEditing(_ textField: UITextField) {
        if(textField.tag == 0) {
            MSNotificationHub.setUserId(textField.text!)
        } else if (textField.text != "") {
            MSNotificationHub.addTag(textField.text!)
            tags = MSNotificationHub.getTags()
            textField.text = ""
            tagsTable.reloadData()
        }
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return tags.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cellIdentifier = "TagCell"
        guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? TagTableViewCell  else {
            fatalError("The dequeued cell is not an instance of TagCell.")
        }

        cell.tagLabel.text = tags[indexPath.row]

        return cell
    }

    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
      if editingStyle == .delete {

        MSNotificationHub.removeTag(tags[indexPath.row])
        tags = MSNotificationHub.getTags()
        tableView.deleteRows(at: [indexPath], with: .automatic)
        tagsTable.reloadData()
      }
    }

    @objc func didReceivePushNotification(notification: Notification) {
        let message = notification.userInfo!["message"] as! MSNotificationHubMessage;
        NSLog("Received notification: %@; %@", message.title ?? "<nil>", message.body)
        notificationsTableView?.addNotification(message);

        let alertController = UIAlertController(title: message.title, message: message.body, preferredStyle: .alert)
        alertController.addAction(UIAlertAction(title: "OK", style: .cancel))
        self.present(alertController, animated: true, completion: nil)

        DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
            alertController.dismiss(animated: true, completion: nil)
        }
    }

}


//----------------------------------------------------------------
//  Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------

import UIKit

class TagTableViewCell: UITableViewCell {
    @IBOutlet weak var tagLabel: UILabel!

    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)

        // Configure the view for the selected state
    }
}


//----------------------------------------------------------------
//  Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------

import UIKit
import UserNotifications
import WindowsAzureMessaging

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, MSNotificationHubDelegate, UNUserNotificationCenterDelegate {

    private var connectionString: String?
    private var hubName: String?
    private var notificationPresentationCompletionHandler: Any?
    private var notificationResponseCompletionHandler: Any?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

        if let path = Bundle.main.path(forResource: "DevSettings", ofType: "plist") {
            if let configValues = NSDictionary(contentsOfFile: path) {
                connectionString = configValues["CONNECTION_STRING"] as? String
                hubName = configValues["HUB_NAME"] as? String

                if (!(connectionString ?? "").isEmpty && !(hubName ?? "").isEmpty)
                {
                    UNUserNotificationCenter.current().delegate = self;
                    MSNotificationHub.setDelegate(self)
                    MSNotificationHub.start(connectionString: connectionString!, hubName: hubName!)

                    addTags()

                    return true
                }
            }
        }

        NSLog("Please setup CONNECTION_STRING and HUB_NAME in DevSettings.plist and restart application")

        exit(-1)
    }

    // Adds some basic tags such as language and country
    func addTags() {
        // Get language and country code for common tag values
        let language = Bundle.main.preferredLocalizations.first ?? "<undefined>"
        let countryCode = NSLocale.current.regionCode ?? "<undefined>"

        // Create tags with type_value format
        let languageTag = "language_" + language
        let countryCodeTag = "country_" + countryCode

        MSNotificationHub.addTags([languageTag, countryCodeTag])
    }

    // MARK: UISceneSession Lifecycle

    func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
        // Called when a new scene session is being created.
        // Use this method to select a configuration to create the new scene with.
        return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
    }

    func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
        // Called when the user discards a scene session.
        // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
        // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
    }

    @available(iOS 10.0, *)
    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        self.notificationPresentationCompletionHandler = completionHandler;
    }

    @available(iOS 10.0, *)
    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
        self.notificationResponseCompletionHandler = completionHandler;
    }

    func notificationHub(_ notificationHub: MSNotificationHub!, didReceivePushNotification message: MSNotificationHubMessage!) {

        let userInfo = ["message": message!]
        NotificationCenter.default.post(name: NSNotification.Name("MessageReceived"), object: nil, userInfo: userInfo)

        if (UIApplication.shared.applicationState == .background) {
            NSLog("Notification received in the background")
        }

        if (notificationResponseCompletionHandler != nil) {
            NSLog("Tapped Notification")
        } else {
            NSLog("Notification received in the foreground")
        }

        // Call notification completion handlers.
        if (notificationResponseCompletionHandler != nil) {
            (notificationResponseCompletionHandler as! () -> Void)()
            notificationResponseCompletionHandler = nil
        }
        if (notificationPresentationCompletionHandler != nil) {
            (notificationPresentationCompletionHandler as! (UNNotificationPresentationOptions) -> Void)([])
            notificationPresentationCompletionHandler = nil
        }
    }

}


//----------------------------------------------------------------
//  Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------

import SwiftUI
import WindowsAzureMessaging

struct ContentView: View {
    @State private var selection = 0
    @ObservedObject var notifications: ObservableMessagesList
    @ObservedObject var installation: ObservableInstallation

    var body: some View {
        TabView(selection: $selection){
            SetupView(installation: installation)
                .tabItem {
                    VStack {
                        Image(systemName: "wrench.fill")
                        Text("Setup")
                    }
                }
                .tag(0)
            NotificationsList(notifications: notifications)
                .tabItem {
                    VStack {
                        Image(systemName: "tray.fill")
                        Text("Notifications")
                    }
                }
                .tag(1)
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView(notifications: ObservableMessagesList(items: []), installation: ObservableInstallation(installationId: "<installationId>", pushChannel: "<pushChannel>"))
    }
}


//----------------------------------------------------------------
//  Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------

import SwiftUI
import WindowsAzureMessaging

struct NotificationsList: View {
    @ObservedObject var notifications: ObservableMessagesList

    var body: some View {
        NavigationView {
            List {
                ForEach(notifications.items, id: \.self) { notification in
                    NavigationLink(destination: NotificationView(notification: notification)) {
                        Row(title: notification.title ?? notification.body ?? "row");
                    }
                }
            }
            .navigationBarHidden(true)
            .navigationBarTitle(Text("Notifications"))
        }
    }
}

struct NotificationsList_Previews: PreviewProvider {
    static var previews: some View {
        NotificationsList(notifications: ObservableMessagesList(items: [MSNotificationHubMessage()]))
    }
}


//----------------------------------------------------------------
//  Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------

import SwiftUI
import WindowsAzureMessaging

struct NotificationView: View {
    var notification: MSNotificationHubMessage;

    var body: some View {
        VStack() {
            HStack {
                Text(notification.body ?? "<Body is missing>")
                    .font(.footnote)
                    .foregroundColor(Color.gray)
                    .padding([.leading, .bottom, .trailing])
                Spacer()
            }

            Spacer()
        }
        .navigationBarTitle(Text(notification.title ?? "<Title is missing>").font(.headline))
    }
}

struct NotificationView_Previews: PreviewProvider {
    static var previews: some View {
        NotificationView( notification: MSNotificationHubMessage())
    }
}


//----------------------------------------------------------------
//  Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------

import SwiftUI

struct Row: View {
    var title: String

    var body: some View {
        HStack {
            Text(title)
            .foregroundColor(Color.gray)
            Spacer()
        }
    }
}

struct Row_Previews: PreviewProvider {
    static var previews: some View {
        Row(title: "Hello World!")
            .previewLayout(.fixed(width: 300, height: 70))
    }
}


//----------------------------------------------------------------
//  Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------

import UIKit
import SwiftUI
import WindowsAzureMessaging

class SceneDelegate: UIResponder, UIWindowSceneDelegate, MSNotificationHubDelegate, MSInstallationLifecycleDelegate {

    var window: UIWindow?
    @ObservedObject var notifications: ObservableMessagesList = ObservableMessagesList(items: []);
    @ObservedObject var installation: ObservableInstallation = ObservableInstallation(installationId: MSNotificationHub.getInstallationId(), pushChannel: MSNotificationHub.getPushChannel())

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
        // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
        // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
        // Create the SwiftUI view that provides the window contents.
        MSNotificationHub.setLifecycleDelegate(self)

        let contentView = ContentView(notifications: notifications, installation: installation)

        // Use a UIHostingController as window root view controller.
        if let windowScene = scene as? UIWindowScene {
            let window = UIWindow(windowScene: windowScene)
            window.rootViewController = UIHostingController(rootView: contentView)
            self.window = window
            window.makeKeyAndVisible()
        }

        NotificationCenter.default.addObserver(self, selector: #selector(self.didReceivePushNotification), name: Notification.Name("MessageReceived"), object: nil)
    }

    deinit {
        NotificationCenter.default.removeObserver(self, name: Notification.Name("MessageReceived"), object: nil)
    }

    func sceneDidDisconnect(_ scene: UIScene) {
        // Called as the scene is being released by the system.
        // This occurs shortly after the scene enters the background, or when its session is discarded.
        // Release any resources associated with this scene that can be re-created the next time the scene connects.
        // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead).
    }

    func sceneDidBecomeActive(_ scene: UIScene) {
        // Called when the scene has moved from an inactive state to an active state.
        // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
    }

    func sceneWillResignActive(_ scene: UIScene) {
        // Called when the scene will move from an active state to an inactive state.
        // This may occur due to temporary interruptions (ex. an incoming phone call).
    }

    func sceneWillEnterForeground(_ scene: UIScene) {
        // Called as the scene transitions from the background to the foreground.
        // Use this method to undo the changes made on entering the background.
    }

    func sceneDidEnterBackground(_ scene: UIScene) {
        // Called as the scene transitions from the foreground to the background.
        // Use this method to save data, release shared resources, and store enough scene-specific state information
        // to restore the scene back to its current state.
    }

    @objc func didReceivePushNotification(notification: Notification) {
        let message = notification.userInfo!["message"] as! MSNotificationHubMessage

        NSLog("Received notification: %@; %@", message.title ?? "<nil>", message.body)

        self.notifications.items.append(message)

        let alertController = UIAlertController(title: message.title, message: message.body, preferredStyle: .alert)
        alertController.addAction(UIAlertAction(title: "OK", style: .cancel))
        self.window?.rootViewController?.present(alertController, animated: true, completion: nil)

        DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
            alertController.dismiss(animated: true, completion: nil)
        }

    }

    func notificationHub(_ notificationHub: MSNotificationHub, didSave installation: MSInstallation) {

        DispatchQueue.main.async {
            self.installation.installationId = installation.installationId
            self.installation.pushChannel = installation.pushChannel
        }
    }
}


//----------------------------------------------------------------
//  Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------

import SwiftUI
import WindowsAzureMessaging

struct SetupView: View {
    @State var tag: String = "";
    @State var tags: [String] = MSNotificationHub.getTags()
    @ObservedObject var installation: ObservableInstallation
    @State var userId: String = MSNotificationHub.getUserId()

    var body: some View {
        VStack(alignment: .leading) {
            Text("Device Token:")
                .font(.headline)
                .padding(.leading)
            Text(installation.pushChannel)
                .font(.footnote)
                .foregroundColor(Color.gray)
                .padding([.leading, .bottom, .trailing])

            Text("Installation ID:")
                .font(.headline)
                .padding(.leading)
            Text(installation.installationId)
                .font(.footnote)
                .foregroundColor(Color.gray)
                .padding([.leading, .bottom, .trailing])

            Text("User ID:")
                .font(.headline)
                .padding(.leading)
            TextField("Set User ID", text: $userId, onEditingChanged: {focus in
                if(!focus) {
                    MSNotificationHub.setUserId(self.userId)
                }
            })
                .textFieldStyle(RoundedBorderTextFieldStyle())
                .padding([.leading, .bottom, .trailing])

            Text("Tags:")
                .font(.headline)
                .padding(.leading)
            TextField("Add new tag", text: $tag, onCommit: {
                if(self.tag != "") {
                    MSNotificationHub.addTag(self.tag)
                    self.tags.append(self.tag)
                    self.tag = ""
                }
            })
                .textFieldStyle(RoundedBorderTextFieldStyle())
                .padding([.leading, .bottom, .trailing])

            TagsList(tags: tags, onDelete: {
                $0.forEach({
                    MSNotificationHub.removeTag(self.tags.remove(at: $0));
                })
            })

            Spacer()
        }
    }
}

struct SetupView_Previews: PreviewProvider {
    static var previews: some View {
        SetupView(installation: ObservableInstallation(installationId: "<installationId>", pushChannel: "pushChannel"))
    }
}


//----------------------------------------------------------------
//  Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------

import SwiftUI

struct TagsList: View {
    var tags: [String]
    var onDelete: (IndexSet) -> Void

    var body: some View {
        List {
            ForEach(tags, id: \.self) {
                Row(title: $0);
            }
            .onDelete(perform: {
                self.onDelete($0)
            })
        }
    }
}

struct TagsList_Previews: PreviewProvider {
    static var previews: some View {
        TagsList(tags: ["tag1", "tag2"], onDelete: {_ in })
    }
}


//----------------------------------------------------------------
//  Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------

import SwiftUI
import WindowsAzureMessaging

struct ContentView: View {
    @EnvironmentObject var notificationViewModel: NotificationViewModel
    @State private var showingAlert = false
    @State var notification: MSNotificationHubMessage = MSNotificationHubMessage()
    @State private var selection = 0

    var body: some View {
        TabView(selection: $selection){
            SetupView()
                .tabItem {
                    VStack {
                        Image(systemName: "wrench.fill")
                        Text("Setup")
                    }
                }
                .tag(0)
            NotificationsList()
                .tabItem {
                    VStack {
                        Image(systemName: "tray.fill")
                        Text("Notifications")
                    }
                }
                .tag(1)
        }
        .onReceive(self.notificationViewModel.messageReceived) { (notification) in
            self.didReceivePushNotification(notification: notification, messageTapped: false)
        }
        .onReceive(self.notificationViewModel.messageTapped) { (notification) in
            self.didReceivePushNotification(notification: notification, messageTapped: true)
        }
        .alert(isPresented: $showingAlert) {
            Alert(title: Text(self.notification.title ?? "Important message"), message: Text(self.notification.body ?? "Wear sunscreen"), dismissButton: .default(Text("Got it!")))
        }
        .environmentObject(notificationViewModel)
    }

    func didReceivePushNotification(notification: Notification, messageTapped: Bool) {
        let message = notification.userInfo!["message"] as! MSNotificationHubMessage
        NSLog("Received notification: %@; %@", message.title ?? "<nil>", message.body)

        // Assign the latest notification to self.notification.
        self.notification = message

        // Display Alert if message is tapped from background.
        if messageTapped {
            self.showingAlert = true
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
            .environmentObject(NotificationViewModel())
    }
}


//----------------------------------------------------------------
//  Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------

import SwiftUI
import UserNotifications
import WindowsAzureMessaging

@main
struct SampleNHAppSwiftUI2App: App {
    @StateObject private var notificationViewModel = NotificationViewModel()
    @Environment(\.scenePhase) private var scenePhase

    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(notificationViewModel)
        }
        .onChange(of: scenePhase) { (newScenePhase) in
            switch newScenePhase {
            case .active:
                print("scene is now active!")
            case .inactive:
                print("scene is now inactive!")
            case .background:
                print("scene is now in the background!")
            @unknown default:
                print("Apple must have added something new!")
            }
        }
    }
}


//----------------------------------------------------------------
//  Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------

import UserNotifications
import WindowsAzureMessaging

class NotificationViewModel: NSObject, ObservableObject, MSNotificationHubDelegate, MSInstallationLifecycleDelegate, UNUserNotificationCenterDelegate {
    private var notificationPresentationCompletionHandler: Any?
    private var notificationResponseCompletionHandler: Any?

    @Published var installationId: String = MSNotificationHub.getInstallationId()
    @Published var pushChannel: String = MSNotificationHub.getPushChannel()
    @Published var items = [MSNotificationHubMessage]()

    let messageReceived = NotificationCenter.default
                .publisher(for: NSNotification.Name("MessageReceived"))

    let messageTapped = NotificationCenter.default
                .publisher(for: NSNotification.Name("MessageTapped"))

    override init() {
        super.init()

        if let path = Bundle.main.path(forResource: "DevSettings", ofType: "plist") {
            if let configValues = NSDictionary(contentsOfFile: path) {
                let connectionString = configValues["CONNECTION_STRING"] as? String
                let hubName = configValues["HUB_NAME"] as? String

                if (!(connectionString ?? "").isEmpty && !(hubName ?? "").isEmpty)
                {
                    MSNotificationHub.setLifecycleDelegate(self)
                    UNUserNotificationCenter.current().delegate = self;
                    MSNotificationHub.setDelegate(self)
                    MSNotificationHub.start(connectionString: connectionString!, hubName: hubName!)

                    addTags()
                }
            }
        }
    }

    func getPlistInfo(resourceName: String, key: String) -> String {
        guard let value = Bundle.main.path(forResource: resourceName, ofType: "plist") else {
            return ""
        }
        return value
    }

    func addTags() {
        // Get language and country code for common tag values
        let language = Bundle.main.preferredLocalizations.first ?? "<undefined>"
        let countryCode = NSLocale.current.regionCode ?? "<undefined>"

        // Create tags with type_value format
        let languageTag = "language_" + language
        let countryCodeTag = "country_" + countryCode

        MSNotificationHub.addTags([languageTag, countryCodeTag])
    }

    @available(iOS 10.0, *)
    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        self.notificationPresentationCompletionHandler = completionHandler;
    }

    @available(iOS 10.0, *)
    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
        self.notificationResponseCompletionHandler = completionHandler;
    }

    func notificationHub(_ notificationHub: MSNotificationHub, didSave installation: MSInstallation) {
        DispatchQueue.main.async {
            self.installationId = installation.installationId
            self.pushChannel = installation.pushChannel
        }
    }

    func notificationHub(_ notificationHub: MSNotificationHub!, didReceivePushNotification message: MSNotificationHubMessage!) {

        let userInfo = ["message": message!]

        // Append receivedPushNotification message to self.items
        self.items.append(message)

        if (notificationResponseCompletionHandler != nil) {
            NSLog("Tapped Notification")
            NotificationCenter.default.post(name: NSNotification.Name("MessageTapped"), object: nil, userInfo: userInfo)
        } else {
            NSLog("Notification received in the foreground")
            NotificationCenter.default.post(name: NSNotification.Name("MessageReceived"), object: nil, userInfo: userInfo)
        }

        // Call notification completion handlers.
        if (notificationResponseCompletionHandler != nil) {
            (notificationResponseCompletionHandler as! () -> Void)()
            notificationResponseCompletionHandler = nil
        }
        if (notificationPresentationCompletionHandler != nil) {
            (notificationPresentationCompletionHandler as! (UNNotificationPresentationOptions) -> Void)([])
            notificationPresentationCompletionHandler = nil
        }
    }

}


//----------------------------------------------------------------
//  Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------

import SwiftUI
import WindowsAzureMessaging

struct NotificationsList: View {
    @EnvironmentObject var notificationViewModel: NotificationViewModel

    var body: some View {
        List {
            ForEach(self.notificationViewModel.items, id: \.self) { notification in
                Row(title: notification.title ?? "<nil_title>", message: notification.body ?? "<nil_body>")
            }
        }
    }
}

struct NotificationsList_Previews: PreviewProvider {
    static var previews: some View {
        NotificationsList()
            .environmentObject(NotificationViewModel())
    }
}


//----------------------------------------------------------------
//  Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------

import SwiftUI

struct Row: View {
    var title: String = "<nil_title>"
    var message: String = ""

    var body: some View {
        HStack {
            Text(title)
            .foregroundColor(Color.gray)
            Text(message)
            .foregroundColor(Color.gray)
            Spacer()
        }
    }
}

struct Row_Previews: PreviewProvider {
    static var previews: some View {
        Row()
            .previewLayout(.fixed(width: 300, height: 70))
    }
}