0

I'm currently starting up work on a project, and my first task is decomposing a God Object that someone else created out of the AppDelegate. I've started by copying code related around managing location out, in the intention of delegating calls to that code into the new object.

I have two statements that are driving me nuts however.

New file:

if locationManager?.location?.horizontalAccuracy > horizontalAccuracyCheck{...}

Old file:

if locationManager?.location?.horizontalAccuracy > horizontalAccuracyCheck{...}

You'll notice the code is identical. In both cases self.locationManager? is defined as:

var locationManager: CLLocationManager?

But in the new file, I'm getting a warning about 'value of optional type no unwrapped' -- why? Exact duplicate code, copied & pasted, what would make this different?

Changing the code to unwrap it fixes things:

if (locationManager?.location?.horizontalAccuracy)! > horizontalAccuracyCheck{...}

I can wrap my head around why I need to explicitly unwrap a potentially optional return. But... why only in one place?

RonLugge
  • 5,086
  • 5
  • 33
  • 61
  • 1
    My guess is that the migrator [inserted a `>` overload](http://stackoverflow.com/q/39251005/2976878) for optional comparison in the old file, but there isn't one in the new file (I don't *believe* you can mix Swift 2 and Swift 3 together in the same target). – Hamish Jan 05 '17 at 09:13
  • @Hamish you should give that as an answer. If it's right, it's a brilliant guess and obviously deserves to be the accepted answer. – matt Jan 05 '17 at 14:27
  • D'oh. @Hamish is almost certainly correct -- the overload is present in the older file. It's on my to-do list to remove it at some point. – RonLugge Jan 05 '17 at 18:55

2 Answers2

3

The reason is that we're talking here about two quite different languages. One file is Swift 2, the other file is Swift 3.

In Swift 2, you can compare an Optional representing a number with another number using the greater-than or less-than operator. In Swift 3, you can't do that.

Here's a simpler example of the same thing:

    let optint : Int? = 7
    let ok = optint < 42

That code is legal in Swift 2 but illegal in Swift 3.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • You're saying that the *same project* can be in both Swift2 and Swift3? Interesting. – RonLugge Jan 05 '17 at 00:24
  • My guess is that you inherited this project after it was migrated into Xcode 8, and thus into Swift 3. Migration can be performed for one target but not another. Also multiple schemes can complicate the picture. Plus my memory is that when migration _first_ takes place, you can even migrate on a per-file basis (info about that is kept in the project file, where you can't see it). I'd need to see the project to make a better guess as to how this situation came about. But my guess is that these files were never migrated to Swift 3, but your new file is, by default, in Swift 3. – matt Jan 05 '17 at 00:45
  • Quite literally, I'm the one who migrated it. That's what I was originally brought on board for -- and the new file was created after the migration. – RonLugge Jan 05 '17 at 00:51
  • Well, then, it sounds like the old file didn't get migrated. However, it may also be that you are seeing a compiler glitch: try quitting Xcode and emptying the Derived Data file and see if things even out. I've seen some weird inconsistencies after migration. – matt Jan 05 '17 at 00:53
  • The file definitely got migrated, and I've already cleared the derived data over another issue. Something funky is going on, no clue what, but if this is 'expected' behaviour it's not work tracking down. – RonLugge Jan 05 '17 at 00:58
  • You could disprove my theory by just pasting the code I gave into the two files. – matt Jan 05 '17 at 01:27
  • No, your theory matches what's going on -- copy & pasted code matches the expected outcome -- the problem is the file *was* migrated. The entire project had to be migrated. I can even point to the file changes made *in this file* to complete the migration. – RonLugge Jan 05 '17 at 06:56
2

As discussed in this Q&A – the Swift migrator will insert a custom fileprivate operator overload in order to allow for optional comparisons to work in Swift 3 the same way they did in Swift 2. Because this overload is fileprivate, you won't be able to use it from any other source files in your project that don't also define it, thus why your comparison fails to compile in the new source file.

Therefore one solution is just to copy it over to your new file. Another is to remove the fileprivate access modifer. The overload will default to internal, and therefore be accessible to other Swift files in your module.

Although really I would just recommend removing the overload entirely and instead write your own explicit logic for optional comparison when it arises – the overload can be too easily misused (as demonstrated by the Swift evolution proposal to remove it).

Community
  • 1
  • 1
Hamish
  • 78,605
  • 19
  • 187
  • 280
  • The removal of the modifier is on my to-do list, I just didn't connect the dots between 'yes, this says it should be removed so lets do it someday soon' and my current issue. – RonLugge Jan 05 '17 at 19:42
  • Amazing. I had no notion that the migrator did this. Now I need to search my own code to make sure it didn't do the same thing to me! – matt Jan 06 '17 at 13:45