4

I am developing a server in Swift and using the Swift Package Manager. And find it convenient when doing my development on my Mac OS system to generate a Xcode project to use Xcode as my IDE (i.e., From time to time, my package dependencies have to be updated. I've been using swift package generate-xcodeproj to do this. My problem comes in at this point-- I have created some settings in Xcode. E.g., I've set a DEBUG flag, and I have a .plist file that is in the Copy Files Phase. These get lost when I regenerate the Xcode project. It seems I cannot simply use swift package update because sometimes files change in the dependencies and these don't get propagated to the Xcode project.

What I'd like is a means to separately establish Xcode settings in a file outside of Xcode, that can be imported into Xcode when I do the swift package generate-xcodeproj. I have not seen a way to do this.

A related question is: When I do a swift build I'd like those same build settings to be used.

Suggestions?

Chris Prince
  • 7,288
  • 2
  • 48
  • 66

3 Answers3

4

I can't help with Copy Files Phase.

However I have just been toying with conditional compilation, like this:

swift package generate-xcodeproj --xcconfig-overrides Sandbox.xcconfig

Sandbox.xcconfig

FLAG_SANDBOX = -DSANDBOX
OTHER_SWIFT_FLAGS = $(FLAG_SANDBOX)

This creates an Xcode project where SANDBOXis definded.

This can be used in swift code like this

#if SANDBOX
    print("sandbox")
#else
    print("production")
#endif
neoneye
  • 50,398
  • 25
  • 166
  • 151
  • any suggestions for things like environment variables or for example commands to pass in? – Logan Dec 06 '18 at 15:20
3

I would use a script or a makefile to import your settings into the generated Xcode project, each time you regenerate it. You can use xcodeproj rubygem.

See an example script.

Regarding using your settings in swift build, can you give an example of such a setting? In general, a makefile can read your settings file and pass the corresponding parameters to swift build.

Vadim Eisenberg
  • 3,337
  • 1
  • 18
  • 14
  • Thanks! I'll give this a shot shortly. An example of a setting I need to use in `swift build` is setting a DEBUG flag to conditionally compile debug only code. Also, I have a Server.plist file that needs to be made available. I make this available differently in test versus non-test builds, but it's something that's part of my Xcode build configuration. – Chris Prince Jan 09 '17 at 04:52
  • DEBUG flag is set by using `-c debug|release` flag of `swift build`. For other options, see [an example of a Makefile](https://github.com/IBM-Swift/GRMustache.swift/blob/master/Makefile) – Vadim Eisenberg Jan 09 '17 at 08:23
  • I got that rubygem solution working! I've put it in as an answer below. Following your link above (http://stackoverflow.com/users/6028420/vadim-eisenberg), I can't find the Makefile example. – Chris Prince Jan 12 '17 at 11:44
  • @ChrisPrince strange, can you access [https://github.com/IBM-Swift/GRMustache.swift](https://github.com/IBM-Swift/GRMustache.swift) ? Makefile is in this repository. – Vadim Eisenberg Jan 12 '17 at 19:31
2

Based on @vadim's answer above, here's a xcodeproj rubygem solution to the first part of my question:

#!/usr/bin/ruby

# Tweak the .xcodeproj after creating with the swift package manager.

# Resources: 
# https://stackoverflow.com/questions/41527782/swift-package-manager-and-xcode-retaining-xcode-settings/41612477#41612477
# https://stackoverflow.com/questions/20072937/add-run-script-build-phase-to-xcode-project-from-podspec
# https://github.com/IBM-Swift/Kitura-Build/blob/master/build/fix_xcode_project.rb
# http://www.rubydoc.info/github/CocoaPods/Xcodeproj/Xcodeproj%2FProject%2FObject%2FAbstractTarget%3Anew_shell_script_build_phase
# http://www.rubydoc.info/github/CocoaPods/Xcodeproj/Xcodeproj/Project/Object/AbstractTarget
# https://gist.github.com/niklasberglund/129065e2612d00c811d0
# https://github.com/CocoaPods/Xcodeproj
# https://stackoverflow.com/questions/34367048/how-do-you-automate-do-copy-files-in-build-phases-using-a-cocoapods-post-insta?rq=1

require 'xcodeproj'

path_to_project = "Server.xcodeproj"
project = Xcodeproj::Project.open(path_to_project)

# 1) Add Copy Files Phase for Server.plist to the Products directory for Server target
target = project.targets.select { |target| target.name == 'Server' }.first
puts "Add Copy Files Phase to #{target}"
phase = target.new_copy_files_build_phase()

# Contrary to the docs (see http://www.rubydoc.info/github/CocoaPods/Xcodeproj/Xcodeproj/Project/Object/PBXCopyFilesBuildPhase) I believe this is not a path, but rather a code, e.g., 16 indicates to copy the file to the Products Directory.
phase.dst_subfolder_spec = "16"

fileRef = project.new(Xcodeproj::Project::Object::PBXFileReference)
fileRef.path = 'Server.plist'

phase.add_file_reference(fileRef)   

# 2) Add in script phase for testing target-- because I haven't figured out to get access to the Products directory at test-run time.
target = project.targets.select { |target| target.name == 'ServerTests' }.first
puts "Add Script Phase to #{target}"
phase = target.new_shell_script_build_phase()
phase.shell_script = "cp Server.plist /tmp"

# 3) Add in DEBUG flag

# A little overkill, but hopefully appending a DEBUG flag in the Debug configuration for each target doesn't hurt it.
project.targets.each do |target|
    puts "Appending DEBUG flag to #{target}"

    if target.build_settings('Debug')['OTHER_SWIFT_FLAGS'].nil?
        target.build_settings('Debug')['OTHER_SWIFT_FLAGS'] = ""
    end

    target.build_settings('Debug')['OTHER_SWIFT_FLAGS'] << '-DDEBUG'
end

project.save()
Community
  • 1
  • 1
Chris Prince
  • 7,288
  • 2
  • 48
  • 66