Song is a subclass of RLMObject (typealias Object) and is used throughout my app, including in searchViewController(_:cellForObject:atIndexPath:)
let song = object as! Song
But in my prepare(for segue:) method (below), when I try to perform the same downcast, the compiler says "Cast from 'RLMObject' to unrelated type 'Song' always fails."
if let row = tableView.indexPathForSelectedRow?.row {
YpbApp.currentRequest?.songObject = results!.object(at: UInt(row)) as? Song
}
This doesn't makes sense, what's wrong here?
RLMObject is not a typealias for Object; they are different classes entirely that have different interfaces. It sounds like you are trying to mix the Swift and Objective-C APIs, which is not supported.
Related
I’m quite new to iOS development and swift.
I’m seeing something like this quite often
let vc = window?.rootViewController as! MainViewController
Would someone mind explaining type casting and what the purpose of this is to me? I’ve been trying to find other questions or just a basic explanation of the purpose - but I’m not having much luck.
Thanks
The code you posted is a crash in the making. The as construct casts a variable of a general type to a more specific type. If it can fail, you need to add a ? (as?) or an exclamation point (as!) The form with an exclamation point is known as a "force cast". It tries to cast the object to a different type, and will crash if the cast fails. Thus it is a bad idea and should be avoided.
Better to use code like this:
func someFunc() {
guard let vc = window?.rootViewController as? MainViewController else {
return
}
//Code that depends on vc being cast to type MainViewController
}
(Read up on the guard statement in the Apple Swift iBook for more information.)
Even this code is better than what you posted:
func someFunc() {
guard let vc = window?.rootViewController as? MainViewController else {
fatalError("Unable to cast rootViewController to type MainViewController")
}
//Code that depends on vc being cast to type MainViewController
}
The second version will still crash, but will display a very clear message in the console when it does crash, so you can tell what went wrong.
I'm creating a UIView that listens to CMDeviceMotion Events:
class MyView: UIView{
private var motionManager = CMMotionManager()
let motionQueue = NSOperationQueue()
override func awakeFromNib() {
self.setupView()
}
func setupView(){
self.motionManager.deviceMotionUpdateInterval = 0.5
self.motionManager.startDeviceMotionUpdatesUsingReferenceFrame(.XArbitraryZVertical, toQueue: self.motionQueue, withHandler: self.motionHandler)
}
// MARK: - CMDeviceMotionHandler
let motionHandler : CMDeviceMotionHandler = {
[unowned self] (motion,error) in
}
}
I'd like to declare my CMDeviceMotionHandler closure as a member variable however I get the error:
'unowned' may only be applied to class and class-bound protocol types,
not 'MyView -> () -> MyView'
MyView is a UIView which in turn is a class so I don't get why it's complaining that unowned can not be applied.
I've searched for other questions with the same issue but most of them dealt with lazily computed variables. How do I resolve this error for my scenario?
The line of code you're on is actually run during the init function. self is not available until after all stored properties are initialized. You're in the middle of the process.
The error message is quite confusing and useless, because self in the context of property initializers is not an instance of a MyView, but a tricky meta-type: a class-member function that is unbound to its instance, but becomes bound and usable once the instance is passed in as the first argument. It's to do with member functions being implemented in Swift with currying, and is rather academic unless you love type calculus.
You have two options:
Declare it indeed as lazy var instead of let, so the code is not run during init but in fact at first use.
Declare it without initialization as an Optional. Depending on your design constraints this is either cumbersome or elegant. No way to know. Anyway, before it is needed, initialize it to a non-nil value. An easy place to do this, if this UIView is used strictly within Storyboard, is to initialize it within awakeFromNib().
I started to use swiftLint and noticed one of the best practices for Swift is to avoid force cast. However I used it a lot when handling tableView, collectionView for cells :
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(cellID, forIndexPath: indexPath) as! MyOffersViewCell
If this is not the best practice, what's the right way to handle this? I guess I can use if let with as?, but does that mean for else condition I will need to return an empty cell? Is that acceptable?
if let cell = collectionView.dequeueReusableCellWithReuseIdentifier(cellID, forIndexPath: indexPath) as? MyOffersViewCell {
// code
} else {
// code
}
This question is probably opinion based, so take my answer with a grain of salt, but I wouldn't say that force downcast is always bad; you just need to consider the semantics and how that applies in a given situation.
as! SomeClass is a contract, it basically says "I guarantee that this thing is an instance of SomeClass". If it turns out that it isn't SomeClass then an exception will be thrown because you violated the contract.
You need to consider the context in which you are using this contract and what appropriate action you could take if you didn't use the force downcast.
In the example you give, if dequeueReusableCellWithIdentifier doesn't give you a MyOffersViewCell then you have probably misconfigured something to do with the cell reuse identifier and an exception will help you find that issue.
If you used a conditional downcast then you are going to get nil and have to handle that somehow - Log a message? Throw an exception? It certainly represents an unrecoverable error and something that you want to find during development; you wouldn't expect to have to handle this after release. Your code isn't going to suddenly start returning different types of cells. If you just let the code crash on the force downcast it will point straight to the line where the issue occurred.
Now, consider a case where you are accessing some JSON retrieved from a web service. There could be a change in the web service that is beyond your control so handling this more gracefully might be nice. Your app may not be able to function but at least you can show an alert rather than simply crashing:
BAD - Crashes if JSON isn't an array
let someArray=myJSON as! NSArray
...
Better - Handle invalid JSON with an alert
guard let someArray=myJSON as? NSArray else {
// Display a UIAlertController telling the user to check for an updated app..
return
}
Update
After using Swiftlint for a while, I am now a total convert to the Zero Force-Unwrapping Cult (in line with #Kevin's comment below).
There really isn't any situation where you need to force-unwrap an optional that you can't use if let..., guard let... else, or switch... case let... instead.
So, nowadays I would do this:
for media in mediaArray {
if let song = media as? Song {
// use Song class's methods and properties on song...
} else if let movie = media as? Movie {
// use Movie class's methods and properties on movie...
}
}
...or, if you prefer the elegance and safety of an exhaustive switch statement over a bug-prone chain of if/elses, then:
switch media {
case let song as Song:
// use Song class's methods and properties on song...
case let movie as Movie:
// use Movie class's methods and properties on movie...
default:
// Deal with any other type as you see fit...
}
...or better, use flatMap() to turn mediaArray into two (possibly empty) typed arrays of types [Song] and [Movie] respectively. But that is outside the scope of the question (force-unwrap)...
Additionally, I won't force unwrap even when dequeuing table view cells. If the dequeued cell cannot be cast to the appropriate UITableViewCell subclass, that means there is something wrong with my storyboards, so it's not some runtime condition I can recover from (rather, a develop-time error that must be detected and fixed) so I bail with fatalError().
Original Answer (for the record)
In addition to Paulw11's answer, this pattern is completely valid, safe and useful sometimes:
if myObject is String {
let myString = myObject as! String
}
Consider the example given by Apple: an array of Media instances, that can contain either Song or Movie objects (both subclasses of Media):
let mediaArray = [Media]()
// (populate...)
for media in mediaArray {
if media is Song {
let song = media as! Song
// use Song class's methods and properties on song...
}
else if media is Movie {
let movie = media as! Movie
// use Movie class's methods and properties on movie...
}
Others have written about a more general case, but I want to give my solution to this exact case:
guard let cell = tableView.dequeueReusableCell(
withIdentifier: PropertyTableViewCell.reuseIdentifier,
for: indexPath) as? PropertyTableViewCell
else {
fatalError("DequeueReusableCell failed while casting")
}
Basically, wrap it around a guard statement and cast it optionally with as?.
"Force Cast" has its place, when you know that what you're casting to is of that type for example.
Say we know that myView has a subview that is a UILabel with the tag 1, we can go ahead and force down cast from UIView to UILabel safety:
myLabel = myView.viewWithTag(1) as! UILabel
Alternatively, the safer option is to use a guard.
guard let myLabel = myView.viewWithTag(1) as? UILabel else {
... //ABORT MISSION
}
The latter is safer as it obviously handles any bad cases but the former, is easier. So really it comes down to personal preference, considering whether its something that might be changed in the future or if you're not certain whether what you are unwrapping will be what you want to cast it to then in that situation a guard would always be the right choice.
To summarise: If you know exactly what it will be then you can force cast otherwise if theres the slightest chance it might be something else use a guard
As described in some casting discussions, forcing the cast for tableView.dequeueReusableCell is ok and can/should be done.
As answered on the Swiftlint Github site you can use a simple way to turn it off for the table cell forced cast.
Link to Swiftlink issue 145
// swiftlint:disable force_cast
let cell = tableView.dequeueReusableCell(withIdentifier: "cellOnOff", for: indexPath) as! SettingsCellOnOff
// swiftlint:enable force_cast
When you are working with your types and are sure that they have an expected type and always have values, it should force cast. If your apps crash you can easily find out you have a mistake on which part of UI, Dequeuing Cell, ...
But when you are going to cast types that you don't know that is it always the same type?
Or is that always have value?
You should avoid force unwrap
Like JSON that comes from a server that you aren't sure what type is that or one of that keys have value or not
Sorry for my bad English I’m trying to improve myself
Good luck🤞🏻
In cases where you are really sure the object should be of the specified type it would be OK to down cast. However, I use the following global function in those cases to get a more meaningful result in the logs which is in my eyes a better approach:
public func castSafely<T>(_ object: Any, expectedType: T.Type) -> T {
guard let typedObject = object as? T else {
fatalError("Expected object: \(object) to be of type: \(expectedType)")
}
return typedObject
}
Example usage:
class AnalysisViewController: UIViewController {
var analysisView: AnalysisView {
return castSafely(self.view, expectedType: AnalysisView.self)
}
override func loadView() {
view = AnalysisView()
}
}
Im having a hard time understanding data passing with delegates, I have looked but I get confused by the answers as I'm usually trying to do the opposite of what needs to be done for mine.
What I am trying to do is pass a PFObject from my main viewController (a UITableView) to a PopOverViewController.
I have done this successfully, but I need to pass the object. What would be the best way to pass from the mainController (SOITableViewController) to the popover (DetailPopViewController)?
Where should the protocol go? Where should the Delegate method be placed, etc.
Thank you!
PrepareForSegue, NSUserDefault and Singleton
You have a few possible options to pass your data to other views depending how you want that data to be handled, I will explain each for you and you can choose which one best fit your need.
prepareForSegue: Method
I recommend this method if you want to hold your data for 1 segue transition, it's a good cause to pass this again to another view afterward you need to create another prepareForSegue within the new view. here is an example on how to do this:
First, you create 2 variables in both views, 1 to send (currentViewController.swift) and 1 to receive (toViewyourGoingController.swift).
currentViewController.swift var dataToSend: AnyObject?
ViewYourGoingController.swift var dataToReceive: AnyObject?
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
//Check your segue, this way you can transfer different data to different view. also make sure the identifier match your segue.
if segue.identifier == "toViewYourGoing" {
//Initial your second view data control
let ExchangeViewData = segue.destinationViewController as! toViewyourGoingController
//Send your data with segue
ExchangeViewData.dataToReceive = dataToSend
}
}
NSUserDefault
Now this method is good if you want to keep your data live as long as the app is installed, once the app is removed this will reset automatically. You also have the option to update the value of the key if you wish, here is how you do NSUserDefault:
I always like to register my NSUserDeafult to default setting, a lot of people just continue with the second step without registering.
Register NSUserDefault in AppDelgate.swift
NSUserDefaults.standardUserDefaults().registerDefaults(["valueName": AnyObject])
Set Value to your NSUserDefault, this depends on what type of data you're storing, should match the one with your registration if you did register. (Example of Boolean data type below)
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "valueName") //Bool Data Type
Make sure you synchronize once you set the value to the NSUserDefault, this way it will update instantly, otherwise it will update when it get a chance.
NSUserDefaults.standardUserDefaults().synchronize()
Receive Value: this will receive boolean value since we set boolean and register boolean.
let Variable: Bool! = NSUserDefaults.standardUserDefaults().boolForKey("valueName")
Singleton
Now singleton is basically a global variable that you can use them in any views, but some developers experience some bugs and difficulties, use it at your own risk, I recommend this method when you're definite that you will use that data a lot (STILL RISKY), but this method is like goddess of data handling :).
Create a NSObject subclass and call it DataManager.swift (I call it data manager cause it handle data.) as following:
import UIKit
class DataManager: NSObject {
//Store Data Globally
static var someData: Boo! //This Boolean, you can choose whatever you want.
}
the static is what keep your data live.
Now you can store and receive someData from anywhere like you handle any data type like this.
//Store
DataManager.someData = true
//Receive
print(DataManager.someData)
Challenges:
You can also use
Keychain
Sergey Kargopolov will walk you through how to use a third party to use swift keychain. Otherwise, you can take even harder challenge and create one yourself :P .
Key-Value Data in iCloud
The best way to do this would be to pass it over when you do the prepare for segue method. So to do this make a variable in your detailPopViewController. In this case your pop over segue in storyboard will have the segue identifier detailView. Also tblSearchResults is your tableView outlet (you can name it whatever you want). Is that what you were looking for?
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "detailView" {
if let detailViewController = segue.destinationViewController as? PopOverViewController{
if let indexPath = self.tblSearchResults.indexPathForCell(sender as! SOITTableViewController) {
detailViewController.objectInSecondVC = objectFromFirstVC[indexPath.row]
}
}
}
}
Before I upgraded to Swift 1.2, I could write the following line:
if let width = imageDetails["width"] as Int?
Now it forces me to write this line:
if let width = imageDetails["width"] as! Int?
My question is, if I'm forced to write it as above, couldn't I just write the code below and it would do the same thing? Would it give me the same result in all values of imageDetails?
if let width = imageDetails["width"] as Int
The as keyword used to do both upcasts and downcasts:
// Before Swift 1.2
var aView: UIView = someView()
var object = aView as NSObject // upcast
var specificView = aView as UITableView // downcast
The upcast, going from a derived class to a base class, can be checked at compile time and will never fail.
However, downcasts can fail since you can’t always be sure about the specific class. If you have a UIView, it’s possible it’s a UITableView or maybe a UIButton. If your downcast goes to the correct type – great! But if you happen to specify the wrong type, you’ll get a runtime error and the app will crash.
In Swift 1.2, downcasts must be either optional with as? or “forced failable” with as!. If you’re sure about the type, then you can force the cast with as! similar to how you would use an implicitly-unwrapped optional:
// After Swift 1.2
var aView: UIView = someView()
var tableView = aView as! UITableView
The exclamation point makes it absolutely clear that you know what you’re doing and that there’s a chance things will go terribly wrong if you’ve accidentally mixed up your types!
As always, as? with optional binding is the safest way to go:
// This isn't new to Swift 1.2, but is still the safest way
var aView: UIView = someView()
if let tableView = aView as? UITableView {
// do something with tableView
}
Got this from a site: SOURCE
as
In Swift 1.2 and later, as can only be used for upcasting (or disambiguation) and pattern matching:
// 'as' for disambiguation
let width = 42 as CGFloat
let block = { x in x+1 } as Double -> Double
let something = 3 as Any? // optional wrapper can also be added with 'as'
// 'as' for pattern matching
switch item {
case let obj as MyObject:
// this code will be executed if item is of type MyObject
case let other as SomethingElse:
// this code will be executed if item is of type SomethingElse
...
}
as?
The conditional cast operator as? tries to perform a conversion, but returns nil if it can't. Thus its result is optional.
let button = someView as? UIButton // button's type is 'UIButton?'
if let label = (superview as? MyView)?.titleLabel {
// ...
}
as!
The as! operator is for forced type conversion.
Use the forced form of the type cast operator (as!) only when you are sure that the downcast will always succeed. This form of the operator will trigger a runtime error if you try to downcast to an incorrect class type.
// 'as!' for forced conversion.
// NOT RECOMMENDED.
let buttons = subviews as! [UIButton] // will crash if not all subviews are UIButton
let label = subviews.first as! UILabel
The correct idiom that should do exactly what you want (in all versions of Swift at least upto and including 1.2) is the as? optional cast.
if let width = imageDetails["width"] as? Int
The optional cast returns an optional (Int? in this case) and is tested at runtime. Your original code probably forced a cast to the optional type.