37

After updating to Xcode 9.3 (which uses Swift 4.1), the following issue was found:

  1. Create an empty project, add a new .swift file to it and create two new classes:

    Created to Codable classes

    class CodableOne: Codable {
    
        let some: String
    
    }
    
    class CodableTwo: Codable {
    
        var some: String
    
    }
    

    Build succeeds

  2. Add a new constant to CodableOne of type CodableTwo:

    Added a new constant to CodableOne

    class CodableOne: Codable {
    
        let some: String
        let another: CodableTwo
    
    }
    
    class CodableTwo: Codable {
    
        var some: String
    
    }
    

    Build succeeds

  3. Now move class CodableTwo to another file (ViewController.swift, for example)

    CodableTwo moved to another file

    Build fails.

Now there's an error, which won't go away. Codable classes should not require initializers (as demonstrated in previous steps).

Any ideas on what could be the problem behind this and how it could be resolved will be much appreciated!


P.S. Issue is not present in Xcode 9.2. Nor cleaning the project/build path, neither re-installing Xcode 9.3 helps.

Tamás Sengel
  • 55,884
  • 29
  • 169
  • 223
EBDOKUM
  • 1,696
  • 3
  • 15
  • 33
  • 9
    Nice find – filed a bug: https://bugs.swift.org/browse/SR-7315 – Hamish Mar 30 '18 at 23:27
  • 2
    @Hamish - That seems premature to me. Turn on "whole module" compilation. – Rob Mar 30 '18 at 23:31
  • 2
    @Rob That didn't make a difference for me (edit: oh, looks it depends on the order of the files in "compile sources"). Though it shouldn't make a difference anyway – the compiler shouldn't give you different behaviour under whole module compilation (it's purpose is to allow for more aggressive optimisations). – Hamish Mar 30 '18 at 23:35
  • @Rob can confirm, 'Whole Module' compilation doesn't fully resolve the issue (although somehow helped in a small project) – EBDOKUM Mar 30 '18 at 23:41
  • @EBDOKUM - Did you check the order of the sources? It would be nice to know if that fixes it for you, too, or to figure out whether there's another issue involved here. That bug report should be updated with deterministic behavior to manifest the problem. – Rob Mar 31 '18 at 00:29
  • A related problem: https://stackoverflow.com/q/49540520/1974224 – Cristik Mar 31 '18 at 04:28
  • 1
    I'm with @Hamish; looks like a compiler bug. Thanks for opening it. – Rob Napier Mar 31 '18 at 13:23
  • Same here, `Whole module` doesn't help. But reordering the files in `Compile Source` fixed the issue. Any ideas on a better approach would be appreciated. – Andrew Apr 02 '18 at 15:38
  • 1
    @Andrew yeah, reordering does help with `Whole Module` compilation, that's mentioned in bug report. Please vote for the issue on bugs.swift.org/browse/SR-7315 – EBDOKUM Apr 02 '18 at 15:41
  • Had same issue only all of my Decodable types were in the same file, so reordering file compilation did nothing. **Enabling Whole Module compilation** fixed it though. Indeed seems to be a bug, but not sure what the exact cause is. – Nathan Hosselton Apr 02 '18 at 16:40
  • Same here on big (41k loc) Swift project. Compiled fine with Swift 4/Xcode9.2, same issues as you with 4.1/9.3. Whole module does not help. Will try rearranging the models in the compile sources. @Hamish thanks for the report. – Oscar Apeland Apr 03 '18 at 11:15
  • 1
    Whole module compilation makes no difference for me. I can't work now because of 9.3.Great. – nickdnk Apr 03 '18 at 18:27
  • Re-ordering the compile list helped. It may have been both things. – nickdnk Apr 03 '18 at 18:36
  • Same here. I have defined a constructor for the Codable class (only for required values) and now it's working. – FrancisNear Apr 04 '18 at 09:33
  • Enabling *Whole Module* for Debug and reordering the files in `Compile Source` (do it random for the affected files) will work. – Caio Apr 06 '18 at 03:28
  • Turning on **whole module** compilation did the trick, reordering files wasn't necessary in my case, thanks @Rob – AamirR Jun 13 '18 at 14:54

4 Answers4

24

As mentioned in the comments, I had to do two things:

  1. changing Compilation Mode to Whole Module inside Project settings/Build Settings:

    Compilation Mode set to Whole Module

  2. reordering the files under Project settings/Build Phases/Compile Sources. Specifically, I brought the files that had an error to the front of the list.

    Protip: if you search for the name of the file and there is more than one result, dragging the file to the top in that smaller list will still bring it to the front.

Tamás Sengel
  • 55,884
  • 29
  • 169
  • 223
  • 1
    The steps in that article is outdated as of Xcode 9.3, that option got moved and renamed. Its now under "Compilation Mode" named "Whole Module". Also won't give you bounty as your answer is literally the exact steps I said didn't work in the bounty caption :P – Oscar Apeland Apr 05 '18 at 05:36
  • 1
    @OscarApeland Thank you for the information, I edited it in. I was not aware of your bounty when I wrote this answer and I tried to organize the solutions that I found working. Also, bringing files with errors to the front was not mentioned in the comments. I am fully against plagiarizing and copy-pasting answers from comments, I always add some additional information (and if not, I mark my answer as community wiki). – Tamás Sengel Apr 05 '18 at 08:21
  • 1
    No worries, great answer, it obviously worked for some people:) The JIRA bug just got assigned to some dude at Apple, so hopefully we will get some real answers soon. – Oscar Apeland Apr 06 '18 at 05:54
  • Unfortunately this doesn't work when I want to create Swift object in Objective C. I tried reordering Swift file to the top and then ObjC class to the top and neither of that works. – Josip B. Apr 20 '18 at 14:35
21

This is a bug in the Swift 4.1 compiler. To work around it, either do the steps outlined in the4kman's answer, or simply change let to var in your declaration, as such:

class C1 : Decodable { 
  let str: String 
  // error: Class 'C1' has no initializers - if class C's `c1` is a let constant. 
}

class C : Decodable {
  var c1: C1 // << Change to `var`, compilation succeeds.
}

Workaround courtesy of Apples Swift engineers.

If neither this nor the4kmans answer helps, you can add another init to the models who won't compile. If your classes have tons of variables, just crash the init to satisy the compiler. The Codable initializer will still be synthesized.

class C1: Decodable {
    let str: String

    @available(*, deprecated, message: "Do not use.")
    private init() {
        fatalError("Swift 4.1") 
    }
}
Oscar Apeland
  • 6,422
  • 7
  • 44
  • 92
0

i had this issue even though all my classes were in the same file, using structs for the deeper ones seems to work though

Hogdotmac
  • 282
  • 6
  • 7
-3

try to give your variable an initial value like this (change your code to this)

class CodableOne: Codable{

    var some = ""

}

class CodableTwo: Codable{

    var some = ""

}
Eric Aya
  • 69,473
  • 35
  • 181
  • 253
Sanad Barjawi
  • 539
  • 4
  • 15