What is the Swift 3 equivalent of NSURL.URLByAppendingPathComponent()? - swift

I'm following a basic tutorial on building a simple iOS app in Swift.
It is written in Swift 2.x, and I work with XCode 8 Beta and Swift 3.
The tutorial suggests using NSFileManager to find a data directory. Class names have changed, so the auto-fixed Swift 3 looks like this:
static let DocumentsDirectory = FileManager().urlsForDirectory(.documentDirectory, inDomains:.userDomainMask).first!
static let ArchiveURL = DocumentsDirectory.URLByAppendingPathComponent("meals")
However, Xcode now complains that
Value of type 'URL' has no member 'URLByAddingPathComponent'
I am unable to find out what the method is called now.
The NSURL Class Reference doesn't contain any hints as to how to address it from Swift 3
What is the new method name?
Where do I have to go to find a complete class reference for Swift 3 (or, the Swift 3 interface to the library the URL class is defined in - I still don't completely understand the nomenclature) so I can research these myself in the future?

As of Xcode 8 beta 4, it is named appendingPathComponent(_:), and does not throw.
static let archiveURL = documentsDirectory.appendingPathComponent("meals")
Also as Leo Dabus points out in the comments, your documentsDirectory property will need changing to use urls(for:in:) in beta 4:
static let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
(Note that I've made your property names lowerCamelCase, as per the Swift API design guidelines. I would also recommend using FileManager.default, rather than creating a new instance.)
You can take a look at Apple's latest API reference guide to see the API naming changes that have taken place in Swift 3.

It has now changed to appendingPathComponent(_:) and it throws, so you need to wrap it in do - catch block
do {
let archiveURL = try documentsDirectory?.appendingPathComponent("meals")
} catch {
print(error)
}
Update
As per Xcode 8 beta 4, the appendingpathcomponent(_:) do not throw error.
For relevant info see answer by #Hamish

func appendingPathComponent(String)
=> Returns a new URL made by appending a path component to the original URL.
static let archiveURL = documentsDirectory?.appendingPathComponent("meals")
If it is directory:
func appendingPathComponent(String, isDirectory: Bool)
=> Returns a new URL made by appending a path component to the original URL, along with a trailing slash if the component is designated a directory.
static let archiveURL = documentsDirectory?.appendingPathComponent("meals", isDirectory: true)

Related

Integrating ML Model in Xcode's Swift Playgrounds App

So I am using Xcode Swift Playground App. In which I want to add my two MLModels (Validator and Classifier).
At first, I created a dummy xcode project and I copied the Swift Model Class file. But still, I am getting this error in bundle.url
The issue is that you are force-unwrapping a variable (which is not a very good practice).
The force-unwrapping is failing, because there is not a resource with the same name and path that you are attempting to use.
Instead of force-unwrapping, I would suggest something like this:
class var urlofModelInThisBundle : URL f
let bundle = Bundle(for: self)
if let resource = bundle.url(forResource: "XrayValidator2", withExtension:"mlmodelc") {
return resource
}
else {
Print("ERR: attempted to use a resource that was not found")
return nil
}
You can control-click on your resource names, choose "Reveal in finder" to verify the full names of your resources.
EDIT:
To list all files in the bundle run this and please paste the output into your question:
let paths = Bundle.main.paths(forResourcesOfType: "mlmodelc", inDirectory: nil)
print(paths)
If you don't have any results from that, try this:
let paths = Bundle.main.paths(forResourcesOfType: "mlmodel", inDirectory: nil)
print(paths)

URL.init?(string: String) returns 'nil' when it shouldn't

I'm experiencing an issue with the Foundation struct URL's string initializer. I'll post some code from the repl below:
Welcome to Apple Swift version 5.1 (swiftlang-1100.0.270.13 clang-1100.0.33.7).
Type :help for assistance.
1> import Foundation
2> let testString = "https://www.apple.com"
testString: String = "https://www.apple.com"
3> let testUrl1 = URL(string: testString)
testUrl1: URL? = nil
4> let testUrl2 = URL(string: "https://www.apple.com")
testUrl2: URL? = nil
I cannot think of why this is happening, if you look at the source for the initializer, located at https://github.com/apple/swift-corelibs-foundation/blob/master/Foundation/URL.swift#L495 you'll see this in the documentation:
/// Initialize with string.
///
/// Returns `nil` if a `URL` cannot be formed with the string (for example, if the string contains characters that are illegal in a URL, or is an empty string).
As far as I can tell though, the string I am testing with is a valid URL and the initializer shouldn't be returning nil. I have tried this on two different Macs and in a freshly installed virtual machine, and have gotten the same result in all of them. macOS 10.15, Xcode 11.1. Anyone have any insight into what might be wrong?
It would appear to be a REPL issue and existed in 11.0, too. But if you print(testUrl1), you’ll see it really is set.

How to use URL(string: someConstant) with declared constant in Swift 3?

I'm newbie in Xcode (Swift 3).
Why this code works:
let someUrl = URL(string: "https://www.apple.com")
But this one returns error:
let someConstant = "https://www.apple.com"
let someUrl = URL(string: someConstant)
Cannot use instance member 'someConstant' within property initializer; property initializers run before 'self' is available
the error is not exactly in your code and not with the object URL. but it depends where you placed the code.
you placed it at Place A where it becomes static variables. and thats why you can not access your someConstant at this place from an other instance variable. if you place your code snippet in a function methodName ( Place B) your code snippets work. for your case it is enough to place just let someUrl = URL(string: someConstant) at Place B
class a {
// Place A: instance constant here
func methodName(){
// Place B
}
}
see also: more results at stackoverflow

Swift 3: NSURL to URL missing init?

I just used XCode 8 and let it convert my existing project. Now I face the error that there is no init function for new URL without parameters.
class fileObj : NSObject, NSCopying, Comparable {
var URL = NSURL() // initial
...
The new code looks like:
class fileObj : NSObject, NSCopying, Comparable {
var myUrl = Foundation.URL() // initial
...
How should I init the new URL var?
it makes absolutely zero sense to do so, but currently (Swift3, Xcode Version 8.0 (8A218a)) it is working and gives you a totally blank URL object with no purpose at all, as you just asked for.
var myURL: URL = NSURLComponents().url!
Your observation is indeed correct, there is no empty initialiser because this would be an invalid URL, therefore they decided to disallow that.
What I recommend you doing is not initialising the variable at first and making it an optional (URL?). Later in the code, you'll be able to initialise it.

Winter 2015 / Lecture 10 - Broken Twitter Package

Trying to follow along and code the Smashtag project while watching the Lecture 10 iTunes video.
When I add the dowloaded Twitter package to my Smashtag project, XCode couldn't find the Tweet class when I made reference to it in the TweetTableViewController.
Because of the problem described above, I added the four classes belonging to the Twitter package individually to the project. XCode found the four classes but adding them in this manner generated 11 compile errors.
I'm using XCode Version 6.3 (6D570) which is subsequent to the iOS 8.3 release.
Has anyone else encountered this issue?
Thank you for reading my question.
~ Lee
Possibly not the most-correct (read: best practice) way to do this, but I'm going to chalk it up to doing what it takes to finish the course.
I just went through the list of compile errors and changed the relevant properties to var instead of let. Constants can't be changed and in the new version of Swift they can only be instantiated once. So for the sake of not rewriting too much code, I chose to make certain properties vars instead of lets.
Other bugs I found following the iTunes U course:
The named ‘handler:’ argument needs the name explicitly in a few places.
The simulator will show "TwitterRequest: Couldn\'t discover Twitter account type.” until you go to Settings (inside the simulator) and set the Twitter account. At this point I had to reboot the device, as the call is made in the ViewDidLoad, and thus is only called the first time the view loads. (Alternatively, you could close out the app from the app switcher in the simulator and relaunch that way.)
Here is a gist with corrected code that you can use as a Twitter package that will work with the course and has fixes for the aforementioned bugs, minus the Twitter account setting:
https://gist.github.com/mattpetters/ccf87678ccce0c354398
As Christian R. Jimenez said, "I went to Settings in the Simulated iphone and add my Twitter Account. And everything works perfect." in http://cs193p.m2m.at/cs193p-lecture-10-table-view-winter-2015/. I just added my Twitter Account and tested it, it works!
I had similar problems with the Twitter packages using Swift 2.0 and Xcode 7.2
I'm very new to Swift, so there is a good chance the changes I made are not best practices, but the updated files do work: https://gist.github.com/awaxman11/9c48c0b4c622bffb879f.
For the most part I used Xcode's suggested changes. The two larger changes I made were:
In Tweet.swift I updated the the IndexedKeyword struct's init method to use advanceBy() instead of advance()
In TwitterRequest.swift I updated the signature of NSJSONSerialization to conform to the new error handling system
I've just had a big session fixing the Twitter package files for this same version of Xcode.
It seems that what has broken is that in this version of Swift, constants ('let x...') may only be initialized once, so if a constant is given a value in the declaration ('let x = false'), it may not be changed in the init() function. The Twitter package gives some constants initial values, but then changes the values in the init() function.
My solution to this was to follow the styles suggested in the current version of the Apple Swift language book: declare (many of) the constants as implicitly unwrapped optionals, unconditionally assign a value to them in the init() function (which value may be nil), then test whether any of them are nil, and, if so, return nil from init().
See https://developer.apple.com/library/mac/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html, click "On This Page" and choose "Failable Initializers"
Also, in TwitterRequest.swift, I needed to add the parameter name 'handler:' in a couple of calls to performTwitterRequest(request, handler: handler).
As an example of constant initialization, in MediaItem.swift:
<< Original Code >>
...
public let aspectRatio: Double = 0
...
init?(data: NSDictionary?) {
var valid = false
if let urlString = data?.valueForKeyPath(TwitterKey.MediaURL) as? NSString {
if let url = NSURL(string: urlString) {
self.url = url
let h = data?.valueForKeyPath(TwitterKey.Height) as? NSNumber
let w = data?.valueForKeyPath(TwitterKey.Width) as? NSNumber
if h != nil && w != nil && h?.doubleValue != 0 {
aspectRatio = w!.doubleValue / h!.doubleValue
valid = true
}
}
}
if !valid {
return nil
}
}
...
<< Updated code >>
...
public let aspectRatio: Double
...
init?(data: NSDictionary?) {
if let urlString = data?.valueForKeyPath(TwitterKey.MediaURL) as? NSString {
if let url = NSURL(string: urlString as String) {
self.url = url
let h = data?.valueForKeyPath(TwitterKey.Height) as? NSNumber
let w = data?.valueForKeyPath(TwitterKey.Width) as? NSNumber
if h != nil && w != nil && h?.doubleValue != 0 {
aspectRatio = w!.doubleValue / h!.doubleValue
return
}
}
}
return nil
}
...