Displaying Contact info on another ViewController with a segue - ios

Im using contact framework to fetch user contact's name, number, email, and contact image, the data is being fetched. All I am able to display right now is only one of the keys in the table view, like either the name number or email, I wish to have the name be clicked and then segue to the other view controller where the rest of the properties show up. but I am having trouble displaying it on the other viewController. I have tried using the get contacts method in the prepareforsegue function, did not seem to work The code I am using to fetch contacts is
func getContacts() {
let store = CNContactStore()
if CNContactStore.authorizationStatusForEntityType(.Contacts) == .NotDetermined {
store.requestAccessForEntityType(.Contacts, completionHandler: { (authorized: Bool, error: NSError?) -> Void in
if authorized {
self.retrieveContactsWithStore(store)
}
})
} else if CNContactStore.authorizationStatusForEntityType(.Contacts) == .Authorized {
self.retrieveContactsWithStore(store)
}
tableView.reloadData()
}
func retrieveContactsWithStore(store: CNContactStore) {
do {
let groups = try store.groupsMatchingPredicate(nil)
// let predicate = CNContact.predicateForContactsInGroupWithIdentifier(groups[0].identifier)
let containerId = CNContactStore().defaultContainerIdentifier()
let predicate: NSPredicate = CNContact.predicateForContactsInContainerWithIdentifier(containerId)
let keysToFetch = [CNContactFormatter.descriptorForRequiredKeysForStyle(.FullName), CNContactEmailAddressesKey, CNContactPhoneNumbersKey,CNContactImageDataAvailableKey,CNContactThumbnailImageDataKey]
print(CNContactEmailAddressesKey.capitalizedString)
let contacts = try store.unifiedContactsMatchingPredicate(predicate, keysToFetch: keysToFetch)
self.objects = contacts
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.tableView.reloadData()
})
} catch {
print(error)
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let identifier = segue.identifier {
if identifier == "DetailContact" {
}
}
}

As i said in my comments, create a new contact object in your other controller. For example: yourContactObject and then set some value to it in the prepare for segue as follows:
var selectedContact;// Your contact which you will set in the did select of the other controller
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let identifier = segue.identifier {
if identifier == "DetailContact" {
let controller = segue.destinationViewController
controller.yourContactObject = selectedContact.
}
}
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
selectedContact = objects[indexPath.row]
}

Instead of trying to re-do the query in prepareForSegue, make a property on your other view controller, say var contact: CNContactStore! and then observe which row gets selected in the table view. So in didSelectRowAtIndexPath you can do the following...
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let contactSelected = yourDataModel[indexPath.row] //now you know which contact was selected
self.performSegueWithIdentifier("DetailContact", sender: contactSelected)
/**you can pass the selected contact as the sender argument,
or set it as a class-level variable,
or use a delegate or notification, all would work */
}
And then in your prepareForSegue method, could do the following...
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let identifier = segue.identifier {
if identifier == "DetailContact" {
let controller = segue.destinationViewController
controller.contact = sender as! CNContactStore
}
}
}

Related

How to perform a segue if child does not exist in Firebase

I would like to check if a user has been banned from a group before to let him have access to the group chat and then perform a segue toward the next view controller.
For that I use exist(). When the child exists, the code works perfectly but when the child does not exist, nothing happens.
I tried to change blockedUserDB.observe(.childAdded, with: { (snapshot) in for blockedUserDB.observeSingleEvent(of: .value without success. I also tried to perform the segue in putting performSegue(withIdentifier: "toConversationControler", sender: Any?.self) in another function triggered when the node does not exist ...
Here is my code
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.selectedGroupID = "\(groupArray[indexPath.row].documentID)"
// Check if user blocked
let uid = Auth.auth().currentUser!.uid as String
// 1 - set breakpoint here
let blockedUserDB = self.ref2.child("blocked").child(self.selectedGroupID).child(uid)
blockedUserDB.observe(.childAdded, with: { (snapshot) in
// 2 - does this print?
print("snapshot.exists() ==", snapshot.exists())
if snapshot.exists(){
// 3 - does this print?
print("true rooms exist")
let snapshotValue = snapshot.value as! String
print("\(snapshotValue) the value")
self.blockUserCheck = snapshotValue
print(self.blockUserCheck)
if self.blockUserCheck == "true" {
SPAlert.present(message: "You have been banned from this group")
}
else
{
print("performSegue!")
self.performSegue(withIdentifier: "toConversationControler", sender: Any?.self)
print("selected group \(self.selectedGroupID)")
}
}
else
{
// 4 - does this print?
print("snapshot.exists() was false!")
self.performSegue(withIdentifier: "toConversationControler", sender: Any?.self)
}
})
}
And here is how I prepared for segue:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let identifier = segue.identifier else {
assertionFailure("Segue had no idientifier")
return
}
if identifier == "toConversationControler" {
let vc = segue.destination as! ConversationViewController
vc.finalGroup = self.selectedGroupID
vc.groupName = self.selectedGroupName
vc.city = self.finalCity
vc.language = self.selectedLanguage
vc.info = self.selectedInfo
}
else if identifier == "toNewConvVC" {
let newConvVC = segue.destination as! NewGroupViewController
newConvVC.city = self.finalCity
}
else {
assertionFailure("Did not recognize storyboard identifier")
}
}
Thank you for your help.

Pass Json response from one Viewcontroller to another Viewcontroller and populate CollectionView

I'm trying to pass a Json response of an Http request from one controller to another, where in the second one I'd like to create a collection view from the recieved data.
import UIKit
class TableViewController: UITableViewController {
let ingredientList = Ingredients().Ingredients
public var arrayDrinks: Array<Any> = []
let session = URLSession.shared
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.reloadData()
tableView.dataSource = self
tableView.delegate = self
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
// MARK: - number of rows
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.ingredientList.count
}
// MARK: - creating cell
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellaIng", for: indexPath)
let singoloIngrediente = self.ingredientList[indexPath.row]
cell.textLabel?.text = singoloIngrediente
return cell
}
// MARK: - get the Selected Item
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedItem: String = ingredientList[indexPath.row]
print("The selected ingredient is: \(selectedItem)")
// parameter for http request
let param = String(selectedItem.replacingOccurrences(of: " ", with: "_"))
let url = URL(string: "https://www.thecocktaildb.com/api/json/v1/1/filter.php?i=\(param)")!
// MARK: - Http request
let task = session.dataTask(with: url) { data, response, error in
if error != nil || data == nil {
print("Client error!")
return
}
guard let response = response as? HTTPURLResponse, (200...299).contains(response.statusCode) else {
print("Server error!")
return
}
do {
// data from network request
let decoder = JSONDecoder()
let response = try decoder.decode(ObjectDrink.self, from: data!) // ObjectDrink from Model
self.arrayDrinks.append(response.drinks)
let destinationVC = DrinksListCollectionViewController()
destinationVC.remoteArray = response.drinks
print("print array drink \(destinationVC.remoteArray)")
} catch { print(error) }
}
performSegue(withIdentifier: "InglistSegue", sender: self)
task.resume()
// let destinationVC = DrinksListCollectionViewController()
// destinationVC.remoteArray = self.arrayDrinks
// destinationVC.performSegue(withIdentifier: "InglistSegue", sender: self)
} // END didSelectRowAt
}
When I print the response to the console, the array of the second controller is empty, so no data is passing from the first response (array) to the other controller
You need to present it inside the callback of the URLSession.shared.dataTask like
DispatchQueue.main.async {
self.arrayDrinks.append(response.drinks)
let destinationVC = DrinksListCollectionViewController()
destinationVC.remoteArray = response.drinks
print("print array drink \(destinationVC.remoteArray)")
self.present(destinationVC,animated:true,completion:nil)
}
If it's a segue then replace above with ( also inside the completion )
DispatchQueue.main.async {
performSegue(withIdentifier: "InglistSegue", sender: response.drinks)
}
Add this method
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let destinationVC = segue.destination as! DrinksListCollectionViewController
destinationVC.remoteArray = sender as! [Model] // model it type of drinks
}
The view controller that you are moving to is not available yet and your line:
let destinationVC = DrinksListCollectionViewController()
Creates a new view controller which is not the one that your app transitions to. To use the view controller that will be shown, you use prepare(for segue: UIStoryboardSegue, sender: Any?) like so:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destinationVC = segue.destination as? DrinksListCollectionViewController {
destinationVC.remoteArray = self.arrayDrinks
}
}

prepareForSegue called before performSegue

I am trying to perform a segue that passes a number of variables to the next view including one variable, currentID, which is retrieved from a parse database. performSegue should not be called until after currentID has been set to the currentID downloaded from the database. However, when I run the code, currentID ends up being an empty string when it is passed to the next view.
Here is my code called by the Button:
#IBAction func submitButtonPressed(_ sender: Any) {
let point = PFGeoPoint(latitude:0.0, longitude:0.0)
let testObject = PFObject(className: "Person")
testObject["inputAmount"] = inputAmount
testObject["outputAmount"] = outputAmount
testObject["inputCurrency"] = inputCurrency
testObject["outputCurrency"] = outputCurrency
testObject["location"] = point
testObject.saveInBackground { (success, error) -> Void in
// added test for success 11th July 2016
if success {
print("Object has been saved.")
self.currentID = String(describing: testObject.objectId!)
if(self.currentID != ""){
self.performSegue(withIdentifier: "mainToListSegue", sender: self)
}
} else {
if error != nil {
print (error)
} else {
print ("Error")
}
}
}
}
And here is the prepareForSegue method:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let listViewController = (segue.destination as! UINavigationController).viewControllers[0] as! ListViewController
listViewController.inputCurrency = inputCurrency
listViewController.outputCurrency = outputCurrency
listViewController.inputAmount = inputAmount
listViewController.outputAmount = outputAmount
listViewController.currentID = currentID
listViewController.cellContent = cellContent
}
To achieve your needs, you MUST connect your segue between viewcontrollers, and not from UIButton to viewcontroller.
Every time you need to prepare your segue before calling it, this is the procedure:
Then, name it and use delegate method
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "mySegue" {
}
}
For navigating from one controller to another, connect your segue from view controller instead of from the button and it will work.

collection view cells fetch data

I am new to collection view. I want to retrieve data from CoreData for collection view cell. I know how to retrieve for table view cell but it failed when I use similar way to fetch for collection view. Here are my functions from CoreDataHelper and ViewController class
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let identifier = segue.identifier {
if identifier == "displayCellDetail" {
print("Task View cell tapped")
CollectionViewCoreDataHelper.retrieveTasks()
let indexPath = collectionView.indexPathsForSelectedItems!
let task = tasks[indexPath.row]
let TaskSettingViewController = segue.destination as! ViewController
TaskSettingViewController.task = task
} else if identifier == "addTask" {
print("+ button tapped")
}
}
}
static func retrieveTasks() -> [Tasks] {
let fetchRequest = NSFetchRequest<Tasks>(entityName: "Tasks")
do {
let results = try managedContext.fetch(fetchRequest)
return results
} catch let error as NSError {
print("Could not fetch \(error)")
}
return []
}
Try this :
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let identifier = segue.identifier {
if identifier == "displayCellDetail" {
print("Task View cell tapped")
CollectionViewCoreDataHelper.retrieveTasks()
let cell = sender as? YourCellName //Cell from which this segue is being performed
let indexPath = self.collectionView!.indexPathForCell(cell)
let task = self.tasks[indexPath.item] //Downcast to type of task
let objTaskSettingVC = segue.destination as! TaskSettingViewController
objTaskSettingVC.tasks = task
}
else if identifier == "addTask" {
print("+ button tapped")
}
}
}
For passing the data from one viewController to other is here
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let identifier = segue.identifier {
if identifier == "displayCellDetail" {
print("Task View cell tapped")
CollectionViewCoreDataHelper.retrieveTasks()
let cell = sender as UICollectionViewCell
let indexPath = self.collectionView!.indexPathForCell(cell)
let task = self.tasks[indexPath.row] as [ToDo]
let objTaskSettingVC = segue.destination as! TaskSettingViewController // ViewController in which you want to send the data
objTaskSettingVC.tasks = [task] //tasks is your variable which is having same type and defined in your TaskSettingViewController
}
else if identifier == "addTask" {
print("+ button tapped")
}
}
}

Swift PrepareForSegue NSIndexPath error

I want to prepare my segue via:
override func prepareForSegue(segue: UIStoryboardSegue?, sender: AnyObject?) {
if segue?.identifier != "fromOpenChatsToLogIn" {
if let controller: ChatViewController? = segue?.destinationViewController as? ChatViewController {
if let cell: onlineUserCell? = sender as? onlineUserCell {
let user = OneRoster.userFromRosterAtIndexPath(indexPath: tableView.indexPathForCell(cell!)!)
controller!.recipient = user
}
}
}
}
where onlineUserCell is my custom cell. Also, that's my userFromRosterAtIndexPath:
class func userFromRosterAtIndexPath(indexPath indexPath: NSIndexPath) -> XMPPUserCoreDataStorageObject {
return sharedInstance.fetchedResultsController()!.objectAtIndexPath(indexPath) as! XMPPUserCoreDataStorageObject
}
so, when I select my cell it crashes with:
fatal error: unexpectedly found nil while unwrapping an Optional value
on line:
let user = OneRoster.userFromRosterAtIndexPath(indexPath: tableView.indexPathForCell(cell!)!)
What is wrong? How can I fix it?
First of all, Swift introduced type inference, and the best practices is to use it properly, so write your code like this
if let controller = segue?.destinationViewController as? ChatViewController {
if let cell = sender as? onlineUserCell {
}
}
Also you should set an exception breakpoint in Xcode (here), to see more clearly where you're crashing.
In your case it was in
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
You should use the following statement :
if segue.identifier == "fromOpenChatsToLogIn" {
} else {
}
Your Login ViewController doesn't require an user, therefore it crashed at
let user = OneRoster.userFromRosterAtIndexPath(indexPath: tableView.indexPathForCell(cell)!)
Because there is no cell, and the sender in prepareForSegue is definitely NOT a cell.
For you second issue, the fix was easy.
You are sending OneRoster.userFromRosterAtIndexPath(indexPath: indexPath) in
performSegueWithIdentifier("fromUsersListToChatView", sender: OneRoster.userFromRosterAtIndexPath(indexPath: indexPath))
So you don't need a cell or anything since you already have the user object !
Set the recipient like this :
controller?.recipient = sender as? XMPPUserCoreDataStorageObject
Just replace your prepareForSegue method to the following and it will work:
if segue.identifier == "fromOpenChatsToLogIn" {
} else {
let controller = segue.destinationViewController as? ChatViewController
controller?.recipient = sender as? XMPPUserCoreDataStorageObject
}
Just check, wether your cell is nil or your userFromRosterAtIndexPath return nil
Check casting like this.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "PhotoScrollerViewController" {
if let controller = segue.destinationViewController as? PhotoScrollerViewController {
var cell:UICollectionViewCell = (sender as? UICollectionViewCell)!
//Code here
}
}
}
My advise would to set tag on each cell and then in your prepareForSegue method, get the cell, get the tag and then tweak the code for userFromRosterAtIndexPath to take that tag instead. That tag value could be row number of the cell. See if this makes sense in your context.

Resources