Swift crashes when listing to value changes on Firebase - arrays

Trying to get my question answered (Retrieve Boolean array from Firebase) I am trying to solve the question by myself. I think I am almost done but 1 thing is bugging me. I want to retrieve a boolean array and store it in a boolean array in my app.
This is what I got:
if snapshot.key == "whatAchievementsunlocked"
{
whatAchievementsUnlocked.removeAll()
if let snapshots = snapshot.children.allObjects as? [FIRDataSnapshot] {
for x in snapshots
{
print(x.value!) //prints 0
var valueForIndexX = x.value as! Int //crash
print(valueForIndexX)
if valueForIndexX == 0
{
whatAchievementsUnlocked.append(false)
}
else
{
whatAchievementsUnlocked.append(true)
}
}
}
}
Why would it crash there? If I use as?, the output would be nil. I also tried to use var valueForIndexX = Int(x.value!) But that gives me an error: Cannot invoke initializer for type Int with an argument list of type (Any). The output of x.value! is clearly an Int, why would this result in an error? The debug just shows "(lldb)". What am I doing wrong?\

Related

Check if optional array is empty in Swift

I realize there are a ton of questions on SO with answers about this but for some reason, I can't get any to work. All I want to do is test if an array has at least one member. For some reason, Apple has made this complicated in Swift, unlike Objective-C where you just tested if count>=1. The code crashes when the array is empty.
Here is my code:
let quotearray = myquotations?.quotations
if (quotearray?.isEmpty == false) {
let item = quotearray[ Int(arc4random_uniform( UInt32(quotearray.count))) ] //ERROR HERE
}
However, I get an error:
Value of optional type '[myChatVC.Quotation]?' must be unwrapped to refer to member 'subscript' of wrapped base type '[myChatVC.Quotation]'.
Neither of the fix-it options to chain or force unwrap solve the error. I have also tried:
if array != nil && array!. count > 0 and if let thearray = quotearray
but neither of those will work either
Thanks for any suggestions.
randomElement already exists, so don't reinvent the wheel:
var pepBoys: [String]? = ["manny", "moe", "jack"]
// ... imagine pepBoys might get set to nil or an empty array here ...
if let randomPepBoy = pepBoys?.randomElement() {
print(randomPepBoy)
}
The if let will fail safely if pepBoys is nil or empty.
You could unwrap the optional array and use that like this, also use the new Int.random(in:) syntax for generating random Ints:
if let unwrappedArray = quotearray,
!unwrappedArray.isEmpty {
let item = unwrappedArray[Int.random(in: 0..<unwrappedArray.count)]
}
check the first element is exist or not
var arr: [Int]? = [1, 2, 3, 4]
if let el = arr?.first{
print(el)
}
I would recommend use guard statement
guard let array = optionalArray, !array.isEmpty else { return }

Swift complains of undeclared type, but that doesn't seem to be the problem

I am trying to fetch records in Core Data.
func fetchOrg() {
var **internalOrganization** = [InternalOrganizationMO]() //NSManagedClass
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "InternalOrganization")
fetchRequest.returnsObjectsAsFaults = false
do {
let result = try managedObjectContext.fetch(fetchRequest) as **internalOrganization** **////Compiler flags Error here**
} catch {
fatalError("Failed to fetch internal organization \(error)")
}
}
InternalOrganizationMO is a ManagedObject Class corresponding to the object model and it seems clear to me that internalOrganization is declared to be an array of those objects, so the flagged error seems to be off. My understanding is that this is the kind of object that is supposed to be the target of a fetch, but I am definitely on the learning curve here.
Is it that the fetch needs to be targeted at a Type instead of an array--thus, the complaint about my not providing a named type? If that is it, am I simply supposed to provide the ManagedObject? If that is so, how on earth do I determine how many records are returned?
Is this really better than just using the interface to SQLite?
Thanks, sorry for the rant.
You typecast objects as types, not objects as objects.
Example:
let a = b as! [String]
and not:
let a = [String]()
let c = b as! a
Solution #1:
Change NSFetchRequest<NSFetchRequestResult> to specify the type to be explicitly InternalOrganizationMO, like so:
NSFetchRequestResult<InternalOrganizationMO>
This fetchRequest has now the proper associated type InternalOrganizationMO and will be used accordingly to return objects of this type.
You then won't need to typecast result again and the following code should work just fine:
func fetchOrg() {
let fetchRequest = NSFetchRequest<InternalOrganizationMO>(entityName: "InternalOrganization")
do {
let internalOrganization = try managedContext.fetch(fetchRequest)
/*
internalOrganization will be of type [InternalOrganizationMO]
as that is the return type of the fetch now.
*/
//handle internalOrganization here (within the do block)
print(internalOrganization.count)
}
catch {
fatalError("Failed to fetch internal organization \(error)")
}
}
Solution #2:
If you want this method to work even if the fetchRequest try or typecasting fails then you can do this:
func fetchOrg() {
var internalOrganization = [InternalOrganizationMO]()
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "InternalOrganization")
do {
internalOrganization = try managedContext.fetch(fetchRequest) as? [InternalOrganizationMO]
/*
You ofcourse wouldn't want the above optional binding to fail but even if it
does, atleast your method can stay consistent and continue with an empty array
*/
}
catch {
fatalError("Failed to fetch internal organization \(error)")
}
//handle internalOrganization here
print(internalOrganization.count)
}
The choice of solution, depends on your design and requirements.

Find an index in an array of structs based on 1 component SWIFT

I have an array of Zombies, each Zombie is a struct as follows:
struct Zombie {
var number: Int
var location : Int
var health : Int
var uid : String
var group: Int
}
I have an array of Zombies
ZombieArray = [Zombie1, Zombie2, Zombie3]
I have to update the zombieHealth when it changes, but I need to find which Zombie it is first. Each zombie's Location, Number, and UID is unique, so any of those can be searched. Here's what I tried and got an error:
let zombieToUpdate : Zombie?
for zombieToUpdate in self.zombieArray {
if zombieToUpdate.location == thisZombieLocation {
let indexOfUpdateZombie = zombieArray.indexOf(zombieToUpdate)
self.zombieArray.remove(at: indexOfUpdateZombie)
self.zombieArray.append(thisNewZombie)
}
}
I'm getting the following error:
Cannot convert value of type 'Zombie' to expected argument type '(Zombie) throws -> Bool'
This error occurs on line:
let indexOfUpdateZombie = zombieArray.indexOf(zombieToUpdate)
Since Zombie doesn't conform to Equatable, you can't use index(of:).
If you don't want to add that functionality, you have a couple of choices for implementing your logic.
Option 1 - Use index(where:):
if let index = zombieArray.index(where: { $0.location == thisZombieLocation }) {
zombieArray.remove(at: index)
zombieArray.append(thisNewZombie)
}
No need for the loop.
Option 2 - Iterate with an index:
for index in 0..<zombieArray.count {
let zombieToUpdate = zombieArray[index]
if zombieToUpdate.location == thisZombieLocation {
zombieArray.remove(at: index)
zombieArray.append(thisNewZombie)
break // no need to look at any others
}
}

Populating an array using Parse queries (Swift)

I am trying to populate an array with a query from my parse database. When I try to print out the content of the array, I get an EXC_BAD_INSTRUCTION error. It doesn't seem like I'm properly appending new elements into my array, I'd appreciate any sort of tips
func loadSampleTasks() {
tasks = [Task]()
let query = PFQuery(className: "Task")
query.whereKey("TaskName", equalTo: "kenny")
query.findObjectsInBackgroundWithBlock() {
(objects: [PFObject]?, error: NSError?) -> Void in
if error == nil && objects != nil {
self.parseResults(objects!)
print(self.tasks) // this prints out Kenny object as expected
}
}
print(tasks) // prints an empty array
}
func parseResults(objects: Array<PFObject>){
for object in objects { //looping through returned data
print("no error in Parse lookup")
let parseResult1 = Task(name: object["TaskName"] as! String)
parseResult1?.completed = object["Completed"] as! Bool
print("Parse result in object loop: \(parseResult1!.name)")
tasks.append(parseResult1!)
}
}
Any help much appreciated!
To what thefredelement said, "This is happening because you're getting your parse results in a closure, which means it may execute after the function itself has already returned." Before this happens, though, it won't work properly and you'll come back with that error.
I got it to work. I had to tableView.reloadData() within the closure. Thank you so much for the help!

Error with APAddressBOOK: "fatal error: Array index out of range"

I am receiving this strange error every time I process an address book ( using APAddressBOOK via cocapods) in Swift and after some debugging I found out that and empty object (record with no phone number) within the array causes this issue but am not sure how to get rid of it.
Here is my code:
func getPersonsNo(contactno: AnyObject) -> String {
println(contactno) // **when the object is empty I get this "[]"**
if let numberRaw = contactno.phones?[0] as? String { // at this statement the program crashes with a fatal error
println(numberRaw)
return numberRaw)
}
return " "
}
Any clues what is happening here?
The subscript of an Array doesn't return an optional to indicate if an index is out of range of the array; instead your program will crash with the message “fatal error: Array index out of range”. Applying this your code: when contactno is empty your program will crash because there's no element at index 0 of the array.
To easiest way to solve your problem is probably to use the first property on Array. first will return the first element in the array, or nil if the array is empty. Taking a look at how first is declared:
extension Array {
var first: T? { get }
}
As of Swift 2, first has been made an extension of the CollectionType protocol:
extension CollectionType {
var first: Self.Generator.Element? { get }
}
You'd use this like so:
if let numberRaw = contactno.phones?.first as? String {
// ...
}

Resources