Adds firebase live data

Also no longer crashes when touching something while loading
This commit is contained in:
Patrick McDonagh
2018-05-31 19:55:47 -05:00
parent c94cdc1369
commit 95467f9161
463 changed files with 62157 additions and 2177 deletions

View File

@@ -11,6 +11,7 @@ import PromiseKit
import Alamofire
import SwiftyJSON
import RealmSwift
import Firebase
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
@@ -35,195 +36,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Chameleon.setGlobalThemeUsingPrimaryColor(UIColor.flatSkyBlue(), with: UIContentStyle.contrast)
// print(Realm.Configuration.defaultConfiguration.fileURL)
FirebaseApp.configure()
return true
}
//MARK: - Data Methods
// func getAddresses() ->Promise<Any> {
// let realm = try! Realm()
// let url = "\(baseURL)/addresses"
// let headers : HTTPHeaders = [
// "Authorization": user!.authToken
// ]
//
// return Promise { promise in
// Alamofire.request(url, method: .get, headers: headers)
// .validate()
// .responseJSON { response in
// switch response.result {
//
// case .success:
// let addressesJSON : JSON = JSON(response.result.value!)
// for(_, json) : (String, JSON) in addressesJSON {
// do {
// try realm.write {
// let addr = Address()
// addr.id = json["id"].intValue
// addr.apartment = json["id"].stringValue
// addr.city = json["city"].stringValue
// addr.country = json["country"].stringValue
// addr.lat = json["lat"].doubleValue
// addr.long = json["long"].doubleValue
// addr.state = json["state"].stringValue
// addr.street1 = json["street1"].stringValue
// addr.street2 = json["street2"].stringValue
// addr.zip = json["zip"].stringValue
// addr.zoneId = json["zoneid"].intValue
// realm.add(addr, update: true)
// }
// } catch {
// promise.reject(error)
// }
// }
// promise.fulfill(true)
// case .failure(let error):
// promise.reject(error)
// }
// }
// }
//
// }
//
// func getDevices(gateways : Results<Gateway>, deviceTypes : Results<DeviceType>) ->Promise<Any> {
// let realm = try! Realm()
// let url = "\(baseURL)/devices"
// let headers : HTTPHeaders = [
// "Authorization": user!.authToken
// ]
//
// return Promise { promise in
// Alamofire.request(url, method: .get, headers: headers)
// .validate()
// .responseJSON { response in
//
// switch response.result {
//
// case .success:
// let devicesJSON : JSON = JSON(response.result.value!)
// for(_, json) : (String, JSON) in devicesJSON {
// do {
// try realm.write {
// let dev = Device()
// dev.companyId = json["companyId"].intValue
// dev.deviceTypeId = json["deviceTypeId"].intValue
// dev.gatewayId = json["gatewayId"].intValue
// dev.id = json["id"].intValue
// dev.macAddress = json["macAddress"].stringValue
// dev.techName = json["techname"].stringValue
// dev.tenantId = json["tenantId"].intValue
// dev.vanityName = json["vanityName"].stringValue
//
// let pGateway = gateways.filter("id == %d", dev.gatewayId).first
// let pDeviceType = deviceTypes.filter("id == %d", dev.deviceTypeId).first
//
// realm.add(dev, update: true)
// pGateway?.devices.append(dev)
// pDeviceType?.devices.append(dev)
// }
// } catch {
// promise.reject(error)
// }
// }
// promise.fulfill(true)
// case .failure(let error):
// promise.reject(error)
// }
// }
// }
//
// }
//
// func getGateways(addresses : Results<Address>) -> Promise<Any> {
//
// let realm = try! Realm()
// let url = "\(baseURL)/gateways"
// let headers : HTTPHeaders = [
// "Authorization": user!.authToken
// ]
//
// return Promise { promise in
// Alamofire.request(url, method: .get, headers: headers)
// .responseJSON { response in
// switch response.result {
//
// case .success:
// let gatewaysJSON : JSON = JSON(response.result.value!)
// for(_, json) : (String, JSON) in gatewaysJSON {
//
// do {
// try realm.write {
// let gtw = Gateway()
// gtw.addressId = json["addressId"].intValue
// gtw.companyId = json["companyId"].intValue
// gtw.diaVersion = json["diaVersion"].stringValue
// gtw.gatewayConfigurationId = json["gatewayConfigurationId"].intValue
// gtw.gatewayTypeId = json["gatewayTypeId"].intValue
// gtw.id = json["id"].intValue
// gtw.macAddress = json["macAddress"].stringValue
// gtw.name = json["name"].stringValue
// gtw.panId = json["panId"].stringValue
// gtw.tenantId = json["tenantId"].intValue
// gtw.userId = json["userId"].intValue
//
// let associatedAddress = addresses.filter("id == %d", gtw.addressId).first
//
// gtw.address = associatedAddress
// realm.add(gtw, update: true)
// }
// } catch {
// promise.reject(error)
// }
// }
// promise.fulfill(true)
// case .failure(let error):
// promise.reject(error)
// }
// }
// }
// }
//
//
// func getDeviceTypes() -> Promise<Any> {
// let realm = try! Realm()
// let url = "\(baseURL)/devicetypes"
// let headers : HTTPHeaders = [
// "Authorization": user!.authToken
// ]
//
// return Promise { promise in
// Alamofire.request(url, method: .get, headers: headers)
// .responseJSON { response in
// switch response.result {
//
// case .success:
// let deviceTypesJson : JSON = JSON(response.result.value!)
// for(_, json) : (String, JSON) in deviceTypesJson {
// do {
// try realm.write {
// let dt = DeviceType()
// dt.companyId = json["CompanyId"].intValue
// dt.id = json["id"].intValue
// dt.imgUrl = json["imgUrl"].stringValue
// dt.name = json["name"].stringValue
// dt.note = json["note"].stringValue
// dt.tenantId = json["tenantId"].intValue
// dt.vanityName = json["vanityName"].stringValue
// realm.add(dt, update: true)
// }
// } catch {
// promise.reject(error)
// }
// }
// promise.fulfill(true)
// case .failure(let error):
// promise.reject(error)
// }
// }
// }
// }
}

View File

@@ -12,6 +12,7 @@ import PromiseKit
import RealmSwift
import SwiftyJSON
import SVProgressHUD
import FirebaseDatabase
class DeviceDetailViewController: UITableViewController {
let realm = try! Realm()
@@ -21,10 +22,18 @@ class DeviceDetailViewController: UITableViewController {
var thisDevice : Device?
var deviceTypes : Results<DeviceType>?
var selectedChannel : Channel?
var channelLookup : [String : Int] = [:]
var values : [String : MeshifyValue] = [:]
var updatedChannelNames : [String] = [String]()
var changedChannelNames : [String] = [String]()
override func viewDidLoad() {
super.viewDidLoad()
let macAddress = String((thisDevice?.macAddress.replacingOccurrences(of: ":", with: "").uppercased().dropLast(4))!)
let deviceTypeName = (thisDevice?.parentDeviceType.first?.name)!
SVProgressHUD.show()
firstly {
loadData()
@@ -34,6 +43,8 @@ class DeviceDetailViewController: UITableViewController {
print("Error in loadData: \(error)")
}
self.navigationItem.title = thisDevice?.vanityName
self.refreshControl = UIRefreshControl()
@@ -45,17 +56,74 @@ class DeviceDetailViewController: UITableViewController {
getChannels(deviceTypeId: thisDevice!.deviceTypeId, baseURL: baseURL, authToken: (user?.authToken)!)
}.then { _ in
getChannelValues(deviceId: self.thisDevice!.id, baseURL: self.baseURL, authToken: (self.user?.authToken)!)
}.done { _ in
}.done { (valueDict) in
self.values = valueDict
self.tableView.reloadData()
SVProgressHUD.dismiss()
self.refreshControl?.endRefreshing()
if let dev = self.thisDevice {
for chanIndex in 0..<dev.values.count {
let chanName = dev.values[chanIndex].name
self.channelLookup[chanName] = chanIndex
}
}
self.getFirebaseDeviceValues(deviceMacAddress: macAddress, deviceTypeName: deviceTypeName)
}.catch { error in
print("Error fetching data in DeviceDetailViewController: \(error)")
}
}
override func viewWillDisappear(_ animated: Bool) {
Database.database().reference().removeAllObservers()
}
func getFirebaseDeviceValues(deviceMacAddress : String, deviceTypeName : String) {
// let getVals = Database.database().reference().child("devices")
// .child(deviceMacAddress)
// .child(deviceTypeName)
// .child("channels")
// .observe(.value) { snapshot in
// let snapValue = snapshot.value as! NSDictionary
//
//
// let chanVal = MeshifyValue()
// chanVal.name = (value["name"] as? String)!
// chanVal.timestamp = Int((value["timestamp"] as? String)!)!
// chanVal.value = (value["value"] as? String)!
// self.values[chanVal.name] = chanVal
// print("should set \(chanVal.value) as \(chanVal.name)")
// self.updatedChannelNames.append(chanVal.name)
// self.tableView.reloadData()
// }
// Database.database().reference().removeObserver(withHandle: getVals)
Database.database().reference().child("devices")
.child(deviceMacAddress)
.child(deviceTypeName)
.child("channels")
.observe(.childChanged) { snapshot in
let value = snapshot.value as! NSDictionary
let chanVal = MeshifyValue()
chanVal.name = (value["name"] as? String)!
chanVal.timestamp = Int((value["timestamp"] as? String)!)!
chanVal.value = (value["value"] as? String)!
let prevChanValue = self.values[chanVal.name]?.value
if prevChanValue != chanVal.value {
self.changedChannelNames.append(chanVal.name)
} else {
self.updatedChannelNames.append(chanVal.name)
}
self.values[chanVal.name] = chanVal
self.tableView.reloadData()
}
}
@objc func refresh() {
updatedChannelNames.removeAll()
changedChannelNames.removeAll()
SVProgressHUD.show()
firstly {
getChannels(deviceTypeId: thisDevice!.deviceTypeId, baseURL: baseURL, authToken: (user?.authToken)!)
@@ -75,7 +143,8 @@ class DeviceDetailViewController: UITableViewController {
// MARK: - Table view data source
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (thisDevice?.parentDeviceType.first?.channels.count)!
// return (thisDevice?.parentDeviceType.first?.channels.count)!
return values.count
}
@@ -84,10 +153,34 @@ class DeviceDetailViewController: UITableViewController {
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "deviceDetailCell", for: indexPath) as! DeviceDetailChannelCell
cell.channelNameLabel?.text = thisDevice?.parentDeviceType.first?.channels[indexPath.row].subTitle
let chanName = thisDevice?.parentDeviceType.first?.channels[indexPath.row].name
let chanVal = thisDevice?.values.filter("name == %@", chanName!)
let chanVal = values[chanName!]
if let i = changedChannelNames.index(of: chanName!) {
changedChannelNames.remove(at: i)
cell.backgroundColor = UIColor.flatLime()
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
UIView.animate(withDuration: 1, animations: {
cell.backgroundColor = UIColor.white
})
}
}
if let i = updatedChannelNames.index(of: chanName!) {
updatedChannelNames.remove(at: i)
cell.backgroundColor = UIColor.flatYellow()
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
UIView.animate(withDuration: 1, animations: {
cell.backgroundColor = UIColor.white
})
}
}
cell.accessoryType = .disclosureIndicator
cell.channelValueLabel?.text = chanVal?.first?.value
cell.channelValueLabel?.text = chanVal?.value
return cell
}

View File

@@ -29,9 +29,9 @@ class DeviceListViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
// self.refreshControl = UIRefreshControl()
// refreshControl?.attributedTitle = NSAttributedString(string: "Pull to refresh")
// refreshControl?.addTarget(self, action: #selector(refresh), for: UIControlEvents.valueChanged)
self.refreshControl = UIRefreshControl()
refreshControl?.attributedTitle = NSAttributedString(string: "Pull to refresh")
refreshControl?.addTarget(self, action: #selector(refresh), for: UIControlEvents.valueChanged)
SVProgressHUD.show()
firstly {
@@ -66,21 +66,21 @@ class DeviceListViewController: UITableViewController {
}
// @objc func refresh() {
//
// SVProgressHUD.show()
// self.realm.refresh()
// firstly {
// self.loadJSONData()
// }.done { _ in
// self.tableView.reloadData()
// self.refreshControl?.endRefreshing()
// SVProgressHUD.dismiss()
// }.catch { error in
// print("Error in getting data in DeviceListViewController: \(error)")
// }
//
// }
@objc func refresh() {
SVProgressHUD.show()
self.realm.refresh()
firstly {
self.loadJSONData()
}.done { _ in
self.tableView.reloadData()
self.refreshControl?.endRefreshing()
SVProgressHUD.dismiss()
}.catch { error in
print("Error in getting data in DeviceListViewController: \(error)")
}
}
// MARK: - Table view data source
@@ -95,8 +95,8 @@ class DeviceListViewController: UITableViewController {
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
let deviceTypesWithDevices = deviceTypes?.filter(deviceTypeFilter)
return deviceTypesWithDevices?[section].vanityName ?? "Unknown"
let deviceTypesWithDevices = deviceTypes?.filter(deviceTypeFilter)
return ((deviceTypesWithDevices?.count)! > 0) ? deviceTypesWithDevices?[section].vanityName : "Unknown"
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
@@ -109,15 +109,20 @@ class DeviceListViewController: UITableViewController {
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "deviceListCell", for: indexPath)
let deviceTypesWithDevices = deviceTypes?.filter(deviceTypeFilter)
cell.textLabel?.text = deviceTypesWithDevices?[indexPath.section].devices[indexPath.row].vanityName
cell.accessoryType = .disclosureIndicator
if (deviceTypesWithDevices?.count)! > 0 {
cell.textLabel?.text = deviceTypesWithDevices?[indexPath.section].devices[indexPath.row].vanityName
cell.accessoryType = .disclosureIndicator
}
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let deviceTypesWithDevices = deviceTypes?.filter(deviceTypeFilter)
selectedDevice = deviceTypesWithDevices?[indexPath.section].devices[indexPath.row]
performSegue(withIdentifier: "openDeviceDetailView", sender: self)
if let thisSection = deviceTypesWithDevices?[indexPath.section] {
selectedDevice = thisSection.devices[indexPath.row]
// print(selectedDevice)
performSegue(withIdentifier: "openDeviceDetailView", sender: self)
}
}

View File

@@ -108,7 +108,7 @@ class LoginViewController: UIViewController {
let encoder = PropertyListEncoder()
do{
let data = try encoder.encode(user)
let data = try encoder.encode((UIApplication.shared.delegate as! AppDelegate).user)
try data.write(to: dataFilePath!)
} catch {
print("Error encoding item array, \(error)")

View File

@@ -247,8 +247,7 @@ func getChannels(deviceTypeId: Int, baseURL : String, authToken : String) -> Pro
}
func getChannelValues(deviceId: Int, baseURL : String, authToken : String) -> Promise<Any> {
let realm = try! Realm()
func getChannelValues(deviceId: Int, baseURL : String, authToken : String) -> Promise<[String : MeshifyValue]> {
let url = "\(baseURL)/devices/\(deviceId)/values"
let headers : HTTPHeaders = [
"Authorization": authToken
@@ -261,24 +260,29 @@ func getChannelValues(deviceId: Int, baseURL : String, authToken : String) -> Pr
case .success:
let channelValJSON : JSON = JSON(response.result.value!)
var valueDict : [String : MeshifyValue] = [:]
for(chName, json) : (String, JSON) in channelValJSON {
do {
try realm.write {
let chv = ChanValue()
chv.uid = "\(deviceId).\(chName)"
chv.name = chName
chv.value = json["value"].stringValue
chv.timestamp = json["timestamp"].intValue
realm.add(chv, update: true)
realm.objects(Device.self).filter("id == %d", deviceId).first?.values.append(chv)
}
} catch {
prom.reject(error)
}
// do {
// try realm.write {
// let chv = ChanValue()
// chv.uid = "\(deviceId).\(chName)"
// chv.name = chName
// chv.value = json["value"].stringValue
// chv.timestamp = json["timestamp"].intValue
//
// realm.add(chv, update: true)
// realm.objects(Device.self).filter("id == %d", deviceId).first?.values.append(chv)
// }
// } catch {
// prom.reject(error)
// }
let chv = MeshifyValue()
chv.name = chName
chv.value = json["value"].stringValue
chv.timestamp = json["timestamp"].intValue
valueDict[chv.name] = chv
}
prom.fulfill(true)
prom.fulfill(valueDict)
case .failure(let error):
prom.reject(error)
}
@@ -286,6 +290,7 @@ func getChannelValues(deviceId: Int, baseURL : String, authToken : String) -> Pr
}
}
func getChannelHistory(deviceId: Int, channelId : Int, baseURL : String, authToken : String) -> Promise<[ChannelHistoryValue]> {
let url = "\(baseURL)/devices/\(deviceId)/values/\(channelId)"
let headers : HTTPHeaders = [

View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>AD_UNIT_ID_FOR_BANNER_TEST</key>
<string>ca-app-pub-3940256099942544/2934735716</string>
<key>AD_UNIT_ID_FOR_INTERSTITIAL_TEST</key>
<string>ca-app-pub-3940256099942544/4411468910</string>
<key>CLIENT_ID</key>
<string>912892220088-1gkj9ms3fq60678tvgn5n1gndn7p4g9j.apps.googleusercontent.com</string>
<key>REVERSED_CLIENT_ID</key>
<string>com.googleusercontent.apps.912892220088-1gkj9ms3fq60678tvgn5n1gndn7p4g9j</string>
<key>API_KEY</key>
<string>AIzaSyCwSawi9ezxz6vqCWtPoHybsxJDkU3gh_E</string>
<key>GCM_SENDER_ID</key>
<string>912892220088</string>
<key>PLIST_VERSION</key>
<string>1</string>
<key>BUNDLE_ID</key>
<string>com.patrickjmcd.pocloud</string>
<key>PROJECT_ID</key>
<string>pocloud-ff2c9</string>
<key>STORAGE_BUCKET</key>
<string>pocloud-ff2c9.appspot.com</string>
<key>IS_ADS_ENABLED</key>
<true></true>
<key>IS_ANALYTICS_ENABLED</key>
<false></false>
<key>IS_APPINVITE_ENABLED</key>
<false></false>
<key>IS_GCM_ENABLED</key>
<true></true>
<key>IS_SIGNIN_ENABLED</key>
<true></true>
<key>GOOGLE_APP_ID</key>
<string>1:912892220088:ios:59660a786d897a5c</string>
<key>DATABASE_URL</key>
<string>https://pocloud-ff2c9.firebaseio.com</string>
</dict>
</plist>

View File

@@ -0,0 +1,15 @@
//
// MeshifyValue.swift
// pocloud
//
// Created by Patrick McDonagh on 5/31/18.
// Copyright © 2018 patrickjmcd. All rights reserved.
//
import Foundation
class MeshifyValue {
var name : String = ""
var value : String = ""
var timestamp : Int = 0
}

View File

@@ -644,6 +644,9 @@
</connections>
</barButtonItem>
</navigationItem>
<refreshControl key="refreshControl" opaque="NO" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" enabled="NO" contentHorizontalAlignment="center" contentVerticalAlignment="center" id="WnO-fc-pmd">
<autoresizingMask key="autoresizingMask"/>
</refreshControl>
<connections>
<segue destination="eOb-fR-aFg" kind="show" identifier="openDeviceDetailView" id="wUb-ND-zk7"/>
</connections>