mac-cain13 / R.swift

Strong typed, autocompleted resources like images, fonts and segues in Swift projects
MIT License
9.49k stars 759 forks source link

Slow build times for R.generated.swift #489

Open jalone87 opened 5 years ago

jalone87 commented 5 years ago

Xcode 10.x Swift 4.2

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.

tomlokhorst commented 5 years ago

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 notifications@github.com 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.

jalone87 commented 5 years ago

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?

parrotbait commented 4 years ago

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.

vandadnp commented 3 years ago

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.

kemchenj commented 3 years ago

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.

Injazz commented 3 years ago

@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

EmDee commented 2 years ago

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).

vandadnp commented 2 years ago

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

EmDee commented 2 years ago

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.