R.swift icon indicating copy to clipboard operation
R.swift copied to clipboard

Slow build times for R.generated.swift

Open jalone87 opened this issue 6 years ago • 9 comments
trafficstars

Xcode 10.x Swift 4.2

  • R.swift (5.0.2):
    • R.swift.Library (~> 5.0.0)

Hello, I'm working on average/big sized project, the compile times are becoming really high, so much that autocomplete and syntax highlighting have become unusable, i started investigating bottle necks and it appears with BuildTimeAnalyzer that R.generated.swift takes the most out of the 2 minutes compilation times with 50 seconds just to compile it's 15.000 lines. Generation of the file takes a neglectable time (around 1 second).

I tried using .rswiftignore file but it has little effect since most of the variable are due to localization strings and xibs, these are the main reason for using it and cannot be ignored.

Are the alternative practices or known build time optimizations appliable? The project is about to take some considerable size leaps and if we don't solve this we will be sadly forced to ditch R.swift pod.

jalone87 avatar Feb 15 '19 17:02 jalone87

This sounds similar to: https://github.com/mac-cain13/R.swift/issues/479

How may lines are in your strings files?

Op 15 feb. 2019 om 18:16 heeft Lorenzo Tognalini [email protected] het volgende geschreven:

Xcode 10.x Swift 4.2

R.swift (5.0.2): R.swift.Library (~> 5.0.0) Hello, I'm working on average/big sized project, the compile times are becoming really high, so much that autocomplete and syntax highlighting have become unusable, i started investigating bottle neck and it appears with BuildTimeAnalyzer that R.generated.swift takes the most out of the 2 minutes compilation times with 50 seconds just to compile it's 15.000 lines. Generation of the file takes a neglectable time (around 1 second).

I tried using .rswiftignore file but it has little effect since most of the variable are due to localization strings and xibs, these are the main reason for using it and cannot be ignored.

Are the alternative practices or known build time optimizations appliable? The project is about to take some considerable size leaps and if we don't solve this we will be sadly forced to ditch R.swift pod.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub, or mute the thread.

tomlokhorst avatar Feb 15 '19 23:02 tomlokhorst

This sounds similar to: #479

Uhm, i read that issue, but it seems there the problem is the generation of the file (the script phase), while for me the issue is the build/compilation of such file (the actual build phase).

How may lines are in your strings files? Excluding empty lines, around 800, it's not that many.

One thing that i've noticed is that for each resource a few methods are created, one to get the Resource instance, one to get the actual value, ecc. maybe there could be a way to not to generate the Resources, or to optimize such functions. The point is even if it is 15.000 loc, it should not take 40 seconds, it's really a trivial things to compile, maybe some of the typical swift-compiler issues is in play?

jalone87 avatar Feb 17 '19 13:02 jalone87

I can make a suggestion here as I had a very similar issue with auto-gen source with GraphQL. It used to generate a single large file e.g. API.swift that took minutes to compile (see my comment here) One nice optimisation for R.swift would be to split out different types into their own files, strings, nibs, images, fonts etc. It would also allow for resource generation by type instead of all in one go, if desired. The compiler can build these files in parallel improving compile times significantly.

parrotbait avatar Jan 30 '20 12:01 parrotbait

Hello everyone

We have the same issue in our file. Our R.generated.swift file is 22259 lines long

image

And it really slows down our compile times.

I would say this unfortunately is not an acceptable solution as it is now for larger production-level apps. So we really need a solution here.

vandadnp avatar Mar 09 '21 09:03 vandadnp

I think there are several things we could do to reduce the generated code. For example, given a image called avatar.png, R will generate:

/// Image `image_tags_text_author_zh-Hant`.
public static let avatar = Rswift.ImageResource(bundle: R.hostingBundle, name: "avatar")

#if os(iOS) || os(tvOS)
/// `UIImage(named: "avatar", bundle: ..., traitCollection: ...)`
public static func avatar(compatibleWith traitCollection: UIKit.UITraitCollection? = nil) -> UIKit.UIImage? {
  return UIKit.UIImage(resource: R.image.avatar, compatibleWith: traitCollection)
}
#endif

The static variable was totally fine. But there is no need to generate the following method for every image, we could just implement this method once in ImageResource.

Maybe this is intended, to align the API with Android's R. If so, we could use callAsFunction to build a similar API:

extension ImageResource {
  #if os(iOS) || os(tvOS) 
  public func callAsFunction(compatibleWith traitCollection: UITraitCollection? = nil) -> UIImage? {
    UIKit.UIImage(resource: self, compatibleWith: traitCollection)
  }
  #endif
}

R.image.avatar(compatibleWith: nil) // call the `callAsFunction` function

Other resources could use the same way to reduce the generated code, except for string. String has intepolation. I don't know how to solve this properly, maybe just stay the old way for string with intepolation.

As a result, it would reduce the LOC from 37000+ to 9000+ for my project, faster to open, faster to build, no API breaking.

One small problem is API discoverability, this method wil not display in code-completion list like before.

kemchenj avatar Mar 10 '21 07:03 kemchenj

@kemchenj you can do that for 'String', the only difference is func parameters won't be strongly typed (which is totally fine for localized strings, but may not be acceptable for others)

extension StringResource {
    func callAsFunction(_ arguments: CVarArg...) -> String {
        return String(format: NSLocalizedString(key, bundle: bundle, comment: ""), locale: bundle.preferredLocalizations.first.flatMap(Locale.init) ?? Locale.current, arguments: arguments)
    }
}

R.string.localizable.test("test")  // call the `callAsFunction` function

for projects with 1000+ localized lines that bring an improvement on build types, though i still have to run benchmarks

Injazz avatar Apr 06 '21 06:04 Injazz

Chiming in here: Our R.generated.swift file is also quite large with 21k lines, which makes it basically impossible to open in Xcode (more of an accident than really intension).

EmDee avatar Jan 28 '22 10:01 EmDee

Chiming in here: Our R.generated.swift file is also quite large with 21k lines, which makes it basically impossible to open in Xcode (more of an accident than really intension).

We've now moved to SwiftGen and couldn't be happier with the change. I am still very very grateful for the fantastic library here which is available as open-source and free so thank you for this @mac-cain13 , really appreciate your efforts and work here.

Due to the problem with the R.generated.swift file we moved to SwiftGen and now we can configure how the generation works and also we are able to generate different .swift files for various categories of resources and the generated Swift files are very short and to the point with lots of configuration options.

I hope this helps

vandadnp avatar Jan 28 '22 10:01 vandadnp

We've now moved to SwiftGen and couldn't be happier with the change. I am still very very grateful for the fantastic library here which is available as open-source and free so thank you for this @mac-cain13 , really appreciate your efforts and work here.

Due to the problem with the R.generated.swift file we moved to SwiftGen and now we can configure how the generation works and also we are able to generate different .swift files for various categories of resources and the generated Swift files are very short and to the point with lots of configuration options.

I hope this helps

Thanks for your input! I really like R.swift's simplicity, but I suppose that's where the limitations come in for larger projects. It probably makes sense to switch to SwiftGen instead of trying to bloat up R.swift for things it was not intended to do.

EmDee avatar Jan 28 '22 10:01 EmDee