I am in the process of updating all my swift syntax after updating to xcode 7.3
In the process, I got some errors about ambiguous use of subscript swift and I believe that this error is also causing the Signal Fault.
The code in question:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
var arry:NSArray = Array(self.participants)
arry = arry.sort {
item1, item2 in
// ambiguous use of subscript swift error for both these lines
let date1 = item1["fullName"] as String
let date2 = item2["fullName"] as String
return date1 > date2
}
Edit
Declaration of participants comes from another controller here:
func gotoMembers(){
let set:NSSet = self.conversation.participants
let arr = set.allObjects //Swift Array
UserManager.sharedManager.queryForAllUsersWithCompletion(arr as! [String], completion:{ (users: NSArray?, error: NSError?) in
if error == nil {
//participants declared here and passed into the participant controller
let participants = NSSet(array: users as! [PFUser]) as Set<NSObject>
let controller = ParticipantTableViewController(participants: participants, sortType: ATLParticipantPickerSortType.FirstName)
controller.delegate = self
self.navigationController?.pushViewController(controller, animated:true);
} else {
appDelegate.log.error("Error querying for All Users: \(error)")
}
})
}
Update
First of all use Swift native types a much as possible, for example the type of the contents of an NSArray object is unspecified.
Second of all use type annotations as few as possible, in the case
var array = Array(self.participants)
without an annotation you get a Swift Array for free and the compiler knows the type of the contents which is PFUser. The function sortInPlace() sorts the array itself without a return value and you have to forced downcast the fullName values to String
array.sortInPlace {
user1, user2 in
let date1 = user1["fullName"] as! String
let date2 = user2["fullName"] as! String
return date1 > date2
}
and use the proper type Set<PFUser> rather then Set<NSObject> and probably users: [PFUser]? in the completion handler rather then users: NSArray?
Edit: The beginning of the queryForAllUsersWithCompletion method is supposed to look like
UserManager.sharedManager.queryForAllUsersWithCompletion(arr as! [String], completion:{ (users: [PFUser]?, error: NSError?) in
if error == nil {
//participants declared here and passed into the participant controller
let participants = Set<PFUser>(array: users!)
Related
In the overall scheme of things I am trying to compare the user's multiple selections from a tableview and compare them to my Parse database. So my problem is twofold 1. Is my current code going about it the correct way? and 2. How can I convert value type Bool to argument type PFObject?
Cannot convert value of type '() -> Bool' to expected argument type 'PFObject'
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showResults" {
// Get reference to destination view controller
let destination = segue.destinationViewController as! CollectionViewController
if let selectedItem = tableView.indexPathsForSelectedRows{
for var i = 0; i < selectedItem.count; ++i {
var currentPath = selectedItem[i] as NSIndexPath
var cell = tableView.cellForRowAtIndexPath(currentPath)
if let cell = cell {
//add major(s) selected to data variable in collectionview as type text(String)
destination.data.append(cell.textLabel!.text!)
}
let imagesQuery = PFQuery(className:"CollegeImages")
imagesQuery.selectKeys(["name"])
imagesQuery.findObjectsInBackgroundWithBlock({(objects: [PFObject]?, error: NSError?) in
if error == nil {
if let returnedobjects = objects {
//objects array isn't nil
//loop through the array to get each object
for object in returnedobjects {
print(object["name"] as! String)
}
}
}
})
let majorSelected:String = (cell?.textLabel!.text!)!
let query = PFQuery(className:"CollegeMajors")
query.selectKeys(["Major"])
query.findObjectsInBackgroundWithBlock ({
(objects: [PFObject]?, error: NSError?) in
if error == nil {
// The find succeeded.
print("Successfully retrieved \(objects!.count) majors.", terminator: "")
// Do something with the found objects
if let returnedobjects = objects {
if returnedobjects.contains ({($0["Major"] as? String)! == majorSelected}) && query.selectKeys(["College Name"]) == imagesQuery.selectKeys(["name"]) {
print("your in!") // transition to the new screen
}
else {
print("your out.") // do whatever
}
}
} else {
// Log details of the failure
print("Error: \(error!) \(error!.userInfo)", terminator: "")
}
})
}
}
}
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
//Keep track of which major(s) the user selected
let path = tableView.indexPathForSelectedRow!
if let cell = tableView.cellForRowAtIndexPath(indexPath){
//Trigger the segue to go to the collection view
self.performSegueWithIdentifier("showResults", sender: self)
}
}
Your line of interest holds several problems.
First of all, you are passing a closure {["returnedobjects"] as? String == path } to contains(_:) method, but the closure does not take any arguments. You need to pass a closure taking one argument, where its type being the same as the element of the array.
Second, inside the closure, ["returnedobjects"] is an array, so, ["returnedobjects"] as? String always fails and generates nil. You need to change this part to a meaningful expression producing String, you may need to utilise the PFObject instances passed to this closure.
Third, you declare path as:
let path = tableView.indexPathForSelectedRow!
which means the local variable has type NSIndexPath. So, even if the left hand side of == returns a valid String, you cannot compare String to NSIndexPath. You may need to get a String value before comparing.
With considering all three above and with some guess, you need to:
Add one line below let path = ...
let majorSelected: String = (Some expression to retrieve "major" from the `path`)
Change the closure in the line containing contains as:
if returnedobjects.contains ({$0["Major"] as? String == majorSelected }) {
I'm trying to get the name of a Client that is owner of a Vitrine.
Here is my code.
To save the data I'm doing this:
self.vitrine["username"] = PFUser.currentUser()?.username
self.vitrine["name"] = nameTextField.text as String
var relation = self.vitrine.relationForKey("client")
relation.addObject(self.client)
self.vitrine.saveEventually { (success, error) -> Void in
if(error == nil){
}else{
println(error?.userInfo)
}
self.fetchAllVitrines()
self.navigationController?.popToRootViewControllerAnimated(true)
}
}
And it works. In the Parse I can see the relation working.
I'm trying to access data of relation doing this:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("vitrineCell", forIndexPath: indexPath) as! VitrineTableViewCell
var object: PFObject = self.vitrineObjects.objectAtIndex(indexPath.row) as! PFObject
cell.nomeLabel.text = object["name"] as? String
let x: PFRelation = object["client"] as! PFRelation
// cell.clientNameTextView.text = object["client"]["name"] as String
return cell
}
But when I log the data inside client column is that what appear for me:
<PFRelation: 0x7b7f1390, 0x7b7cfdc0.client -> Client>
Please somebody helps me. I'm on that for 2 days. I read more than 3 times the Parse Doc. and I don't find a way to do that.
Regards,
Diogo Amaral
Well, I add the code:
query!.getFirstObjectInBackgroundWithBlock {
(object: PFObject?, error: NSError?) -> Void in
if error != nil || object == nil {
println("The getFirstObject request failed.")
} else {
println(object)
cell.clientNameTextView.text = object["name"] as? String
}
}
But the line: cell.clientNameTextView.text = object["name"] as? String
throws me an error. "Cannot assign a value of type 'String?' to a value of type 'String!'"...
I already tried:
cell.clientNameTextView.text = object["name"] as! String
cell.clientNameTextView.text = object["name"] as String
cell.clientNameTextView.text = object["name"] as? String
How do I can fix that?
If you're using a relation (rather than a pointer) then you need to be aware that relations store arrays of their destination objects. So, when you execute
var relation = self.vitrine.relationForKey("client")
relation.addObject(self.client)
you're adding self.client to the array of clients. If a vitrine can only have a single owner, and this should be stored in the client field, then you probably want to use a Pointer rather than a Relation.
Because of this array, your code as written just won't work. You need get the relation from your vitrine object, query it, extract the element from the array you want, and then you can use it.
let x: PFRelation = object["client"] as! PFRelation
let query = x.query()
// Lets say that you are only interested in the first element in your array...
query.getFirstObjectInBackgroundWithBlock { first, error in
// Should do error checking here
cell.clientNameTextView.text = first!.objectForKey("name") as? String
}
This method is also a bit inefficient. You should use some form of caching, or ideally, determine if you really do need to use a Relation or if a Pointer will suffice. If a Pointer will do, you can also include the data it points to when you run your original query via the includeKey method on PFQuery. You can't use includeKey on a Relation.
I am loading some data into NSUserDefaults on application startup and when the user views the according View loading the data out into a TableView. I'm having an issue with the data types though and always seem to get an error whichever way I try.
Where am I going wrong. I am skipping some code and only giving the important parts. I keep getting errors like "Can't assign Bool to AnyObject"
// startup function which loads the data
var categorys : [[String:Bool]] = [["All": true]]
for (index: String, category: JSON) in output {
categorys.append([category["name"].string!: true])
}
NSUserDefaults.standardUserDefaults().setObject(categorys, forKey: "categorys")
// view controller
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var categorys: [[String:Bool]] = []
func buildTableView() {
if let categorys = NSUserDefaults.standardUserDefaults().arrayForKey("categorys") {
for category in categorys {
self.categorys.append([category["name"] as! String: category["selected"] as! Bool])
}
// Was trying to just assign straight away but fails
// self.categorys = categorys
}
}
// then later on I want to be able to do the following
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
cell.textLabel?.text = self.categorys[indexPath.row]["name"] as? String
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if (self.categorys[indexPath.row]["selected"] as? Bool == true) {
self.categorys[indexPath.row]["selected"] = "false"
}
}
}
I should probably also mention i was trying a different datatype before which is more logical for my usage but also had similar issues.
["name": [category["name"].string!, "selected": true]]
I should also mention this is data for my tableview and I want to update the boolean value for if the cell is selected to not.
You said in the comment that you've got an error about "finding nil", but it's not really the same as an error about "Can't assign Bool to AnyObject"...
For your nil error, you have to replace those forced typecasts:
category["name"] as! String
with optional binding:
if let name = category["name"] as? String {
}
Now it will fail but not crash if a property is nil, and will properly append to the array of dictionaries:
let category = ["name":"me", "selected":true]
var categorys: [[String:Bool]] = []
if let name = category["name"] as? String, let selected = category["selected"] as? Bool {
categorys.append([name:selected])
} else {
// oops, a value was nil, handle the error
}
Same for your other operations, don't use forced casts:
if let myBool = self.categorys[indexPath.row]["selected"] where myBool == true {
self.categorys[indexPath.row]["selected"] = false
}
And since self.categorys[indexPath.row]["selected"] is a Bool type, don't assign a string like "false" but the actual false value.
In order to use Bool in NSUserDefaults, you must use setBool: forKey: :
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "key")
otherwise it's problematic and you have to do workarounds, like storing the Bool in NSNumber or String.
I am using a framework that asynchronously loads data from the cache of the device and returns it. However, the method, whose definition I can't change, returns an array of AnyObjects. I have class called Attendee of which I am certain the objects belong to. I need to transform the [AnyObject] array into an [Attendee] array. Currently, I am doing this by integrating through the returned array, typecasting each individual object, and storing it elsewhere. I tried to just typecast the array, but I get a swift run-time error with the typecast. Is there a more efficient way to transform the [AnyObject] into [Attendee] than just looping through it?
var attendees: [Attendee] = []
let query = PFQuery(className: "Attendee")
query.findObjectsInBackgroundWithBlock { (objects: [AnyObject]!, error: NSError!) -> Void in
if( error == nil ) {
attendees = objects as! [Attendee]
} else {
println("Error fetching attendees: \(error) ")
}
}
Attendee Class Definition
class Attendee: PFObject, PFSubclassing {
override class func initialize() {
var onceToken : dispatch_once_t = 0;
dispatch_once(&onceToken) {
self.registerSubclass()
}
}
class func parseClassName() -> String! {
return "Attendee"
}
}
Maybe map will work?
let arrayOfAttendee : [Attendee] = arrayOfAnyObjects.map{$0 as! Attendee}
Is there a more efficient way to transform the [AnyObject] into [Attendee] than just looping through it
No. Even the direct cast with as! conceals a loop, since it must cast the individual objects. That, in fact, is why you're crashing - the concealed loop reaches an object in the array that it can't cast.
To typecast an array anyObjectArray, use as!. Make sure you have the exclamation mark.
let anyObjectArray: [AnyObject] = ["Hi", "1", "3"]
let castedArray = anyObjectArray as! [String]
Hope this helps!
If you are receiving a run-time error with the typecasting operator as! that could mean that the array doesn't only contain instances of the Attendee class.
The compiler cannot know for certain until runtime that all of the elements in the array can be downcasted to the type you specified so you could receive a run-time error if you use a forced cast.
There are two ways to downcast the array:
Safe one:
if let attendeeArray = anyObjectArray as? [Attendee] {
// If we are here means that attendeeArray contains only Attendee objects
for attendee in attendeeArray{
}
} else{
// You should warn someone if you were expecting just attendee elements
}
If any element in the Swift array is not actually a Attendee object at runtime, the cast returns nil and the for in block won't be executed.
Unsafe one:
for attendee in anyObjectArray as! [Attendee] {
// aView is of type UIView
}
This cast is a forced cast, and results in a runtime error if the cast does not succeed.
Finally if there are elements of different types in you array you will need to filter the array.
let attendeeArray = anyObjectArray.filter() {
return ($0 is Attendee)
}
I want to query a custom object from an NSArray, here is a function I wrote:
func retrieveObject (objects: [CustomClass], identifier : String) -> CustomClass {
var retrievedObject : [CustomClass] = objects.filter({
return $0.identifier == identifier
})
return retrievedObject.first!
}
When I use it, the resulted object seems to have lost most of the property values in that object:
let obj : CustomClass = self.retrieveObject(objectList as! [CustomClass], "one")
println("\(obj.propertyA)")
The result shows "", while printing the object from the original object list shows value:
println("\(objectList.first!.propertyA)")
What seems to be the issue?
More Information:
The objectList above is a result of an asynchronous web request, let's assume that the objects in it are problem-free because they return the correct property value when printed.
Code of one step above before the array filter:
private var objectList : [AnyObject]!
private var object : CustomClass
self.webRequest(request, onSuccess: {(objects: [AnyObject]!) -> Void in
self.objectList = objects
self.object = self.retrieveObject(self.objectList, identifier: "one")
//I tried passing both self.objectList and objects
})
Problem Solved
This is not an issue with Swift or whatever. This is a data issue. The above code works fine.
Not sure, there doesn't seem to be anything wrong with the code you've provided us. I've recreated it in playgrounds and seems to print normally.
Code:
class CustomClass {
let identifier: String
let propertyA = "Printing!"
init(identifier: String) {
self.identifier = identifier
}
}
let objectList = [CustomClass(identifier: "one")]
func retrieveObject (objects: [CustomClass], identifier: String) -> CustomClass {
return objects.filter { $0.identifier == identifier }.first!
}
let object = retrieveObject(objectList, "one")
println("\(object.propertyA)") // Prints "Printing!"
println("\(objectList.first!.propertyA)") // Prints "Printing!"
EDIT:
Simplified it a bit
Except for line:
var retrievedObject : [CustomClass] = objects.filter({
return $0.identifier == identifier
})
that should be:
var retrievedObject : [CustomClass] = objects.filter { $0.identifier == identifier;};
In your version, you pass a strange value for filter argument (because of misplaced parenthesis) that does not match awaited filter argument type: (T) -> Bool
I tested Jacobson's code and I confirm it is working.