Parse SDK 1.7.1 not working in Xcode 6.3 - xcode

My code worked fine in Xcode 6.2. After the update to Xcode 6.3 I had some Nullabilty Errors.
I could solve these errors after I downloaded the Parse SDK 1.7.1. So I deleted the old Parse framework files in my project and pasted the new ones into it. Additional I convert my code to the latest swift syntax "Edit/Convert/latest swift syntax". Now I haven't problems with Nullabilty Errors but several others.
In my project I have a simple Tableviewcontroller with the following code:
import UIKit
class HaendlerTableViewController: PFQueryTableViewController {
// Initialise the PFQueryTable tableview
override init!(style: UITableViewStyle, className: String!) { //1. Falialbe initialize init/style:className:)' cannot override a non-failable initializer
super.init(style: style, className: className)
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
// Configure the PFQueryTableView
self.parseClassName = "Haendler"
self.textKey = "name"
self.pullToRefreshEnabled = true
self.paginationEnabled = false
}
// Define the query that will provide the data for the table view
override func queryForTable() -> PFQuery! { //2. Ovverriding method with selector queryForTable has incompatitble typ () -> PFQuery
var query = PFQuery(className: "Haendler")
query.orderByAscending("name")
return query
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath, object: PFObject) -> PFTableViewCell { //3. Ovverriding method with selector 'tableView:cellForRowAtindexPath:object:' has incompatible type '(UITableView, NSIndexPath, PFObject) -> PFTableViewCell
var cell = tableView.dequeueReusableCellWithIdentifier("HaendlerCell") as! HaendlerCell!
if cell == nil {
cell = HaendlerCell(style: UITableViewCellStyle.Default, reuseIdentifier: "Cell")
}
// Extract values from the PFObject to display in the table cell
cell.haendlerName.text = object["name"] as! String!
var thumbnail = object["logo"] as! PFFile
var initialThumbnail = UIImage(named: "haendler")
cell.haendlerBild.image = initialThumbnail
cell.haendlerBild.file = thumbnail
cell.haendlerBild.loadInBackground()
return cell
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var detailScene = segue.destinationViewController as! HaendlerDetailViewController
// Pass the selected object to the destination view controller.
if let indexPath = self.tableView.indexPathForSelectedRow() {
let row = Int(indexPath.row)
detailScene.currentObject = objects[row] as? PFObject //4. Could not find an overload for 'subscript' that accepts the supplied agruments
}
}
}
I wrote the errors in a comment on the right side of the code and
below.
Falialbe initialize init/style:className:)' cannot override a non-failable initializer
Ovverriding method with selector queryForTable has incompatitble typ () -> PFQuery
Ovverriding method with selector 'tableView:cellForRowAtindexPath:object:' has incompatible type '(UITableView, NSIndexPath, PFObject) -> PFTableViewCell
Could not find an overload for 'subscript' that accepts the supplied agruments
I have the same errors when I make a new Swift project from the Parse Quickstart and add one Tableviewcontroller. In my old project was an objective-C bridging header which one I deleted because I had the oppurtunity to add the Parse SDK 1.7.1 directly in my Swift project.
Now I need help because I don't see what I have to change..
PS: Sorry for the mix of German and English code I'll adjust it once the project is running again

I had the same issue as I just updated Xcode to 6.3 about 20 minutes ago.
For your 2nd error, remove the '!' after 'PFQuery'. So it should now look like..
override func queryForTable() -> PFQuery {
This solved my problem in regards to that specific error.
I never used an init method as you did in your first error, but try removing it and see what you get. My PFQueryTableViewController works fine without it.

Had the same issues.
To solve the first initialise issue remove the '!' after 'override init'. Should look like this:
// Initialise the PFQueryTable tableview
override init(style: UITableViewStyle, className: String!) { //1. Falialbe initialize init/style:className:)' cannot override a non-failable initializer
super.init(style: style, className: className)
}
Do the same for the 2nd error after 'PFQuery'
override func queryForTable() -> PFQuery {
Hope its helpful. Since the latest update unwrapping elements usually needs to be revised for possible errors.

Related

NSTableView - Better solution for sorting collection with NSSortDescriptor

I have a NSTableView with 2 columns bound with a custom type (SelectedFiles) array as File Name and File Path, after clicking the header, I want it to sort the data in ascending / descending order, I tried these codes with NSSortDescriptor:
class ViewController: NSViewController, NSTableViewDataSource, NSTableViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let fileNameSortDescriptor = NSSortDescriptor(key: "fileName", ascending: true, selector: #selector(NSString.localizedStandardCompare(_:)))
tableView.tableColumns[0].sortDescriptorPrototype = fileNameSortDescriptor
// other codes
}
}
extension ViewController: NSTableViewDataSource, NSTableViewDelegate {
func tableView(_ tableView: NSTableView, sortDescriptorsDidChange oldDescriptors: [NSSortDescriptor]) {
let selectedFilesArray = NSMutableArray(array: selectedFiles)
selectedFilesArray.sort(using: tableView.sortDescriptors) // Signal SIGABRT
selectedFiles = selectedFilesArray as! [SelectedFiles]
tableView.reloadData()
}
}
My custom collection for the data in table view:
struct SelectedFiles: CustomStringConvertible {
let fileName: String
let filePath: String
var description: String {
return "\(fileName) at path \(filePath)"
}
}
var selectedFiles: [SelectedFiles] = []
It turns out it doesn't work at all, IDK if its anything wrong with my code or I'm missing something.
So, I came up with this awkward solution:
var tableViewSortingOrder = ComparisonResult.orderedAscending
extension ViewController: NSTableViewDataSource, NSTableViewDelegate {
func tableView(_ tableView: NSTableView, sortDescriptorsDidChange oldDescriptors: [NSSortDescriptor]) {
switch tableViewSortingOrder {
case .orderedAscending:
tableViewSortingOrder = .orderedDescending
selectedFiles.sort { (previous, next) -> Bool in
return previous.fileName.compare(next.fileName) == tableViewSortingOrder
}
default:
tableViewSortingOrder = .orderedAscending
selectedFiles.sort { (previous, next) -> Bool in
return previous.fileName.compare(next.fileName) == tableViewSortingOrder
}
tableView.reloadData()
}
}
After I changed to this solution, it worked perfectly as it switches swiftly between ascending / descending order. But, when it comes to deleting objects in the collection, it throws Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value when I'm trying to delete multiple objects from both collection and table view with some specific files.
So, I'm thinking if I should change a way of achieving this header sorting thing by using NSSortDescriptor (use the old-fashioned way by correcting my first method) in order to get away from this issue, I have to admit that my second way is a bit of awkward (is more like a plan C).
I've red through multiple StackOverflow posts on this topic and I tried all of their ways, especially this one, I am not using CoreData which its solutions does not work for my situation.
Anyone can help point out the way please? 😊
I red the guide to NSTableView from Apple Developer Site and few other StackOverflow posts, I found myself a workable solution for Swift 4:
I set the sortDescriptorPrototype to fileNameSortDescriptor in viewDidLoad() under ViewController class.
class ViewController: NSViewController {
override func viewDidLoad()
super.viewDidLoad()
let fileNameSortDescriptor = NSSortDescriptor(key: "fileName", ascending: true, selector: #selector(NSString.localizedStandardCompare))
let tableColumn = tableView.tableColumn(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "fileNameColumn"))!
tableColumn.sortDescriptorPrototype = fileNameSortDescriptor
// other codes
}
}
And then I added an inheritance from NSObject and inserted #objcMembers to prevent warning: Object <#object#> of class '<#class#>' does not implement methodSignatureForSelector: -- trouble ahead from occurring and then cause Signal SIGABRT while calling selectedFiles.sort(using: tableView.sortDescriptors) (Reference: Object X of class Y does not implement methodSignatureForSelector in Swift).
#objcMembers class SelectedFiles: NSObject {
let fileName: String
let filePath: String
override var description: String {
return "\(fileName) at path \(filePath)"
init(fileName: String, filePath: String) {
self.fileName = fileName
self.filePath = filePath
}
}
Here's the code for tableView(_:sortDescriptorsDidChange:) in NSTableViewDataSource:
extension ViewController: NSTableViewDataSource {
func tableView(_ tableView: NSTableView, sortDescriptorsDidChange oldDescriptors: [NSSortDescriptor]) {
var selectedFilesArray = NSArray(array: selectedFiles)
selectedFilesArray = selectedFilesArray.sortedArray(using: tableView.sortDescriptors) as NSArray
selectedFiles = selectedFilesArray as! [SelectedFiles]
tableView.reloadData()
}
}
Now, everything works perfectly fine.

Initializing Custom UITableViewCell with parameters

I am trying to create an array of UITableViewCells and before I append the cells I need to initialize them with parameters. When I try to initialize the cell I get an error that the two properties in the class were never defined. But after I defined them I get an error that the variables were used before being initialized. Here is the class
class SimpleCellClassTableViewCell: UITableViewCell {
#IBOutlet var artist: UILabel!
#IBOutlet var picture: UIImageView!
#IBOutlet var songTitle: UILabel!
#IBOutlet var sender: UILabel!
var audioFile: AnyObject? = nil
var mediaType: songType! = nil
var id: NSNumber! = nil
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func configureCell(Title title: NSString?,
File audioFile: AnyObject?,
Type mediaType: songType,
Artist artist: NSString?,
Image image: UIImage?,
Sender sender: NSString?,
ID id: NSNumber?) -> UITableViewCell {
self.audioFile = audioFile
self.mediaType = mediaType
if let newSender = sender{
self.sender.text = newSender as String
}
if let newTitle = title{
self.songTitle.text = newTitle as String
}
if let newImage = image {
self.picture.image = newImage
}
if let newArtist = artist {
self.artist.text = newArtist as String
}
if let newId = id {
self.id = newId as NSNumber
}
return self
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
And this is where Im trying to initialize and then add values to it with configure cell method:
let newSongCell = SimpleCellClassTableViewCell.init(style: .Default, reuseIdentifier: "SimpleCell")
newSongCell.configureCell(Title: setTitle,
File: setAudioFile,
Type: setMediaType,
Artist: setArtist,
Image: setImage,
Sender: setSender,
ID: setId)
The parameters for File and Type are the ones throwing the same error. Also if I need to use the initializer with NSCoder what should I put as the argument?
There are several problems with your code. I suggest you to look some examples where UITableView. Apple docs or github are good sources for this.
Issue #1
You do not need to override designated `UITableViewCell's initializers (below) because you do nothing in the override.
public init(style: UITableViewCellStyle, reuseIdentifier: String?)
public init?(coder aDecoder: NSCoder)
Issue #2
Your code does not reuse the cell object which is bad.
Issue #3
In Swift init is not used on the call site, so your cell's initialization code (considering also issue #2) should be
var simpleCell = tableView.dequeueReusableCellWithIdentifier("SimpleCell")
if simpleCell == nil {
let cell: SimpleCellClassTableViewCell = SimpleCellClassTableViewCell(style: .Default, reuseIdentifier: "SimpleCell")
cell.configureCell(Title: "test",
File: "",
Type: 2,
Artist: "test",
Image: UIImage(),
Sender: "test",
ID: 2)
simpleCell = cell
}
return simpleCell!
Issue #4
Do not name function parameters with capitalised first letter (Title, File, etcetera). This might be confused with a type. Instead, use title, file, etc.. Again, there are a lot of examples out there.
Try to fix this issues. This might help.
I don't see if audioFile and mediaType properties are defined wrong. There should be no error with them. If you use .xib file - usually you should not use explicit initializer. And for sure you must not do this in your case, because you trying to use outlets. When you use init(style:reuseIdentifier:) you miss your .xib file and all UI. Assume that .xib filename is SimpleCellClassTableViewCell.xib, Custom Class for UITableViewCell in Identity Inspector is set to SimpleCellClassTableViewCell, reuse identifier is set to "SimpleCell". I offer you use this code for initializing your cell:
guard let newSongCell = UINib(nibName: "SimpleCellClassTableViewCell", bundle: nil).instantiateWithOwner(nil, options: nil).first as? SimpleCellClassTableViewCell
else
{
fatalError("Error loading SimpleCellClassTableViewCell")
}
Usually you should not use initializer with NSCoder explicitly. This initializer used by storyboard or .nib file.
At last, if you use outlets from storyboard - then you shouldn't try to get cell instance by any initializer at all. You should use UITableView's dequeueReusableCellWithIdentifier(forIndexPath:) method.
That was all about your direct question, but in fact you rarely need to initialize UITableViewCell by yourself. If you do, probably you do something wrong. Assume again, that you use .xib file, then in most cases you simply register your file in viewDidLoad of your table view controller
tableView.registerNib(UINib(nibName: "SimpleCellClassTableViewCell", bundle: nil), forCellReuseIdentifier: "SimpleCell")
and then your tableView will initialize or reuse SimpleCellClassTableViewCell instances for you by dequeueReusableCellWithIdentifier(forIndexPath:) method

NSManagedObject.setValue(value: AnyObject?, forKey key: String) causes error in Swift 2.0

I have this piece of code which was working ok in XCode6 (Swift 1.2) but not with the Swift 2:
class func findOrCreate<T: NSManagedObject>(type: T.Type, attribute: String, value: AnyObject?) -> T {
if let object = T.MR_findFirstByAttribute(attribute, withValue: value) as? T {
return object
} else {
let object = T.MR_createEntity() as! T
if let value:AnyObject = value {
object.setValue(value, forKey: attribute)
}
return object
}
}
Error shows on the line containing object.setValue with the message:
Ambiguous use of 'setValue(_:forKey:)'
I think it does not recognise object to be of NSManagedObject type but I'm not 100% sure, any clue why this happens much appreciated.
I've posted same question on Apple Forum and got an answer with a workaround for this problem:
let object = T.MR_createEntity() as! NSManagedObject
if let value:AnyObject = value {
object.setValue(value, forKey: attribute)
}
return object as! T
This works as expected. I've submitted a bug report to Apple as well.
One more possible solution is this:
(object as NSManagedObject).setValue(value, forKey: attribute)

Why am I receiving these errors when trying to pass a variable on a segue in swift?

I am trying to build upon answer which I was given here. What I am trying to is very simple - I want a text field which you can enter text into. You press the go button and it takes you to a new view and replaces the text on a label on that page with whatever the user entered in the box. The is the code I am using on the first page.
import UIKit
class ViewController: UIViewController {
#IBOutlet var entry: UITextField!
let dictionary = entry.text // Line 7 ERROR
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "viewTwo"
{
if let destinationVC = segue.destinationViewController as? viewTwo{
destinationVC.dictionary = self.dictionary // Line 24 ERROR
}
}
}
#IBAction func goToViewTwo(sender: AnyObject) {
performSegueWithIdentifier("viewTwo", sender: self)
}
}
I am only including the code from the first view because i know the code from the second view is working.
I didn't encounter an error until I tried to use the text field - before when I just had a pre-choses text to transfer over it worked. Before, instead of having let dictionary = entry.text I had let dictionary = "foo" and it worked.
So my question is exactly the same thing but have a text field instead of pre-chosen text - what I really want to know is why my code didn't work before.
The errors I got were on line 7 (I have labeled the lines above which had the errors) - 'ViewController.Type' does not have member names 'entry' and there was also an error on line 24 but I suspect this is related to this error and will be fixed if this error is also fixed. Just incase though, the error on line 24 was: 'ViewController.Type' does not have member names 'dictionary'
Thank you.
You should set the dictionary to var dictionary = "" in the declaration. You use var instead of let here, so that you can change the value of the dictionary later.
Then inside your #IBAction func goToViewTwo(sender: AnyObject){} method, you set the self.dictionary = entry.text
#IBAction func goToViewTwo(sender: AnyObject) {
dictionary = entry.text
performSegueWithIdentifier("viewTwo", sender: self)
}
Alternatively, you can just do the following inside prepareForSegue() method.
This way, you dont need to declare a dictionary to hold the text value of your UITextField, you can just pass the text value from your entry to the second view controller's dictionary variable.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "viewTwo"
{
if let destinationVC = segue.destinationViewController as? viewTwo{
destinationVC.dictionary = self.entry.text
}
}
}
A dictionary is not constant, so declare it as lazy var, not let:
lazy var dictionary: String {
return entry.text
}()

Passing Data in Swift

I have been looking for an answer for this, but have only found answers for segues.
I have viewController1 with a button that segues to viewController2. There is no code for this, I set it up through Interface builder. On viewController2 I have a button that dismisses itself with
self.dismissViewControllerAnimated(true, completion, nil)
I want to pass a string from viewController2 back to viewController1 when the view is dismissed. How do I go about doing this? Also, I am using swift.
Thanks in advance!
There are two common patterns, both of which eliminate the need for viewController2 to know explicitly about viewController1 (which is great for maintainability):
Create a delegate protocol for your for viewController2 and set viewController1 as the delegate. Whenever you want to send data back to viewController1, have viewController2 send the "delegate" the data
Setup a closure as a property that allows passing the data. viewController1 would implement that closure on viewController2 when displaying viewController2. Whenever viewController2 has data to pass back, it would call the closure. I feel that this method is more "swift" like.
Here is some example code for #2:
class ViewController2 : UIViewController {
var onDataAvailable : ((data: String) -> ())?
func sendData(data: String) {
// Whenever you want to send data back to viewController1, check
// if the closure is implemented and then call it if it is
self.onDataAvailable?(data: data)
}
}
class ViewController1 : UIViewController {
func doSomethingWithData(data: String) {
// Do something with data
}
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
// When preparing for the segue, have viewController1 provide a closure for
// onDataAvailable
if let viewController = segue.destinationViewController as? ViewController2 {
viewController.onDataAvailable = {[weak self]
(data) in
if let weakSelf = self {
weakSelf.doSomethingWithData(data)
}
}
}
}
}
I used the code from the first answer in a transition between controllers WITHOUT prepareForSegue and worked for me as well.
Here's the sample code.
The First View Controller:
#IBAction func dpAgendaClick(sender:UIBarButtonItem) {
///instantiating view controller with identifier
if let datePickerViewController = storyboard?.instantiateViewControllerWithIdentifier("DatePickerViewController")
as? DatePickerViewController {
///bring instantiated view controller to front
self.presentViewController(datePickerViewController, animated: true, completion: nil)
///wrapping the data returned
datePickerViewController.onDataFiltroAvailable = {[weak self]
(dataFiltro) in
if let weakSelf = self {
///use dataFiltro here
}
}
The second View Controller:
var onDataFiltroAvailable: ((dataFiltro: String) -> ())?
///private var
var dataFiltro: String = ""
///the returning data is obtained on the datePickerChanged event
#IBAction func datePickerChanged(sender: UIDatePicker) {
let dateFormatter = NSDateFormatter()
dateFormatter.dateStyle = NSDateFormatterStyle.ShortStyle
dateFormatter.dateFormat = "yyyy-MM-dd"
dataFiltro = dateFormatter.stringFromDate(datePicker.date)
}
///dismiss the controller on button click
#IBAction func dpOkClick(sender: UIButton) {
///"returning" the data
self.onDataFiltroAvailable?(dataFiltro: dataFiltro)
dismissViewControllerAnimated(true, completion: nil)
}
(Swift 2.1, Xcode 7, iOS9)
If you don't want it to be tightly coupled only between 2 ViewControllers,
You can also use the Notification Design Pattern (Post & Observe), which is mainly used to pass on the same object/information from one VC to multiple View Controllers.
For your scenario :
In VC2.swift :
#IBAction func BackBtn(sender: UIButton) {
NSNotificationCenter.defaultCenter().postNotificationName("ThisIsTheMessage", object: nil, userInfo:["ObjectBeingSent":yourObject])
}
And in VC1.swift :
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("yourFunction:"), name: "ThisIsTheMessage", object: nil)
}
func yourFunction(theNotification : NSNotification) {
if let extractInfo = theNotification.userInfo {
//code to use the object sent from VC2, by extracting the object details
}
}
Common Practise is:
Pass data forward -> Use PrepareForSegue
Pass data backward to the previous View Controller-> Protocol and Delegation
Pass data across multiple View Controllers -> Notifications : Post and Observe(observe in all the View controllers where you are using the object details)

Resources