Strange Generic Function Appear in View Controller After Converting to Swift 3

Strange generic function appear in view controller after converting to swift 3

That is interesting. Before the latest Swift 3, you could
compare optional values, for example

let a: Int? = nil
let b: Int? = 4

print(a < b) // true

and nil was considered less than all non-optional values.

This feature has been removed (SE-0121 – Remove Optional Comparison Operators) and the above code would fail to compile
in Xcode 8 beta 6 with


error: value of optional type 'Int?' not unwrapped; did you mean to use '!' or '?'?

Apparently, the Swift migrator solves that problem for you by
providing a custom < operator which takes two optional operands
and therefore "restores" the old behavior.

If you remove that definition then you should see where the
comparison is done in your code. Then try to update your code
and remove the optional comparisons.

Generic func transfer from view controller to view controller

Change method signature as below specifying T of type UIViewController,

private func toVC(_ id: String, vc: T.Type) {

To pass data for specific controller,

private func toVC(_ id: String, vc: T.Type, data: Any) {
if let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: id) as? T {
if let window = self.window, let rootVC = window.rootViewController {
var currVC = rootVC
let navVC = UINavigationController()
while let presentVC = currVC.presentedViewController {
currVC = presentVC
}
(vc as? MyViewController1)?.data = data
(vc as? MyViewController2)?.data = data
navVC.viewControllers = [vc]
currVC.present(navVC, animated: true, completion: nil)
}
}
}

Swift3 optionals chaining in IF conditions bug?

Optional comparison operators are removed from Swift 3.
SE-0121

You need to write something like this:

if test?.optionalInt ?? 0 > 4
{

}

Why would identical code throw an error in one location but not another?

As discussed in this Q&A – the Swift migrator will insert a custom fileprivate operator overload in order to allow for optional comparisons to work in Swift 3 the same way they did in Swift 2. Because this overload is fileprivate, you won't be able to use it from any other source files in your project that don't also define it, thus why your comparison fails to compile in the new source file.

Therefore one solution is just to copy it over to your new file. Another is to remove the fileprivate access modifer. The overload will default to internal, and therefore be accessible to other Swift files in your module.

Although really I would just recommend removing the overload entirely and instead write your own explicit logic for optional comparison when it arises – the overload can be too easily misused (as demonstrated by the Swift evolution proposal to remove it).

What does `_?` mean in Swift?

_ is the wildcard pattern which matches anything, see for example

  • Swift : What does "_, _ " means?
  • Why do I need underscores in swift?

And x? is the optional pattern, a shortcut for .some(x), it matches
an optional which is not nil.

Here we have the combination of both: _? matches anything that is
not nil.

case (nil, _?) matches if the left operand is nil
and the right operand is not. You could also write it as case (.none, .some).

Xcode can insert this function during migration from older Swift versions,
compare Strange generic function appear in view controller after converting to swift 3.

How to pass a Type of UIViewController to a function and type cast a variable in iOS Swift?

This is a good situation to use generics.

func showAsPopUp(currentVC: UIViewController,currentVCname: String, popupStoryboardID: String, popUpVC: T.type) {
let popOverVC = UIStoryboard(name: currentVCname, bundle: nil).instantiateViewController(withIdentifier: popupStoryboardID) as! T
currentVC.addChildViewController(popOverVC)
}

Not sure what your functionality is here, but this is how the generics would fit in with the above mentioned code.

The usage of the above method would look like:

showAsPopUp(currentVC: UIViewController(), currentVCname: "asdsadv", popupStoryboardID: "asd", popUpVC: SomePopUpViewController.self)

Custom view controller class isn't listed in storyboard's class menu

The answer by @vikingosegundo, while explaining Xcode's complaint and being generally very informative, didn't help me solve my particular problem. My project was started in Xcode 8.3.3 and I already have lots of windows and views in the storyboard so I don't really want to abandon or work around the storyboard/generic issue.

That being said, I did some more research and came to the realization that many people prefer delegation to class inheritance so I decided to explore that approach. I was able to get something working that satisfies my needs.

I present here, a simplified, but functional approach.

First, a protocol that our data models must conform to:

protocol RestModel
{
static var entityName: String { get }
var id: Int { get }
}

Next, a data model:

///
/// A dummy model for testing. It has two properties: an ID and a name.
///
class ModelOne: RestModel
{
static var entityName: String = "ModelOne"
var id: Int
var name: String

init(_ id: Int, _ name: String)
{
self.id = id
self.name = name
}
}

Then, a protocol to which all classes that extend our base class must conform:

///
/// Protocol: ListViewControllerDelegate
///
/// All classes that extend BaseListViewController must conform to this
/// protocol. This allows us to separate all knowledge of the actual data
/// source, record formats, etc. into a view-specific controller.
///
protocol ListViewControllerDelegate: class
{
///
/// The actual table view object. This must be defined in the extending class
/// as @IBOutlet weak var tableView: NSTableView!. The base class saves a weak
/// reference to this variable in one of its local variables and uses that
/// variable to access the actual table view object.
///
weak var tableView: NSTableView! { get }

///
/// This method must perform whatever I/O is required to load the data for the
/// table view. Loading the data is assumed to be asyncronous so the method
/// must accept a closure which must be called after the data has been loaded.
///
func loadRecords()

///
/// This method must simply return the number of rows in the data set.
///
func numberOfRows() -> Int

///
/// This method must return the text that is to be displayed in the specified
/// cell.
/// - parameters:
/// - row: The row number (as supplied in the call to tableView(tableView:viewFor:row:).
/// - col: The column identifier (from tableColumn.identifier).
/// - returns: String
///
func textForCell(row: Int, col: String) -> String

} // ListViewControllerDelegate protocol

Now the actual base class:

class BaseListViewController: NSViewController,  
NSTableViewDataSource,
NSTableViewDelegate
{
//
// The instance of the extending class. Like most delegate variables in Cocoa
// applications, this variable must be set by the delegate (the extending
// class, in this case).
//
weak var delegate: ListViewControllerDelegate?

//
// The extending class' actual table view object.
//
weak var delegateTableView: NSTableView!

//
// Calls super.viewDidLoad()
// Gets a reference to the extending class' table view object.
// Sets the data source and delegate for the table view.
// Calls the delegate's loadRecords() method.
//
override func viewDidLoad()
{
super.viewDidLoad()
delegateTableView = delegate?.tableView
delegateTableView.dataSource = self
delegateTableView.delegate = self
delegate?.loadRecords()
delegateTableView.reloadData()
}


//
// This is called by the extending class' table view object to retreive the
// number of rows in the data set.
//
func numberOfRows(in tableView: NSTableView) -> Int
{
return (delegate?.numberOfRows())!
}


//
// This is called by the extending class' table view to retrieve a view cell
// for each column/row in the table. We call the delegate's textForCell(row:col:)
// method to retrieve the text and then create a view cell with that as its
// contents.
//
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView?
{
if let col = tableColumn?.identifier, let text = delegate?.textForCell(row: row, col: col)
{
if let cell = delegate?.tableView.make(withIdentifier: (tableColumn?.identifier)!, owner: nil) as? NSTableCellView
{
cell.textField?.stringValue = text
return cell
}
}
return nil
}
} // BaseListViewController{}

And, finally, an extending class:

///
/// A concrete example class that extends BaseListViewController{}.
/// It loadRecords() method simply uses a hard-coded list.
/// This is the class that is specified in the IB.
///
class ViewOne: BaseListViewController, ListViewControllerDelegate
{
var records: [ModelOne] = []

//
// The actual table view in our view.
//
@IBOutlet weak var tableView: NSTableView!

override func viewDidLoad()
{
super.delegate = self
super.viewDidLoad()
}

func loadRecords()
{
records =
[
ModelOne(1, "AAA"),
ModelOne(2, "BBB"),
ModelOne(3, "CCC"),
ModelOne(4, "DDD"),
]
}

func numberOfRows() -> Int
{
return records.count
}

func textForCell(row: Int, col: String) -> String
{
switch col
{
case "id":
return "\(records[row].id)"

case "name":
return records[row].name

default:
return ""
}
}
} // ViewOne{}

This is, of course, a simplified prototype. In a real-world implementation, loading the records and updating the table would happen in closures after asynchronously loading the data from a database, web service, or some such.

My full prototype defines two models and two view controllers that extend BaseListViewClass. It works as desired. The production version of the base class will contain numerous other methods (which is why a wanted it to be a base class in the first place :-)



Related Topics



Leave a reply



Submit