I want to compare types of objects in swift.
I've got a function which takes an object of NSError as parameter. It should return a custom string.
It looks like this:
static func getLocalizedErrorText(error: NSError) -> String{
switch error {
case is NoConnection: //class NoConnection: NSError
return "....."
...
}
But the function is not working as expected. I think the main problem is that this example is not working:
var dummy = MySubError() //class MySubError: MyBaseError
var dummy2: MyBaseError?
dummy2 = MySubError()
if dummy.dynamicType == MySubError.self {
//This will work
}
if dummy2.dynamicType == MySubError.self {
//This will not work
}
How can I check which type the parameter got?
You can check for a type using
if error is MySubError {
// do stuff
}
You can also do an optional cast, which will succeed, if the type matches or return nil, if not:
let subError = error as? MySubError
which you can also use in a guard predicate or if let statement:
if let subError = error as? MySubError {
// do stuff
}
or
guard let subError = error as? MySuberror else { return }
Related
So my swift skills arent great... I want to try and retrieve an audio track description for a video and have come up with this method. If anything fails the method should return null
func getAudioTrackDescription(path: String) -> AudioStreamBasicDescription? {
let asset = getAssetFromPath(path: path)
guard let track = asset.tracks(withMediaType: AVMediaType.audio).first else {
return nil
}
guard let audioDesc = track.formatDescriptions.first else {
return nil
}
// let casted = audioDesc as! CMAudioFormatDescription
// THE SDF IS JUST AN EXAMPLE THAT CAUSES IT TO FAIL
// I ACTUALLY WANT TO USE THE COMMENTED LINE ABOVE
if let casted = ("Sdf" as! CMAudioFormatDescription) {
let basic = CMAudioFormatDescriptionGetStreamBasicDescription(casted)
guard let pointee = basic?.pointee else {
return nil
}
return pointee
} else {
return nil
}
}
I am really struggling to understand how the casting works in swift...
The track.formatDescriptions list holds type Any which means it could crash if the type is not what i expect it to be (CMAudioFormatDescription)
For example I have changed the if audioDesc to be the string "Sdf" and the code crashes.
I want to be able to check if audioDesc can be casted to CMAudioFormatDescription and if cant i want to return nil.
I tried using as? and this always gives me this error
Conditional downcast to CoreFoundation type 'CMAudioFormatDescription' (aka 'CMFormatDescription') will always succeed
Can someone help?
---------------------- EDIT
is below a safe approach?
func getVideoAudioChannelCount(path: String) -> Int {
guard let audioFile = getAudioInformation(path: path) else {
return DEFAULT_AUDIO_CHANNEL
}
return Int(audioFile.channelCount)
}
func getAudioInformation(path: String) -> AVAudioFormat? {
var returnVar: AVAudioFormat?;
do {
returnVar = try AVAudioFile(forReading: URL(fileURLWithPath: path)).fileFormat
} catch _ {
returnVar = nil
}
return returnVar
}
According to the documentation, the array will always contain CMFormatDescription (aka CMAudioFormatDescription), so you can safely cast with as!.
The array contains CMFormatDescriptions (see CMFormatDescription), each of which indicates the format of media samples referenced by the track.
You get the error that conditional casts always succeed because CMAudioFormatDescription is a Core Foundation type (it conforms to _CFObject). For more info, see here.
Edit:
is below a safe approach?
Yes, if by "safe" you mean it won't crash. You can simplify the code if you use try?:
func getAudioInformation(path: String) -> AVAudioFormat? {
try? AVAudioFile(forReading: URL(fileURLWithPath: path)).fileFormat
}
Does Swift have a syntax similar to case .enumCase(let value) = enum that can be used as a one liner to read the enum as a specific case, or a nil
For instance with a Swift.Result instead of
let error: Error?
if case let .failure(value) = result {
error = value
} else {
error = nil
}
can I write something as
case let .failure(error) = result // Well this I can't
let error = result.as(.failure)
To simplify working with Result (or other enums) you can use next extension:
extension Result {
var value: Success? {
if case let .success(value) = self {
return value
}
return nil
}
var error: Failure? {
if case let .failure(error) = self {
return error
}
return nil
}
}
Then you can get optional values in single line:
let value = result.value
let error = result.error
if let value = result.value {
print(value)
}
if let error = result.error {
print(error)
}
I'm new to object oriented programming and am trying to move logic out of my view controller and into its own model so I can reuse endpoint calls and such, but I can't figure out how to alert my view controller that the result has occurred from within the object. Within javascript, I'd normally just set the function to return a value but it is not allowing me to return anything other than Void Cannot convert return expression of type 'Void' to return type 'String'.
import Foundation
import Firebase
class CloudFunctions {
let functions = Functions.functions()
func addNewAccount(param1: String) -> String {
functions.httpsCallable(FunctionsConstants.myFunction).call(["param1Name":param1]) { (result, error) in
if let e = error {
return "\(e)"
} else {
if let result = result {
return "\(result.data)"
} else {
return "Sorry, we couldn't unwrap the result."
}
}
}
}
}
and in my view controller I'm calling it like
let cloudFunctions = CloudFunctions()
self.result = cloudFunctions.addNewAccount(param1: dataHere)
I thought I might be able to create a didSet on result to update the UI, but it won't let the function go through because I get the error: Cannot convert return expression of type 'Void' to return type 'String'
You need a completion
func addNewAccount(param1: String,completion:#escaping(String -> ())){
functions.httpsCallable(FunctionsConstants.myFunction).call(["param1Name":param1]) { (result, error) in
if let e = error {
return "\(e)"
} else {
if let result = result {
completion("\(result.data)")
} else {
completion("Sorry, we couldn't unwrap the result.")
}
}
}
}
addNewAccount(param1:"") { str in
}
I have an enum and I'd like to create a method to return a different type for every case.
For example, I have a dictionary [String: Any]. To process the values I'm using the enum to create an array of keys:
enum Foo {
case option1
case option2
func createKey() -> [String] {
switch self {
case .option1: return ["scenario1"]
case .option2: return ["scenario2"]
}
}
}
Once I have the values, I need to cast them to a the proper type to be able to use them. Right now I'm doing it manually using if-statements but it would reduce a lot of code if I can somehow create a method in the enum to return the proper type. My current code:
let origin: [String: Any] = ["scenario2": "someText"]
let option: Foo = .option2
option.createKey().forEach {
guard let rawValue = origin[$0] else { return }
switch option {
case .option1:
guard let value = rawValue as? Int else { return }
print("Value is an Int:", value)
case .option2:
guard let value = rawValue as? String else { return }
print("Value is a String:", value)
}
}
What I would like to achieve is something like:
option.createKey().forEach {
guard let rawValue = origin[$0] as? option.getType() else { return }
}
Is this possible?
I think the core of the problem here is that Swift has strict typing. That means types must be known at compile time. This, obviously, is legal:
let s : Any = "howdy"
if let ss = s as? String {
print(ss)
}
But this is not legal:
let s : Any = "howdy"
let someType = String.self
if let ss = s as? someType { // *
print(ss)
}
someType must be a type; it cannot be a variable hiding a type inside itself. But that is precisely what, in effect, you are asking to do.
I know there is new error handling i.e. do/catch but not sure if it applies here and even if it does it's pretty difficult for me even going through the documentation. Could someone show me the correct code block please.
/*** error Extra argument 'error' in call ***/
var plistDic = NSPropertyListSerialization.propertyListWithData(plistData!,
options:Int(NSPropertyListMutabilityOptions.MutableContainersAndLeaves.rawValue),
format: nil, error: &error) as Dictionary<String, Dictionary<String, String>>
assert(error == nil, "Can not read data from the plist")
return plistDic
}
// END
EDIT:
let YALCityName = "name"
let YALCityText = "text"
let YALCityPicture = "picture"
private let kCitiesSourcePlist = "Cities"
class YALCity: Equatable {
var name: String
var text: String
var image: UIImage
var identifier: String
// MARK: Class methods
class internal func defaultContent() -> Dictionary<String, Dictionary<String, String>> {
let path = NSBundle.mainBundle().pathForResource(kCitiesSourcePlist, ofType: "plist")
let plistData = NSData(contentsOfFile: path!)
assert(plistData != nil, "Source doesn't exist")
do {
let plistDic = try NSPropertyListSerialization.propertyListWithData(plistData!,
options:NSPropertyListMutabilityOptions.MutableContainersAndLeaves,
format: nil
)
if let dictionary = plistDic as? Dictionary< String, Dictionary<String, String> > {
print("\(dictionary)")
}
else {
print("Houston we have a problem")
}
}
catch let error as NSError {
print(error)
}
return defaultContent()
}
init(record:CKRecord) {
self.name = record.valueForKey(YALCityName) as! String
self.text = record.valueForKey(YALCityText) as! String
let imageData = record.valueForKey(YALCityPicture) as! NSData
self.image = UIImage(data:imageData)!
self.identifier = record.recordID.recordName
}
}
func ==(lhs: YALCity, rhs: YALCity) -> Bool {
return lhs.identifier == rhs.identifier
}
Try this code:
do {
var plistDic = try NSPropertyListSerialization.propertyListWithData(plistData!,
options:NSPropertyListMutabilityOptions.MutableContainersAndLeaves,
format: nil
)
// plistDic is of type 'AnyObject'. We need to cast it to the
// appropriate dictionary type before using it.
if let dictionary = plistDic as? Dictionary<String, Dictionary<String, String>> {
// You are good to go.
// Insert here your code that uses dictionary (otherwise
// the compiler will complain about unused variables).
// change 'let' for 'var' if you plan to modify the dictionary's
// contents.
// (...)
}
else {
// Cast to dictionary failed: plistDic is NOT a Dictionary with
// the structure: Dictionary<String, Dictionary<String, String>>
// It is either a dictionary of a different internal structure,
// or not a dictionary at all.
}
}
catch let error as NSError {
// Deserialization failed (see console for details:)
print(error)
}
Note: I split the call to a function that throws (try...) and the casting to your specific type of Dictionary (if let...) because I'm not really sure exactly what would happen if the call succeeds but the cast fails, or if it would be clear which one failed from the debugger. Also, I don't like too many things happening in one line...
EDIT: I fixed the options parameter. In Swift, Ints and enums aren't interchangeable; you need to pass the right type (I missed it the first time when modifying your code).