Last active
July 10, 2019 06:10
-
-
Save thedore17/b8a1f7d92b9df53aa3b50d415e90a771 to your computer and use it in GitHub Desktop.
This is the source code for the Cloakroom iOS app's private chat feature. It's end-to-end cryptography is powered by Virgil Security's open source cryptographic library. All encryption and decryption is handled here on the front-end. The users' private encryption keys never leave the local device.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// ChatPostDetailViewController.swift | |
// Cloakroom | |
// | |
// Created by Theodore Henderson on 3/31/16. | |
// Copyright © 2016 Capitol Bells, Inc. All rights reserved. | |
// | |
import SwiftyJSON | |
private extension Selector { | |
static let newChat = | |
#selector(ChatPostDetailViewController.handleRefresh) | |
} | |
class ChatPostDetailViewController: PostDetailViewController { | |
// MARK: - Outlets | |
@IBOutlet weak var navItem: UINavigationItem! | |
@IBOutlet weak var optionsButton: UIBarButtonItem! | |
@IBOutlet weak var containerView: UIView! | |
@IBOutlet weak var encryptionButton: UIBarButtonItem! | |
// MARK : - Actions | |
@IBAction func optionsActionTUI(_ sender: AnyObject) { | |
presentChatOptions() | |
} | |
@IBAction func backButtonTUI(_ sender: AnyObject) { | |
//deleteCounterpartyChats() | |
navigationController?.popViewController(animated: true) | |
} | |
@IBAction func encryptionAction(_ sender: UIBarButtonItem) { | |
if CurrentUser.hasBeenAskedToEnableEncryption().get() == false { | |
CurrentUser.hasBeenAskedToEnableEncryption().set() | |
enableEncryptionAlert() | |
} else { | |
encryptionActionLogic(sender) | |
} | |
} | |
// MARK : - Variables | |
var intro: IntroModel? | |
var chatArray = [ChatModel]() | |
var blockReasonTextField: UITextField! | |
var firstLoad = true | |
var privateKey: Data? | |
var publicKey: Data? | |
var chatVisible = false | |
//MARK: - Override Functions | |
override func viewDidLoad() { | |
super.viewDidLoad() | |
//populateRandomChats() | |
navigationController?.navigationBar.barTintColor = Constants.Colors.appDarkBlue | |
showHUDOnView(containerView, text: "Loading") | |
getChatsForIntro(refresh: false) | |
watchForScreenCapture() | |
getEncryptionKeys() | |
postDetailTableView.addSubview(refreshControl) | |
autofillTableViewSetup() | |
setScrollViewsScrollToTap() | |
} | |
override func viewDidAppear(_ animated: Bool) { | |
super.viewDidAppear(animated) | |
chatVisible = true | |
DispatchQueue.main.async(execute: {self.checkIntroForEncryptionState(self.intro)}) | |
} | |
override func viewDidDisappear(_ animated: Bool) { | |
chatVisible = false | |
} | |
override func commentTableViewSetup() { | |
if intro?.counterpartyAlias != nil { | |
navItem.title = "\(intro!.counterpartyAlias!)" | |
} | |
placeholderText = "As \(intro!.myAlias!)" | |
postDetailTableView.register(UINib(nibName: "ChatCommentCell", bundle: nil), forCellReuseIdentifier: Constants.CellIdentifer.chatCell) | |
postDetailTableView.rowHeight = UITableViewAutomaticDimension | |
postDetailTableView.estimatedRowHeight = 120.0 | |
NotificationCenter.default.addObserver(self, selector: Selector.newChat, name:NSNotification.Name(rawValue: "NewChat"), object: nil) | |
} | |
override func setAliasPlaceHolderText() { | |
placeholderText = "As \(intro!.myAlias!)" | |
commentTextView.text = placeholderText | |
} | |
override func longCommentAlert() { | |
self.displayHelperAlert("Chat messages must be less than \(Constants.CharacterLimits.maxCommentLength) characters long.") | |
} | |
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { | |
if tableView.tag == 99 { | |
if chatArray.count > 0 { | |
return chatArray.count | |
} else if firstLoad == true { | |
return 0 | |
} else { | |
return 1 | |
} | |
} else { | |
return filteredAutofillAliasArray.count | |
} | |
} | |
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { | |
if tableView.tag == 99 { | |
if chatArray.count > 0 { | |
let chatCell = tableView.dequeueReusableCell(withIdentifier: Constants.CellIdentifer.chatCell, for: indexPath) as! ChatCommentTableViewCell | |
let chatIndex = indexPath.row | |
let chat = chatArray[chatIndex] | |
chatCell.setupChatCell(chat) | |
chatCell.formatCommentCellUI() | |
chatCell.commentTableViewCellDelegate = self | |
return chatCell | |
} else { | |
// Return empty cell here | |
let emptyCell = tableView.dequeueReusableCell(withIdentifier: "emptyChatCell", for: indexPath) | |
return emptyCell | |
} | |
} else { | |
let autofillCell = tableView.dequeueReusableCell(withIdentifier: "autofillAliasCell")! as UITableViewCell | |
autofillCell.textLabel?.text = filteredAutofillAliasArray[indexPath.row] | |
return autofillCell | |
} | |
} | |
override func sendCommentLogic(_ sender: UIButton) { | |
askForCommentResponsePushIfNecessaryAndComment(UIApplication.shared, sender: sender) | |
commentTextView.resignFirstResponder() | |
} | |
override func attemptSendComment(_ sender: UIButton) { | |
super.activeTextViewHeight.constant = defaultTextViewHeight | |
let commentText = commentTextView.text | |
if commentText!.characters.count > 0 && commentText!.characters.count <= Constants.CharacterLimits.maxCommentLength && !isStringEmpty(commentText!) && commentText != placeholderText { | |
if intro?.isEncrypted == false { | |
sendChatAPIHandler(sender, chatText: commentText!) | |
} else { | |
sendEncryptedChatAPIHandler(sender, chatText: commentText!) | |
} | |
} else if commentTextView.text.characters.count > Constants.CharacterLimits.maxCommentLength { | |
longCommentAlert() | |
print("Long comment alert") | |
} else { | |
print("What happened here??") | |
} | |
} | |
override func didDeleteCommentWithKey(_ commentKeyToDelete: String) { | |
// CommentKey here is actually a ChatKey | |
showHUDForDuration("Message Deleted") | |
var i = 0 | |
for chat in chatArray { | |
if chat.chatKey == commentKeyToDelete { | |
chatArray.remove(at: i) | |
postDetailTableView.reloadData() | |
break | |
} | |
i += 1 | |
} | |
} | |
//MARK: - Controls | |
override func addPostAuthorAndCommentersToMentionsArray() { | |
// THis is chat, so only add coutnerparty alias here | |
if intro?.counterpartyAlias != nil { | |
if !originalAutofillAliasArray.contains((intro?.counterpartyAlias)!) { | |
originalAutofillAliasArray.append((intro?.counterpartyAlias)!) | |
} | |
} | |
} | |
override func getCommentsForPostShowLoadingIndicator() { | |
//nothing | |
} | |
override func handleRefresh() { | |
// Get chats for intro API call | |
if intro != nil { | |
getChatsForIntro(refresh: true) | |
} | |
} | |
// func populateRandomChats() { | |
// var i = 0 | |
// while i < 10 { | |
// let newChat = ChatModel.init(NSNumber(i)) | |
// | |
// chatArray.append(newChat) | |
// i += 1 | |
// } | |
// } | |
func presentChatOptions() { | |
let actionSheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) | |
// ACTION 1 | |
let viewProfileAction = UIAlertAction(title: "View Profile", style: .default) { (action) in | |
self.segueToCounterpartyProfile() | |
} | |
actionSheet.addAction(viewProfileAction) | |
// ACTION 2 | |
if checkIntroForEncryptionState(intro) == true { | |
var encryptionTitle = "Turn Encryption On" | |
if intro?.isEncrypted == true {encryptionTitle = "Turn Encryption Off"} | |
let encryptionAction = UIAlertAction(title: encryptionTitle, style: .default) { (action) in | |
self.enableEncryptionAlert() | |
} | |
actionSheet.addAction(encryptionAction) | |
} | |
// ACTION 3 | |
let clearAction = UIAlertAction(title: "Wipe Conversation", style: .default) { (action) in | |
self.deleteAllChats() | |
} | |
actionSheet.addAction(clearAction) | |
// ACTION 4 | |
let blockAction = UIAlertAction(title: "Block User", style: .destructive) { (action) in | |
self.blockUserAlert() | |
} | |
actionSheet.addAction(blockAction) | |
// ACTION 5 | |
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { (action) in | |
// do nothing | |
} | |
actionSheet.addAction(cancelAction) | |
if actionSheet.popoverPresentationController != nil { | |
actionSheet.popoverPresentationController!.sourceView = self.view | |
actionSheet.popoverPresentationController!.sourceRect = CGRect(x: self.view.bounds.size.width / 2.0, y: view.bounds.size.height / 2.0, width: 1.0, height: 1.0) | |
} | |
DispatchQueue.main.async(execute: {self.present(actionSheet, animated: true, completion: nil)}) | |
} | |
func blockUserAlert() { | |
present( | |
AlertViewManager.blockUserAlert({ (textField) in | |
//handle the contents of the textField | |
textField.placeholder = "Reason for blocking" | |
self.blockReasonTextField = textField | |
}, targetAction: blockAlertAction()), animated: true, completion: nil) | |
} | |
func blockAlertAction() -> UIAlertAction { | |
let blockAction = UIAlertAction(title: "Block", style: .destructive, handler: {(action) in | |
if self.blockReasonTextField.text == nil { | |
self.blockUser(reason: "") | |
} else { | |
self.blockUser(reason: self.blockReasonTextField.text!) | |
} | |
}) | |
return blockAction | |
} | |
// MARK: - Encryption Logic | |
func enableEncryptionAlert() { | |
present( | |
AlertViewManager.defaultActionAlert(title: "Enable Encryption?", message: "Secure this conversation by enabling end-to-end encryption. You will only be able to read these encrypted messages from this device.", style: .alert, action: EnableEncryptionAlertAction()), animated: true, completion: nil) | |
} | |
func EnableEncryptionAlertAction() -> UIAlertAction { | |
let encryptAction = UIAlertAction(title: "Encrypt", style: .destructive, handler: {(action) in | |
self.toggleEncryptionEndpoint(true) | |
}) | |
return encryptAction | |
} | |
func encryptionActionLogic(_ sender: UIBarButtonItem) { | |
if intro!.isEncrypted == true { | |
toggleEncryptionEndpoint(false) | |
} else { | |
enableEncryptionAlert() | |
} | |
} | |
func toggleEncryptionUI(_ encrypted: Bool) { | |
if encrypted == true { | |
encryptionButton.title = "🔒" | |
navigationController?.navigationBar.barTintColor = Constants.Colors.appDarkGrey | |
} else { | |
encryptionButton.title = "🔓" | |
navigationController?.navigationBar.barTintColor = Constants.Colors.appDarkBlue | |
} | |
} | |
func checkPublicKeyConsistency() { | |
if intro?.myPublicKey != publicKey { | |
updateEncryptionKeyForIntro(intro!.introKey!) | |
} | |
} | |
func getEncryptionKeys() { | |
publicKey = CurrentUser.encryptionKeys().getPublicKey() as Data? | |
privateKey = CurrentUser.encryptionKeys().getPrivateKey() as Data? | |
if publicKey == nil || privateKey == nil { | |
let newKeyPair = CurrentUser.encryptionKeys().generateNewKeyPair() | |
privateKey = newKeyPair.privateKey() | |
publicKey = newKeyPair.publicKey() | |
} | |
} | |
func encryptMessage(_ message: String) -> Data? { | |
let message = NSString(string: message) | |
// Convert message String to NSData | |
if let toEncrypt = message.data(using: String.Encoding.utf8.rawValue, allowLossyConversion: false) { | |
let cryptor = VSSCryptor() | |
do { | |
try cryptor.addKeyRecipient(intro!.counterpartyAlias!, publicKey: intro!.counterpartyPublicKey! as Data, error: ()) | |
// Encrypting the message data using counterparty's public key | |
var encryptedData = Data() | |
encryptedData = try cryptor.encryptData(toEncrypt, embedContentInfo: true, error: ()) | |
return encryptedData | |
} | |
catch let error as NSError { | |
print("Error: \(error.localizedDescription)") | |
} | |
//... | |
} | |
return nil | |
} | |
func decryptMessage(_ encryptedData: Data) -> String? { | |
let decryptor = VSSCryptor() | |
do { | |
// Decrypting the data with current user's private key | |
let decryptedData = try decryptor.decryptData(encryptedData, recipientId: intro!.myAlias!, privateKey: privateKey!, keyPassword: nil, error: ()) | |
// Convert the decrypted data to a String | |
if let decryptedMessage = NSString(data: decryptedData, encoding: String.Encoding.utf8.rawValue) { | |
return decryptedMessage as String | |
} else { | |
return nil | |
} | |
} | |
catch let error as NSError { | |
print("Error: \(error.localizedDescription)") | |
return nil | |
} | |
} | |
func checkIntroForEncryptionState(_ currentIntro: IntroModel?) -> Bool { | |
// check if is encrypted | |
// if yes, toggle encryption UI | |
if currentIntro!.myPublicKey == nil && currentIntro != nil { | |
updateEncryptionKeyForIntro(currentIntro!.introKey!) | |
disableAndHideEncryptionButton() | |
return false | |
} else if currentIntro!.counterpartyPublicKey?.count == 0 { | |
disableAndHideEncryptionButton() | |
return false | |
} else { | |
encryptionButton.isEnabled = true | |
if currentIntro != nil {toggleEncryptionUI(currentIntro!.isEncrypted!)} | |
return true | |
} | |
} | |
func disableAndHideEncryptionButton() { | |
encryptionButton.isEnabled = false | |
encryptionButton.title = "" | |
intro!.isEncrypted = false | |
} | |
//MARK: - Screen Capture Logic | |
func watchForScreenCapture() { | |
NotificationCenter.default.addObserver( | |
forName: NSNotification.Name.UIApplicationUserDidTakeScreenshot, | |
object: nil, | |
queue: OperationQueue.main) | |
{ | |
notification in | |
self.screenCaptureDetected() | |
} | |
} | |
func screenCaptureDetected() { | |
if self == navigationController?.visibleViewController && chatVisible == true { | |
if let counterpartyAlias = intro?.counterpartyAlias { | |
if chatArray.count > 0 { | |
reportScreenCaptureToBackend(counterpartyAlias) | |
} | |
} | |
} else { | |
// This isn't the visible view controller, so ignore this. | |
} | |
} | |
func reportScreenCaptureToBackend(_ counterpartyAlias: String) { | |
RequestManager.alertByScreenshot(CurrentUser.key().get()!, introKey: (intro?.introKey)!, offenderAlias: (intro?.myAlias)!, counterpartyAlias: counterpartyAlias, success: {(data) in | |
self.reportedScreenCaptureSuccess(counterpartyAlias) | |
}, failure: {(error) in | |
print("Failure reporting screenshot to backend.") | |
self.failureGetPostDetails(error) | |
}) | |
} | |
func reportedScreenCaptureSuccess(_ counterpartyAlias: String) { | |
displayHelperAlertDefault("Shame", message: "Shame. Shame. We've notified \(counterpartyAlias) that you took a screenshot.") | |
} | |
//MARK: - API Calls | |
func getChatsForIntro(refresh shouldRefresh: Bool) { | |
RequestManager.getChatMessages(CurrentUser.key().get()!, introKey: (intro?.introKey)!, recipient: (intro?.counterpartyAlias)!, sender: (intro?.myAlias)!, success: {(data) in | |
//print(data) | |
self.refreshControl.endRefreshing() | |
self.hideHUDOnView(self.containerView) | |
self.firstLoad = false | |
self.successGetChats(data, refresh: shouldRefresh) | |
}, failure: {(error) in | |
self.refreshControl.endRefreshing() | |
self.firstLoad = false | |
self.hideHUDOnView(self.postDetailTableView) | |
}) | |
} | |
func sendChatAPIHandler(_ sender: UIButton, chatText: String) { | |
self.showHUDOnView(postDetailTableView, text: "") | |
sender.isEnabled = false | |
RequestManager.sendChatMessage(CurrentUser.key().get()!, forIntro: intro!.introKey!, from: intro!.myAlias!, to: (intro?.counterpartyAlias!)!, message: chatText, success: {(data) -> Void in | |
self.hideHUDOnView(self.postDetailTableView) | |
sender.isEnabled = true | |
self.successSentChat(data, chatText: chatText) | |
}, failure: {error in | |
self.hideHUDOnView(self.postDetailTableView) | |
self.failureGetPostDetails(error) | |
}) | |
} | |
func sendEncryptedChatAPIHandler(_ sender: UIButton, chatText: String) { | |
self.showHUDOnView(postDetailTableView, text: "🔒") | |
sender.isEnabled = false | |
let encryptedData = encryptMessage(chatText) | |
RequestManager.sendEncryptedChatMessage(userKey: CurrentUser.key().get()!, introKey: (intro?.introKey!)!, sender: (intro?.myAlias!)!, recipient: (intro?.counterpartyAlias!)!, encryptedData: encryptedData!, success: { (data) -> Void in | |
self.hideHUDOnView(self.postDetailTableView) | |
sender.isEnabled = true | |
//print(data) | |
self.successSentChat(data, chatText: chatText) | |
}) { (error) -> Void in | |
self.hideHUDOnView(self.postDetailTableView) | |
self.failureGetPostDetails(error) | |
} | |
} | |
func updateEncryptionKeyForIntro(_ introKey: String) { | |
var publicKey = CurrentUser.encryptionKeys().getPublicKey() | |
if publicKey == nil { | |
let keyPair = CurrentUser.encryptionKeys().generateNewKeyPair() | |
publicKey = keyPair.publicKey() | |
} | |
RequestManager.updateEncryptionKey(userKey: CurrentUser.key().get()!, alias: (intro?.myAlias!)!, introKey: introKey, publicKey: publicKey!, success: { (data) in | |
// Replace intro with returned intro object | |
//print(data) | |
self.successUpdateEncryptionKeyForIntro(data) | |
}) { (error) in | |
// Background call, do nothing on failure | |
print(error) | |
} | |
} | |
func toggleEncryptionEndpoint(_ enableEncryption: Bool) { | |
RequestManager.toggleEncryption(CurrentUser.key().get()!, introKey: intro!.introKey!, enable: enableEncryption, success: { (data) in | |
self.successToggleEncryptionEndpoint(data) | |
}) { (error) in | |
self.hideHUDOnView(self.postDetailTableView) | |
self.failureGetPostDetails(error) | |
} | |
} | |
// MARK: API Call Response Handlers | |
func successGetChats(_ data: (AnyObject!), refresh: Bool) { | |
var json = JSON(data!) | |
let result = json["result"].string! | |
if (result == "ok") { | |
//print(privateKey) | |
for (_, object) in json["chats"] { | |
let chat = ChatModel(object: object) | |
if isDuplicateChat(chat) == false { | |
chatArray.append(chat) | |
} | |
if chat.encrypted_message != "" { | |
let encryptedData = chat.encrypted_message!.data(using: String.Encoding.isoLatin1) | |
chat.message = decryptMessage(encryptedData!) | |
if chat.message == nil { | |
chat.message = "***ENCRYPTED MESSAGE***" | |
} | |
chat.encrypted_message = "" | |
chat.isEncrypted = true | |
} else { | |
chat.isEncrypted = false | |
} | |
} | |
chatArray = chatArray.sorted(by: { $0.timestamp!.intValue < $1.timestamp!.intValue}) | |
postDetailTableView.reloadData() | |
let numberOfRows = self.postDetailTableView.numberOfRows(inSection: 0) | |
if numberOfRows > 0 { | |
self.scrollTableToBottom(IndexPath(row: numberOfRows-1, section: 0)) | |
} | |
checkPublicKeyConsistency() | |
encryptionButton.isEnabled = true | |
} else { | |
let message = json["message"].string! | |
self.displayErrorAlert(message) | |
} | |
} | |
func successToggleEncryptionEndpoint(_ data: (AnyObject!)) { | |
var json = JSON(data!) | |
let result = json["result"].string! | |
if (result == "ok") { | |
//print(json["intro"]) | |
self.intro = IntroModel(object: json["intro"]) | |
self.checkIntroForEncryptionState(self.intro) | |
if intro?.isEncrypted == true { | |
showHUDForDuration("Encryption ON") | |
} else { | |
showHUDForDuration("Encryption OFF") | |
} | |
} else { | |
//let message = json["message"].string! | |
//print(json) | |
//print(message) | |
} | |
} | |
func successUpdateEncryptionKeyForIntro(_ data: (AnyObject!)) { | |
var json = JSON(data!) | |
let result = json["result"].string! | |
if (result == "ok") { | |
self.intro = IntroModel(object: json["intro"]) | |
self.toggleEncryptionUI((self.intro?.isEncrypted)!) | |
} else { | |
let message = json["message"].string! | |
self.displayErrorAlert(message) | |
} | |
} | |
func successSentChat(_ data: (AnyObject!), chatText: String) { | |
var json = JSON(data!) | |
let result = json["result"].string! | |
if (result == "ok") { | |
let myNewChat = ChatModel(object: json["chat"]) | |
if myNewChat.encrypted_message != "" { | |
myNewChat.encrypted_message = "" | |
myNewChat.message = chatText | |
myNewChat.isEncrypted = true | |
} else { | |
myNewChat.isEncrypted = false | |
} | |
chatArray.append(myNewChat) | |
chatArray = chatArray.sorted(by: { $0.timestamp!.intValue < $1.timestamp!.intValue}) | |
postDetailTableView.reloadData() | |
commentTextView.text = "" | |
activeTextViewHeight.constant = defaultTextViewHeight | |
characterCountLabel.isHidden = true | |
let numberOfRows = self.postDetailTableView.numberOfRows(inSection: 0) | |
if numberOfRows > 0 { | |
scrollTableToBottom(IndexPath(row: numberOfRows-1, section: 0)) | |
} | |
} else { | |
let message = json["message"].string! | |
self.displayErrorAlert(message) | |
} | |
} | |
func getChatKeysToDelete() -> [String] { | |
var chatKeysToDeleteArray = [String]() | |
for chat in chatArray { | |
chatKeysToDeleteArray.append(chat.chatKey!) | |
} | |
return chatKeysToDeleteArray | |
} | |
func deleteIntro() { | |
RequestManager.deleteIntro(CurrentUser.key().get()!, intro: (intro?.introKey!)!, success: { (data) -> Void in | |
self.hideHUD() | |
self.successDeleteIntroJSONResponseHandler(data) | |
}) { (error) -> Void in | |
self.hideHUD() | |
self.displayErrorAlert((error?.localizedDescription)!) | |
} | |
} | |
func blockUser(reason reasonToBlock: String) { | |
RequestManager.blockChat(CurrentUser.key().get()!, aliasToBlock: (intro?.counterpartyAlias!)!, reasonForBlocking: reasonToBlock, success: { (data) -> Void in | |
self.hideHUD() | |
self.successDeleteIntroJSONResponseHandler(data) | |
}) { (error) -> Void in | |
self.hideHUD() | |
self.displayErrorAlert((error?.localizedDescription)!) | |
} | |
} | |
func deleteAllChats() { | |
let chatKeysToDelete: [String] = getChatKeysToDelete() | |
if chatKeysToDelete.count > 0 { | |
RequestManager.deleteChatMessages(CurrentUser.key().get()!, chatKeys: chatKeysToDelete, success: { (data) -> Void in | |
self.successDeleteCounterpartyChatsJSONResponseHandler(data) | |
}) { (error) -> Void in | |
if error != nil { | |
self.displayErrorAlert(error!.localizedDescription) | |
} else { | |
print("Error: \(error)") | |
} | |
} | |
} else { | |
// No chats to delete | |
} | |
} | |
override func getUserFollows() { | |
RequestManager.getUserFollows(userKey: CurrentUser.key().get()!, success: { (data) in | |
let json = JSON(data) | |
let result = json["result"] | |
if result == "ok" { | |
for alias in json["follows"].arrayObject as! [String] { | |
if !self.originalAutofillAliasArray.contains(alias) { | |
if CurrentUser().isAliasInAliasArray(alias) == false { | |
self.originalAutofillAliasArray.insert(alias, at: 0) | |
} | |
} | |
} | |
self.originalAutofillAliasArray = self.originalAutofillAliasArray.reversed() | |
} | |
}) { (error) in | |
self.displayErrorAlert(error!.localizedDescription) | |
} | |
} | |
func successDeleteIntroJSONResponseHandler(_ data: (AnyObject!)) { | |
let json = JSON(data!) | |
let result = json["result"].string! | |
if (result == "ok") { | |
showHUDForDuration("Cleared") | |
navigationController?.popViewController(animated: true) | |
} else { | |
hideHUD() | |
displayErrorAlert(json["message"].string!) | |
//showHUDForDuration("Error. Failed to delete.") | |
} | |
} | |
func successDeleteCounterpartyChatsJSONResponseHandler(_ data: (AnyObject!)) { | |
let json = JSON(data!) | |
//print(json) | |
let result = json["result"].string! | |
if (result == "ok") { | |
//print("Post successfully deleted.") | |
chatArray.removeAll() | |
postDetailTableView.reloadData() | |
} else { | |
self.displayErrorAlert(json["message"].string!) | |
//showHUDForDuration("Error. Failed to delete.") | |
} | |
} | |
func isDuplicateChat(_ newChat: ChatModel) -> Bool { | |
for existingChat in chatArray { | |
if existingChat.chatKey == newChat.chatKey { | |
return true | |
} | |
} | |
return false | |
} | |
//MARK: - Navigation Methods | |
func segueToCounterpartyProfile() { | |
let counterpartyProfileVC: CounterpartyProfileViewController = self.storyboard?.instantiateViewController(withIdentifier: "counterpartyProfileVC") as! CounterpartyProfileViewController | |
counterpartyProfileVC.profileAlias = intro?.counterpartyAlias | |
navigationController?.pushViewController(counterpartyProfileVC, animated: true) | |
} | |
} | |
struct CurrentUser { | |
struct encryptionKeys { | |
let publicKeyIdentifier = "encodedPublicKey-\(key().get())" | |
let privateKeyIdentifier = "encodedPrivateKey-\(key().get())" | |
func getPublicKey() -> Data? { | |
return defaults.data(forKey: publicKeyIdentifier) | |
} | |
func getPrivateKey() -> Data? { | |
return defaults.data(forKey: privateKeyIdentifier) | |
} | |
func set(_ encryptedKeyPair: VSSKeyPair) { | |
defaults.set(encryptedKeyPair.publicKey(), forKey: publicKeyIdentifier) | |
defaults.set(encryptedKeyPair.privateKey(), forKey: privateKeyIdentifier) | |
} | |
func remove() { | |
defaults.set(nil, forKey: publicKeyIdentifier) | |
defaults.set(nil, forKey: privateKeyIdentifier) | |
} | |
func generateNewKeyPair() -> VSSKeyPair { | |
let newKeyPair = VSSKeyPair(password:nil) | |
set(newKeyPair) | |
return newKeyPair | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment