PFObject subclassing and pointers - ios

I have a class UserFeed where I store all the posts. And I have a class UserProfile where I store all the user details(name, age, occupation). Currently I have a pointer to UserProfile. But when I try to set the occupationLabel it gives me nil.
// PostsCollectionViewCell.swift
import UIKit
import DateTools
import Parse
class PostsCollectionViewCell: UICollectionViewCell {
var post: Post! {
didSet {
updateUI()
}
}
#IBOutlet var postLabel: UILabel!
#IBOutlet var genderLabel: UILabel!
#IBOutlet var occupationLabel: UILabel!
#IBOutlet var timeLabel: UILabel!
#IBOutlet var likeButton: UIButton!
func layoutSubview() {
super.layoutSubviews()
}
private func updateUI() {
occupationLabel?.text! = post.userProfile.occupation
timeLabel?.text! = post.createdAt?.shortTimeAgoSinceDate(NSDate()) ?? ""
postLabel?.text! = post.postText
}
#IBAction func likeButtonDidTouch(sender: AnyObject) {
}
}
Post query in my DiscoverViewController
func queryForPosts() {
PFGeoPoint.geoPointForCurrentLocationInBackground { (geopoint, error) in
if !(error != nil) {
if let geoPoint = geopoint {
let query = PFQuery(className: "UserFeed")
query.whereKey("location", nearGeoPoint: geoPoint, withinMiles: 5)
query.addDescendingOrder("createdAt")
query.includeKey("userProfile")
query.findObjectsInBackgroundWithBlock({ (objects, error) -> Void in
if error == nil {
if let postObjects = objects as? [PFObject] {
self.posts.removeAll()
for postObject in postObjects {
let post = postObject as! Post
self.posts.append(post)
}
self.collectionView.reloadData()
}
} else {
print("\(error!.localizedDescription)")
}
})
}
}
}
}
Post subclassing
import UIKit
import Parse
public class Post: PFObject, PFSubclassing{
// MARK: - Public API
#NSManaged public var username: PFUser
#NSManaged public var location: PFGeoPoint?
#NSManaged public var userProfile: String!
#NSManaged public var postText: String!
#NSManaged public var numberOfLikes: Int
#NSManaged public var likedUserIdCollection: [String]!
public func incrementNumberOfLikes() {
numberOfLikes++
self.saveInBackground()
}
//Mark: - Convience init
init(username: PFUser, location: PFGeoPoint?, userProfile: String, postText: String, numberOfLikes: Int) {
super.init()
self.username = username
self.location = location
self.userProfile = userProfile
self.postText = postText
self.numberOfLikes = numberOfLikes
self.likedUserIdCollection = [String]()
}
override init() {
super.init()
}
//MARK: - Like / Dislike
public func like(){
let currentUserObjectId = PFUser.currentUser()!.objectId!
if !likedUserIdCollection.contains(currentUserObjectId) {
numberOfLikes++
likedUserIdCollection.insert(currentUserObjectId, atIndex: 0)
self.saveInBackground()
}
}
public func dislike() {
let currentUserObjectId = PFUser.currentUser()!.objectId!
if likedUserIdCollection.contains(currentUserObjectId) {
numberOfLikes--
for (index, userId) in likedUserIdCollection.enumerate() {
if userId == currentUserObjectId {
likedUserIdCollection.removeAtIndex(index)
break
}
}
self.saveInBackground()
}
}
// MARK: - PFSubClassing
override public class func initialize() {
struct Static {
static var onceToken : dispatch_once_t = 0;
}
dispatch_once(&Static.onceToken) {
self.registerSubclass()
}
}
public static func parseClassName() -> String {
return "UserFeed"
}
}

Object can't store value until it intialize, need to call below init as present in your code
init(username: PFUser, location: PFGeoPoint?, userProfile: String, postText: String, numberOfLikes: Int) {
super.init()
self.username = username
self.location = location
self.userProfile = userProfile
self.postText = postText
self.numberOfLikes = numberOfLikes
self.likedUserIdCollection = [String]()
}

Need to initialize Post object, seems you have not made it.

Related

Adding a local variable to downloaded MySQL Data with Models

I am using MySQL and PHP to download a restaurants menu but the user of the app should be able to add a certain amount to which item from the menu they want. Currently I am using a stepper to indicate the amount and adding that amount to a UserDefaults key which gets called when the menu is downloaded again.
This makes me have to download the menu again when I go to another viewController which sums up the order but I can't seem to filter out only them items which do have an amount.
What is a better way to add that amount to the downloaded data and how can I filter these items in my cart ViewController to only show and use the items which have an amount.
My current downloadModel, MenuModel, cellViewController (for the menu tableview) look like this:
MenuDownload.swift:
import UIKit
protocol MenuDownloadProtocol: class {
func productsDownloaded(products: NSArray)
}
class MenuDownload: NSObject {
//properties
weak var delegate: MenuDownloadProtocol!
func downloadProducts() {
let urlPath = "http://server.com/download.php" // Fake URL obviously
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("Failed to download data")
}else {
print("Menu downloaded")
self.parseJSON(data!)
}
}
task.resume()
}
func parseJSON(_ data:Data) {
var jsonResult = NSArray()
do{
jsonResult = try JSONSerialization.jsonObject(with: data, options:JSONSerialization.ReadingOptions.allowFragments) as! NSArray
} catch let error as NSError {
print(error)
}
var jsonElement = NSDictionary()
let products = NSMutableArray()
for i in 0 ..< jsonResult.count
{
jsonElement = jsonResult[i] as! NSDictionary
let restomenu = MenuModel()
//the following insures none of the JsonElement values are nil through optional binding
if let product = jsonElement["product"] as? String,
let price = jsonElement["price"] as? String,
let info = jsonElement["info"] as? String,
let imageurl = jsonElement["imageurl"] as? String
{
let productandprice = product + " " + "€" + price
let quantityy = UserDefaults.standard.object(forKey: productandprice) as? String
restomenu.product = product
restomenu.price = price
restomenu.info = info
restomenu.imageurl = imageurl
restomenu.quantity = quantityy
}
products.add(restomenu)
}
DispatchQueue.main.async(execute: { () -> Void in
self.delegate.productsDownloaded(products: products)
})
}
}
extension String {
func chopPrefix(_ count: Int = 1) -> String {
return substring(from: index(startIndex, offsetBy: count))
}
func chopSuffix(_ count: Int = 1) -> String {
return substring(to: index(endIndex, offsetBy: -count))
}
}
MenuModel.swift:
import UIKit
class MenuModel: NSObject {
//properties
var product: String?
var price: String?
var info: String?
var imageurl: String?
var quantity: String?
//empty constructor
override init()
{
}
init(product: String, price: String, info: String, imageurl: String, quantity: String) {
self.product = product
self.price = price
self.info = info
self.imageurl = imageurl
self.quantity = quantity
}
//prints object's current state
override var description: String {
return "product: \(String(describing: product)), price: \(String(describing: price)), info: \(String(describing: info)), imageurl: \(String(describing: imageurl)), quantity: \(String(describing: quantity))"
}
}
tableViewCell.swift:
import UIKit
class productTableViewCell: UITableViewCell {
#IBOutlet weak var productLabel: UILabel!
#IBOutlet weak var productImage: UIImageView!
#IBOutlet weak var cellView: UIView!
#IBOutlet weak var orderCount: UILabel!
#IBOutlet weak var stepper: UIStepper!
var amount: String?
#IBAction func stepperValueChanged(_ sender: UIStepper) {
amount = Int(sender.value).description
orderCount.text = amount
// let defaultkey = String(productLabel.text!)
UserDefaults.standard.setValue(amount, forKey: productLabel.text!)
if amount == "0"
{
orderCount.isHidden = true
UserDefaults.standard.removeObject(forKey: productLabel.text!)
}
else
{
orderCount.isHidden = false
}
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
EDIT: after trying filtering options and many different ways I still haven't found how to fix this. I think I'm overthinking it too much.

TableView not displaying Firebase data in xcode (swiift)

I am using Xcode 11 to try and display 'post' data in Firebase in a tableview, and I have tested calling the information with prints, which works.
This is my code for the table view controller:
import UIKit
import Firebase
import FirebaseDatabase
import SwiftKeychainWrapper
import FirebaseAuth
class FeedVC: UITableViewController {
var currentUserImageUrl: String!
var posts = [Post]()
var selectedPost: Post!
override func viewDidLoad() {
super.viewDidLoad()
getUsersData()
getPosts()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func getUsersData(){
guard let userID = Auth.auth().currentUser?.uid else { return }
Database.database().reference().child("users").child(userID).observeSingleEvent(of: .value) { (snapshot) in
if let postDict = snapshot.value as? [String : AnyObject] {
self.tableView.reloadData()
}
}
}
func getPosts() {
Database.database().reference().child("textPosts").observeSingleEvent(of: .value) { (snapshot) in
guard let snapshot = snapshot.children.allObjects as? [DataSnapshot] else { return }
self.posts.removeAll()
for data in snapshot.reversed() {
guard let postDict = data.value as? Dictionary<String, AnyObject> else { return }
let post = Post(postKey: data.key, postData: postDict)
print(DataSnapshot.self)
self.posts.append(post)
}
self.tableView.reloadData()
}
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "postCell") as? PostCell else { return UITableViewCell() }
cell.configCell(post: posts[indexPath.row])
return cell
}
}
This is the code for the postCell:
import UIKit
import Firebase
import FirebaseStorage
import FirebaseDatabase
import SwiftKeychainWrapper
class PostCell: UITableViewCell {
#IBOutlet weak var userImg: UIImageView!
#IBOutlet weak var username: UILabel!
#IBOutlet weak var postText: UILabel!
#IBOutlet weak var commentBtn: UIButton!
var post: Post!
let currentUser = KeychainWrapper.standard.string(forKey: "uid")
func configCell(post: Post) {
self.post = post
self.username.text = post.username
self.postText.text = post.postText
print(self.post)
print(self.post.username)
let ref = Storage.storage().reference(forURL: post.userImg)
ref.getData(maxSize: 100000000, completion: { (data, error) in
if error != nil {
print("couldnt load img")
} else {
if let imgData = data {
if let img = UIImage(data: imgData){
self.userImg.image = img
}
}
}
})
}}
and this is for the Post:
import Foundation
import Firebase
import FirebaseDatabase
class Post {
private var _username: String!
private var _userImg: String!
private var _postText: String!
private var _postKey: String!
private var _postRef: DatabaseReference!
var username: String {
return _username
}
var userImg: String {
return _userImg
}
var postText: String {
return _postText
}
var postKey: String {
return _postKey
}
init(postText: String, username: String, userImg: String) {
_postText = postText
_username = username
_userImg = userImg
}
init(postKey: String, postData: Dictionary<String, AnyObject>) {
_postKey = postKey
if let username = postData["username"] as? String {
_username = username
}
if let userImg = postData["userImg"] as? String {
_userImg = userImg
}
if let postText = postData["postText"] as? String {
_postText = postText
}
_postRef = Database.database().reference().child("posts").child(_postKey)
}
}
I have been stuck on this for a while and any help would be much appreciated!
Did you check the data source methods are getting called?
Also please confirm if your cells have height.

Passing data in PageViewControllers swift

I have a page controller where I added UIViewControllers and display a bunch of form in each viewcontroller. The issue I am facing now is that I need to get the data supplied in each of the forms and save it which is done in the last view controller. I have tried using delegates but the moment the next button is clicked, the previous value stored becomes nil and only the value of the latest VC is displayed. How can I pass data in this textfields. Any help is appritated.
My delegate
protocol NextDelegate: AnyObject {
func next(pageIndex: Int, model: CreatePropertyModel)
func previous(pageIndex: Int, model: CreatePropertyModel)
}
how I created array of VC
lazy var controllers: [UIViewController] = {
let descVC = DescVC()
descVC.delegate = self
let priceVC = PriceVC()
priceVC.delegate = self
let featuresVC = FeaturesVC()
featuresVC.delegate = self
let picturesVC = PicturesVC()
picturesVC.delegate = self
return [descVC, priceVC, featuresVC, picturesVC]
}()
Model Example
class CreatePropertyModel: DictionaryEncodable {
var title: String?
var desc: String?
var property_type_id: Int?
var property_sub_type_id: Int?
var location_id: Int?
var currency: String?
var price: Int?
}
For all your steps, store it in a singleton.
protocol Answer {
var isDone: Bool { get }
}
class Answer1: Answer {
static public let updatedNotification = Notification.Name("Answer1Updated")
var name: String? {
didSet {
NotificationCenter.default.post(name: Answer1.updatedNotification, object: nil)
}
}
var isDone: Bool {
return name != nil
}
}
class Answer2: Answer {
var age: Int?
var isDone: Bool {
return age != nil
}
}
class Form {
static let shared = Form()
var answers: [Answer] = [Answer1(), Answer2()]
var isDone: Bool {
return answers.allSatisfy { $0.isDone == true }
}
private init() {}
func reset() {
answers = [Answer1(), Answer2()]
}
var answer1: Answer1? {
return Form.shared.answers.filter { $0 is Answer1 }.first as? Answer1
}
var answer2: Answer2? {
return Form.shared.answers.filter { $0 is Answer2 }.first as? Answer2
}
}
Then, in your view controller, read / write values like this.
class MyViewControllerForAnswer1: UIViewController {
var answer: Answer1? {
return Form.shared.answer1
}
var name: String? {
get {
return answer?.name
}
set {
answer?.name = newValue
}
}
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(answerUpdated(notification:)), name: Answer1.updatedNotification, object: nil)
}
#objc func answerUpdated(notification: Notification) {
// Update your content
}
}

Parse/Swift fatal error: use of unimplemented initializer 'init()

If anyone has any experience working with Parse using Swift, specifically subclassing PFObject..... I cannot figure out why the saveinbackground call below is throwing the above error?
Thanks!
func saveNewPerson(name: String) {
var myPeeps = [Person]()
if let currentUser = PFUser.currentUser() {
if currentUser.valueForKey("myPeeps")?.count < 1 {
myPeeps = []
} else {
myPeeps = currentUser.valueForKey("myPeeps") as! [Person]
}
let newPerson = Person(name: name, stores: [:])
myPeeps.append(newPerson)
currentUser.setObject(myPeeps, forKey: "myPeeps")
println(currentUser.valueForKey("myPeeps")?.count)
//WHY DOES THIS SAVE THROW ERROR FOR NOT INITIALZING?
currentUser.saveInBackgroundWithBlock{ succeeded, error in
if succeeded {
//3
println("Saved")
} else {
//4
if let errorMessage = error?.userInfo?["error"] as? String {
self.showErrorView(error!)
}
}
}
}
}
This is my Person class:
class Person: PFObject, PFSubclassing {
override class func initialize() {
struct Static {
static var onceToken : dispatch_once_t = 0;
}
dispatch_once(&Static.onceToken) {
self.registerSubclass()
}
}
static func parseClassName() -> String {
return "Person"
}
var name: String = ""
var stores: [String : Store] = [:]
init(name: String, stores: [String : Store]) {
self.name = name
self.stores = stores
super.init()
}
}
My Store Class:
class Store: PFObject, PFSubclassing {
override class func initialize() {
struct Static {
static var onceToken : dispatch_once_t = 0;
}
dispatch_once(&Static.onceToken) {
self.registerSubclass()
}
}
static func parseClassName() -> String {
return "Store"
}
var name: String = ""
var clothingSizes: [String: String] = [:]
init(name: String, clothingSizes: [String: String]){
self.name = name
self.clothingSizes = clothingSizes
super.init()
}
}
For both Parse subclasses, you need to make your inits convenience inits. Basically, what's going on is there is no implementation of init(), which you could do, by calling
override init() {
super.init()
}
Another option is to make your init a convenience init, and calling self.init()
convenience init(name: String, stores: [String : Store]) {
self.init()
self.name = name
self.stores = stores
}

Parse PFSubclassing in Swift of Object type

I'm pretty new to iOS/Swift/Parse and I'm trying to build a model of a class using PFSubclassing.
The data I'm trying to represent should look something like this
{
text: ""
location : {
name: "",
longitude: "",
latitude: ""
}
}
So fare the model I'm have is
class LocationModel {
var name: String?
var longitude: Float?
var latitude: Float?
}
class PostModel: PFObject, PFSubclassing {
class func parseClassName() -> String! {
return "Post"
}
#NSManaged var text: String?
var location: LocationModel?
}
The test property is being saved successfully but I'm unable to get the location properties to save.
The code I'm using to save a record to parse is
var test = PostModel()
test.location?.name = "ESB"
test.location?.latitude = 1
test.location?.longitude = 1
test.text = "This is a test post to see if this works!"
test.saveEventually { (success: Bool, error: NSError!) -> Void in
println(error)
println(success)
}
I did a lot of digging online but I'm unable to find a solution on how to represent an Object datatype in Swift using Parse PFSubclassing
Any help would be greatly appreciated.
Thank you
Here's my solution:
I will create a Hero object for example.
class Hero: PFObject, PFSubclassing {
#NSManaged var strengthPoint: Double
#NSManaged var name: String
static func parseClassName() -> String {
return "Hero"
}
init(strengthPoint: Double, name: String) {
super.init()
self.strengthPoint = strengthPoint
self.name = name
}
init(pfObject: PFObject) {
super.init()
self.strengthPoint = pfObject.object(forKey: "strengthPoint") as! Double
self.name = pfObject.object(forKey: "name") as! String
}
override init() {
super.init()
}
override class func query() -> PFQuery<PFObject>? {
let query = PFQuery(className: self.parseClassName())
query.order(byDescending: "createdAt")
query.cachePolicy = .networkOnly
return query
}
}
Now, after defining your model, you can use these methods to store and retrieve
Create your object in server
func createHero() {
let hero = Hero(strengthPoint: 2.5, name: "Superman")
hero.saveInBackground { (isSuccessful, error) in
print(error?.localizedDescription ?? "Success")
}
}
Retrieve object from server
func retrieveHero() {
let query = Hero.query()
query?.getFirstObjectInBackground(block: { (object, error) in
if error != nil {
print(error?.localizedDescription ?? "Something's wrong here")
} else if let object = object {
let hero = Hero(pfObject: object)
print(hero.strengthPoint) // 2.5
print(hero.name) // Superman
}
})
}
I have seen several different methods for PFSubclassing in Swift 1.2, but the following works best for me:
To begin with, make sure that you have the following in your Objective-C Bridging Header:
#import <Parse/PFObject+Subclass.h>
Here is a very basic example of subclassing PFObject:
import Foundation
import Parse
class Car: PFObject, PFSubclassing {
override class func initialize() {
self.registerSubclass()
}
static func parseClassName() -> String {
return "Car"
}
#NSManaged var model: String
#NSManaged var color: String
#NSManaged var yearManufactured: Int
}
So in your case, this would be:
class PostModel: PFObject, PFSubclassing {
override class func initialize() {
self.registerSubclass()
}
static func parseClassName() -> String {
return "Post"
}
#NSManaged var text: String?
}
Concerning your LocationModel...I'm a bit confused as to what exactly you are trying to accomplish with that. I hope this helps.

Resources