0

I have Parse class called Product that has 238 rows. Note that this class is not the Parse.com implementation of Product, it is a custom class implemented by myself, as I didn't require all the columns Parse adds to their Product class.

The Product class has a Pointer column (basically a foreign key in SQL tables), called ShopId, because each product belongs to a specific Shop (I have a Parse class called Shop with an ObjectId column used in the Product Pointer.

My Product class also has a File column called imageFile that holds the image of the product.

I want to download all Products from a specific shop, unpackage their image file and put it in my Swift Product class which consists of the PFObject of the Parse Product, and a UIImageView and a UIImage. Here is my Product Class in Swift:

class Product {
    private var object: PFObject
    private var imageView: MMImageView!
    private var image: UIImage

    init(object: PFObject, image: UIImage) {
        self.object = object
        self.image = image
    }

    func getName() -> String {
        if let name = object["name"] as? String {
            return name
        } else {
            return "default"
        }
    }

    func setImageView(size: CGFloat, target: DressingRoomViewController) {
        self.imageView = MMImageView(frame:CGRectMake(0, 0, size, size))
        imageView.contentMode = UIViewContentMode.ScaleAspectFit
        imageView.image = self.image
        imageView.setName(object["category"] as! String)
        imageView.backgroundColor = UIColor.clearColor()
        imageView.userInteractionEnabled = true
        let tapGestureRecognizer =
        UITapGestureRecognizer(target: target, action: "imageTapped:")
        tapGestureRecognizer.numberOfTapsRequired = 1
        imageView.addGestureRecognizer(tapGestureRecognizer)
    }

    func getImageView() -> MMImageView {
        return self.imageView
    }
}

I am currently downloading all the products just fine, and getting their image file and creating my Swift Products with their images. However my UIProgressView logic is slightly off. I have the UIProgressView running for every product, every time I unpackage the product image. I need to shift the Parse.com ProgressBlock out of the getProduct swift function and into the loadProducts @IBAction. When I try it, it causes a lot of errors before compilation. How do I shift the ProgressBlock up to the loadProducts @IBAction? Here is my current code:

//
//  ChooseShopViewController.swift
//  MirrorMirror
//
//  Created by Ben on 12/09/15.
//  Copyright (c) 2015 Amber. All rights reserved.
//

import UIKit
import Parse

class ChooseShopViewController: UIViewController {

    var progressView: UIProgressView?
    private var allProducts: [Product] = []
    private var categories: [ProductCategory] = []

    @IBAction func loadProducts(sender: AnyObject) {
        let shopQuery = PFQuery(className:"Shop")
        shopQuery.getObjectInBackgroundWithId("QjSbyC6k5C") {
            (glamour: PFObject?, error: NSError?) -> Void in
            if error == nil && glamour != nil {
                let query = PFQuery(className:"Product")
                query.whereKey("shopId", equalTo: glamour!)
                query.findObjectsInBackgroundWithBlock {
                    (objects: [AnyObject]?, error: NSError?) -> Void in
                    self.getAllProductsAndCategories(objects, error: error)
                }
            } else {
                print(error)
            }
        }

    }
    override func viewDidLoad() {
        super.viewDidLoad()
        // Create Progress View Control
        progressView = UIProgressView(  progressViewStyle:
                                        UIProgressViewStyle.Default)
        progressView?.center = self.view.center
        view.addSubview(progressView!)
    }

    override func prepareForSegue(  segue: UIStoryboardSegue,
        sender: AnyObject?) {
        if (segue.identifier == "dressingRoom") {
            ShopDisplay.sharedInstance.setAllProducts(self.allProducts)
            ShopDisplay.sharedInstance.setAllProductCategories(self.categories)
        }
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }



    func getAllProductsAndCategories(objects: [AnyObject]?, error: NSError?) {
        if error == nil {
            if let objects = objects as? [PFObject] {
                for product in objects {
                    self.getCategory(product)
                    self.getProduct(product)
                }
            }
        } else {
            print("Error: \(error!) \(error!.userInfo)")
        }
    }

    func getCategory(product: PFObject) {
        if let category = product["category"] as? String {
            var alreadyThere: Bool = false
            for item in self.categories {
                if category == item.rawValue {
                    alreadyThere = true
                    break
                }
            }
            if alreadyThere == false {
                self.categories.append(ProductCategory(rawValue: category)!)
            }
        }
    }

    func getProduct(product: PFObject) {
        if let productImage = product["imageFile"] as? PFFile {
            productImage.getDataInBackgroundWithBlock ({
                (imageData: NSData?, error: NSError?) -> Void in
                if let imageData = imageData {
                    let image = UIImage(data:imageData)
                    self.allProducts.append(
                        Product(object: product, image: image!))
                }
                if let downloadError = error {
                    print(downloadError.localizedDescription)
                }
            }, progressBlock: {
                (percentDone: Int32) -> Void in
                    self.progressView?.progress = Float(percentDone)
                if (percentDone == 100) {
                    //self.performSegueWithIdentifier("dressingRoom", sender: UIColor.greenColor())
                }
            })
        }
    }
}
BeniaminoBaggins
  • 11,202
  • 41
  • 152
  • 287

2 Answers2

1

I decided to not use the progressBlock, and instead to update my UIProgressView manually with a calculation. So here is the code. It's a little rusty. I could refactor now and maybe implement a calculated variable to make it cleaner. If my solution is a bad practice then I'm appreciative if that gets pointed out, and a better solution suggested (It doesn't seem good for performance to check the UIProgressView.progress value every iteration to perform the completion task of performing the segue).

import UIKit
import Parse

class ChooseShopViewController: UIViewController {

    var progressView: UIProgressView?
    private var allProducts: [Product] = []
    private var categories: [ProductCategory] = []
    static var numberOfProducts: Float = 0

    @IBAction func loadProducts(sender: AnyObject) {
        let shopQuery = PFQuery(className:"Shop")
        shopQuery.getObjectInBackgroundWithId("QjSbyC6k5C") {
            (glamour: PFObject?, error: NSError?) -> Void in
            if error == nil && glamour != nil {
                let query = PFQuery(className:"Product")
                query.whereKey("shopId", equalTo: glamour!)
                query.findObjectsInBackgroundWithBlock {
                    (objects: [AnyObject]?, error: NSError?) -> Void in
                    ChooseShopViewController.numberOfProducts =
                        Float((objects?.count)!)
                    print(ChooseShopViewController.numberOfProducts)
                    self.getAllProductsAndCategories(objects, error: error)
                }
            } else {
                print(error)
            }
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        // Create Progress View Control
        progressView = UIProgressView(  progressViewStyle:
                                        UIProgressViewStyle.Default)
        progressView?.center = self.view.center
        progressView?.progress = 0.00
        view.addSubview(progressView!)
    }

    override func prepareForSegue(  segue: UIStoryboardSegue,
        sender: AnyObject?) {
        if (segue.identifier == "dressingRoom") {
            ShopDisplay.sharedInstance.setAllProducts(self.allProducts)
            ShopDisplay.sharedInstance.setAllProductCategories(self.categories)
        }
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    func getAllProductsAndCategories(objects: [AnyObject]?, error: NSError?) {
        if error == nil {
            if let objects = objects as? [PFObject] {
                for product in objects {
                    self.getCategory(product)
                    self.getProduct(product)
                }
            }
        } else {
            print("Error: \(error!) \(error!.userInfo)")
        }
    }

    func getCategory(product: PFObject) {
        if let category = product["category"] as? String {
            var alreadyThere: Bool = false
            for item in self.categories {
                if category == item.rawValue {
                    alreadyThere = true
                    break
                }
            }
            if alreadyThere == false {
                self.categories.append(ProductCategory(rawValue: category)!)
            }
        }
    }

    func getProduct(product: PFObject) {
        if let productImage = product["imageFile"] as? PFFile {
            productImage.getDataInBackgroundWithBlock ({
                (imageData: NSData?, error: NSError?) -> Void in
                if let imageData = imageData {
                    let image = UIImage(data:imageData)
                    self.allProducts.append(
                        Product(object: product, image: image!))
                    self.progressView?.progress += (100.00 /
                        ChooseShopViewController.numberOfProducts) / 100.00
                    print(self.progressView?.progress)
                    if self.progressView?.progress == 1 {
                        self.performSegueWithIdentifier("dressingRoom",
                            sender: UIColor.greenColor())
                    }
                }
                if let downloadError = error {
                    print(downloadError.localizedDescription)
                }

            })
        }
    }
}
BeniaminoBaggins
  • 11,202
  • 41
  • 152
  • 287
0

I found this on the Parse website. It may be useful as it has a block that shows the percentage done that updates regularly during the download!

    let str = "Working at Parse is great!"
    let data = str.dataUsingEncoding(NSUTF8StringEncoding)
    let file = PFFile(name:"resume.txt", data:data)
    file.saveInBackgroundWithBlock({
          (succeeded: Bool, error: NSError?) -> Void in
          // Handle success or failure here ...
       }, progressBlock: {(percentDone: Int32) -> Void in

     // Update your progress spinner here. percentDone will be between 0 and 100.

 })

Did you find a better solution? besides this? I am trying to do something similar.

Gugulethu
  • 1,426
  • 3
  • 18
  • 36
  • This seems to only work with PFFIle and not PFObject that contains a PFFIle. Im stumped – Pippo Jan 22 '17 at 23:21