Swift - Cannot invoke initializer for type 'NSDictionary' - ios

Since upgrading to Xcode7, I am getting the following error:
Cannot invoke initializer for type 'NSDictionary' with an argument list of type '(objects: [AnyObject!], forKeys: [String])'
on this line of code:
self.sessionBids!.addObject(NSDictionary(objects: [PFUser.currentUser().objectId, PFUser.currentUser().objectForKey("username"), self.bidTextField.text], forKeys: ["user", "name", "bid"]))
Can someone explain why?
EDIT: Here is the full block of code
if(self.bidTextField.text!.rangeOfString("^[0-9]*$", options: .RegularExpressionSearch) != nil) {
self.sessionBids = array[0].objectForKey("bids") as? NSMutableArray
var lastSessionBid : NSDictionary
SVProgressHUD.showProgress(50)
var previousHighBid : Int! = 0
if(self.sessionBids == nil) {
self.sessionBids = NSMutableArray()
} else {
lastSessionBid = self.sessionBids.objectAtIndex(self.sessionBids.count - 1) as! NSDictionary
previousHighBid = Int(lastSessionBid.objectForKey("bid") as! String)
}
if( previousHighBid >= Int(self.bidTextField.text!)) {
print("bid is lower than current bid")
SVProgressHUD.showErrorWithStatus("Bid is lower than current bid!")
return
} else {
self.sessionBids!.addObject(NSDictionary(objects: [PFUser.currentUser().objectId, PFUser.currentUser().objectForKey("username"), self.bidTextField.text], forKeys: ["user", "name", "bid"]))
SVProgressHUD.showProgress(75)
self.session.setObject(self.sessionBids, forKey: "bids")
self.session.save()
self.keyboardShowing = false
self.reloadSessionBids()
SVProgressHUD.showProgress(100)
SVProgressHUD.showSuccessWithStatus("Successfully Added Bid")
}
} else {
SVProgressHUD.showErrorWithStatus("Bid must be a number!")
}

You're trying to call init(objects: [AnyObject], forKeys keys: [NSCopying]) initializer of NSDictionary. objects: [AnyObject] can't contain Optionals (according to its declaration), and it seems that PFUser.currentUser().objectId, PFUser.currentUser().objectForKey("username"), self.bidTextField.text are all Optionals, that's why you're getting the error.
To resolve this, as vadian suggested, you'll need to unwrap all the Optionals in that array.

Both the objects and the keys of a dictionary must not be nil.
Make sure that all objects are non-optionals.

Try calling NSDictionary's
public init(objects: UnsafePointer<AnyObject?>, forKeys keys: UnsafePointer<NSCopying?>, count cnt: Int) instead:
self.sessionBids!.addObject(NSDictionary(objects: [PFUser.currentUser().objectId, PFUser.currentUser().objectForKey("username"), self.bidTextField.text], forKeys: ["user", "name", "bid"], count: 3))
Alternatively, try using a native dictionary instead of an NSDictionary.
Say:
var bidDict = [String : AnyObject]()
bid["user"] = PFUser.currentUser().objectId ?? "unknown"
bid["name"] = PFUser.currentUser().objectForKey("username") ?? "unknown"
bid["bid"] = self.bidTextField.text ?? "unknown"
Then insert into your array:
self.sessionBids!.addObject(bid)

Related

How can I get String from Any in swift3

Such as I get a dic from fetchData:
(lldb) po dic
▿ 3 elements
▿ 0 : 2 elements
- .0 : "total"
- .1 : 0.00
▿ 1 : 2 elements
- .0 : "year"
- .1 : 2016
▿ 2 : 2 elements
- .0 : "month"
- .1 : 12
(lldb) po dic["year"]
▿ Optional<Any>
(lldb) po dic["year"]!
2016
Is there a function to get String form Any?
The function's usage is like below:
let total = UtilSwift.getStrFromAny(dic["total"] as Any )
In objective-c, I written a method:
+ (NSString*)getStringWithoutNull:(id)value
{
NSString *strValue = #"";
if(value != nil){
strValue = [NSString stringWithFormat:#"%#", value];
}
if([strValue isEqualToString:#"null"])
{
return #"";
}
if ([strValue isEqual:[NSNull null]]) {
return #"";
}
return strValue;
}
Is in swift could write a method like this to get String form Any?
The Any maybe Int, String, "", Double, or other type.
EDIT - 1
After the tried in Playground. :
import UIKit
let dict:[String:Any] = ["aa": 123, "year":1994, "month":"12"]
let string = String(describing:dict["year"]) ?? "" // Try to turn result into a String, o
print(string) // If I print(string!), and there will report error.
The warning:
EDIT 2
I know the edit -2 maybe paint the lily, but if when use the func below, when I deliver a Opitional value to the func, the return String will be Opitinal too, how to avoid that?
This below is my test in Playground, dic["b"] as Any convert the parameter to Opitional:
let dic:[String:Any] = [ // ["b": 12, "A": "A"]
"A":"A",
"b":12
]
func stringFromAny(_ value:Any?) -> String {
if let nonNil = value, !(nonNil is NSNull) {
return String(describing: nonNil) // "Optional(12)"
}
return ""
}
let str = stringFromAny(dic["b"] as Any) // "Optional(12)"
Try this one:
func stringFromAny(_ value:Any?) -> String {
if let nonNil = value, !(nonNil is NSNull) {
return String(describing: nonNil)
}
return ""
}
Update:
If the calling code invokes the above function with an Any? parameter that is explicitly cast to Any (a strange scenario which the Swift 3 compiler allows), then it will consider the final type of the parameter to be a non-optional optional, i.e. an Any value where the type Any represents is Any?. Or, in other terms, the value would be considered to be Optional<Any>.some(value:Any?).
In this case, the if let to unwrap the "some" case returns an optional value as the result in the function implementation. Which means that the final string description will include the "Optional" designation.
Because of the various oddities around the fact that the Swift 3 compiler will happily cast between Any and Any? and consider a value of type Any to be a value of type Any? and vice versa, it's actually pretty complicated to detect if an Any really contains an `Any?' or not, and to unwrap accordingly.
A version of this same function, along with some necessary additional extensions is provided below. This version will recursively flatten an Any value containing any number of nested Any? cases inside to retrieve the innermost non-optional value.
While this is what you seem to be looking for, I am of the opinion that it's a lot of hassle to work around something a programmer should not be doing anyway, namely miscasting a known Any? value to be Any because the compiler has a weird exception for that, even when it is not actually true.
Here's the "developer-proof" version of the code:
protocol OptionalType {
var unsafelyUnwrapped: Any { get }
var unsafelyFlattened: Any { get }
}
extension Optional: OptionalType {
var unsafelyUnwrapped: Any { return self.unsafelyUnwrapped }
var unsafelyFlattened: Any { return (self.unsafelyUnwrapped as? OptionalType)?.unsafelyFlattened ?? self.unsafelyUnwrapped }
}
func stringFromAny(_ value:Any?) -> String {
switch value {
case .some(let wrapped):
if let notNil = wrapped as? OptionalType, !(notNil.unsafelyFlattened is NSNull) {
return String(describing: notNil.unsafelyFlattened)
} else if !(wrapped is OptionalType) {
return String(describing: wrapped)
}
return ""
case .none :
return ""
}
}
Use ! if the value is an optional
String(describing: nonNil) // "Optional(12)"
String(describing: nonNil!) // "12"

If let condition true when value is missing in optional type, swift

I have parser in Objc, parser returns NSDictionary. I am using this parser in swift class. But when some value is missing on that dictionary, it shows nil value. e.g. ->
wirlessData = {
"anon" = {
};
"channel" = {
"text" = 1;
};
}
I am checking through
if let wepauthValue = wirlessData["wepauth"] {
if let value = wepauthValue["text"] {
print("\(value)") // nil
}
}
I don't how it satisfy the if let condition. Any one faced this types of problem can help me out.
Thanks,
vikash
You don't need any special code to do this, because it is what a dictionary already does. When you fetch dict[key] you know whether the dictionary contains the key, because the Optional that you get back is not nil (and it contains the value).
So, if you just want to answer the question whether the dictionary contains the key, ask:
let keyExists = dict[key] != nil
If you want the value and you know the dictionary contains the key, say:
let val = dict[key]!
But if, as usually happens, you don't know it contains the key - you want to fetch it and use it, but only if it exists - then use something like if let:
if let val = dict[key] {
// now val is not nil and the Optional has been unwrapped, so use it
}
I have tested it and found that value is still optional.Take a look at screenshot below to understand it better.
"anon" would be an empty dictionary. An empty dictionary is not nil, it is a dictionary. Just an empty one. A JSON parser will never, ever give nil values unless you ask for a key that is not in a dictionary. For example wirlessData ["nonexistingkey"] would give you nil.
If you be more type-strong about it with the if..let's then:
if let anonValue = wirlessData["anon"] {
if let value = anonValue["text"] as? String {
// This won't execute if value isn't converted from `anonvalue["text"]` to String specifically. This includes null been a false match too
print("\(value)") // nil
}else{
print("Value did't match string at all")
}
}
or even more specifically in your case:
if let anonValue = wirlessData["anon"] {
if let value = anonValue["text"] as? Int {
// This won't execute if value isn't converted from `anonvalue["text"]` to String specifically. This includes null been a false match too
print("\(value)") // nil
}else{
print("Value did't match int at all")
}
}
The value your parser is returning not nil, its empty so you need to check on count if inner data type is dictionary or array, I have past 1 sample here
Please use below code and correct your logic accordingly to get it work properly
let wirlessData:[String:AnyObject] = [
"anon" : [],
"channel" : [
"text" : 1
]
]
if wirlessData["anon"]?.count > 0 {
if let value = wirlessData["anon"]!["text"] {
print("\(value)") // nil
}
}
Try this below code using type check operator (is) -
if wirlessData["anon"] is [String:AnyObject]
{
let anon = wirlessData["anon"]!
print(anon)
if anon["random"] is String {
let stringValue = anon["random"]!
print("\(stringValue)")
}
else if anon["random"] is Int
{
let intValue = anon["random"]!
print("\(intValue)") // nil
}
else
{
print(" may be value did't match string & Int or nil ")
}
}

Removing NSNull from Key Path Results with Partial Matches

Given a data structure with mismatching objects:
1> import Foundation
2> let d: NSDictionary = ["test": [["name": "Dick", "age": 101], ["name": "Jane"]]]
valueForKeyPath: will return the values for the total number of sub-objects:
3> d.valueForKeyPath("test.name") as! NSArray
$R2: NSArray = "2 values" {
[0] = "Dick"
[1] = "Jane"
}
Even when the leaf key doesn't exist in all cases:
4> d.valueForKeyPath("test.age") as! NSArray
$R3: NSArray = "2 values" {
[0] = Int64(101)
[1] = {
NSObject = {
isa = NSNull
}
}
}
Is there some way to only get the existing ages, without an instances of NSNull?
#distinctUnionOfArrays and so on helps if there are multiple sub-objects without the leaf key, but you're still left with the one NSNull.
On a somewhat side note, if the leaf key is entirely unknown, then only NSNulls are returned:
5> d.valueForKeyPath("test.dog") as! NSArray
$R4: NSArray = "2 values" {
[0] = {
NSObject = {
isa = NSNull
}
}
[1] = {
NSObject = {
isa = NSNull
}
}
}
In contrast, if the root key is unknown, nil is returned:
6> d.valueForKeyPath("dog.name")
$R5: AnyObject? = nil
This logic strikes me as inconsistent, but perhaps I'm missing something?
var array:[AnyObject] = [1.1, 1.2, 1.3, 1.4, 1.5, 1.6, NSNull(),1.7, 1.8, 1.9]
let newArr = array.filter{ !($0 is NSNull) }
newArr
The second part of your question doesn't make sense to me:
This code:
let x = d.valueForKeyPath("dog.name")
Makes x an optional AnyObject?.
It returns nil with the key "dog.name" on your data. That's different than an array with nil/NSNULL entries.
If you try to force-unwrap it, it crashes:
let x = d.valueForKeyPath("dog.name") as! NSArray
If you want to get rid of the NSNull entries, use a filter:
let y = (d.valueForKeyPath("test.age") as? NSArray)?.filter{!($0 is NSNull)}
In the above code, I use as? to cast the result of valueForKeyPath to an array so it can return nil if the call does not return any results. (Otherwise it crashes.)
I then only call filter if the results are not nil.
Finally, I filter the array to only those objects that are not NSNull.
Note that y is an optional, and will be nil if d.valueForKeyPath("test.age") does not return a result.

How to compare values of NSDictionary with String

I have two orgunit_id's, test["orgunit_id"] and API.loginManagerInfo.orgUnit, which I would like to compare. The problem is that the variables have different types. test["orgunit_id"] is value of a NSDictionary and the other one is a String.
I've tried several ways to cast it into Integers, but without success.
Code:
if(!orgUnits.isEmpty){
print(orgUnits) //See at console-output
for test: NSDictionary in orgUnits {
println(test["orgunit_id"]) //See at console-output
println(API.loginManagerInfo.orgUnit) //See at console-output
if(Int(test["orgunit_id"]? as NSNumber) == API.loginManagerInfo.orgUnit?.toInt()){ // This condition fails
...
}
}
}
Output:
[{
name = Alle;
"orgunit_id" = "-1";
shortdescription = Alle;
}, {
name = "IT-Test";
"orgunit_id" = 1;
shortdescription = "";
}]
Optional(-1)
Optional("-1")
Edit:
Here's the definition of API.loginManagerInfo.orgUnit: var orgUnit:String?
Use if let to safely unwrap your values and typecast the result.
If test["orgunit_id"] is an Optional Int and if API.loginManagerInfo.orgUnit is an Optional String:
if let testID = test["orgunit_id"] as? Int, let apiIDString = API.loginManagerInfo.orgUnit, let apiID = Int(apiIDString) {
if testID == apiID {
// ...
}
}
You may have to adapt this example given what is in your dictionary, but you get the point: safely unwrap the optional value and either typecast it (with if let ... = ... as? ...) or transform it (with Int(...)) before comparing.

Swift filter array of strings

I've had troubles filtering array of keywords (strings) in swift ,My code:
self.filteredKeywords=filter(keywords.allValues, {(keyword:NSString) ->
Bool in
let words=keyword as? NSString
return words?.containsString(searchText)
})
As AnyObject can't be subtype of NSString, I'm stuck with this!
[Updated for Swift 2.0]
As NSString is toll-free bridged to Swift String, just avoid the coercions with:
3> ["abc", "bcd", "xyz"].filter() { nil != $0.rangeOfString("bc") }
$R1: [String] = 2 values {
[0] = "abc"
[1] = "bcd"
}
But, if you think allValues aren't strings:
(keywords.allValues as? [String]).filter() { nil != $0.rangeOfString("bc") }
which returns an optional array.
Your filter is over [AnyObject], but your closure takes NSString. These need to match. Also, your result needs to be a Bool, not a Bool?. You can address these simply like this:
self.filteredKeywords = filter(keywords.allValues, {
let keyword = $0 as? NSString
return keyword?.containsString(searchText) ?? false
})
This accepts AnyObject and then tries to coerce it down to NSString. It then nil-coalleces (??) the result to make sure it always is a Bool.
I'd recommend, though, treating keywords as a [String:String] rather than an NSDictionary. That would get rid of all the complications of AnyObject. Then you can just do this:
self.filteredKeywords = keywords.values.filter { $0.rangeOfString(searchText) != nil }
Whenever possible, convert Foundation collections into Swift collections as soon as you can and store those. If you have incoming Foundation objects, you can generally convert them easily with techniques like:
let dict = nsdict as? [String:String] ?? [:]
Or you can do the following to convert them such that they'll crash in debug (but silently "work" in release):
func failWith<T>(msg: String, value: T) -> T {
assertionFailure(msg)
return value
}
let dict = nsdict as? [String:String] ?? failWith("Couldn't convert \(d)", [:])
Swift 4.2 provides a new way to do this:
var theBigLebowski = ["The Dude", "Angry Walter", "Maude Lebowski", "Donny Kerabatsos", "The Big Lebowski", "Little Larry Sellers"]
// after removeAll -> ["The Dude", "Angry Walter", "Donny Kerabatsos", "Little Larry Sellers"]
theBigLebowski.removeAll{ $0.contains("Lebowski")}
print(theBigLebowski)
There is both a problem with GoZoner's answer for certain data types and also a slightly better way to do this. The following examples can show this:
let animalArray: NSMutableArray = ["Dog","Cat","Otter","Deer","Rabbit"]
let filteredAnimals = animalArray.filter { $0.rangeOfString("er") != nil }
print("filteredAnimals:", filteredAnimals)
filteredAnimals: [Dog, Cat, Otter, Deer, Rabbit]
Likely not the set you expected!
However this works fine this way if we don't type animalArray as an NSMutableArray:
let animalArray = ["Dog","Cat","Otter","Deer","Rabbit"]
let filteredAnimals = animalArray.filter { $0.rangeOfString("er") != nil }
print("filteredAnimals:", filteredAnimals)
filteredAnimals: [Otter, Deer]
However I'd recommend using $0.contains() instead of $0.rangeOfString() != nil because it functions in both circumstances and slightly enhances the readability of the code:
let animalArray: NSMutableArray = ["Dog","Cat","Otter","Deer","Rabbit"]
let filteredAnimals = animalArray.filter { $0.contains("er") }
print("filteredAnimals:", filteredAnimals)
filteredAnimals: [Otter, Deer]

Resources