iOS 10 NSMutableDictionary and NSMutableArray not working as before - swift3

It seems with the release of iOS 10, a few things have broken. The major one for me has been the use of NSMutableDictionary and NSMutableArray. Both no longer seem to be able to parse a string of JSON and instead give out a nil while in pre iOS 10 they populated as expected. The only way around this I've found is to use NSDictionary and NSArray respectively and then use the init methods to cast back. For example:
let json = "{ \"code\": \"abcde\", \"name\": \"JP Morgan\" }"
json as! NSMutableDictionary // gives nil
NSMutableDictionary(dictionary: json as! NSDictionary) // works :)
let json = "[{ \"code\": \"abcde\", \"name\": \"JP Morgan\" }]"
json as! NSMutableArray // gives nil
NSMutableArray(array: json as! NSArray) // works :)
I would like to know why?
And I hope this helps someone solve their issue...

The Foundation types NSMutableArray / NSMutableDictionary are not related to the Swift counterparts and cannot be bridged / coerced from a literally created Swift type. But that's not new in Swift 3.
Basically do not use NSMutableArray / NSMutableDictionary in Swift unless you have absolutely no choice for example interacting with a few low level CoreFoundation API. The native Array / Dictionary types used with var provide the same functionality (except value vs. reference semantics) and in addition the types of the containing objects.

Related

Regular Expression in put request [duplicate]

This question already has answers here:
Safely turning a JSON string into an object
(28 answers)
Closed 7 years ago.
I want to parse a JSON string in JavaScript. The response is something like
var response = '{"result":true,"count":1}';
How can I get the values result and count from this?
The standard way to parse JSON in JavaScript is JSON.parse()
The JSON API was introduced with ES5 (2011) and has since been implemented in >99% of browsers by market share, and Node.js. Its usage is simple:
const json = '{ "fruit": "pineapple", "fingers": 10 }';
const obj = JSON.parse(json);
console.log(obj.fruit, obj.fingers);
The only time you won't be able to use JSON.parse() is if you are programming for an ancient browser, such as IE 7 (2006), IE 6 (2001), Firefox 3 (2008), Safari 3.x (2009), etc. Alternatively, you may be in an esoteric JavaScript environment that doesn't include the standard APIs. In these cases, use json2.js, the reference implementation of JSON written by Douglas Crockford, the inventor of JSON. That library will provide an implementation of JSON.parse().
When processing extremely large JSON files, JSON.parse() may choke because of its synchronous nature and design. To resolve this, the JSON website recommends third-party libraries such as Oboe.js and clarinet, which provide streaming JSON parsing.
jQuery once had a $.parseJSON() function, but it was deprecated with jQuery 3.0. In any case, for a long time, it was nothing more than a wrapper around JSON.parse().
WARNING!
This answer stems from an ancient era of JavaScript programming during which there was no builtin way to parse JSON. The advice given here is no longer applicable and probably dangerous. From a modern perspective, parsing JSON by involving jQuery or calling eval() is nonsense. Unless you need to support IE 7 or Firefox 3.0, the correct way to parse JSON is JSON.parse().
First of all, you have to make sure that the JSON code is valid.
After that, I would recommend using a JavaScript library such as jQuery or Prototype if you can because these things are handled well in those libraries.
On the other hand, if you don't want to use a library and you can vouch for the validity of the JSON object, I would simply wrap the string in an anonymous function and use the eval function.
This is not recommended if you are getting the JSON object from another source that isn't absolutely trusted because the eval function allows for renegade code if you will.
Here is an example of using the eval function:
var strJSON = '{"result":true,"count":1}';
var objJSON = eval("(function(){return " + strJSON + ";})()");
alert(objJSON.result);
alert(objJSON.count);
If you control what browser is being used or you are not worried people with an older browser, you can always use the JSON.parse method.
This is really the ideal solution for the future.
If you are getting this from an outside site it might be helpful to use jQuery's getJSON. If it's a list you can iterate through it with $.each
$.getJSON(url, function (json) {
alert(json.result);
$.each(json.list, function (i, fb) {
alert(fb.result);
});
});
If you want to use JSON 3 for older browsers, you can load it conditionally with:
<script>
window.JSON ||
document.write('<script src="//cdnjs.cloudflare.com/ajax/libs/json3/3.2.4/json3.min.js"><\/scr'+'ipt>');
</script>
Now the standard window.JSON object is available to you no matter what browser a client is running.
The following example will make it clear:
let contactJSON = '{"name":"John Doe","age":"11"}';
let contact = JSON.parse(contactJSON);
console.log(contact.name + ", " + contact.age);
// Output: John Doe, 11
If you pass a string variable (a well-formed JSON string) to JSON.parse from MVC #Viewbag that has doublequote, '"', as quotes, you need to process it before JSON.parse (jsonstring)
var jsonstring = '#ViewBag.jsonstring';
jsonstring = jsonstring.replace(/"/g, '"');
You can either use the eval function as in some other answers. (Don't forget the extra braces.) You will know why when you dig deeper), or simply use the jQuery function parseJSON:
var response = '{"result":true , "count":1}';
var parsedJSON = $.parseJSON(response);
OR
You can use this below code.
var response = '{"result":true , "count":1}';
var jsonObject = JSON.parse(response);
And you can access the fields using jsonObject.result and jsonObject.count.
Update:
If your output is undefined then you need to follow THIS answer. Maybe your json string has an array format. You need to access the json object properties like this
var response = '[{"result":true , "count":1}]'; // <~ Array with [] tag
var jsonObject = JSON.parse(response);
console.log(jsonObject[0].result); //Output true
console.log(jsonObject[0].count); //Output 1
The easiest way using parse() method:
var response = '{"a":true,"b":1}';
var JsonObject= JSON.parse(response);
this is an example of how to get values:
var myResponseResult = JsonObject.a;
var myResponseCount = JsonObject.b;
JSON.parse() converts any JSON String passed into the function, to a JSON object.
For better understanding, press F12 to open the Inspect Element of your browser, and go to the console to write the following commands:
var response = '{"result":true,"count":1}'; // Sample JSON object (string form)
JSON.parse(response); // Converts passed string to a JSON object.
Now run the command:
console.log(JSON.parse(response));
You'll get output as Object {result: true, count: 1}.
In order to use that object, you can assign it to the variable, let's say obj:
var obj = JSON.parse(response);
Now by using obj and the dot(.) operator you can access properties of the JSON Object.
Try to run the command
console.log(obj.result);
Without using a library you can use eval - the only time you should use. It's safer to use a library though.
eg...
var response = '{"result":true , "count":1}';
var parsedJSON = eval('('+response+')');
var result=parsedJSON.result;
var count=parsedJSON.count;
alert('result:'+result+' count:'+count);
If you like
var response = '{"result":true,"count":1}';
var JsonObject= JSON.parse(response);
you can access the JSON elements by JsonObject with (.) dot:
JsonObject.result;
JsonObject.count;
I thought JSON.parse(myObject) would work. But depending on the browsers, it might be worth using eval('('+myObject+')'). The only issue I can recommend watching out for is the multi-level list in JSON.
An easy way to do it:
var data = '{"result":true,"count":1}';
var json = eval("[" +data+ "]")[0]; // ;)
If you use Dojo Toolkit:
require(["dojo/json"], function(JSON){
JSON.parse('{"hello":"world"}', true);
});
As mentioned by numerous others, most browsers support JSON.parse and JSON.stringify.
Now, I'd also like to add that if you are using AngularJS (which I highly recommend), then it also provides the functionality that you require:
var myJson = '{"result": true, "count": 1}';
var obj = angular.fromJson(myJson);//equivalent to JSON.parse(myJson)
var backToJson = angular.toJson(obj);//equivalent to JSON.stringify(obj)
I just wanted to add the stuff about AngularJS to provide another option. NOTE that AngularJS doesn't officially support Internet Explorer 8 (and older versions, for that matter), though through experience most of the stuff seems to work pretty well.
If you use jQuery, it is simple:
var response = '{"result":true,"count":1}';
var obj = $.parseJSON(response);
alert(obj.result); //true
alert(obj.count); //1

Web scraping in apple swift 3

I'm a Swift beginner and I'm trying to figure out how to retrieve text from a web article, create a new text file and save the text data into it (Using Swift Playgrounds). Is this possible?
The only thing I could find online regarding the subject was this, and I don't think it is even written for Swift 3:
P.S. If my question needs more details, please let me know instead of putting it on hold. Thanks!
import Cocoa
var url = NSURL(string: "http://finance.yahoo.com/news/tv-news-ces-2017-120931816.html")
if url != nil {
let task = NSURLSession.sharedSession().dataTaskWithURL(url!, completionHandler: { (data, response, error) -> Void in
print(data)
if error == nil {
var urlContent = NSString(data: data, encoding: NSUTF8StringEncoding) as NSString!
print(urlContent)
That's Swift 2.3. In Swift 3 use URL instead of NSURL and use URLSession rather than NSURLSession, etc. You'd also use String rather than NSString. E.g.
let url = URL(string: "http://finance.yahoo.com/news/tv-news-ces-2017-120931816.html")!
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else {
print("\(error)")
return
}
let string = String(data: data, encoding: .utf8)
print("\(string)")
}
task.resume()
If you're going to do this in a playground, remember that this runs asynchronously, so you'll need to set needsIndefiniteExecution.
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
To actually parse the HTML, you should consider using a HTML parser like TFHpple (written in Objective-C, but still works great from Swift) or NDHpple (a Swift version, in which I don't have as much confidence as TFHpple, but probably would work fine).
You might want to see How to Parse HTML on iOS. It's dated, but walks you through the concepts (making sure you're not violating ToS of the web site, how to use the parsers, etc.).
If you want to save this to a file, you can do something like:
let fileURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
.appendingPathComponent("test.dat")
try! data.write(to: fileURL)
You can use whatever file extension you want.

Swift 3. Argument Labels do not match error

I have just upgraded to Swift 3 and get an error on the following code that is within a UITableViewController.
The code is return segue from a form, and is intended to refresh the table inserting the new entry from the form. This worked fine on 2.2, and I have changed NSIndexPath to IndexPath
#IBAction func saveTripFormViewList(segue:UIStoryboardSegue){
trips = uiRealm.objects(Trip.self)
let indexPath = IndexPath(forRow: trips!.count - 1, inSection: 0)
tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
}
The error I get is
Argument labels '(forRow:, inSection:)' do not match any available overloads
Any advice on how to resolve the error
In Swift 3 it's just IndexPath(row:section:).
FYI, IndexPath is a new value type (a struct) that does the same job as NSIndexPath (a class). (NSIndexPath's initializer has changed as well.) Also, I believe they're toll-free bridged, but don't quote me on that.

How to display an image from documents directory to UIImageView in Swift 3?

The below Swift 2 example gives this error:
Value of type String has no member 'stringByAppendingPathComponent'
What do I need to change for Swift 3?
Apple is trying to move everyone off the path-as-string paradigm to URL (i.e. file:///path/to/file.text). The Swift API pretty much removes all path in favor of URL.
You can still find it in Objective-C (NSString):
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
let getImagePath = NSString.path(withComponents: [paths, "fileName"])
The more Swifty way:
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
let url = URL(fileURLWithPath: paths).appendingPathComponent("fileName")
I personally like getting of this value from the App delegate. Put this code (stands alone like normal function) into the AppDelegate.swift.
lazy var applicationDocumentsDirectory: URL = {
let urls = FileManager.default.urls(for: FileManager.SearchPathDirectory.documentDirectory, in: FileManager.SearchPathDomainMask.userDomainMask)
return urls[urls.count-1]
}()
So in all your files you can use it this way:
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let imageUrl = appDelegate.applicationDocumentsDirectory.appendingPathComponent("YourFileName")
let imageUrlString = imageUrl.urlString //if String is needed

How to convert NSURLRequest to NSMutableURLRequest in Swift 3?

I'm in the process of migrating one of my projects to Swift 3 and I'm hung up on converting a NSURLRequest to NSURLMutableRequest. In Swift 2 I could simply:
let mreq = req.mutableCopy() as! NSMutableURLRequest
But now mutableCopy is no longer a thing in Swift 3. I tried various permutations of constructors and looked in the docs for info to no avail. I must be missing something. There has to be a way to make a mutable copy of an object.
I just figured it out. Dang it was too obvious.
let mreq = req.mutableCopy() as! NSMutableURLRequest
becomes
var mreq = req