Having trouble with nil optional using parse - swift

I'm trying to download a file from Parse but I'm getting a fatal error: crash, it seems as if the message variable is nil. I've tried to troubleshoot, and I know there is an easy miss I'm just not seeing. Hoping the community can help as I am new to swift.
class InboxViewController: UITableViewController {
var messages: [PFObject] = []
var selectedMessage: PFObject?
var moviePlayer: MPMoviePlayerController?
override func viewDidLoad() {
super.viewDidLoad()
self.moviePlayer = MPMoviePlayerController()
let currentUser = PFUser.currentUser()
if currentUser != nil {
// Do stuff with the user
print("Current user: \(currentUser!.username)")
} else {
}
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
let query = PFQuery(className: "Messages")
query.whereKey("recipientIds", equalTo: PFUser.currentUser()!.objectId!)
query.findObjectsInBackgroundWithBlock {
(objects : [PFObject]?, error: NSError?) -> Void in
if error == nil {
for object in objects! {
let messages = object["file"] as! PFFile
self.tableView.reloadData()
print("Retreived \(self.messages.count) messages")
}
}
}
}
The crash breakpoint is at the beginning of the query.
Thank you community, another set of experienced eyes always helps.

you can safely unwrap the objects using if let construct
if let objects = objects {
for object in objects
let messages = object["file"] as? PFFile
print(messages)
// or you can also safely unwarp here
// if let messages = object["file"] as? PFFile {
// print(message)
// }
self.tableView.reloadData()
print("Retreived \(self.messages.count) messages")
}
}

Related

How do I pull this value out of a closure to update a cutom Object in Swift?

I am building am application with a Firestore back end, and I am trying to call a document down with the current user's info for their settings page. I am able to do this no problems when updating an empty array, but am having a terrible time trying to populate a single document. I have a custom object called DxUser:
struct DxUser {
var email:String?
var name:String?
var timeStamp:Date?
var profileImageURL:String?
var ehr: String?
var dictionary:[String:Any]? {
return [
"email":email,
"name":name,
"timeStamp":timeStamp,
"profileImageURL":profileImageURL,
"ehr": ehr as Any
]
}
}
extension DxUser : DocumentSerializable {
init?(dictionary: [String : Any]) {
guard let email = dictionary["email"] as? String,
let name = dictionary["name"] as? String,
let timeStamp = dictionary["timeStamp"] as? Date,
let profileImageURL = dictionary["profileImageURL"] as? String,
let ehr = dictionary["ehr"] as? String else {return nil}
self.init(email: email, name: name, timeStamp: timeStamp, profileImageURL: profileImageURL, ehr: ehr)
}
}
In the view controller, I am trying to update this variable with the current user, but I can only grab it in the closure, and it populates as nil anywhere outside the block. Here is the basic code I am using, but can anyone tell me what I am missing?
class SettingsController: UIViewController {
var dxUser = DxUser()
override func viewDidLoad() {
super.viewDidLoad()
fetchUser()
}
func fetchUser() {
guard let uid = Auth.auth().currentUser?.uid else {
return
}
let userRef = db.collection("users").document(uid)
userRef.getDocument { (document, error) in
if error != nil {
print(error as Any)
} else {
self.dxUser = DxUser(dictionary: (document?.data())!)!
self.navigationItem.title = self.dxUser.name
print (self.dxUser)
}
}
}
Yeah, this is how I am doing it on the table view, but I didn't see anything comparable on the regular VC.
db.collection("users").document(uid!).collection("goals").getDocuments { (snapshot, error) in
if error != nil {
print(error as Any)
} else {
//set the profile array to equal whatever I am querying below
goalsArray = snapshot!.documents.flatMap({Goal(dictionary: $0.data())})
DispatchQueue.main.async {
self.collectionView?.reloadData()
}
}
}
It's not so much about the location of where you can access dxUser. It's the timing. Firebase APIs are asynchronous, which means userRef.getDocument() returns immediately, and you receive a callback only after the request completes. If you try to access dxUser right after that call within your fetchUser() method, it will not be available, because the request isn't complete. Given that's how async APIs work, you should only work with dxUser after the callback invoked, which usually means delaying the rendering of that data in your app (such as where your print statement is).
Please read more here about why Firebase APIs are asynchronous and what you can expect from them.
I actually figured it out. I pulled the fetchUser() function out to the viewWillAppear function so I called it before the view appeared. Then I did the async function to re-run viewDidLoad. Anyone else have any better suggestions?
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
fetchUser()
}
func fetchUser() {
let userRef = db.collection("users").document(uid)
userRef.getDocument { (document, error) in
if error != nil {
print(error as Any)
} else {
self.dxUser = DxUser(dictionary: (document?.data())!)!
DispatchQueue.main.async {
self.viewDidLoad()
}
self.navigationItem.title = self.dxUser.name
}
}
}

Swift Compiler Error: Type '[AnyHashable : Any]?' has no subscript members

I'm currently making an ecommerce app in Swift 4 and linking data with Moltin. I'm doing this for school so I'm new at this. I'm also following CodeWithChris's tutorial but that is out of date and my layout is a bit different. I followed everything on it but I'm getting this error:
Type '[AnyHashable : Any]?' has no subscript members
I don't understand why.
self.objects = responseDictionary["result"] as? [AnyObject]
I tried changing as? to as! and it still didn't work.
Here's my complete code;
import UIKit
import Moltin
class TableViewController: UITableViewController {
var objects = [AnyObject]()
override func viewDidLoad() {
super.viewDidLoad()
Moltin.sharedInstance().setPublicID('***my store ID***')
Moltin.sharedInstance().product.listing(withParameters: nil, success: { (responseDictionary) in
self.objects = responseDictionary["result"] as? [AnyObject]
self.tableView.reloadData()
}) { (responseDictionary, error) in
print ("Something went wrong")
}
}
responseDictionary appears to be an optional which needs to be checked for existence:
Moltin.sharedInstance().product.listing(withParameters: nil, success: { (response) in
guard let responseDictionary = response as? [AnyHashable : Any] else {
print("Error: respnonse is empty")
return
}
self.objects = responseDictionary["result"] as? [AnyObject]
// ...
}

Core data issue Swift

Hello I am currently following a Swift tutorial which uses the old version of Swift and I am not able to follow this as I believe the syntax has changed in the newer version of Swift.
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
var appDel:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
var context:NSManagedObjectContext = appDel.managedObjectContext
var newUser = NSEntityDescription.insertNewObjectForEntityForName("users", inManagedObjectContext: context) as NSManagedObject
newUser.setValue("Joe", forKey: "username")
newUser.setValue("pass", forKey: "password")
do {
try context.save()
} catch let error {
print("Couldn't save user data")
print(error)
}
let request = NSFetchRequest(entityName: "Users")
request.returnsObjectsAsFaults = false
// var results = context.executeFetchRequest(request)
do {
var results =
try context.executeFetchRequest(request)
results = results as! [NSManagedObject]
} catch let error as NSError {
print(error)
}
for result: AnyObject in results {
print(results)
}
}
The error I am receiving is to do with the results on the line for result: AnyObject in results and the error is unresolved identifier 'results' which is giving me the impression that this should be declared somewhere as a variable as it is currently unresolved but I cannot figure out how to fix this, any help would be greatly appreciated, thanks!
do {
var results = try context.executeFetchRequest(request)
results = results as! [NSManagedObject]
} catch let error as NSError {
print(error)
}
for result: AnyObject in results {
print(results)
}
results only has scope inside the do block there. You need to move the processing of the array inside the do:
do {
var results = try context.executeFetchRequest(request)
results = results as! [NSManagedObject]
for result in results {
print(results)
}
} catch let error as NSError {
print(error)
}
Also there's no need to lose information by casting as AnyObject.
With the new API's against CoreData (in Swift 3.0) you should do:
let request = NSFetchRequest<Users>(entityName: "Users")
request.returnsObjectsAsFaults = false
do {
var results =
try context.fetch(request)
// `results` will here be [Users]
} catch let error as NSError {
print(error)
}
The new signature of fetch is fetch<T : NSFetchRequestResult>(_ request: NSFetchRequest<T>) throws -> [T] where T is the same as provided in <HERE> (ex. NSFetchRequest<Users>)

Unexpectedly found nil when unwrapping optional value - NSMutableArray - Swift

I am retrieving posts in a data handler from a DB for a news feed and I run a php script and echo the JSON encoded data back to my application, at which point the data is parsed and stored in a model of a "post", there is a protocol that is used in the view controller to get the data once it has been downloaded. My problem is that I am getting the notorious "Unexpectedly found nil when unwrapping optional value" error when I pass the NSMutableArray of "post" objects to the function "itemsDownloaded" which is function of the protocol. I checked all the values being parsed and they exist, and I also checked the count of the array to make sure it has values. The exception is occurring on the line self.delegate.itemsDownloaded(posts)
The code to handle the data is this :
import Foundation
protocol PostDataHandlerProtocol: class {
func itemsDownloaded(items: NSArray)
}
class PostDataHandler: NSObject, NSURLSessionDataDelegate {
weak var delegate: PostDataHandlerProtocol!
var data : NSMutableData = NSMutableData()
//The path to the php script to be executed
let urlPath: String = "www.something.com/myphpscript.php"
func downloadItems() {
let url: NSURL = NSURL(string: urlPath)!
var session: NSURLSession!
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
session = NSURLSession(configuration: configuration, delegate: self, delegateQueue: nil)
let task = session.dataTaskWithURL(url)
task.resume()
}
func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) {
self.data.appendData(data);
}
func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) {
if error != nil {
print("Failed to download data")
}else {
print("Data downloaded")
self.parseJSON()
}
}
func parseJSON() {
var jsonResult: NSMutableArray = NSMutableArray()
do{
jsonResult = try NSJSONSerialization.JSONObjectWithData(self.data, options:NSJSONReadingOptions.AllowFragments) as! NSMutableArray
} catch let error as NSError {
print(error)
}
var jsonElement: NSDictionary = NSDictionary()
let posts: NSMutableArray = NSMutableArray()
for(var i = 0; i < jsonResult.count; i++)
{
jsonElement = jsonResult[i] as! NSDictionary
let post = PostModel()
//The following insures none of the JsonElement values are nil through optional binding
let username = jsonElement["username"] as? String
let imagePath = jsonElement["user_imagePath"] as? String
let postID = (jsonElement["post_id"] as! NSString).integerValue
let postRep = (jsonElement["post_rep"] as! NSString).integerValue
let postType = jsonElement["post_type"] as? String
let postDate = jsonElement["post_date"] as? String
let comment = jsonElement["comment"] as? String
post.username = username
post.imagePath = imagePath
post.postID = postID
post.postRep = postRep
post.postType = postType
post.postDate = postDate
post.comment = comment
posts.addObject(post)
}
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.delegate.itemsDownloaded(posts)
})
}
}
In my view controller, I create a new data handler let postHandler = PostDataHandler() and then once the view has loaded I call postHandler.downloadItems() and in the view controller declaration I conformed to the protocol and implemented itemsDownloaded:
func itemsDownloaded(items: NSArray) {
allPosts = items as! [PostModel]
indicator.stopAnimating()
self.tableVIew.reloadData()
}
Does anyone know why this is happening? I tried to look at the numerous postings regarding this error as I'm aware it's quite common, but couldn't find anything that helped me. Many of the postings say there should be a check to ensure it's not nil, the problem is I didn't think NSMutableArray can be optional, and also I checked all the values and they don't appear to be nil, so those answers did not help me. In the thread exceptions it says something related to a closure, which I think could be causing the issue, I'm just not exactly sure what or how.
None of the code that you showed ever set your PostDataHandler's delegate property to anything, so it is reasonable to suppose that it is still nil, which perfectly explains why you crash at runtime when you try to access it as if it were an actual object.

In Swift, how do you check if pointer in Parse column is empty or not

Within my user object I added a column to add a users favorite team. The column is identified as favTeam and is a pointer to a teams class
Here is my code. I have populated my user with a favorite team however the logic is always showing that "favteam nil"
if let object = PFUser.currentUser()!["favTeam"] as? [PFObject]{
print("favteam not nil")
print(object)
let favTeam = PFUser.currentUser()!["favTeam"]
favTeamText.text = favTeam["Name"] as? String
if let favTeamImageView = favTeam["teamLogo"] as? PFFile {
favTeamImageView.getDataInBackgroundWithBlock { (imageData: NSData?, error: NSError?) -> Void in
if error == nil {
if let imageData = imageData {
self.teamLogo.image = UIImage(data: imageData)
}
}
}
}
}
else {
print("favteam nil")
}
I can accomplish this by using a PFUser.query() as follows...
func fetchFavoriteTeam() {
let userQuery: PFQuery = PFUser.query()!
userQuery.whereKey("username", equalTo: (currentUser?.username)!)
userQuery.findObjectsInBackgroundWithBlock({
(users, error) -> Void in
var favTeam = users!
if error == nil {
if favTeam != nil {
favTeamContainer = favTeam.valueForKey("favTeam") as! PFObject
}
} else {
print(error)
}
})
}