iOS Multiple save of parse objects with progress bar - ios

I found this very interesting approach (Parse : is it possible to follow progress of PFObject upload) and I tried to convert the objective-c category in a swift extension. But I am overstrained with the values types unsigned long.
Please have a look in the following code - it throws the exception (line: let progress:Int32 = Int32(100*count/numberOfCyclesRequired)): fatal error: floating point value can not be converted to Int32 because it is either infinite or NaN.
I am also not sure how to handle the __block prefix in swift that the count changes will also occur out of the block.
extension PFObject {
class func saveAllInBackground(objects: [AnyObject]!, chunkSize:Int, block: PFBooleanResultBlock!, progressBlock:PFProgressBlock) {
let numberOfCyclesRequired:Double = Double(objects.count / chunkSize)
var count:Double = 0
PFObject.saveAllInBackground(objects, chunkSize: chunkSize, block: block) { (trig:Bool) -> Void in
count++
let progress:Int32 = Int32(100*count/numberOfCyclesRequired)
progressBlock(progress)
}
}
class func saveAllInBackground(objects: [AnyObject]!, chunkSize:Int, block: PFBooleanResultBlock!, trigger:(Bool) -> Void) {
let range = NSMakeRange(0, objects.count <= chunkSize ? objects.count:chunkSize)
var saveArray:NSArray = (objects as NSArray).subarrayWithRange(range)
var nextArray:NSArray = []
if range.length < objects.count {
nextArray = (objects as NSArray).subarrayWithRange(NSMakeRange(range.length, objects.count-range.length))
}
PFObject.saveAllInBackground(saveArray) { (succeeded:Bool, error: NSError!) -> Void in
if (error == nil && succeeded && nextArray.count != 0) {
trigger(true)
PFObject.saveAllInBackground(nextArray, chunkSize: chunkSize, block: block, trigger: trigger)
} else {
trigger(true)
block(succeeded,error)
}
}
}
}
Thanks for your help in advance.

Related

Error handling with assert

Suppose I have a class.
I want to give an error in init if my class doesn't follow the described rule
class Puzzle {
var puzzle_array: [Int]
var zero_index: Int
public init(array: [Int]) {
assert(array.count == 9, "Array should be lenght 9")
assert(array.index(of: 0) != nil, "There should ne 0 in array")
puzzle_array = array
zero_index = puzzle_array.index(of: 0)!
}
}
Then I need to create several instances of this class in a loop. Some of them, won't satisfy the condition, I described in init and complier and I will get an error.
But what I want is to skip creation of this instances without executing an error. And I want to keep checking condition logic inside init.
My initial idea might be wrong, but I'll appreciate if you help me to make it more properly.
You could use a failable initialiser to accomplish this, your object will be nil if you don't satisfy your checks.
class Puzzle {
var puzzle_array: [Int]
var zero_index: Int
public init?(array: [Int]) {
guard array.count == 9, array.index(of: 0) != nil else {
return nil
}
puzzle_array = array
zero_index = puzzle_array.index(of: 0)!
}
}
I disagree with #colmg that you should use failable initialiser, because it's destroys information about what actually went wrong.
Instead you should use throwable initalizer:
class Puzzle {
var puzzle_array: [Int]
var zero_index: Int
public init(array: [Int]) throws {
try assert(array.count == 9, "Array should be lenght 9")
try assert(array.index(of: 0) != nil, "There should ne 0 in array")
puzzle_array = array
zero_index = puzzle_array.index(of: 0)!
}
}
Assuming that assert is not standard version here, but this function:
struct AssertError: Error {
let description: String
}
func assert(_ condition: #autoclosure () -> Bool, _ description: String) throws {
if !condition() {
throw AssertError(description: description)
}
}
Now you can do this:
do {
let puzzle = try Puzzle([0, 1, 2, 3, 4])
} catch let error {
// Here you can handle error, and see what exactly went wrong, instead of just knowing that initialisation failed
}
Or you can use more advanced version:
func assert(_ condition: #autoclosure () -> Bool, _ error: Error) throws {
if !condition() {
throw error
}
}
class Puzzle {
var puzzle_array: [Int]
var zero_index: Int
public init(array: [Int]) throws {
try assert(array.count == 9, PuzzleError.invalidArrayLength)
try assert(array.index(of: 0) != nil, PuzzleError.arrayContainsZero)
puzzle_array = array
zero_index = puzzle_array.index(of: 0)!
}
enum PuzzleError: Error {
case invalidArrayLength
case noZeroInArray
}
}

Completion handler not working as expected in Swift

I have these two functions below, using a completion handler. The questions is highlighted in comments of the 2nd function... why is the result part getting executed even before the asynchronous call in function checforViolationStatus() been completed.
func checkViolationStatus(usr: PFUser, completion: (result: Int32) -> Void) {
var violations: Int32 = 0
var query = PFQuery(className: PF_BLOCKEDUSERS_CLASS_NAME)
query.whereKey(PF_BLOCKEDUSERS_USER, equalTo: usr)
query.countObjectsInBackgroundWithBlock {
(count: Int32, error: NSError?) -> Void in
if error == nil {
print("Result = \(count)")
//The result here returned is 4, I can see it but always ZERO(0) gets printed in the main function. Unable to understand why.
violations = count
}
}
completion(result: violations)
}
func goToMainMenu() {
if PFUser.currentUser() != nil {
self.mCould.checkViolationStatus(PFUser.currentUser()!) {
(result: Int32) in
//QUESTION: result is getting returned as ZERO even before the actual asynchronous call in the checkforViolation function has been completed - why????
if result < 4 {
//Go to Main Menu Screen
print("result<=4 so calling segue")
self.performSegueWithIdentifier("segueLoginVCToMainVC", sender: nil)
} else {
print("result >=4, so should not be doing anything")
}
print("Number of Violations Received Back: \(result)")
}
}
}
Try change your function to this,you should call completion in the countObjectsInBackgroundWithBlock,this method is async.
Or this function return before countObjectsInBackgroundWithBlock is finished
func checkViolationStatus(usr: PFUser, completion: (result: Int32) -> Void) {
var violations: Int32 = 0
var query = PFQuery(className: PF_BLOCKEDUSERS_CLASS_NAME)
query.whereKey(PF_BLOCKEDUSERS_USER, equalTo: usr)
query.countObjectsInBackgroundWithBlock {
(count: Int32, error: NSError?) -> Void in
if error == nil {
print("Result = \(count)")
//The result here returned is 4, I can see it but always ZERO(0) gets printed in the main function. Unable to understand why.
violations = count
completion(result: violations)
}
}
}

Function produces expected type '(() -> ())?'; did you mean to call it with '()'?

Function produces expected type '(() -> ())?'; did you mean to call it with '()'?
I am getting the above error. Auto fix from Xcode does not help.
The error is on self.performOnCommunicationQueue():
func getAuthParams(authClosure:((error:NSError?) -> ())?) {
logDebug("Starting sync session with Max device")
if let statusError = self.assertReady() {
logError("Start sync session failed with error: \(statusError)")
if (authClosure != nil) {
authClosure!(error: statusError)
}
} else {
self.performOnCommunicationQueue() {
let error:NSError?
// Set random starting byte
let oAbsTime:[UInt64] = [mach_absolute_time()]
let payload:NSData = NSData(bytes: oAbsTime, length: 8)
let absTime:UInt8 = UnsafePointer<UInt8>(payload.bytes).memory
self.randomCryptoByte = (0x01 | ( absTime & 0xfe))
}
}
func sendAuthChallenge(authChal:String, completion:((error:NSError?) -> ())?) {
}
func performOnCommunicationQueue(closure:(()->())?){
if (closure != nil)
{
self.communicationQueue?.addOperationWithBlock(closure!)
}
}
I think this is what you want:
self.performOnCommunicationQueue({ () -> () in
let error:NSError?
// Set random starting byte
let oAbsTime:[UInt64] = [mach_absolute_time()]
let payload:NSData = NSData(bytes: oAbsTime, length: 8)
let absTime:UInt8 = UnsafePointer<UInt8>(payload.bytes).memory
self.randomCryptoByte = (0x01 | ( absTime & 0xfe))
})
This creates the void to void closure that performOnCommunicationQueue requires.
Also, check that your braces match up once you've made this change, I think you might be one short, which is why autocomplete wasn't a help.

Fixed size array in swift

i have a fixed size array as
var fieldNameArray = [String?](count: 4, repeatedValue: nil)
i am doing this to search if there is the element in array or not
if let temp = find(fieldNameArray,"profile_picture"){//i get a compile error here
//remove the data
....
}else{
println(" //append the value")
.....
}
But i get a compile time error as
Cannot invoke 'find' with an argument list of type '([(String?)],
String)'
I think i should unwrap it? How can i do it
UPDATED
SRWebClient.POST(registerURl)
.data(registerImagesArray, fieldName: fieldNameArray, data: parametersToPost)
.send({(response:AnyObject!, status:Int) -> Void in//here compile time error
println("response object: \(response)")
Again after i changed my array to fixed size array i got this error
Cannot invoke 'send' with an argument list of type '((AnyObject!, Int)
-> Void, failure: (NSError!) -> Void)
For efficiency you should not do what Manav Gabhawala suggests but write a find function yourself:
func myFind(array: [String?], value: String) -> Int? {
for (i, av) in enumerate(array) {
if av != nil && av! == value {
return i
}
}
return nil;
}
As Swift compiles to machine code, you will have nearly the same performance as with the standard library’s find.
Try using this instead (Swift 2.0):
if let index = fieldNameArray.indexOf("profile_picture") {
//remove the data using the index
....
} else {
print("// append the value")
.....
}
In swift 1.2 (a little inefficient but it works) :
if let temp = find(fieldNameArray.filter { $0 != nil}.map { $0! },"profile_picture") {
// Then same code as question...

Arithmetic operation fails on AnyObject

I'm doing an app in iOS Swift with Parse. I need to increment 1 every time a product is viewed. Following is my code to do that:
var prodQuery = PFQuery(className: "prodDet")
let id = prodId[currprod] as String
prodQuery.whereKey("objectId", equalTo:id)
prodQuery.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]!, error: NSError!) -> Void in
if error == nil {
for object in objects {
var key1: Int = object["key1Total"]! as Int
var key1New: Int = key1 + 1
object["key1Total"] = key1New
}
}
}
The above code gives error "#lvalue$T5 is not identical to AnyObject...". Is anything wrong the way I'm dealing with AnyObject object here; How do we do arithmatic calculation on Parse AnyObjects?
Could somebody please shed some light?
I sorted it out, seems like I should cast the object as PFObject and use object.setValue. The following is the working code:
var prodQuery = PFQuery(className: "prodDet")
let id = prodId[currprod] as String
prodQuery.whereKey("objectId", equalTo:id)
prodQuery.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]!, error: NSError!) -> Void in
if error == nil {
for object in objects as [PFObject] {
var keyVal = object["key1Total"]! as Int
var keyNew = keyVal + 1
object.setValue(keyNew, forKey: "key1Total")
object.saveInBackgroundWithBlock {
(success: Bool, error: NSError?) -> Void in
if (success) {
//
} else {
//
}
}
}
}
}
object.saveInBackgroundWithBlock {
(success: Bool, error: NSError?) -> Void in
if (success) {
//
} else {
//
}
}
}
}
}

Resources