That error tells me that "Result value in ‘?’ :’ expression have mismatching types ’NSJONWritingOptions’ and ‘_'". Does anyone know how to fix this? I wrote these codes on xcode6.3.1 and converted to xcode7 just now. worked on xcode6 though....
public func toString(pretty:Bool=false)->String {
switch _value {
case is NSError: return "\(_value)"
case is NSNull: return "null"
case let o as NSNumber:
switch String.fromCString(o.objCType)! {
case "c", "C":
return o.boolValue.description
case "q", "l", "i", "s":
return o.longLongValue.description
case "Q", "L", "I", "S":
return o.unsignedLongLongValue.description
default:
switch o.doubleValue {
case 0.0/0.0: return "0.0/0.0" // NaN
case -1.0/0.0: return "-1.0/0.0" // -infinity
case +1.0/0.0: return "+1.0/0.0" // infinity
default:
return o.doubleValue.description
}
}
case let o as NSString:
return o.debugDescription
default:
let opts = pretty
//below is the code I got an error for
? NSJSONWritingOptions.PrettyPrinted : nil
if let data = (try? NSJSONSerialization.dataWithJSONObject(
_value, options:opts)) as NSData? {
if let result = NSString(
data:data, encoding:NSUTF8StringEncoding
) as? String {
return result
}
}
return "YOU ARE NOT SUPPOSED TO SEE THIS!"
}
}
options in NSJSONSerialization.dataWithJSONObject:options: should be an empty array if you don't want to specify any options. So your code should look like this:
let opts = pretty ? NSJSONWritingOptions.PrettyPrinted : []
Previously it expected nil, but there was change in the way iOS SDK is mapped in Swift.
I use this in my Swift 2.0 implementations-
let options = prettyPrinted ?
NSJSONWritingOptions.PrettyPrinted : NSJSONWritingOptions(rawValue: 0)
Related
I am trying to implement something like this,
let api1 = Observable.of(["documents"]) //Replace with observable to download docs
let api2 = Observable.of(["applications"]) //Replace with observable to download apps
let api3 = Observable.of(["videos"]) //Replace with observable to download videos
Observable.combineLatest(api1, api2, api3){(docs, apps, videos) in
return (docs, apps, videos)
}.skipWhile{ (docs, apps, videos) in
return docs.count == 0 && apps.count == 0 && videos.count == 0
}.subscribe(onNext:{(docs, apps, videos) in
})
.disposed(by:disposeBag)
In my case, I am trying to create observables dynamically and add it to an array like this,
private var discoverObservables = [Observable<Any>]()
func loadDiscoverFeeds(){
self.feeds.forEach({
feed in
switch feed.feedType{
case "a":
let observable = self.aObservable(url: feed.feedURL ?? "")
self.discoverObservables.append(observable)
break
case "b":
let observable = self.bObservable(url: feed.feedURL ?? "")
self.discoverObservables.append(observable)
break
case "c":
let observable = self.cObservable(url: feed.feedURL ?? "")
self.discoverObservables.append(observable)
break
case "d" :
let observable = self.dObservable(url: feed.feedURL ?? "")
self.discoverObservables.append(observable)
break
default:
break
}
})
}
private func aObservable(url : String) -> Observable<A?>{
return APIManager.shared.getA(url: url)
}
private func bObservable(url : String) -> Observable<B?>{
return APIManager.shared.getB(url: url)
}
private func cObservable(url : String) -> Observable<C?>{
return APIManager.shared.getC(url: url)
}
But this is not working because discoverObservables array is expecting the value of Type Observable<Any> and I am trying to add Observable<A?>
How can I do this correctly, I want to make sure all the observables return data before I start processing the data.
Edit
I am trying to load data from different sources before that is added to the view, basically, I have a collectionview, each section loads data from different API, I am trying to get all the required data from all sources before that is added to collection view.
Add the same protocol to A, B and C.
protocol YourProtocol {...}
class A: YourProtocol {...}
class B: YourProtocol {...}
class C: YourProtocol {...}
Then you can make :
private var discoverObservables = [Observable<YourProtocol>]()
The first code block seems to be doing the job with one exception, the condition checks if all of the (docs, apps, videos) are empty, perhaps you wanted to use || instead of &&.
As for the second code block with an array, I did something that could help.
struct A {}
let observable1 = Observable.just(A())
let observable2 = Observable.just(A())
let observable3 = Observable.just(A())
let observables: [Observable<A>] = [observable1, observable2, observable3]
Observable.combineLatest(observables).skipWhile { (streams) -> Bool in
streams.forEach {
if $0.count == 0 { return true }
}
return false
}.subscribe(...
This subscription will result with Observable<[A]>.
I'm going to specifically address this from your question: "I want to make sure all the observables return data before I start processing the data."
Strictly speaking, you probably don't want an Any structure. Better would be a protocol or enum. I see that other answers have addressed the protocol idea so I will use the enum idea:
enum EndpointResponse {
case a(A?)
case b(B?)
// etc...
}
let responses = Observable.zip(
feeds.map { (feed) -> Observable<EndpointResponse> in
switch feed.feedType {
case "a":
return aObservable(url: feed.feedURL ?? "").map { EndpointResponse.a($0) }
case "b":
return bObservable(url: feed.feedURL ?? "").map { EndpointResponse.b($0) }
default:
fatalError()
}
}
)
The above responses observable will contain an array of all the responses once they have all emitted values. In other words, the zip operator will gather up all the responses from all the network calls and emit a single array containing all of them.
My Previous answer:
There really isn't a lot of information to go on in the question, but something like this answers the direct question you ask about converting an Observable<X> to an Observable<Any>...
let discoverObservables = Observable.zip(
feeds.map { (feed) -> Observable<Any> in
switch feed.feedType {
case "a":
return aObservable(url: feed.feedURL ?? "").map { $0 as Any }
case "b":
return bObservable(url: feed.feedURL ?? "").map { $0 as Any }
case "c":
return cObservable(url: feed.feedURL ?? "").map { $0 as Any }
case "d":
return dObservable(url: feed.feedURL ?? "").map { $0 as Any }
default:
break
}
}
)
I have some data from server, and I'm using Alamofire SwiftyJSON to convert it to [String: Any]. Then I'm saving it to plist using SwiftyPlistManager. The point is that SwiftyPlistManager crashed when saving <null>, so I need to replace all <null>or nilto "".
My Dictionary after Alamofire SwiftyJSON looks this way:
["info_editable": true,
"name": Android Q,
"is_message": true,
"images": [["id": 92,
"image": /media/product/102.png]],
"video_description": <null>,
"is_valid": true]
or it could be -
["info_editable": true,
"name": Android Q,
"is_message": true,
"images": <null>,
"video_description": <null>,
"is_valid": true]
I suppose to use Codable from raw data, but have no idea how to set initial value as empty string or [[]], then check if parced data is <null> and leave initial value as default.
Or is there any way to list nested dictionary to replace <null>to ""?
You can try
var dic = ["1":nil,"2":"33","3":"5444"]
let res = dic.mapValues { $0 == nil ? "" : $0 }
print(res) // ["1": Optional(""), "2": Optional("33"), "3": Optional("5444")]
for now, my best idea is -
1) stringify every value except array of dictionaries,
2) check if string content "null", then replace it with ""if true.
3) array of dictionaries, if stringified, will have 2 option - with [[ and ]](then checking every dictionary like above) or without - in case of "images": <null>,(so <null> should be replaced with[[]].
but I have about 7 requests with different data, should be parsed this strange way, and I hope to found more pretty decision.
Here's a protocolish solution, that goes recursively through dictionaries and arrays:
/// Allows clients to ask the receiver to remove any `NSNull` instances
protocol NullStripable {
func strippingNulls() -> Self
}
extension Array: NullStripable {
func strippingNulls() -> Self {
return compactMap {
switch $0 {
case let strippable as NullStripable:
// the forced cast here is necessary as the compiler sees
// `strippable` as NullStripable, as we casted it from `Element`
return (strippable.strippingNulls() as! Element)
case is NSNull:
return nil
default:
return $0
}
}
}
}
extension Dictionary: NullStripable {
func strippingNulls() -> Self {
return compactMapValues {
switch $0 {
case let strippable as NullStripable:
// the forced cast here is necessary as the compiler sees
// `strippable` as NullStripable, as we casted it from `Value`
return (strippable.strippingNulls() as! Value)
case is NSNull:
return nil
default:
return $0
}
}
}
}
Usage example:
let dict: [String: Any] = [
"items": ["info_editable": true,
"name": "Android Q",
"is_message": true,
"images": [["id": 92,
"image": "/media/product/102.png"]],
"video_description": NSNull(),
"is_valid": true],
"somethingElse": NSNull()
]
print(dict.strippingNulls())
Sample output:
["items": ["name": "Android Q", "info_editable": true, "images": [["image": "/media/product/102.png", "id": 92]], "is_message": true, "is_valid": true]]
Try this one. It will remove null and replace with blank string, without loosing key.
func removeNullFromResponse(response:NSDictionary) -> NSDictionary{
let blankString = "\"\""
let myMutableDict: NSMutableDictionary = NSMutableDictionary(dictionary: response)
var data = try? JSONSerialization.data(withJSONObject:myMutableDict, options: JSONSerialization.WritingOptions.prettyPrinted)
var strData = NSString.init(data: data!, encoding:String.Encoding.utf8.rawValue)
strData = strData?.replacingOccurrences(of: "<NULL>", with: blankString) as NSString?
strData = strData?.replacingOccurrences(of: "<null>", with: blankString) as NSString?
strData = strData?.replacingOccurrences(of: "<Null>", with: blankString) as NSString?
strData = strData?.replacingOccurrences(of: "NULL", with: blankString) as NSString?
strData = strData?.replacingOccurrences(of: "null", with: blankString) as NSString?
data = strData?.data(using: String.Encoding.utf8.rawValue)!
var dictionary = NSDictionary()
do
{
dictionary = try JSONSerialization.jsonObject(with: data! , options: JSONSerialization.ReadingOptions()) as! NSDictionary
} catch {
print(error)
}
return dictionary as NSDictionary
}
I have converted a XML into a Dictionary and I would like to check first if any of the values is nil.
Is there any alternative to this ugly code (the Dictionary contains 30 key-value pairs):
if (xmlDict["key1" != nil && xmlDict["key2" != nil && .... && xmlDict["key30" != nil) {
//Do something with these values
} else {
//Do not do anything at all with this dictionary
}
[Update]
The dictionary has this format (mixed value types):
let futuramaDict = NSArray(contentsOf: xmlFile!) as? [NSDictionary]
futuramaDict {
"Cartoon" : "Futurama"
"Length" : 120
"MainCharacter" : "Philip J. Fry"
"ClosestFriends" : ["Bender","Leela","Zoidberg"]
}
I left the other key-value pairs so I don't fill the thread with irrelevant content
You can check with one line
xml.values.filter({ $0 == nil }).isEmpty
Here's the complete snippet
let xml: [String: String?] = [
"key1" : "Value1",
"key2" : "Value2",
"key3" : nil,
"key4" : "Value4"
]
if xml.values.filter({ $0 == nil }).isEmpty
{
print("Plenty of values")
}
else
{
print("nil :(")
}
A more "swifty" way:
for (key, val) in xmlDict where val == nil {
print("Found a nil value for this key: \(key)")
}
A quick and short solution:
if xmlDict.values.contains(where: {$0 == nil}) {
print("Dict contains at least one nil value")
}
In the case when you have a predefined set of keys you want to look for:
let keysToLookFor = ["k1", "k2", "k3", "k4"]
if keysToLookFor.contains(where: {xmlDict[$0] == nil}) {
print("Dict contains at least one nil value")
}
A sample dict for testing:
let xmlDict: [String: Any?] = ["k1": 22, "k2": Int(2), "k3": nil]
How about this -
let hasAnyKeyNil = dic.keys.contains(where: { dic[$0]! == nil })
This code should achieve what you want:
var values = [Any]()
for index in 1 ... 30 {
let key = "key\(index)" // Generates "key1" through "key30"
guard let value = xmlDict[key] else {
values.removeAll()
break // Abort loop
}
values.append(values)
}
// Next: Process contents of array 'values'
// (will be empty if any key was absent)
I'm attempting to convert the following code from this library (https://github.com/dankogai/swift-json) into Swift 3 Compatible code.
There are a multitude of errors regarding the fromCString function that was previously available on the String in Swift 2
The error is roughly the same everywhere:
'fromCString' is unavailable: Please use String.init?(validatingUTF8:) instead. Note that it no longer accepts NULL as a valid input. Also consider using String(cString:), that will attempt to repair ill-formed code units.
Seeing as I have 2 choices to choose from I'm not sure the correct one given the original authors intentions.
For example here is a snippet with the error.
extension JSON : CustomStringConvertible {
/// stringifies self.
/// if pretty:true it pretty prints
public func toString(pretty:Bool=false)->String {
switch _value {
case is NSError: return "\(_value)"
case is NSNull: return "null"
case let o as NSNumber:
switch String.fromCString(o.objCType)! {
case "c", "C":
return o.boolValue.description
case "q", "l", "i", "s":
return o.int64Value.description
case "Q", "L", "I", "S":
return o.uint64Value.description
default:
switch o.doubleValue {
case 0.0/0.0: return "0.0/0.0" // NaN
case -1.0/0.0: return "-1.0/0.0" // -infinity
case +1.0/0.0: return "+1.0/0.0" // infinity
default:
return o.doubleValue.description
}
}
case let o as NSString:
return o.debugDescription
default:
let opts = pretty ? JSONSerialization.WritingOptions.prettyPrinted : JSONSerialization.WritingOptions()
if let data = (try? JSONSerialization.data(
withJSONObject: _value, options:opts)) as NSData? {
if let result = NSString(
data:data as Data, encoding:String.Encoding.utf8.rawValue
) as? String {
return result
}
}
return "YOU ARE NOT SUPPOSED TO SEE THIS!"
}
}
public var description:String { return toString() }
}
Notice fromCString in the above code. What is the correct way to simulate the Swift 2 behavior correctly?
As error suggest use init(cString:) like this way.
String(cString:o.objCType)
Check Apple Documentation for more detail.
Let's say we have String?? value. Optional of Optional may have 3 states:
let strOptOpt1: String?? = .Some(.Some("actual value"))
let strOptOpt2: String?? = .Some(.None)
let strOptOpt3: String?? = .None
To safely unwrap them to String, in Swift 1.1 we could:
if let str:String = strOptOpt? {
println(str)
}
else {
println("was nil") // `.Some(.None)` or `.None`
}
But it does not work anymore in Swift 1.2:
if let str:String = strOptOpt? {
// ^ [!] error: '?' must be followed by a call, member lookup, or subscript
At the moment, I think, I have to do like this:
if let strOpt = strOptOpt, str = strOpt {
println(str)
}
OR using switch:
switch strOptOpt {
case let .Some(.Some(str)):
println(str)
default:
println("was nil")
}
But, there must be simpler way to do this, I think. Anyone know that?
As of Swift 2, you can use if/case with a pattern, and x? as
a synonym for .Some(x):
if case let str?? = strOptOpt1 {
print(str)
} else {
print("was nil") // `.Some(.None)` or `.None`
}
For me, the best solution I found is using ?? operator in optional binding.
let strOptOpt: String?? = ...
if let str = strOptOpt ?? nil {
println(str) // "actual value"
}
Works for all these cases:
.Some(.Some("actual value"))
.Some(.None)
.None
But, if strOptOpt is String???, you should do:
let strOptOpt: String??? = ...
if let str = (strOptOpt ?? nil) ?? nil {
println(str)
}
I always get double optional in this situation
let field: ChangeField?? = json["field_id"].int.map{ ChangeField(rawValue: $0) }
Instead of map you can use flatMap
let field: ChangeField? = json["field_id"].int.flatMap{ ChangeField(rawValue: $0) }