Extra argument 'error' in call - do/catch? - swift

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).

Related

Property declared in my class isn't recognized when attempting to use it inside a function?

I've checked for the misspelling of the property, that's definitely not the case. I'm trying to use the property mySong that I declared in my class inside the parseSongs() function.
That function isn't inside the class but it's in the same file. And the target membership of that class is set to the project name as are the other files as well.
I'm very confused why the compiler isn't recognizing the name of my property in the parseSongs()?
I can declare the property outside of the class but I should be able to use it even if it's declared inside the class.
import UIKit
class SongsTableViewController: UITableViewController {
//A property that is an array of type 'Song'
var mySong = [Song]()
private let cache = NSCache()
private func fetchMyData(){
let myUrl = NSURL(string: "http://itunes.apple.com/search?term=beatles&country=us")!
let mySession = NSURLSession.sharedSession()
//The work to be queued initiates
let myTask = mySession.dataTaskWithURL(myUrl){
//This closure right here is the Completion Handler
data, response, error in
if error != nil{
//Handle error
}else{
let myHttpResponse = response as! NSHTTPURLResponse
switch myHttpResponse.statusCode {
case 200..<300:
print("OK")
print("data: \(data)")
default: print("request failed: \(myHttpResponse.statusCode)")
}
}
}
myTask.resume()
}
}
func parseJson(myData data: NSData){
do{
let json: AnyObject? = try NSJSONSerialization.JSONObjectWithData(data, options: [])
if let unwrappedJson: AnyObject = json{
parseSongs(unwrappedJson)
}
}catch{
}
}
func parseSongs(json1: AnyObject){
mySong = []
//Optional Binding
if let array = json1["results"] as? [[String:AnyObject]]{
//For-In loop
for songDictionary in array{
if let title = songDictionary["trackName"] as? NSString{
if let artist = songDictionary["artistName"] as? NSString{
if let albumName = songDictionary ["collectionName"] as? NSString{
if let artWorkUrl = songDictionary["artWorkUrl100"] as? NSString {
let song = Song(artist: (artist as String), title: (title as String), albumName: (albumName as String), artWorkUrl: (artWorkUrl as String))
mySong.append(song)
}
}
}
}
}
dispatch_async(dispatch_get_main_queue()){
self.tableView.reloadData()
}
}
}
To use the property outside which declared inside a class you have to follow this
SongsTableViewController().theProperty
If you declare it outside class then you can access it in function of outside class

Swift check type of object

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 }

Cannot convert value of type 'String?!' to expected argument type 'Notifications'

I am trying to check the id of a record before I put it into the array, using xcode swift
here is the code. But, i get the following error
Notifications.swift:50:46: Cannot convert value of type 'String?!' to expected argument type 'Notifications'
on this line
*if (readRecordCoreData(result["MessageID"])==false)*
Please can some one help to explain this error
import CoreData
struct Notifications{
var NotifyID = [NSManagedObject]()
let MessageDesc: String
let Messageid: String
init(MessageDesc: String, Messageid:String) {
self.MessageDesc = MessageDesc
self.Messageid = Messageid
// self.MessageDate = MessageDate
}
static func MessagesWithJSON(results: NSArray) -> [Notifications] {
// Create an empty array of Albums to append to from this list
var Notification = [Notifications]()
// Store the results in our table data array
if results.count>0 {
for result in results {
//get fields from json
let Messageid = result["MessageID"] as! String
let MessageDesc = result["MessageDesc"] as? String
let newMessages = Notifications(MessageDesc: MessageDesc!, Messageid:Messageid)
//check with id's from core data
if (readRecordCoreData(result["MessageID"])==false)
{
Notification.append(newMessages)
}
}
}
return Notification
}
//check id
func readRecordCoreData(Jsonid: String) -> Bool {
var idStaus = false
let appDelegate =
UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
//2
let fetchRequest = NSFetchRequest(entityName: "ItemLog")
//3
do {
let resultsCD = try! managedContext.executeFetchRequest(fetchRequest)
if (resultsCD.count > 0) {
for i in 0 ..< resultsCD.count {
let match = resultsCD[i] as! NSManagedObject
let id = match.valueForKey("notificationID") as! String
if (Jsonid as String! == id)
{
idStaus = true
}
else{
idStaus = false
}
}
}
} catch let error as NSError {
print("Could not fetch \(error), \(error.userInfo)")
}
return idStaus
}
One of your methods is static and the other one is not :
func readRecordCoreData(Jsonid: String) -> Bool
static func MessagesWithJSON(results: NSArray) -> [Notifications]
Depending on what you want to accomplish you could declare both static, none, or replace
//check with id's from core data
if (readRecordCoreData(result["MessageID"])==false)
{
Notification.append(newMessages)
}
By
//check with id's from core data
if (Notifications.readRecordCoreData(Messageid)==false)
{
Notification.append(newMessages)
}
Not sure if the code will work past compilation however as there are many readability issues

How to use optional chaining while searching through a dictionary in swift?

I want to safely search through values in a swift Dictionary using if lets and making sure it is type safe as I get deeper and deeper into the dictionary. The dictionary contains dictionaries that contains NSArray that contains more dictionary.
At first attempt my code looks like this:
if let kkbox = ticket["KKBOX"] as? Dictionary<String, AnyObject> {
if let kkboxDlUrlDict = kkbox["kkbox_dl_url_list"] as? Dictionary<String, AnyObject> {
if let kkboxDlUrlArray = kkboxDlUrlDict["kkbox_dl_url"] as? NSArray {
for dict in kkboxDlUrlArray {
if let name = dict["name"] as? String {
if name == mediaType.rawValue {
urlStr = dict["url"] as String
}
}
}
} else { return nil }
} else { return nil }
} else { return nil }
How do I shorten it to perhaps one or 2 line?
I realised I can chain it if it is 2 layers. This works:
if let kkboxDlUrlArray = ticket["KKBOX"]?["kkbox_dl_url_list"] as? NSArray {
}
But any chain longer than that, will not compile.
Is there a way to chain through a dictionary more than once?
Thank you
You can chain, but with proper downcast at each step:
if let kkboxDlUrlArray = ((((ticket["KKBOX"] as? Dictionary<String, AnyObject>)?["kkbox_dl_url_list"]) as? Dictionary<String, AnyObject>)?["kkbox_dl_url"]) as? NSArray {
for dict in kkboxDlUrlArray {
println(dict)
}
}
That doesn't look good though - it's one line, but not readable.
Personally, without using any fancy functional way to do it, I would make the chain more explicit with just one optional binding:
let kkbox = ticket["KKBOX"] as? Dictionary<String, AnyObject>
let kkboxDlUrlDict = kkbox?["kkbox_dl_url_list"] as? Dictionary<String, AnyObject>
if let kkboxDlUrlArray = kkboxDlUrlDict?["kkbox_dl_url"] as? NSArray {
for dict in kkboxDlUrlArray {
println(dict)
}
}
In my opinion much easier to read, and with no unneeded indentation
Alternatively, you could use this extension, which mimics the original valueForKeyPath method that used to exist in NSDictionary but got axed for whatever reason:
Swift 4.1
extension Dictionary where Key: ExpressibleByStringLiteral {
func valueFor(keyPath: String) -> Any? {
var keys = keyPath.components(separatedBy: ".")
var val : Any = self
while keys.count > 0 {
if let key = keys[0] as? Key {
keys.remove(at: 0)
if let dic = val as? Dictionary<Key, Value> {
if let leaf = dic[key] {
val = leaf
} else {
return nil
}
} else {
return nil
}
} else {
return nil
}
}
return val
}
Your code would then read:
if let array = ticket.valueFor("KKBOX.kkbox_dl_url_list.kkbox_dl_url") as? [] {
// do something with array
}
Seems like swift 1.2 has added this feature.
"More powerful optional unwrapping with if let — The if let construct can now unwrap multiple optionals at once, as well as include intervening boolean conditions. This lets you express conditional control flow without unnecessary nesting."
https://developer.apple.com/swift/blog/

Swift: Stuck with a compiler error

While exploring a structure read from a json file, I've got this message on the “if let” line which I'm stuck with:
'String' is not a subtype of '(String, AnyObject)'
The code is as follows:
if let descriptions: Array<Dictionary<String,AnyObject>> = fields["description"] as? Array {
let description = descriptions[0]
if let text:String = description["text"] as? String { // where the error occurs
poi.description = text
}
}
You have to unwrap what's read from the description dictionary:
if let text:String = description["text"]! as? String { // where the error occurs
...
}
But that's not safe, because if the key is not found in the dict, it throws a runtime exception. A safer way is:
if let text:String = (description["text"] as AnyObject?) as? String { // where the error occurs
...
}
However, I presume that you're using NSJSONSerialization to deserialize your json data, so a better way to do that is to stick with obj-c types rather than pure swift data types with generics:
if let descriptions = fields["description"] as? NSArray {
let description = descriptions[0] as NSDictionary
if let text = description["text"] as? String {
let x = text
}
}
More compact and much easier to read.
Use the new syntax and less of it.
Test declarations: Dictionary of Array of Dictionary
// let testFields: [String:[[String:Any]]]
or
// let testFields: [String:[[String:String]]]
let testFields = ["description":[["text":"value"]]]
if let descriptions = testFields["description"] {
let description = descriptions[0]
if let text = description["text"] as String? {
println("text: \(text)")
}
}
Output:
text: value