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 {
// ...
}
Related
Im trying to iterate thorough and array of structures using generics and I keep getting this error. Value of type [T] has no member 'printMessage'
My 2nd questions is - What message would print? The statement in the Foo protocol extension or the statement in the struct instance?
Not sure what the issue is.. and its driving me insane!
protocol Foo {
func printMessage()
}
extension Foo {
func printMessage() {
print("Hello")
}
}
struct test: Foo {
func printMessage() {
print("Goodbye")
}
}
func MessagePrinter<T: Foo>(for message: [T]) {
for message in [message] {
message.printMessage()
}
For more clarity name the array in plural form and and the element in singular form.
And the square brackets in the for loop are wrong, the parameter is already an array
func messagePrinter<T: Foo>(for messages: [T]) {
for message in messages {
message.printMessage()
}
}
And please name functions always with starting lowercase letter.
The method in the protocol extension is called unless the method is implemented.
But consider that T is a single concrete type at runtime, you cannot call the method on a heterogenous Foo array like this
let messages : [Foo] = [Test()]
messagePrinter(for: messages)
You will get the error
Protocol 'Foo' as a type cannot conform to the protocol itself
To be able to call the method on an heterogenous array whose elements conform to Foo you have to declare
func messagePrinter(for messages: [Foo]) { ...
You are wrapping an array in another array here:
func MessagePrinter<T: Foo>(for messages: [T]) {
for message in [messages] {
message.printMessage()
}
}
(I've renamed your function argument from message to messages to make it clearer.)
When you write [messages] you end up with an array containing another array containing T, so the type is [[T]]. The single element message thus has type [T], and an array has no method printMessage.
What you want is this:
func MessagePrinter<T: Foo>(for messages: [T]) {
for message in messages {
message.printMessage()
}
}
As for what's printed when you execute it: that depends on what elements you feed it. If the elements implement printMessage those methods are called (e.g. "Goodbye"). Otherwise the default implementation you have provided for the protocol is called ("Hello").
I have an empty global array. The only simple thing I want to do is add an element to this array. It seems in swift this seemingly simple task is proving to be difficult. I am just left with an empty array and nothing is appending to my global array.
I can see that it prints out values in the for loop. So the values are actually there.
This is some stuff I have declared globally (Yes, I know global variables are bad but I will sort that out later):
struct HouseDetails: Decodable {
let median_price: String
let sale_year: String
let transaction_count: String
let type: String
}
var hsArray: [HouseDetails] = []
and in the viewDidLoad() function I have the data which I am storing in local variable "houses". When I loop through the array it prints median_price, showing that the values are there.
However when I do hsArray.append(h) it seems to do nothing.
let jsonUrlString = "https://data.melbourne.vic.gov.au/resource/i8px-csib.json"
guard let url = URL(string: jsonUrlString)
else { return }
URLSession.shared.dataTask(with: url) { (data, response, err) in
guard let data = data else { return }
do {
let houses = try JSONDecoder().decode([HouseDetails].self, from: data)
for h in houses {
hsArray.append(h)
print(h.median_price)
}
}
catch let jsonErr {
print("Error with json serialization", jsonErr)
}
}.resume()
Thank you for any help. In other languages I am used to being able to append an element to the end of an existing array, so I am sure it is just a small error.
Firstly, why don't you simply do
hsArray.append(contentsOf: houses)
instead of all that for loop
for h in houses {
hsArray.append(h)
print(h.median_price)
}
The issue might be the time at which you are using hsArray. See if the response is received after you use hsArray.
When I try to store arrays from Parse into a local array I can only access it within the findObjectsInBackgroundWithBlock {...}. When I print it outside of that block, it shows []...
Code :
var qArray : [[Int]] = []
override func viewDidLoad() {
super.viewDidLoad()
let query = PFQuery(className: "Trivia")
query.findObjectsInBackgroundWithBlock {
(objects: [PFObject]?, error: NSError?) in
if objects != nil {
if let objects = objects {
for object in objects {
self.qArray.append(object["mainPattern"] as! [Int])
}
print(self.qArray) // Prints a multi dimension array
}
}
if error != nil {
print(error)
}
}
print(self.qArray) // prints []
}
It's most likely because the array hasn't been populated yet because it's running in the background. You can try using dispatch_group to circumvent this issue.
I think you're misunderstanding what findInBackground means. It means the code after the callback continues to execute, so it calls query.findInBackground.... and then it continues with the next line, which is print(self.qArray). At some point later on, it hears back from the database and it executes all the code inside the Callback, which is when the array finally gets populated.
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!
I want the user to upload an image, and other users leave replies to that image. Everything works fine so far except if the row for that specific image object is empty, the app crashes.
fatal error: unexpectedly found nil while unwrapping an Optional value
Here is my code :
override func viewDidLoad() {
super.viewDidLoad()
var error = ""
var query = PFQuery(className:"Posts")
query.getObjectInBackgroundWithId(cellID) {
(objects: PFObject!, error: NSError!) -> Void in
if error == nil {
var array = objects.objectForKey("replies") as [String] // <- when error occurs the compiler point here.
for object in array {
self.repliesArray.append(object as String)
}
} else {
self.displayError("Error", error: "Error retreiving")
}
self.tableView.reloadData()
}
}
Does this work? You can't append null objects to an array so this appends an empty string instead of null. Also you need the explanation mark so that it can be nil.
var array = objects.objectForKey("replies") as [String!]
for object in array {
if object != nil {
self.repliesArray.append(object as String)
}
else {
self.repliesArray.append("")
}
}
query.getObjectInBackgroundWithId(cellID) {
(objects: PFObject!, error: NSError!) -> Void in
if error == nil {
var array = objects.objectForKey("replies") as [String] // <- when error occurs the compiler point here.
The key here is that you're accepting an implicitly unwrapped optional (PFObject!) which is implying a promise that it will never be nil, but is nil.
Ideally (*), the type should be (objects: PFObject?, error: NSError?) -> Void to make the optionals explicit and force you to do the nil checking that is required. I believe you can do this even if the caller claims to send you (PFObject!, NSError!) -> Void, since I think Swift will make the implicit->explicit conversion for you.
If that's still impossible, then you will have to manually verify that objects is not nil before using it. (This is unlike #Dehli's solution, which checks that the things contained in objects are non-nil. That's not the problem; the problem is that objects itself is nil.)
(*) I say "ideally" here in terms of the existing interface. Passing a tuple of optional value/error is a lousy pattern. The better solution is to use something like a Result object.