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
Related
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.
I have a variable called service, like so:
var service: AnyObject = [] //Swift 2.3
My question is how to migrate this to Swift 3. I'm a noob in iOS, I've looked in Internet but could't get it.
PS. I get an error "Contextual type 'AnyObject' cannot be used with array literal"
Thanks in advance!
What you were doing was always wrong. If you have no value to supply at initialization time, use an Optional. Ideally you should declare this as the actual type of value that it will be when gets a value (rather than a catch-all type such as AnyObject). But if you can't do that, then just use Any?:
var service : Any?
Or, if this thing's type is known — for example, if you know it's going to be a Dictionary — then declare it as a Dictionary, possibly by supplying an empty Dictionary, like this:
var service = [AnyHashable:Any]()
var service = [AnyObject]() will work.
Here is an example of using it in Swift -3
var service = [AnyObject]() //Swift 3
service = ["a" as AnyObject,"b" as AnyObject]
print(service)
Output:
a,b
I have a fair amount of code that looks something like this...
let wantsIgnored = UserDefaults.standard.bool(forKey: "ViewShowIgnoredElements") ?? true
The idea was that if the defaults didn't include that key, it would still get a reasonable default value. This worked fine in Swift 2, but in Swift 3 it warns...
Left side of nil coalescing operator '??' has non-optional type 'Double', so the right side is never used
So it seems that UserDefaults.standard.bool will always return a value, which I guess makes sense if it's a Bool. Ok fine, but what is the best way to solve this problem? I could check every key with objectForKey, but that makes the code much messier. Or I could not do this check at all, and make sure that every key is in the initial set of defaults, by hand I guess.
I'm sure I'm not the only one to have faced this, any canonical solutions?
I'm afraid you would think this as messy, but you can write something like this:
let wantsIgnored = UserDefaults.standard.object(forKey: "ViewShowIgnoredElements") as? Bool ?? true
Please try.
Shouldn't it be:
let wantsIgnored = UserDefaults.standard.set(true, forKey: "ViewShowIgnoredElements")
Then to change it:
UserDefaults.standard.set(false, forKey: "ViewShowIgnoredElements")
And to call it:
let xxxx = UserDefaults.standard.bool(forKey: "ViewShowIgnoredElements")
or:
func boolTest (_myBool: Bool){
if _myBool{
//do something
}
}
//and call that function
boolTest(_myBool: UserDefault.standard.bool(forKey: "ViewShowIgnoredElements"))
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.
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