Query from Parse.com if object not exist - xcode

This is my query code from parse:
func queryFromParse(){
//var query = PFQuery(className: "Posts")
var query = PFQuery(className: "currentUploads")
query.orderByDescending("createdAt")
query.findObjectsInBackgroundWithBlock { (objects:[AnyObject]?, error:NSError?) -> Void in
if error == nil
{
if let newObjects = objects as? [PFObject] {
for oneobject in newObjects {
let text = oneobject["imageText"] as! String
let username = oneobject["username"] as! String
let time = oneobject.createdAt!
let userImage = oneobject["imageFile"] as! PFFile
let imageURL = userImage.url // <- Bruker nå userImage.URL, henter ikke bildefilen med en gang
var OneBigObject = Details(username: username, text: text, CreatedAt: time, image: imageURL!)
self.arrayOfDetails.append(OneBigObject)
dispatch_async(dispatch_get_main_queue()) { self.collectionView.reloadData() }
}
}
}
}
}
I get error in code:
let userImage = oneobject["imageFile"] as! PFFile
fatal error: unexpectedly found nil while unwrapping an Optional value
I know why, because one of the rows in parse does not contains image:
Any ideas? What can I do to not get crash if there is not image there?

Here you go, try something likt this:
func queryFromParse(){
var query = PFQuery(className: "currentUploads")
query.orderByDescending("createdAt")
query.findObjectsInBackgroundWithBlock { (objects:[AnyObject]?, error:NSError?) -> Void in
if error == nil
{
if let newObjects = objects as? [PFObject] {
for oneobject in newObjects {
let text = oneobject["imageText"] as! String
let username = oneobject["username"] as! String
let time = oneobject.createdAt!
if let userImage = oneobject["imageFile"] as? PFFile {
let userImage = oneobject["imageFile"] as! PFFile
let imageURL = userImage.url // <- Bruker nå userImage.URL, henter ikke bildefilen med en gang
var OneBigObject = Details(username: username, text: text, CreatedAt: time, image: imageURL!)
self.arrayOfDetails.append(OneBigObject)
dispatch_async(dispatch_get_main_queue()) { self.collectionView.reloadData() }
}
}
}
}
}
}

optionally unwrap that object:
let imageFile = oneobject["imageFile"] as PFFile
imageFile.getDataInBackgroundWithBlock {
(imageData: NSData?, error: NSError?) -> Void in
if error == nil {
if let imageData = imageData {
let image = UIImage(data:imageData)
}
}
}

try use this
if let userImage = oneobject["imageFile"] as? PFFile {
let imageURL = userImage.url
}

Related

Saving User a/c details from parse on device for offline use. Swift 3

I have setup the parse & all, sign up works perfectly as needed. But after that, I am unable to get the user details back on device for offline use.
I need those objects offline once the user Logs In / Signs Up.
Class Name : User
Objects:
coverPhoto : Image.png (saved as File)
profilePicture : Image.png (saved as File)
usersUsername : String
username / email : String
firstName : String
middleName : String
lastName : String
mobileNumber : String
birthDate : String
gender : String
about : String
I tried to store it using UserDefaults.standard.set(_variable name_, forKey: "String") but didn't work. :\
This is what i am geting repeatedly.
This is what i wrote:
#IBAction func loginButtonPressed(_ sender: AnyObject) {
activityIndicator = UIActivityIndicatorView(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
activityIndicator.center = self.view.center
activityIndicator.hidesWhenStopped = true
activityIndicator.activityIndicatorViewStyle = .gray
activityIndicator.layer.zPosition = 1
view.addSubview(activityIndicator)
activityIndicator.startAnimating()
UIApplication.shared.beginIgnoringInteractionEvents()
if email.text == "" || password.text == "" {
createAlert(title: "Error", message: "Please enter your username & password!")
self.activityIndicator.stopAnimating()
UIApplication.shared.endIgnoringInteractionEvents()
} else {
PFUser.logInWithUsername(inBackground: email.text!, password: password.text!, block: { (suser, error) in
if error != nil {
var displayErrorMessage = "Some Error Occured! Please Try Again Later!"
if let errorMessage = error as? NSError {
displayErrorMessage = errorMessage.userInfo["error"] as! String
}
self.createAlert(title: "Dang it!", message: displayErrorMessage)
self.activityIndicator.stopAnimating()
UIApplication.shared.endIgnoringInteractionEvents()
} else {
self.performSegue(withIdentifier: "toMainFunction", sender: nil)
let query = PFUser.query()
query?.findObjectsInBackground(block: { (objects, error) in
if error != nil {
print(error)
} else if let users = objects {
for object in users {
if let user = object as? PFUser {
if user.objectId != PFUser.current()?.objectId {
if let email = user.username {
UserDefaults.standard.set(email, forKey: "email")
}
if let coverFile = user["coverPhoto"] {
UserDefaults.standard.set(coverFile, forKey: "coverPhoto")
}
if let profileFile = user["profilePicture"] {
UserDefaults.standard.set(profileFile, forKey: "profilePicture")
}
if let firstName = user["firstName"] {
UserDefaults.standard.set(firstName, forKey: "firstName")
}
if let middleName = user["middleName"] {
UserDefaults.standard.set(middleName, forKey: "middleName")
}
if let lastName = user["lastName"] {
UserDefaults.standard.set(lastName, forKey: "lastName")
}
if let mobileNumber = user["mobileNumber"] {
UserDefaults.standard.set(mobileNumber, forKey: "mobileNumber")
}
if let birthday = user["BirthDate"] {
UserDefaults.standard.set(birthday, forKey: "BirthDate")
}
if let gender = user["gender"] {
UserDefaults.standard.set(gender, forKey: "gender")
}
if let aboutYou = user["about"] {
UserDefaults.standard.set(aboutYou, forKey: "about")
}
if let username = user["usersUsername"] {
UserDefaults.standard.set(username, forKey: "username")
}
gloFirstName = UserDefaults.standard.object(forKey: "firstName") as! String
gloMiddleName = UserDefaults.standard.object(forKey: "middleName") as! String
gloLastName = UserDefaults.standard.object(forKey: "lastName") as! String
gloMobNumber = UserDefaults.standard.object(forKey: "mobileNumber") as! String
gloEmail = UserDefaults.standard.object(forKey: "email") as! String
gloUName = UserDefaults.standard.object(forKey: "username") as! String
gloBirthDate = UserDefaults.standard.object(forKey: "BirthDate") as! String
gloGenderStr = UserDefaults.standard.object(forKey: "gender") as! String
gloAbout = UserDefaults.standard.object(forKey: "about") as! String
if let coverImageData = UserDefaults.standard.object(forKey: "coverPhoto"),
let coverPhoto = UIImage(data: coverImageData as! Data){
gloCoverImage = coverPhoto
}
if let profileImageData = UserDefaults.standard.object(forKey: "profilePicture"),
let profilePicture = UIImage(data: profileImageData as! Data){
gloProfilePicture = profilePicture
}
}
}
}
}
})
self.activityIndicator.stopAnimating()
UIApplication.shared.endIgnoringInteractionEvents()
}
})
}
}
I am in a bind here.
EDIT:
EDIT#2:
Highlighted Code :
let query = PFUser.query()
query?.findObjectsInBackground(block: { (objects, error) in
if error != nil {
print(error)
} else if let users = objects {
for object in users {
if let user = object as? PFUser {
//-------------------------------------------------------------------------------------//
if user.objectId != PFUser.current()?.objectId {
if let email = user.username {
UserDefaults.standard.set(email, forKey: "email")
}
if let coverFile = user["coverPhoto"] {
UserDefaults.standard.set(coverFile, forKey: "coverPhoto")
}
if let profileFile = user["profilePicture"] {
UserDefaults.standard.set(profileFile, forKey: "profilePicture")
}
if let firstName = user["firstName"] {
UserDefaults.standard.set(firstName, forKey: "firstName")
}
if let middleName = user["middleName"] {
UserDefaults.standard.set(middleName, forKey: "middleName")
}
if let lastName = user["lastName"] {
UserDefaults.standard.set(lastName, forKey: "lastName")
}
if let mobileNumber = user["mobileNumber"] {
UserDefaults.standard.set(mobileNumber, forKey: "mobileNumber")
}
if let birthday = user["BirthDate"] {
UserDefaults.standard.set(birthday, forKey: "BirthDate")
}
if let gender = user["gender"] {
UserDefaults.standard.set(gender, forKey: "gender")
}
if let aboutYou = user["about"] {
UserDefaults.standard.set(aboutYou, forKey: "about")
}
if let username = user["usersUsername"] {
UserDefaults.standard.set(username, forKey: "username")
}
gloFirstName = UserDefaults.standard.object(forKey: "firstName") as! String
gloMiddleName = UserDefaults.standard.object(forKey: "middleName") as! String
gloLastName = UserDefaults.standard.object(forKey: "lastName") as! String
gloMobNumber = UserDefaults.standard.object(forKey: "mobileNumber") as! String
gloEmail = UserDefaults.standard.object(forKey: "email") as! String
gloUName = UserDefaults.standard.object(forKey: "username") as! String
gloBirthDate = UserDefaults.standard.object(forKey: "BirthDate") as! String
gloGenderStr = UserDefaults.standard.object(forKey: "gender") as! String
gloAbout = UserDefaults.standard.object(forKey: "about") as! String
if let coverImageData = UserDefaults.standard.object(forKey: "coverPhoto"),
let coverPhoto = UIImage(data: coverImageData as! Data){
gloCoverImage = coverPhoto
}
if let profileImageData = UserDefaults.standard.object(forKey: "profilePicture"),
let profilePicture = UIImage(data: profileImageData as! Data){
gloProfilePicture = profilePicture
}
}//-----------------------------------------------------------------------------//
}
}
}
})
Nevermind, solved it. This
if user.objectId != PFUser.current()?.objectId {
should've been this,
if user.objectId == PFUser.current()?.objectId {
since i needed my currently logged in account details to show up.
Also images can't be saved to UserDefaults. So just load it in every time you open the app and store it in a var myVariableName = UIImage() along with few if statement checks you can force unwrap it to this: var myVariableName = UIImage()!
Thanks Anyway!

Is it possible to catch error "fatal error: unexpectedly found nil while unwrapping an Optional value" using do/catch block with swift 2?

Sample code:
if let result = Locksmith.loadDataForUserAccount("UserAccount") {
do {
account = Account(
id: result["id"] as! String,
uid: result["uid"] as! String,
username: result["username"] as! String,
name: result["name"] as! String,
image: result["image"] as! String,
token: result["token"] as! String,
client: result["client"] as! String
)
} catch _ {
// Xcode show this warning:
// 'catch' block is unreachable because no errors are thrown in 'do' block
}
}
Any of the result's keys can be nil.
Is there a way to just catch the error in case any of them is nil?
You can't do that because the force unwrap of nil value does generate a fatal_error (which is not like throwing an ErrorType).
Failable Initializer
But you can solve your problem simply adding a failable initializer to Account (it's easier if it's a struct).
struct Account {
let id: String
let uid: String
let username: String
let name: String
let image: String
let token: String
let client: String
init?(dict:[String:Any]) {
guard let
id = dict["id"] as? String,
uid = dict["uid"] as? String,
username = dict["username"] as? String,
name = dict["name"] as? String,
image = dict["image"] as? String,
token = dict["token"] as? String,
client = dict["client"] as? String else { return nil }
self.id = id
self.uid = uid
self.username = username
self.name = name
self.image = image
self.token = token
self.client = client
}
}
Now
if let
result = Locksmith.loadDataForUserAccount("UserAccount"),
account = Account(dict:result) {
// use account here
}
Throwing Initializer
If knowing that some field is missing is not enough for you, and you need to know which is the first missing field that stopped the initialisation of Account you can define a throwing initializer.
First of all you need a way to represent the error
enum AppError: ErrorType {
case MissingField(String)
}
Next
struct Account {
let id: String
let uid: String
let username: String
let name: String
let image: String
let token: String
let client: String
init(dict:[String:Any]) throws {
guard let id = dict["id"] as? String else { throw AppError.MissingField("id") }
guard let uid = dict["uid"] as? String else { throw AppError.MissingField("uid") }
guard let username = dict["username"] as? String else { throw AppError.MissingField("username") }
guard let name = dict["name"] as? String else { throw AppError.MissingField("name") }
guard let image = dict["image"] as? String else { throw AppError.MissingField("image") }
guard let token = dict["token"] as? String else { throw AppError.MissingField("token") }
guard let client = dict["client"] as? String else { throw AppError.MissingField("client") }
self.id = id
self.uid = uid
self.username = username
self.name = name
self.image = image
self.token = token
self.client = client
}
}
Let's see how it does work
do {
let dict:[String:Any] = ["id": "1", "uid": "1234"]
let account = try Account(dict: dict)
} catch let appError {
print(appError)
}
Output
MissingField("username")

Swift 2.1 Searching Contacts by E-mail in iOS 9

In the new Contacts framework there appears to be a way to search by name:
let predicate = CNContact.predicateForContactsMatchingName("john")
let toFetch = [CNContactGivenNameKey, CNContactFamilyNameKey]
do {
let contacts = try store.unifiedContactsMatchingPredicate(
predicate, keysToFetch: toFetch)
for contact in contacts{
print(contact.givenName)
print(contact.familyName)
print(contact.identifier)
}
}
catch let err {
print(err)
}
But no apparent way to search by e-mail that I can find in the documentation or searches.
How do I search contacts by e-mail address?
These articles here and here have been helpful in learning the new framework but neither of these have revealed how to search by e-mail.
Having the same problem, I managed to find a solution. I have solved this by fetching all the contacts and then iterating over them to find matching contacts.
The code can surely be refactored.
import UIKit
import Contacts
public class ContactFinder: NSObject {
private lazy var store = CNContactStore()
private var allContacts: [CNContact] = []
private let keysToFetch: [CNKeyDescriptor] = [CNContactEmailAddressesKey, CNContactPhoneNumbersKey, CNContactFamilyNameKey, CNContactGivenNameKey, CNContactPostalAddressesKey, CNContactBirthdayKey, CNContactImageDataKey, CNContactImageDataAvailableKey]
public func requestAccess(completion:((success: Bool, error: NSError?)->())) {
let status = CNContactStore.authorizationStatusForEntityType(.Contacts)
if status == .NotDetermined {
store.requestAccessForEntityType(.Contacts, completionHandler: { [weak self](success, error) -> Void in
if success {
self?.getAllContacts(completion)
} else {
completion(success: false, error: error)
}
})
} else if status == .Authorized {
getAllContacts(completion)
} else {
completion(success: false, error: nil)
}
}
public func searchForContactUsingEmail(email: String, completion:((contacts: [CNContact])->())) {
var contacts: [CNContact] = []
for contact in allContacts {
let em = contact.emailAddresses
if em.count > 0 {
let results = em.filter({ val in
let ce = (val.value as! String).trimmedString.lowercaseString
return ce == email.trimmedString.lowercaseString
})
if results.count > 0 {
contacts.append(contact)
}
}
}
completion(contacts: contacts)
}
private func getAllContacts(completion:((success: Bool, error: NSError?)->())) {
let request = CNContactFetchRequest(keysToFetch: keysToFetch)
request.predicate = nil
request.unifyResults = true
do {
try store.enumerateContactsWithFetchRequest(request, usingBlock: { [weak self](contact, _) -> Void in
self?.allContacts.append(contact)
})
} catch let e as NSError {
print(e.localizedDescription)
completion(success: false, error: e)
return
}
completion(success: true, error: nil)
}
}

Pass asynchronously fetched data to another class

I'm designing an iOS App using Swift. I have a View Controller, DisplayBrand that's displaying data fetched from a backend. I have made another class, FetchAndParseBrand, for fetching and parsing data from the backend.
The view controller is calling a method, getBrand, in the fetching class. This method then revieces data asynchronously from the backend.
How do I return the received data to the View Controller? I guess there are multiple ways of solving this problem, do you have any suggestion for the best practice?
Here is some code for context:
DisplayBrand
class DisplayBrand: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
var brand = Brand()
FetchAndParseBrand().getBrand()
self.updateUI()
}
...
}
FetchAndParseBrand:
class FetchAndParseBrand: NSObject {
// Fetches one brand on static url
func getBrand() -> Brand{
let qos = Int(QOS_CLASS_USER_INITIATED.value)
var jsonError: NSError?
var brand = Brand()
if let url = NSURL(string: "http://backend.com/api/brand")
{
dispatch_async(dispatch_get_global_queue(qos, 0))
{
// simulate long load delay
println("going to sleep")
sleep(2)
if let brandData = NSData(contentsOfURL: url)
{
dispatch_async(dispatch_get_main_queue())
{
if let jsonBrand = NSJSONSerialization.JSONObjectWithData(brandData, options: nil, error: &jsonError) as? NSDictionary {
if let newName = jsonBrand.valueForKey("Name") as? String {
brand.name = newName
}
if let newGender = jsonBrand.valueForKey("Gender") as? Int {
if newGender == 0 {brand.gender = "male"}
else if newGender == 1 {brand.gender = "female"}
}
if let newId = jsonBrand.valueForKey("Id") as? Int {
brand.id = newId
}
brand.printBrand()
}
}
}
}
}
return brand
}
}
Brand
class Brand: NSObject {
var id: Int?
var name: String?
var image: UIImage?
var gender: String?
func printBrand() {
if id != nil { println("ID:\t\t\(self.id!)") }
if name != nil { println("Name:\t\(self.name!)") }
if image != nil { println("Image:\t\(self.image?.description)") }
if gender != nil { println("Gender:\t\(self.gender!)") }
}
}
You can use a completion handler for this, this will return the variable when it is read:
class FetchAndParseBrand: NSObject {
class func getBrand(completion: (brand: Brand) -> ()) {
let qos = Int(QOS_CLASS_USER_INITIATED.value)
var jsonError: NSError?
var brand = Brand()
if let url = NSURL(string: "http://backend.com/api/brand")
{
dispatch_async(dispatch_get_global_queue(qos, 0))
{
// simulate long load delay
println("going to sleep")
sleep(2)
if let brandData = NSData(contentsOfURL: url)
{
dispatch_async(dispatch_get_main_queue())
{
if let jsonBrand = NSJSONSerialization.JSONObjectWithData(brandData, options: nil, error: &jsonError) as? NSDictionary {
if let newName = jsonBrand.valueForKey("Name") as? String {
brand.name = newName
}
if let newGender = jsonBrand.valueForKey("Gender") as? Int {
if newGender == 0 {brand.gender = "male"}
else if newGender == 1 {brand.gender = "female"}
}
if let newId = jsonBrand.valueForKey("Id") as? Int {
brand.id = newId
}
completion(brand: brand)
brand.printBrand()
}
}
}
}
}
}
}
Then you can call the function like so:
FetchAndParseBrand.getBrand { (brand) -> () in
println(brand)
}
there are many ways. with callback function and delegates. you can also use singleton if you will call back only to a specific view controller:
class DisplayBrand: UIViewController {
static var sharedInstance :DisplayBrand?
override func viewDidLoad() {
super.viewDidLoad()
DisplayBrand.sharedInstance = self;
}
.
.
.
//on async over:
displayBrand.sharedInstance?.someFunction();
There is a major issue in your code:
FetchAndParseBrand.getBrand() is supposed to be a class method and has no parameter
The easiest way to get a asynchronous callback is a closure
Try this
Class DisplayBrand
class DisplayBrand: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
FetchAndParseBrand.getBrand() { (brand) -> () in
// do something with variable brand
self.updateUI() }
}
...
}
Class FetchAndParseBrand
class FetchAndParseBrand: NSObject {
// Fetches one brand on static url
class func getBrand(completion: (Brand)->()) {
let qos = Int(QOS_CLASS_USER_INITIATED.value)
var jsonError: NSError?
var brand = Brand()
if let url = NSURL(string: "http://backend.com/api/brand")
{
dispatch_async(dispatch_get_global_queue(qos, 0))
{
// simulate long load delay
println("going to sleep")
sleep(2)
if let brandData = NSData(contentsOfURL: url)
{
dispatch_async(dispatch_get_main_queue())
{
if let jsonBrand = NSJSONSerialization.JSONObjectWithData(brandData, options: nil, error: &jsonError) as? NSDictionary {
if let newName = jsonBrand.objectForKey("Name") as? String {
brand.name = newName
}
if let newGender = jsonBrand.objectForKey("Gender") as? Int {
if newGender == 0 {brand.gender = "male"}
else if newGender == 1 {brand.gender = "female"}
}
if let newId = jsonBrand.objectForKey("Id") as? Int {
brand.id = newId
}
brand.printBrand()
completion(brand)
}
}
}
}
}
}
}
Side note: valueForKey is a key value coding method. The standard method to get a value from a dictionary by key is objectForKey or use subscripting

Swift issue when using Parse's signup in background with block

I've issue with Swift when using Parse's signup in background with block
func Signup()
{
var user = PFUser()
user.username = self.username.text
user.password = self.password.text
user.email = "email#example.com"
user.signUpInBackgroundWithBlock {
(succeeded: Bool! , error:NSError!) -> Void in
if (error != nil)
{
}
else
{
let errorString = error.userInfo["error"] as? NSString
}
}
}
then I got these two errors
http://s15.postimg.org/geexdxwob/image.png
func Signup()
{
var user = PFUser()
user.username = self.username.text
user.password = self.password.text
user.email = "email#example.com"
user.signUpInBackgroundWithBlock {
(succeeded: Bool , error:NSError!) -> Void in
if (error != nil)
{
}
else
{
let errorString = error.userInfo["error"] as? NSString
}
}
}
You can't set the userInfo like that. You should use the error.code instead.
Check what the error-codes represent.
Also the other error should disappear, because I've tested it, and it works. So just remove the line
let errorString = error.userInfo["error"] as? NSString
`And work with error.code instead and with switch-cases where you handle the different error-codes and build a errorString by yourself.
user.signUpInBackgroundWithBlock {
(succeeded: Bool! , error:NSError!) -> Void in
if (error != nil)
{
var errorString:String!
switch error.code{
case 100:
errorString = "Error 100 appeared. It means..."
case 101:
errorString = "Error 101 appeared. It means..."
case 102:
errorString = "Error 102 appeared. It means..."
default:
break
}
}
else{
}
}

Resources