Table view not showing data from realm database on first launch Swift - ios

My goal is to display data fetched from realm database on the table view that I previously asynchronously added whenever I launch the app.
The problem is that when I first launch my app the table view doesn't show any data fetched from realm database but if I pull it to refresh the data is displayed.
Here is what I do in the following code:
I fetch data (contacts) from apple's contacts framework and pass them through a closure to a helper method where I:
Add them to realm database
fetch the contacts and store them in the data source array (setDataSource)
create the section titles for the table view
So far I made the debugging and noticed that the problem maybe with threading and concurrency, that is, on first launch the data is being added to realm asynchronously so when I fetch the contacts there's nothing to fetch.
Saying that, I haven't been able to solve the above identified problem so far because I don't know how to. I am learning to code with threading and concurrency and to use realm.
I've also seen some posts but in none of them were a similar problem.
I'd be very grateful for your help.
Thank you!
Table view controller:
override func viewDidLoad() {
super.viewDidLoad()
populateTableView() // TODO: Contacts don't show up on first launch of the app after installation.
}
func populateTableView() {
contactManager.populateDataSource(for: self)
tableView.reloadData()
}
Contact manager Class:
let store = CNContactStore()
let dataBase = DataBase()
var contactsDataSource = [[Contact]]()
var sectionTitles = [String]()
var deletedContactsDataSource = [[Contact]]()
var sectionTitlesForDeletedContactsTable = [String]()
func fetchUserContacts(completion: #escaping ContactsFetchingResult) {
print("Attempting to fetch contacts today...")
store.requestAccess(for: .contacts) { (granted, error) in
if let errorToCatch = error {
print("Failed to request access: ", errorToCatch)
} else if granted {
print("Access granted")
let keys = [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactPhoneNumbersKey, CNContactThumbnailImageDataKey, CNContactImageDataAvailableKey, CNContactIdentifierKey, CNContactOrganizationNameKey]
let request = CNContactFetchRequest(keysToFetch: keys as [CNKeyDescriptor])
do {
try self.store.enumerateContacts(with: request, usingBlock: { (contact, stopPointerIfYouWantToStopEnumerating) in
completion(contact, nil)
})
} catch {
completion(nil, error)
}
} else {
print("Access denied")
}
}
}
func populateDataSource(for viewController: UIViewController?) {
fetchUserContacts { (result, error) in
if let errorToCatch = error {
guard let vc = viewController else { return }
UITableViewController.Alert.showFetchingErrorAlert(on: vc, message: errorToCatch.localizedDescription)
} else if let contact = result {
self.dataBase.insert(each: Contact(contact: contact, wasDeleted: false))
self.setDataSource()
self.setSectionTitles()
}
}
}
func setDataSource(from searchTerm: String = "") {
//Not deleted contacts
if !searching {
contactsDataSource = formatResults(from: dataBase.fetchContacts(), using: sectionTitles)
} else {
setDataSourceForFilteredContacts(from: searchTerm)
}
//Deleted contacts
deletedContactsDataSource = formatResults(from: dataBase.fetchDeletedContacts(), using: sectionTitlesForDeletedContactsTable)
}
func setSectionTitles() {
sectionTitles = generateSectionTitles(from: dataBase.fetchContacts())
sectionTitlesForDeletedContactsTable = generateSectionTitles(from: dataBase.fetchDeletedContacts())
}
Database:
func insert(each contact: Contact) {
let realm = try! Realm()
try! realm.write {
realm.add(contact, update: .modified)
}
}
func fetchContacts() -> Results<Contact> {
let realm = try! Realm()
return realm.objects(Contact.self).filter("wasDeleted = false").sorted(byKeyPath: "firstName", ascending: true)
}

Related

reading data from firestore and save locally in an array

I want to retrieve usernames from a users collection and save in an array. I use this:
var usernames:[String] = []
override func viewDidLoad() {
super.viewDidLoad(
populateUsernames()
}
func populateUsernames() {
let db = Firestore.firestore()
db.collection("users").getDocuments() { [self] (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in querySnapshot!.documents {
let username = document.get("username") as! String
usernames.append(username)
print(usernames) //THIS PRINTS ["user1", "user2"] WHICH IS CORRECT
}
print(usernames) // THIS PRINTS [] WHICH IS FALSE
}
}
}
Why does the array reset to [] after the for loop?
There is nothing in your code that would cause this behavior. You're either printing the wrong array or something else is overwriting it, which doesn't seem likely. I notice that you aren't referring to the array with self which you would need to do in this closure. Therefore, rename the array for testing purposes.
var usernames2: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
populateUsernames()
}
func populateUsernames() {
Firestore.firestore().collection("users").getDocuments { (snapshot, error) in
if let snapshot = snapshot {
for doc in snapshot.documents {
if let username = doc.get("username") as? String {
self.usernames2.append(username)
print(username)
} else {
print("username not found")
}
}
print(self.usernames2)
} else {
if let error = error {
print(error)
}
}
}
}
You also crudely parse these documents which may not be harmful but is nonetheless unsafe, which this code addresses.

DispatchGroup with SQLite database

I'm trying to get my head around GCD, specifically DispatchGroup to organise downloads to a SQLite database via the FMDB wrapper. My app does the following:
Downloads info on available subjects at app startup from remote server with SQL db. Saves these locally in SQLite db for future sessions and presents what's available via UITableViewController
If a subject is selected, its contents are downloaded from the server and saved locally for future sessions. I do it this way rather than all at once at startup as this is a precursor to in-app purchases. I also download some other stuff here. Then segue to new tableview of subject contents.
I can achieve the above by chaining the download & save functions together with completion handlers, however I'd like to make use of DispatchGroup so I can utilise wait(timeout:) function in the future.
However, with my implementation of DispatchGroup (below) I'm receiving the following errors.
API call with NULL database connection pointer
[logging] misuse at line 125820 of [378230ae7f]
And also
BUG IN CLIENT OF libsqlite3.dylib: illegal multi-threaded access to database connection
Code as follows:
didSelectRow
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//Download from server
if availableSubjects[indexPath.row].isDownloaded == 0 {
//CHAINING THIS WAY WORKS
/* downloadModel.downloadCaseBundle(withSubjectID: indexPath.row, completion: {
self.downloadModel.downloadToken(forSubject: indexPath.row, completion: {
self.caseBundle = DBManager.sharedDBManager.getCaseBundle(forSubject: indexPath.row)
self.availableSubjects[indexPath.row].isDownloaded = 1
DispatchQueue.main.async {
self.performSegue(withIdentifier: "showCaseList", sender: self)
}
})
})*/
let dispatchGroup = DispatchGroup()
//Download content
dispatchGroup.enter()
downloadModel.downloadCaseBundle(withSubjectID: indexPath.row) {
dispatchGroup.leave()
}
//Download token
dispatchGroup.enter()
downloadModel.downloadToken(forSubject: indexPath.row) {
dispatchGroup.leave()
}
//Execute
dispatchGroup.notify(queue: .main) {
self.caseBundle = DBManager.sharedDBManager.getCaseBundle(forSubject: indexPath.row)
self.availableSubjects[indexPath.row].isDownloaded = 1
self.performSegue(withIdentifier: "showCaseList", sender: self)
}
} else { //Already downloaded, just retrieve from local db and present
caseBundle = DBManager.sharedDBManager.getCaseBundle(forSubject: indexPath.row)
self.performSegue(withIdentifier: "showCaseList", sender: self)
}
}
DownloadModel, downloadCaseBundle
downloadToken function is more or less identical
func downloadCaseBundle(withSubjectID subjectID: Int, completion: #escaping () -> Void) {
let urlPath = "someStringtoRemoteDB"
let url: URL = URL(string: urlPath)!
let defaultSession = Foundation.URLSession(configuration: URLSessionConfiguration.default)
let task = defaultSession.dataTask(with: url) { (data, response, error) in
if error != nil {
print("Error")
} else {
print("cases downloaded")
self.parseCasesJSON(data!, header: self.remoteMasterTable, forSubject: subjectID)
completion()
}
}
task.resume()
}
Download Mode, parseJSON
func parseCasesJSON(_ data:Data, header: String, forSubject subjectID: Int) {
var jsonResult = NSArray()
var jsonElement = NSDictionary()
let cases = NSMutableArray()
do {
jsonResult = try JSONSerialization.jsonObject(with: data, options:JSONSerialization.ReadingOptions.allowFragments) as! NSArray
} catch let error as NSError {
print(error)
print("error at serialisation")
}
//Iterate through JSON result (i.e. case), construct and append to cases array
for i in 0 ..< jsonResult.count {
jsonElement = jsonResult[i] as! NSDictionary
var caseObject = CaseModel()
//The following insures none of the JsonElement values are nil through optional binding
if let uniqueID = jsonElement["id"] as? Int,
let subjectTitle = jsonElement["subjectTitle"] as? String,
let subjectID = jsonElement["subjectID"] as? Int,
let questionID = jsonElement["questionID"] as? Int,
//And so on
{
caseObject.uniqueID = uniqueID
caseObject.subjectTitle = subjectTitle
caseObject.subjectID = subjectID
caseObject.questionID = questionID
//And so on
}
cases.add(caseObject)
}
DBManager.sharedDBManager.saveCasesLocally(dataToSave: cases as! [CaseModel])
DBManager.sharedDBManager.setSubjectAsDownloaded(forSubjectID: subjectID)
}
Turns out it was nothing to do with those methods and I needed to implement FMDatabaseQueue instead of FMDatabase in my DBManager singleton.

Fetching all contacts in ios Swift with all keys? [duplicate]

I'm trying to insert into var contacts: [CNContact] = []
the var store = CNContactStore() but I did not find the right code for this job, i found this function that I need to give that a name
func findContactsWithName(name: String) {
AppDelegate.sharedDelegate().checkAccessStatus({ (accessGranted) -> Void in
if accessGranted {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
do {
let predicate: NSPredicate = CNContact.predicateForContactsMatchingName(name)
let keysToFetch = [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactBirthdayKey, CNContactViewController.descriptorForRequiredKeys()]
self.contacts = try self.store.unifiedContactsMatchingPredicate(predicate, keysToFetch:keysToFetch)
self.tableView.reloadData()
}
catch {
print("Unable to refetch the selected contact.")
}
})
}
})
}
I want to insert self.contacts all the records and not only one with name equal
Update
Based on comment from OP, please try the following CNContactFetchRequest-based API to retrieve all contacts without a filter. I run this on a background thread to reduce any possible issues huge numbers of contacts.
func findContactsOnBackgroundThread ( completionHandler:(contacts:[CNContact]?)->()) {
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), { () -> Void in
let keysToFetch = [CNContactFormatter.descriptorForRequiredKeysForStyle(.FullName),CNContactPhoneNumbersKey] //CNContactIdentifierKey
let fetchRequest = CNContactFetchRequest( keysToFetch: keysToFetch)
var contacts = [CNContact]()
CNContact.localizedStringForKey(CNLabelPhoneNumberiPhone)
fetchRequest.mutableObjects = false
fetchRequest.unifyResults = true
fetchRequest.sortOrder = .UserDefault
let contactStoreID = CNContactStore().defaultContainerIdentifier()
print("\(contactStoreID)")
do {
try CNContactStore().enumerateContactsWithFetchRequest(fetchRequest) { (contact, stop) -> Void in
//do something with contact
if contact.phoneNumbers.count > 0 {
contacts.append(contact)
}
}
} catch let e as NSError {
print(e.localizedDescription)
}
dispatch_async(dispatch_get_main_queue(), { () -> Void in
completionHandler(contacts: contacts)
})
})
}
Generally speaking you would normally set a predicate to nil to retrieve all of the contacts when using CNContactFetchRequest class rather than as described in your code.
Note
If you want to use your existing API then I recommend setting the predicate to true:
NSPredicate(value: true)
This should make all contacts return. If that does not work consider switching to the CNConctactFetchRequest API to enumerate the Contacts. In that event you could then set the predicate to nil to fetch all contacts (using CNConctactFetchRequest).
This is how you might modify the existing method:
func findContacts()->[CNContact] {
AppDelegate.sharedDelegate().checkAccessStatus({ (accessGranted) -> Void in
if accessGranted {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
do {
let predicate: NSPredicate = NSPredicate(value: true)
let keysToFetch = [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactBirthdayKey, CNContactViewController.descriptorForRequiredKeys()]
self.contacts = try self.store.unifiedContactsMatchingPredicate(predicate, keysToFetch:keysToFetch)
self.tableView.reloadData()
}
catch {
print("Unable to refetch the selected contact.")
}
})
}
})
}
And to use:
let contacts = findContacts()
Apple has a simpler sample:
let store = CNContactStore()
let contacts = try store.unifiedContactsMatchingPredicate(CNContact.predicateForContactsMatchingName("Appleseed"), keysToFetch:[CNContactGivenNameKey, CNContactFamilyNameKey])
For your use-case, you could try to modify the Apple Sample like this:
//Use the reference to look up additional keys constants that you may want to fetch
let store = CNContactStore()
let contacts = try store.unifiedContactsMatchingPredicate(NSPredicate(value: true), keysToFetch:[CNContactGivenNameKey, CNContactFamilyNameKey])
More Apple Samples for the Contacts Framework
Modified Tommie C's answer for XCode 8 & Swift 3.0.
func findContactsOnBackgroundThread ( completionHandler:#escaping (_ contacts:[CNContact]?)->()) {
DispatchQueue.global(qos: .userInitiated).async(execute: { () -> Void in
let keysToFetch = [CNContactFormatter.descriptorForRequiredKeys(for: .fullName),CNContactPhoneNumbersKey] as [Any] //CNContactIdentifierKey
let fetchRequest = CNContactFetchRequest( keysToFetch: keysToFetch as! [CNKeyDescriptor])
var contacts = [CNContact]()
CNContact.localizedString(forKey: CNLabelPhoneNumberiPhone)
if #available(iOS 10.0, *) {
fetchRequest.mutableObjects = false
} else {
// Fallback on earlier versions
}
fetchRequest.unifyResults = true
fetchRequest.sortOrder = .userDefault
let contactStoreID = CNContactStore().defaultContainerIdentifier()
print("\(contactStoreID)")
do {
try CNContactStore().enumerateContacts(with: fetchRequest) { (contact, stop) -> Void in
//do something with contact
if contact.phoneNumbers.count > 0 {
contacts.append(contact)
}
}
} catch let e as NSError {
print(e.localizedDescription)
}
DispatchQueue.main.async(execute: { () -> Void in
completionHandler(contacts)
})
})
}
override func viewDidLoad() {
findContactsOnBackgroundThread { (contacts) in
self.contactsList = contacts
self.tableView.reloadData()
}
}
/// The default key descriptors to fetch
public var defaultFetchKeys: [String] = [CNContactGivenNameKey,
CNContactFamilyNameKey,
CNContactPhoneNumbersKey,
CNContactImageDataAvailableKey,
CNContactThumbnailImageDataKey]
/**
Fetch contacts from the user's device
- parameter sortOrder: The sort order that should be used. Default none.
- returns: A list of contacts
*/
public func fetchContacts(withSortOrder sortOrder: CNContactSortOrder = .givenName,
qos: DispatchQoS.QoSClass = .background) -> Promise<[Contact]>
{
return Promise { seal in
DispatchQueue.global(qos: qos).async {
let store = CNContactStore()
var contacts = [Contact]()
/// A `PhoneNumberKit` object used to parse and format numbers (expensive!)
let phoneNumberKit = PhoneNumberKit()
let request = CNContactFetchRequest(keysToFetch: self.defaultFetchKeys as [CNKeyDescriptor])
request.sortOrder = sortOrder
request.unifyResults = true
do {
try store.enumerateContacts(with: request) { contact, _ in
if let user = Contact(contact: contact, kit: phoneNumberKit) {
contacts.append(user)
}
}
DispatchQueue.main.async {
seal.fulfill(contacts)
}
} catch {
DispatchQueue.main.async {
seal.reject(error)
}
}
}
}
}
I am also using PhoneNumberKit to standardise all numbers and PromiseKit, to chain my requests, but you can pretty much adjust it whatever you want it.

how to retrive all CNContactStore from device without filter

I'm trying to insert into var contacts: [CNContact] = []
the var store = CNContactStore() but I did not find the right code for this job, i found this function that I need to give that a name
func findContactsWithName(name: String) {
AppDelegate.sharedDelegate().checkAccessStatus({ (accessGranted) -> Void in
if accessGranted {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
do {
let predicate: NSPredicate = CNContact.predicateForContactsMatchingName(name)
let keysToFetch = [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactBirthdayKey, CNContactViewController.descriptorForRequiredKeys()]
self.contacts = try self.store.unifiedContactsMatchingPredicate(predicate, keysToFetch:keysToFetch)
self.tableView.reloadData()
}
catch {
print("Unable to refetch the selected contact.")
}
})
}
})
}
I want to insert self.contacts all the records and not only one with name equal
Update
Based on comment from OP, please try the following CNContactFetchRequest-based API to retrieve all contacts without a filter. I run this on a background thread to reduce any possible issues huge numbers of contacts.
func findContactsOnBackgroundThread ( completionHandler:(contacts:[CNContact]?)->()) {
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), { () -> Void in
let keysToFetch = [CNContactFormatter.descriptorForRequiredKeysForStyle(.FullName),CNContactPhoneNumbersKey] //CNContactIdentifierKey
let fetchRequest = CNContactFetchRequest( keysToFetch: keysToFetch)
var contacts = [CNContact]()
CNContact.localizedStringForKey(CNLabelPhoneNumberiPhone)
fetchRequest.mutableObjects = false
fetchRequest.unifyResults = true
fetchRequest.sortOrder = .UserDefault
let contactStoreID = CNContactStore().defaultContainerIdentifier()
print("\(contactStoreID)")
do {
try CNContactStore().enumerateContactsWithFetchRequest(fetchRequest) { (contact, stop) -> Void in
//do something with contact
if contact.phoneNumbers.count > 0 {
contacts.append(contact)
}
}
} catch let e as NSError {
print(e.localizedDescription)
}
dispatch_async(dispatch_get_main_queue(), { () -> Void in
completionHandler(contacts: contacts)
})
})
}
Generally speaking you would normally set a predicate to nil to retrieve all of the contacts when using CNContactFetchRequest class rather than as described in your code.
Note
If you want to use your existing API then I recommend setting the predicate to true:
NSPredicate(value: true)
This should make all contacts return. If that does not work consider switching to the CNConctactFetchRequest API to enumerate the Contacts. In that event you could then set the predicate to nil to fetch all contacts (using CNConctactFetchRequest).
This is how you might modify the existing method:
func findContacts()->[CNContact] {
AppDelegate.sharedDelegate().checkAccessStatus({ (accessGranted) -> Void in
if accessGranted {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
do {
let predicate: NSPredicate = NSPredicate(value: true)
let keysToFetch = [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactBirthdayKey, CNContactViewController.descriptorForRequiredKeys()]
self.contacts = try self.store.unifiedContactsMatchingPredicate(predicate, keysToFetch:keysToFetch)
self.tableView.reloadData()
}
catch {
print("Unable to refetch the selected contact.")
}
})
}
})
}
And to use:
let contacts = findContacts()
Apple has a simpler sample:
let store = CNContactStore()
let contacts = try store.unifiedContactsMatchingPredicate(CNContact.predicateForContactsMatchingName("Appleseed"), keysToFetch:[CNContactGivenNameKey, CNContactFamilyNameKey])
For your use-case, you could try to modify the Apple Sample like this:
//Use the reference to look up additional keys constants that you may want to fetch
let store = CNContactStore()
let contacts = try store.unifiedContactsMatchingPredicate(NSPredicate(value: true), keysToFetch:[CNContactGivenNameKey, CNContactFamilyNameKey])
More Apple Samples for the Contacts Framework
Modified Tommie C's answer for XCode 8 & Swift 3.0.
func findContactsOnBackgroundThread ( completionHandler:#escaping (_ contacts:[CNContact]?)->()) {
DispatchQueue.global(qos: .userInitiated).async(execute: { () -> Void in
let keysToFetch = [CNContactFormatter.descriptorForRequiredKeys(for: .fullName),CNContactPhoneNumbersKey] as [Any] //CNContactIdentifierKey
let fetchRequest = CNContactFetchRequest( keysToFetch: keysToFetch as! [CNKeyDescriptor])
var contacts = [CNContact]()
CNContact.localizedString(forKey: CNLabelPhoneNumberiPhone)
if #available(iOS 10.0, *) {
fetchRequest.mutableObjects = false
} else {
// Fallback on earlier versions
}
fetchRequest.unifyResults = true
fetchRequest.sortOrder = .userDefault
let contactStoreID = CNContactStore().defaultContainerIdentifier()
print("\(contactStoreID)")
do {
try CNContactStore().enumerateContacts(with: fetchRequest) { (contact, stop) -> Void in
//do something with contact
if contact.phoneNumbers.count > 0 {
contacts.append(contact)
}
}
} catch let e as NSError {
print(e.localizedDescription)
}
DispatchQueue.main.async(execute: { () -> Void in
completionHandler(contacts)
})
})
}
override func viewDidLoad() {
findContactsOnBackgroundThread { (contacts) in
self.contactsList = contacts
self.tableView.reloadData()
}
}
/// The default key descriptors to fetch
public var defaultFetchKeys: [String] = [CNContactGivenNameKey,
CNContactFamilyNameKey,
CNContactPhoneNumbersKey,
CNContactImageDataAvailableKey,
CNContactThumbnailImageDataKey]
/**
Fetch contacts from the user's device
- parameter sortOrder: The sort order that should be used. Default none.
- returns: A list of contacts
*/
public func fetchContacts(withSortOrder sortOrder: CNContactSortOrder = .givenName,
qos: DispatchQoS.QoSClass = .background) -> Promise<[Contact]>
{
return Promise { seal in
DispatchQueue.global(qos: qos).async {
let store = CNContactStore()
var contacts = [Contact]()
/// A `PhoneNumberKit` object used to parse and format numbers (expensive!)
let phoneNumberKit = PhoneNumberKit()
let request = CNContactFetchRequest(keysToFetch: self.defaultFetchKeys as [CNKeyDescriptor])
request.sortOrder = sortOrder
request.unifyResults = true
do {
try store.enumerateContacts(with: request) { contact, _ in
if let user = Contact(contact: contact, kit: phoneNumberKit) {
contacts.append(user)
}
}
DispatchQueue.main.async {
seal.fulfill(contacts)
}
} catch {
DispatchQueue.main.async {
seal.reject(error)
}
}
}
}
}
I am also using PhoneNumberKit to standardise all numbers and PromiseKit, to chain my requests, but you can pretty much adjust it whatever you want it.

Xcode 6.3.2 - can't get authorization from Facebook via Parse.com - Swift

Rebuilding an app from Xcode 6.2 to 6.3.2
Parse linking is working, as tested with their dummy code from guide.
I think the problem is in the authorization's request, something must be changed in the way Parse gets access to user info, but can't solve where.
Update HINT 1:
if I sign up, no problem, but if I stop and re-open the app, the problem is there, since auto-complete on saveInBackgroundWithBlock failed, there could be a problem there, but where?
Update HINT 2:
I see on Parse's site, that "sessions" keep growing each time i log in
got this error:
the code in which I request authorizations:
import UIKit
import Parse
class LoginViewController: UIViewController, FBLoginViewDelegate {
#IBOutlet weak var fbLoginView: FBLoginView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func pressedLoginFB(sender: UIButton) {
//MARK: facebook personlized login from 440
//to start, request permissions
PFFacebookUtils.logInWithPermissions(["public_profile", "user_about_me", "user_birthday"], block: {
user, error in
if user == nil
{
println("BAD! user cancelled login")
//add a uialertcontrolelr before pushing to app store
return
}
else if user!.isNew
{
println("GOOD! - User signed up with Facebook")
//now, ask facebook for user's data
FBRequestConnection.startWithGraphPath("/me?fields=picture,first_name,birthday", completionHandler: {
connection, result, error in
var resultDictionary = result as! NSDictionary
user!["firstName"] = resultDictionary["first_name"]
user!["gender"] = resultDictionary["gender"]
var dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "dd/MM/YYYY"
user!["birthday"] = dateFormatter.dateFromString(resultDictionary["birthday"] as! String)
let pictureURL = ((resultDictionary["picture"] as! NSDictionary)["data"] as! NSDictionary) ["url"] as! String
let url = NSURL(string: pictureURL)
let request = NSURLRequest(URL: url!) //in old Xcode was an optional
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue(), completionHandler: {
response, data, error in
let imageFile = PFFile(name: "avatar.jpg", data: data)
user!["picture"] = imageFile as PFFile //MARK: not sure about this downcast
user!.saveInBackgroundWithBlock({
success, error in
println("hello")
println(success)
println(error)
})
})
}
)
}
else
{
println("GOOD - User logged in with Facebook")
}
let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("mapNavVCID") as! UIViewController
self.presentViewController(vc, animated: true, completion: nil)
})
}
}
this is my personal user struct, made for decoupling Parse:
import Foundation
import MapKit
import Parse
struct User {
let id: String
// let pictureUrl: String
let name: String
private let pfUser :PFUser //pfUser is private because I will use it only with our backend
//this is a nested function, I use this design pattern because of getDataInBackgroundWithBlock
func getPhoto(callback:(UIImage) -> ()) {
let imageFile = pfUser.objectForKey("picture") as! PFFile
imageFile.getDataInBackgroundWithBlock({
data, error in
if let data = data{
callback(UIImage(data: data)!)
}
})
}
}
private func pfUserToUser(user: PFUser) -> User {
return User(id: user.objectId!, name: user.objectForKey("firstName") as! String , pfUser: user)
// I cut off this:
// "pictureUrl: user.objectForKey("picture") as String," because now it is an image thanks to gePhoto function
}
//test on optionals "if user exists, give the value to the user constant,
//and return a User instance via pfUserToUser function"
// the "-> User?" is the reason why I have an optional in loginViewController on " currentUser()? "
func currentUser() -> User? {
if let user = PFUser.currentUser() {
return pfUserToUser(user)
}
else {
//if optional user doesn't exist, return nil
return nil
}
}
thanks in advance

Resources