REF: watchOS app code optimization

This commit is contained in:
Marcos Rodriguez Velez 2024-01-25 00:39:01 -04:00
parent b060c21e89
commit cfabf827fc
No known key found for this signature in database
GPG Key ID: 6030B2F48CCE86D7
16 changed files with 164 additions and 158 deletions

View File

@ -3,7 +3,7 @@
// BlueWalletWatch Extension // BlueWalletWatch Extension
// //
// Created by Marcos Rodriguez on 3/6/19. // Created by Marcos Rodriguez on 3/6/19.
// Copyright © 2019 Facebook. All rights reserved.
// //
import WatchKit import WatchKit

View File

@ -3,79 +3,72 @@
// BlueWalletWatch Extension // BlueWalletWatch Extension
// //
// Created by Marcos Rodriguez on 3/6/19. // Created by Marcos Rodriguez on 3/6/19.
// Copyright © 2019 Facebook. All rights reserved.
// //
import WatchKit import WatchKit
import WatchConnectivity import WatchConnectivity
import Foundation import Foundation
class InterfaceController: WKInterfaceController { class InterfaceController: WKInterfaceController, WCSessionDelegate {
@IBOutlet weak var walletsTable: WKInterfaceTable! @IBOutlet weak var walletsTable: WKInterfaceTable!
@IBOutlet weak var noWalletsAvailableLabel: WKInterfaceLabel! @IBOutlet weak var noWalletsAvailableLabel: WKInterfaceLabel!
override func awake(withContext context: Any?) { override func awake(withContext context: Any?) {
super.awake(withContext: context) super.awake(withContext: context)
if let contextUnwrapped = context as? [String: Any] { setupSession()
WatchDataSource.shared.processData(data: contextUnwrapped) processContextData(context)
}
if WCSession.isSupported() {
print("Activating watch session")
WCSession.default.delegate = self
WCSession.default.activate()
}
} }
override func willActivate() { override func willActivate() {
// This method is called when watch view controller is about to be visible to user
super.willActivate() super.willActivate()
updateUI()
if (WatchDataSource.shared.wallets.isEmpty) { NotificationCenter.default.addObserver(self, selector: #selector(updateUI), name: WatchDataSource.NotificationName.dataUpdated, object: nil)
noWalletsAvailableLabel.setHidden(false)
} else {
processWalletsTable()
}
NotificationCenter.default.addObserver(self, selector: #selector(processWalletsTable), name: WatchDataSource.NotificationName.dataUpdated, object: nil)
} }
@objc private func processWalletsTable() { private func setupSession() {
walletsTable.setNumberOfRows(WatchDataSource.shared.wallets.count, withRowType: WalletInformation.identifier) guard WCSession.isSupported() else { return }
WCSession.default.delegate = self
WCSession.default.activate()
}
for index in 0..<walletsTable.numberOfRows { private func processContextData(_ context: Any?) {
guard let controller = walletsTable.rowController(at: index) as? WalletInformation else { continue } guard let contextUnwrapped = context as? [String: Any] else { return }
let wallet = WatchDataSource.shared.wallets[index] WatchDataSource.shared.processData(data: contextUnwrapped)
if wallet.identifier == nil {
WatchDataSource.shared.wallets[index].identifier = index
} }
controller.walletBalanceLabel.setHidden(wallet.hideBalance)
controller.name = wallet.label @objc private func updateUI() {
controller.balance = wallet.hideBalance ? "" : wallet.balance let wallets = WatchDataSource.shared.wallets
controller.type = WalletGradient(rawValue: wallet.type) ?? .SegwitHD let isEmpty = wallets.isEmpty
noWalletsAvailableLabel.setHidden(!isEmpty)
walletsTable.setHidden(isEmpty)
if isEmpty { return }
walletsTable.setNumberOfRows(wallets.count, withRowType: WalletInformation.identifier)
for index in 0..<wallets.count {
updateRow(at: index, with: wallets[index])
} }
noWalletsAvailableLabel.setHidden(!WatchDataSource.shared.wallets.isEmpty) }
walletsTable.setHidden(WatchDataSource.shared.wallets.isEmpty)
private func updateRow(at index: Int, with wallet: Wallet) {
guard let controller = walletsTable.rowController(at: index) as? WalletInformation else { return }
controller.configure(with: wallet)
} }
override func contextForSegue(withIdentifier segueIdentifier: String, in table: WKInterfaceTable, rowIndex: Int) -> Any? { override func contextForSegue(withIdentifier segueIdentifier: String, in table: WKInterfaceTable, rowIndex: Int) -> Any? {
return rowIndex; return rowIndex
} }
}
extension InterfaceController: WCSessionDelegate {
func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any]) { func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any]) {
WatchDataSource.shared.processData(data: applicationContext) WatchDataSource.shared.processData(data: applicationContext)
} }
func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any] = [:]) { func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any] = [:]) {
WatchDataSource.shared.processData(data: userInfo) WatchDataSource.shared.processData(data: userInfo)
} }
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) { func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
WatchDataSource.shared.companionWalletsInitialized = activationState == .activated
if activationState == .activated { if activationState == .activated {
WatchDataSource.shared.processWalletsData(walletsInfo: WCSession.default.applicationContext) WatchDataSource.shared.processWalletsData(walletsInfo: WCSession.default.applicationContext)
} }
@ -90,3 +83,13 @@ extension InterfaceController: WCSessionDelegate {
} }
} }
// WalletInformation extension for configuration
extension WalletInformation {
func configure(with wallet: Wallet) {
walletBalanceLabel.setHidden(wallet.hideBalance)
name = wallet.label
balance = wallet.hideBalance ? "" : wallet.balance
type = WalletGradient(rawValue: wallet.type) ?? .SegwitHD
}
}

View File

@ -3,7 +3,7 @@
// BlueWalletWatch Extension // BlueWalletWatch Extension
// //
// Created by Marcos Rodriguez on 3/6/19. // Created by Marcos Rodriguez on 3/6/19.
// Copyright © 2019 Facebook. All rights reserved.
// //
import WatchKit import WatchKit

View File

@ -3,7 +3,7 @@
// BlueWalletWatch Extension // BlueWalletWatch Extension
// //
// Created by Marcos Rodriguez on 3/23/19. // Created by Marcos Rodriguez on 3/23/19.
// Copyright © 2019 Facebook. All rights reserved.
// //
import WatchKit import WatchKit

View File

@ -3,7 +3,7 @@
// BlueWalletWatch Extension // BlueWalletWatch Extension
// //
// Created by Marcos Rodriguez on 3/13/19. // Created by Marcos Rodriguez on 3/13/19.
// Copyright © 2019 Facebook. All rights reserved.
// //
import Foundation import Foundation

View File

@ -3,7 +3,7 @@
// BlueWalletWatch Extension // BlueWalletWatch Extension
// //
// Created by Marcos Rodriguez on 3/10/19. // Created by Marcos Rodriguez on 3/10/19.
// Copyright © 2019 Facebook. All rights reserved.
// //
import WatchKit import WatchKit
@ -50,3 +50,13 @@ class TransactionTableRow: NSObject {
} }
} }
// TransactionTableRow extension for configuration
extension TransactionTableRow {
func configure(with transaction: Transaction) {
amount = transaction.amount
type = transaction.type
memo = transaction.memo
time = transaction.time
}
}

View File

@ -3,7 +3,7 @@
// BlueWalletWatch Extension // BlueWalletWatch Extension
// //
// Created by Marcos Rodriguez on 3/13/19. // Created by Marcos Rodriguez on 3/13/19.
// Copyright © 2019 Facebook. All rights reserved.
// //
import Foundation import Foundation

View File

@ -3,7 +3,7 @@
// BlueWalletWatch Extension // BlueWalletWatch Extension
// //
// Created by Marcos Rodriguez on 3/23/19. // Created by Marcos Rodriguez on 3/23/19.
// Copyright © 2019 Facebook. All rights reserved.
// //
import Foundation import Foundation

View File

@ -3,7 +3,7 @@
// BlueWalletWatch Extension // BlueWalletWatch Extension
// //
// Created by Marcos Rodriguez on 3/10/19. // Created by Marcos Rodriguez on 3/10/19.
// Copyright © 2019 Facebook. All rights reserved.
// //
import WatchKit import WatchKit

View File

@ -3,7 +3,7 @@
// BlueWalletWatch Extension // BlueWalletWatch Extension
// //
// Created by Marcos Rodriguez on 3/20/19. // Created by Marcos Rodriguez on 3/20/19.
// Copyright © 2019 Facebook. All rights reserved.
// //

View File

@ -3,7 +3,7 @@
// BlueWalletWatch Extension // BlueWalletWatch Extension
// //
// Created by Marcos Rodriguez on 3/12/19. // Created by Marcos Rodriguez on 3/12/19.
// Copyright © 2019 Facebook. All rights reserved.
// //
import WatchKit import WatchKit

View File

@ -3,7 +3,7 @@
// BlueWalletWatch Extension // BlueWalletWatch Extension
// //
// Created by Marcos Rodriguez on 3/23/19. // Created by Marcos Rodriguez on 3/23/19.
// Copyright © 2019 Facebook. All rights reserved.
// //
import WatchKit import WatchKit

View File

@ -1,10 +1,6 @@
//
// ReceiveInterfaceController.swift
// BlueWalletWatch Extension // BlueWalletWatch Extension
// //
// Created by Marcos Rodriguez on 3/12/19. // Created by Marcos Rodriguez on 3/11/19.
// Copyright © 2019 Facebook. All rights reserved.
//
import WatchKit import WatchKit
import Foundation import Foundation
@ -15,91 +11,85 @@ class ViewQRCodefaceController: WKInterfaceController {
static let identifier = "ViewQRCodefaceController" static let identifier = "ViewQRCodefaceController"
@IBOutlet weak var imageInterface: WKInterfaceImage! @IBOutlet weak var imageInterface: WKInterfaceImage!
@IBOutlet weak var addressLabel: WKInterfaceLabel! @IBOutlet weak var addressLabel: WKInterfaceLabel!
var address: String? { var address: String? {
didSet { didSet {
if let address = address, !address.isEmpty{ updateQRCode()
userActivity.userInfo = [HandOffUserInfoKey.Xpub.rawValue: address] updateUserActivity()
userActivity.becomeCurrent()
}
} }
} }
private var interfaceMode = InterfaceMode.Address private var interfaceMode = InterfaceMode.Address
private let userActivity: NSUserActivity = NSUserActivity(activityType: HandoffIdentifier.Xpub.rawValue) private let userActivity: NSUserActivity = NSUserActivity(activityType: HandoffIdentifier.Xpub.rawValue)
override func awake(withContext context: Any?) { override func awake(withContext context: Any?) {
super.awake(withContext: context) super.awake(withContext: context)
userActivity.title = HandOffTitle.Xpub.rawValue configureUserActivity()
userActivity.requiredUserInfoKeys = [HandOffUserInfoKey.Xpub.rawValue]
userActivity.isEligibleForHandoff = true
guard let passedContext = context as? String else { guard let passedContext = context as? String else {
pop() pop()
return return
} }
address = passedContext address = passedContext
addressLabel.setText(passedContext) addressLabel.setText(passedContext)
toggleViewButtonPressed()
}
DispatchQueue.main.async { private func configureUserActivity() {
guard let cgImage = EFQRCode.generate( userActivity.title = HandOffTitle.Xpub.rawValue
for: passedContext) else { userActivity.requiredUserInfoKeys = [HandOffUserInfoKey.Xpub.rawValue]
userActivity.isEligibleForHandoff = true
}
private func updateUserActivity() {
if let address = address, !address.isEmpty {
userActivity.userInfo = [HandOffUserInfoKey.Xpub.rawValue: address]
userActivity.becomeCurrent()
} else {
userActivity.invalidate()
}
}
private func updateQRCode() {
guard let address = address, !address.isEmpty else {
imageInterface.setImage(nil)
return return
} }
DispatchQueue.global(qos: .userInteractive).async {
guard let cgImage = EFQRCode.generate(for: address) else {
return
}
DispatchQueue.main.async {
let image = UIImage(cgImage: cgImage) let image = UIImage(cgImage: cgImage)
self.imageInterface.setImage(nil)
self.imageInterface.setImage(image) self.imageInterface.setImage(image)
} }
if #available(watchOSApplicationExtension 6.0, *) {
if let image = UIImage(systemName: "textformat.subscript") {
addMenuItem(with: image, title: "Address", action:#selector(toggleViewButtonPressed))
} else {
addMenuItem(with: .shuffle, title: "Address", action: #selector(toggleViewButtonPressed))
}
} else {
addMenuItem(with: .shuffle, title: "Address", action: #selector(toggleViewButtonPressed))
} }
} }
@IBAction @objc func toggleViewButtonPressed() { @IBAction @objc func toggleViewButtonPressed() {
clearAllMenuItems() clearAllMenuItems()
switch interfaceMode {
case .Address:
addressLabel.setHidden(false)
imageInterface.setHidden(true)
if #available(watchOSApplicationExtension 6.0, *) {
if let image = UIImage(systemName: "qrcode") {
addMenuItem(with: image, title: "QR Code", action:#selector(toggleViewButtonPressed))
} else {
addMenuItem(with: .shuffle, title: "QR Code", action: #selector(toggleViewButtonPressed))
}
} else {
addMenuItem(with: .shuffle, title: "QR Code", action: #selector(toggleViewButtonPressed))
}
case .QRCode:
addressLabel.setHidden(true)
imageInterface.setHidden(false)
if #available(watchOSApplicationExtension 6.0, *) {
if let image = UIImage(systemName: "textformat.subscript") {
addMenuItem(with: image, title: "Address", action:#selector(toggleViewButtonPressed))
} else {
addMenuItem(with: .shuffle, title: "Address", action: #selector(toggleViewButtonPressed))
}
} else {
addMenuItem(with: .shuffle, title: "Address", action: #selector(toggleViewButtonPressed))
}
}
interfaceMode = interfaceMode == .QRCode ? .Address : .QRCode interfaceMode = interfaceMode == .QRCode ? .Address : .QRCode
let menuItemTitle = interfaceMode == .QRCode ? "QR Code" : "Address"
let systemImageName = interfaceMode == .QRCode ? "textformat.subscript" : "qrcode"
let defaultMenuItemIcon = interfaceMode == .QRCode ? WKMenuItemIcon.shuffle : WKMenuItemIcon.shuffle
addressLabel.setHidden(interfaceMode != .Address)
imageInterface.setHidden(interfaceMode != .QRCode)
if #available(watchOSApplicationExtension 6.0, *), let image = UIImage(systemName: systemImageName) {
addMenuItem(with: image, title: menuItemTitle, action: #selector(toggleViewButtonPressed))
} else {
addMenuItem(with: defaultMenuItemIcon, title: menuItemTitle, action: #selector(toggleViewButtonPressed))
}
} }
override func willActivate() { override func willActivate() {
super.willActivate() super.willActivate()
update(userActivity) updateUserActivity()
} }
override func didDeactivate() { override func didDeactivate() {
super.didDeactivate() super.didDeactivate()
userActivity.invalidate() userActivity.invalidate()
invalidateUserActivity()
} }
} }

View File

@ -1,10 +1,6 @@
//
// WalletDetailsInterfaceController.swift
// BlueWalletWatch Extension // BlueWalletWatch Extension
// //
// Created by Marcos Rodriguez on 3/11/19. // Created by Marcos Rodriguez on 3/11/19.
// Copyright © 2019 Facebook. All rights reserved.
//
import WatchKit import WatchKit
import Foundation import Foundation
@ -23,29 +19,50 @@ class WalletDetailsInterfaceController: WKInterfaceController {
@IBOutlet weak var noTransactionsLabel: WKInterfaceLabel! @IBOutlet weak var noTransactionsLabel: WKInterfaceLabel!
@IBOutlet weak var transactionsTable: WKInterfaceTable! @IBOutlet weak var transactionsTable: WKInterfaceTable!
override func awake(withContext context: Any?) { override func awake(withContext context: Any?) {
super.awake(withContext: context) super.awake(withContext: context)
guard let identifier = context as? Int else { guard let identifier = context as? Int else {
pop() pop()
return return
} }
processInterface(identifier: identifier) loadWalletDetails(identifier: identifier)
} }
func processInterface(identifier: Int) { private func loadWalletDetails(identifier: Int) {
let wallet = WatchDataSource.shared.wallets[identifier] let wallet = WatchDataSource.shared.wallets[identifier]
self.wallet = wallet self.wallet = wallet
updateWalletUI(wallet: wallet)
updateTransactionsTable(forWallet: wallet)
}
private func updateWalletUI(wallet: Wallet) {
walletBalanceLabel.setHidden(wallet.hideBalance) walletBalanceLabel.setHidden(wallet.hideBalance)
walletBalanceLabel.setText(wallet.hideBalance ? "" : wallet.balance) walletBalanceLabel.setText(wallet.hideBalance ? "" : wallet.balance)
walletNameLabel.setText(wallet.label) walletNameLabel.setText(wallet.label)
walletBasicsGroup.setBackgroundImageNamed(WalletGradient(rawValue: wallet.type)?.imageString) walletBasicsGroup.setBackgroundImageNamed(WalletGradient(rawValue: wallet.type)?.imageString)
createInvoiceButton.setHidden(!(wallet.type == WalletGradient.LightningCustodial.rawValue || wallet.type == WalletGradient.LightningLDK.rawValue))
let isLightningWallet = wallet.type == WalletGradient.LightningCustodial.rawValue || wallet.type == WalletGradient.LightningLDK.rawValue
createInvoiceButton.setHidden(!isLightningWallet)
receiveButton.setHidden(wallet.receiveAddress.isEmpty) receiveButton.setHidden(wallet.receiveAddress.isEmpty)
viewXPubButton.setHidden(!((wallet.type != WalletGradient.LightningCustodial.rawValue || wallet.type != WalletGradient.LightningLDK.rawValue) && !(wallet.xpub ?? "").isEmpty)) viewXPubButton.setHidden(!isXPubAvailable(wallet: wallet))
processWalletsTable()
} }
private func isXPubAvailable(wallet: Wallet) -> Bool {
return (wallet.type != WalletGradient.LightningCustodial.rawValue && wallet.type != WalletGradient.LightningLDK.rawValue) && !(wallet.xpub ?? "").isEmpty
}
private func updateTransactionsTable(forWallet wallet: Wallet) {
let transactions = wallet.transactions
transactionsTable.setNumberOfRows(transactions.count, withRowType: TransactionTableRow.identifier)
for index in 0..<transactions.count {
guard let controller = transactionsTable.rowController(at: index) as? TransactionTableRow else { continue }
let transaction = transactions[index]
controller.configure(with: transaction)
}
transactionsTable.setHidden(transactions.isEmpty)
noTransactionsLabel.setHidden(!transactions.isEmpty)
}
@IBAction func toggleBalanceVisibility(_ sender: Any) { @IBAction func toggleBalanceVisibility(_ sender: Any) {
guard let wallet = wallet else { guard let wallet = wallet else {
@ -59,13 +76,12 @@ class WalletDetailsInterfaceController: WKInterfaceController {
} }
} }
@objc func showBalanceMenuItemTapped() { @objc func showBalanceMenuItemTapped() {
guard let identifier = wallet?.identifier else { return } guard let identifier = wallet?.identifier else { return }
WatchDataSource.toggleWalletHideBalance(walletIdentifier: identifier, hideBalance: false) { [weak self] _ in WatchDataSource.toggleWalletHideBalance(walletIdentifier: identifier, hideBalance: false) { [weak self] _ in
DispatchQueue.main.async { DispatchQueue.main.async {
WatchDataSource.postDataUpdatedNotification() WatchDataSource.postDataUpdatedNotification()
self?.processInterface(identifier: identifier) self?.loadWalletDetails(identifier: identifier)
} }
} }
} }
@ -75,7 +91,7 @@ class WalletDetailsInterfaceController: WKInterfaceController {
WatchDataSource.toggleWalletHideBalance(walletIdentifier: identifier, hideBalance: true) { [weak self] _ in WatchDataSource.toggleWalletHideBalance(walletIdentifier: identifier, hideBalance: true) { [weak self] _ in
DispatchQueue.main.async { DispatchQueue.main.async {
WatchDataSource.postDataUpdatedNotification() WatchDataSource.postDataUpdatedNotification()
self?.processInterface(identifier: identifier) self?.loadWalletDetails(identifier: identifier)
} }
} }
} }
@ -89,34 +105,21 @@ class WalletDetailsInterfaceController: WKInterfaceController {
override func willActivate() { override func willActivate() {
super.willActivate() super.willActivate()
transactionsTable.setHidden(wallet?.transactions.isEmpty ?? true) guard let wallet = wallet else { return }
noTransactionsLabel.setHidden(!(wallet?.transactions.isEmpty ?? false)) updateTransactionsTable(forWallet: wallet)
} }
@IBAction func receiveMenuItemTapped() { @IBAction func receiveMenuItemTapped() {
guard let wallet = wallet else { return }
presentController(withName: ReceiveInterfaceController.identifier, context: (wallet, "receive")) presentController(withName: ReceiveInterfaceController.identifier, context: (wallet, "receive"))
} }
@objc private func processWalletsTable() {
transactionsTable.setNumberOfRows(wallet?.transactions.count ?? 0, withRowType: TransactionTableRow.identifier)
for index in 0..<transactionsTable.numberOfRows {
guard let controller = transactionsTable.rowController(at: index) as? TransactionTableRow, let transaction = wallet?.transactions[index] else { continue }
controller.amount = transaction.amount
controller.type = transaction.type
controller.memo = transaction.memo
controller.time = transaction.time
}
transactionsTable.setHidden(wallet?.transactions.isEmpty ?? true)
noTransactionsLabel.setHidden(!(wallet?.transactions.isEmpty ?? false))
}
@IBAction func createInvoiceTapped() { @IBAction func createInvoiceTapped() {
if (WatchDataSource.shared.companionWalletsInitialized) { if WatchDataSource.shared.companionWalletsInitialized {
pushController(withName: ReceiveInterfaceController.identifier, context: (wallet?.identifier, "createInvoice")) guard let wallet = wallet else { return }
pushController(withName: ReceiveInterfaceController.identifier, context: (wallet.identifier, "createInvoice"))
} else { } else {
WKInterfaceDevice.current().play(.failure)
presentAlert(withTitle: "Error", message: "Unable to create invoice. Please open BlueWallet on your iPhone and unlock your wallets.", preferredStyle: .alert, actions: [WKAlertAction(title: "OK", style: .default, handler: { [weak self] in presentAlert(withTitle: "Error", message: "Unable to create invoice. Please open BlueWallet on your iPhone and unlock your wallets.", preferredStyle: .alert, actions: [WKAlertAction(title: "OK", style: .default, handler: { [weak self] in
self?.dismiss() self?.dismiss()
})]) })])
@ -124,7 +127,8 @@ class WalletDetailsInterfaceController: WKInterfaceController {
} }
override func contextForSegue(withIdentifier segueIdentifier: String) -> Any? { override func contextForSegue(withIdentifier segueIdentifier: String) -> Any? {
return (wallet?.identifier, "receive") guard let wallet = wallet else { return nil }
return (wallet.identifier, "receive")
} }
} }

View File

@ -3,7 +3,6 @@
// BlueWallet // BlueWallet
// //
// Created by Marcos Rodriguez on 9/19/19. // Created by Marcos Rodriguez on 9/19/19.
// Copyright © 2019 Facebook. All rights reserved.
// //
import Foundation import Foundation

View File

@ -3,7 +3,7 @@
// TodayExtension // TodayExtension
// //
// Created by Marcos Rodriguez on 11/2/19. // Created by Marcos Rodriguez on 11/2/19.
// Copyright © 2019 Facebook. All rights reserved.
// //
import Foundation import Foundation