// // ViewController.swift // ZeroToApp // import UIKit import Firebase import FBSDKLoginKit import Photos struct ChatMessage { var name: String! var message: String! var image: UIImage? } class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate { // Outlets @IBOutlet weak var messageTextField: UITextField! @IBOutlet weak var sendButton: UIButton! @IBOutlet weak var tableView: UITableView! // Useful app properties let imagePicker = UIImagePickerController() var messages: [ChatMessage]! var username: String! // Firebase services var database: FIRDatabase! var auth: FIRAuth! var storage: FIRStorage! override func viewDidLoad() { super.viewDidLoad() // Initialize navigation bar self.title = "Zero To App" navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Log in", style: UIBarButtonItemStyle.Plain, target: self, action: #selector(toggleAuthState)) navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Camera, target: self, action: #selector(selectImage)) // Initialize send button sendButton.addTarget(self, action: #selector(sendMessage), forControlEvents: .TouchUpInside) // Initialize UIImagePicker imagePicker.delegate = self // Initialize other properties messages = [] username = "iOS" // Initialize UITableView tableView.delegate = self tableView.dataSource = self let nib = UINib(nibName: "ChatMessageTableViewCell", bundle: nil) tableView.registerNib(nib, forCellReuseIdentifier: "chatMessageCell") // Initialize Database, Auth, Storage database = FIRDatabase.database() auth = FIRAuth.auth() storage = FIRStorage.storage() // Listen for when child nodes get added to the collection let chatRef = database.reference().child("chat") chatRef.observeEventType(.ChildAdded, withBlock: { (snapshot) -> Void in // Get the chat message from the snapshot and add it to the UI let data = snapshot.value as! Dictionary guard let name = data["name"] as String! else { return } guard let message = data["message"] as String! else { return } let chatMessage = ChatMessage(name: name, message: message, image: nil) self.addMessage(chatMessage) }) // Observe auth state change self.auth.addAuthStateDidChangeListener { (auth, user) in if (user != nil) { self.username = user?.displayName self.navigationItem.rightBarButtonItem?.title = "Log out" } else { self.username = "iOS" self.navigationItem.rightBarButtonItem?.title = "Log in" } } } // Send a chat message func sendMessage(sender: AnyObject) { // Create chat message let chatMessage = ChatMessage(name: self.username, message: messageTextField.text!, image: nil) messageTextField.text = "" // Create a reference to our chat message let chatRef = database.reference().child("chat") // Push the chat message to the database chatRef.childByAutoId().setValue(["name": chatMessage.name, "message": chatMessage.message]) } // Show a popup when the user asks to sign in func toggleAuthState() { if (auth.currentUser != nil) { // Allow the user to sign out do { try auth.signOut() } catch {} } else { // Log in to Facebook let login = FBSDKLoginManager() login.logInWithReadPermissions(["public_profile"], fromViewController: self, handler: { (result, error) in if (error != nil || result.isCancelled) { print(error) } else { // Log in to Firebase via Facebook let credential = FIRFacebookAuthProvider.credentialWithAccessToken(result.token.tokenString) FIRAuth.auth()?.signInWithCredential(credential) { (user, error) in if (error != nil) { print(error) } } } }) } } // Handle photo uploads button func selectImage() { imagePicker.allowsEditing = false imagePicker.sourceType = .PhotoLibrary presentViewController(imagePicker, animated: true, completion: nil) } // pragma mark - UIImagePickerDelegate overrides func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) { // Get local file URLs guard let image: UIImage = info[UIImagePickerControllerOriginalImage] as? UIImage else { return } let imageData = UIImagePNGRepresentation(image)! guard let imageURL: NSURL = info[UIImagePickerControllerReferenceURL] as? NSURL else { return } // Get a reference to the location where we'll store our photos let photosRef = storage.reference().child("chat_photos") // Get a reference to store the file at chat_photos/ let photoRef = photosRef.child("\(NSUUID().UUIDString).png") // Upload file to Firebase Storage let metadata = FIRStorageMetadata() metadata.contentType = "image/png" photoRef.putData(imageData, metadata: metadata).observeStatus(.Success) { (snapshot) in // When the image has successfully uploaded, we get it's download URL let text = snapshot.metadata?.downloadURL()?.absoluteString // Set the download URL to the message box, so that the user can send it to the database self.messageTextField.text = text } // Clean up picker dismissViewControllerAnimated(true, completion: nil) } func addMessage(var chatMessage: ChatMessage) { // Handle remote image messages if (chatMessage.message.containsString("https://firebasestorage.googleapis.com")) { self.storage.referenceForURL(chatMessage.message).dataWithMaxSize(25 * 1024 * 1024, completion: { (data, error) -> Void in let image = UIImage(data: data!) chatMessage.image = image! self.messages.append(chatMessage) self.tableView.reloadData() self.scrollToBottom() }) // Handle asset library messages } else if (chatMessage.message.containsString("assets-library://")) { let assetURL = NSURL(string: chatMessage.message) let assets = PHAsset.fetchAssetsWithALAssetURLs([assetURL!], options: nil) let asset: PHAsset = assets.firstObject as! PHAsset let manager = PHImageManager.defaultManager() manager.requestImageForAsset(asset, targetSize: CGSize(width: 100.0, height: 100.0), contentMode: .AspectFit, options: nil, resultHandler: {(result, info)->Void in chatMessage.image = result! self.messages.append(chatMessage) self.tableView.reloadData() self.scrollToBottom() }) // Handle regular messages } else { self.messages.append(chatMessage) self.tableView.reloadData() self.scrollToBottom() } } func scrollToBottom() { if (self.messages.count > 8) { let bottomOffset = CGPoint(x: 0, y: tableView.contentSize.height - tableView.bounds.size.height) tableView.setContentOffset(bottomOffset, animated: true) } } // pragma mark - UITableViewDataSource overrides func numberOfSectionsInTableView(tableView: UITableView) -> Int { return 1 } func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCellWithIdentifier("chatMessageCell", forIndexPath: indexPath) as! ChatMessageTableViewCell let chatMessage = messages[indexPath.row] cell.nameLabel.text = chatMessage.name cell.messageLabel.text = chatMessage.message cell.photoView.image = chatMessage.image return cell } // pragma mark - UITableViewDelegate overrides func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { let chatMessage = messages[indexPath.row] if (chatMessage.image != nil) { return 345.0 } else { return 58.0 } } func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return messages.count } func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { tableView.deselectRowAtIndexPath(indexPath, animated: true) } // Create a chat message from a FIRDataSnapshot func chatMessageFromSnapshot(snapshot: FIRDataSnapshot) -> ChatMessage? { let data = snapshot.value as! Dictionary guard let name = data["name"] as String! else { return nil } guard let message = data["message"] as String! else { return nil } let chatMessage = ChatMessage(name: name, message: message, image: nil) return chatMessage } }