iOS extensions with multiple targets - ios

In iOS 8, when we create an app extension, we have to decide which target it is attached to. The extension will have the same bundle ID's prefix as the target.
Is there any way to change the target afterward?
If my project contains 2 (or more) targets (for example one for debug/simulator, one for production/device), what's the best way to work with extensions? Do I need to create another extension and duplicate the code (very bothersome to keep the same code for both targets)?

To share one widget with others targets, you only need to add widget.appex target
to Embedded Binaries for every parent target in General configuration tab
Then you'll get Embed App Extensions area at Build Phases automatically

This is my setup: I have 3 targets (production, staging, local) and an extension target that I don't want to duplicate 3 times.
Just to clarify Neo Chen's answer, edit each of your parent targets' schemes:
Build > Pre-actions > New Run Script Action > Provide build settings from (parent scheme).
Paste this for each extension:
#!/bin/bash
buildID=${PRODUCT_BUNDLE_IDENTIFIER}
extId="notification-service"
/usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier $buildID.$extId" "${SRCROOT}/${extId}/Info.plist"
Seems to work on first build.

It seems like you should be able to just duplicate the Extension target with its own Info.plist, but not anything else.
However, when you create an Extension, Xcode adds "Embed App Extensions" to the Build Phases of the app's target, as seen below, and there's no UI to do that yet.
Still, you can create the extension for the second target, then delete all the files except the .plist, and fix what needs to be fixed. Here's a step-by-step:
Create "Extension 1" for "Target 1"
Create "Extension 2" for "Target 2"
Delete all files created for "Extension 2", except its Info.plist
Make the "Build Phases" for "Extension 2" target the same as the build phases for "Extension 1". Usually that's adding the necessary .m files to the "Compile Sources" phase, and resources to the "Copy Bundle Resources" phase

I have create a Run Script to support this requirement
#!/bin/sh
buildNumber=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "$INFOPLIST_FILE")
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumber" "${SRCROOT}/ImagePush/Info.plist"
buildVersion=$(/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" "$INFOPLIST_FILE")
/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $buildVersion" "${SRCROOT}/ImagePush/Info.plist"
buildID=${PRODUCT_BUNDLE_IDENTIFIER}
/usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier $buildID.ImagePush" "${SRCROOT}/ImagePush/Info.plist"
ImagePush is my Extension
add to the target which one you need and add make sure this script run before your extension setting in Build Phases, then you just need to do the build action twice (PS: first time it will fail, will try to improve) and it will support multiple target

In my project I need to build a bit different versions of apps (differ in details, e.g. each application is branded with a different logo).
Let's say there is about 10 "app" targets, I can't imagine adding Notification Content and Notification Service extensions per each main target (in this case I would maintaining 30 targets in total - madness).
I run a script (https://gist.github.com/damian-rzeszot/0b23ad87e5ab5d52aa15c095cbf43c59) after "Embed App Extensions" phase,
that overrides bundle id in app extension plists and entitlements, app version, changes the provisioning profile and re-signs the bundle.

Xcconfigs are a great way to modify plist entries and in code variable based on an Xcode scheme change.
If you are wanting to change the Bundle identifier for example of a given extension based on scheme:
Create an Xcconfig file for your extension. My iMessageExtension-Debug.xcconfig has this entry:
PRODUCT_BUNDLE_IDENTIFIER = $(APP_BUNDLE_IDENTIFIER).iMessageExtension
From the Xcode file inspector click on your project > in the details pane click on your project > Info tab > Add configuration
I created a debug config.
In your newly create config drill to your extension > Select the config file
Create a new Scheme: in the run tab of that new scheme you can select your newly created config. This same process can be done for release extra.
The Xcconfig option we created can be used directly inside of the iMessageExtension > Info.plist:
Bundle identifier : $(PRODUCT_BUNDLE_IDENTIFIER)
If you need to reference a variable in code based on an xcconfig variable:
For example I wanted to change my application initial screen based on the Xcode scheme that was selected:
Xcconfig:
INITIAL_SCREEN = tabBarHome
Plist:
initialScreen : $(INITIAL_SCREEN)
Swift Code:
var initialScreen = object(forInfoDictionaryKey: "initialScreen") as? String

EDIT
2 solutions:
Use XCConfig. See answer mentioned here: https://stackoverflow.com/a/63583849/5175709 and here. Also search for XCConfig on this SO question. You might find some comments.
Change everything through scripts. My answer is doing this.
To be honest I think the XCConfig solution is much much more elegant. You just swap things in and out rather than trying to override everything in a very specific order...
Every other answer had a piece of what was necessary. With some important modifications to this post I was able to get things right.
You need to do three things:
Change bundleId of appex (app extension)
re-sign appex after its bundle is changed. While the app runs on simulator correctly it won't work on real device without this step.
set appropriate provisioning profiles. (excluded from this answer as I don't know how to do that yet)
Note: You can't sign the appex until it's embedded. So the 're-sign appex' step needs to happen after the 'embed App extension' step. Similarly the appex can't be embedded if the bundleId isn't prefixed with the parent app's bundleId.
The final order should look like this:
change bundleId of appex
plutil's sytanx is like this:
-replace keypath -type value
So just do:
plutil -replace \
CFBundleIdentifier -string \
$PRODUCT_BUNDLE_IDENTIFIER.contentExt \
"$BUILT_PRODUCTS_DIR/contentExt.appex/Info.plist"
In case you wanted to learn more about plutil (see here and here for more). PlistBuddy is kinda old.
Note: ContentExtension is the target name I have. Make sure you use yours correctly
re-sign appex
/usr/bin/codesign \
--force \
--sign $EXPANDED_CODE_SIGN_IDENTITY \
--entitlements $CONFIGURATION_TEMP_DIR/ContentExtension.build/ContentExtension.appex.xcent \
--timestamp=none \
"$BUILT_PRODUCTS_DIR/$FULL_PRODUCT_NAME/$BUNDLE_PLUGINS_FOLDER_PATH/ContentExtension.appex"
Note: ContentExtension is the target name I have. Make sure you use yours correctly
The end result is like this:
Don't forget to repeat the steps for every target. The best way to make sure that you have it right is to set the appex's bundleId to something totally wrong and then test all your targets on a real device. If you test it on the sim then you won't be able to validate if the code-signing works correctly or not.
FWIW it's usually a good idea to dump all your shells in one directory like and then reference them from there. But for the sake of simplicity of this post I'm not doing that.
Also make sure you see the original gist it's much smarter if you use that to change all your appexes. You just have to pass the name of the appex and then it would figure out the rest...

You need to create multiple extensions for each ID, but you can create dynamic framework and just link it with each extension. Then you will not need to duplicate your code.

Related

iOS getting stuck at multiple commands found [duplicate]

error: Multiple commands produce '/Users/uesr/Library/Developer/Xcode/DerivedData/OptimalLive-fxatvygbofczeyhjsawtebkimvwx/Build/Products/Debug-iphoneos/OptimalLive.app/Info.plist':
1) Target 'OptimalLive' has copy command from '/Users/uesr/Desktop/workSpace/SEALIVE/SeaLive1.1/OptimalLive/Info.plist' to '/Users/uesr/Library/Developer/Xcode/DerivedData/OptimalLive-fxatvygbofczeyhjsawtebkimvwx/Build/Products/Debug-iphoneos/OptimalLive.app/Info.plist'
2) Target 'OptimalLive' has copy command from '/Users/uesr/Desktop/workSpace/SEALIVE/SeaLive1.1/OptimalLive/Server/Masonry/Info.plist' to '/Users/uesr/Library/Developer/Xcode/DerivedData/OptimalLive-fxatvygbofczeyhjsawtebkimvwx/Build/Products/Debug-iphoneos/OptimalLive.app/Info.plist'
3) Target 'OptimalLive' has process command with input '/Users/uesr/Desktop/workSpace/SEALIVE/SeaLive1.1/OptimalLive/Info.plist'
Running the code in Xcode 9 works, but there is an error in Xcode 10.
The issue might be occurring because of multiple Plist or other files within App-
Solution -> Open target -> Build phases > Copy Bundle Resources and remove info.plist from there.
Note: If you have developed a watch app too then you will have to remove the plist from the watch and watch-extension too.
This answer is deprecated - Xcode 12 has deprecated the Legacy Build System, it will be removed in a further release
I found the solution for this build error, for anybody else having the same issue with Xcode 10 build system, follow the following steps to fix it:
In Xcode, go to File->Project/Workspace settings.
Change the build system to Legacy Build system.
It will resolve the build issue with the new Xcode 10.
If you want to work with the new build system, then you can find the troubleshooting help from this apple Xcode help page.
Go to Xcode -> File ->Workspace Settings.
You will find one pop up like.
Select "Legacy Build System" from Build System tag.
Press on "Done"
Note:- Make sure clear your project with "cmd+shift+alt+k" and "Derived Data"
Build your project it will work charm :)
I was experimenting with Core Data. I built a data model for a simple checklist program and generated the NSManagedObjects. When I compiled the project I got the following error:
error: Multiple commands produce '/Users/myUSerName/Library/Developer/Xcode/DerivedData/myCoreDateExperiment-gzbslaqdwglkzxemijpdqmizgyzc/Build/Intermediates.noindex/ myCoreDateExperiment /Debug-iphonesimulator/ myCoreDateExperiment.build/Objects-normal/x86_64/CheckListItem+CoreDataProperties.o':
1) Target ' myCoreDateExperiment ' (project ' myCoreDateExperiment ') has compile command for Swift source files
2) Target ' myCoreDateExperiment ' (project ' myCoreDateExperiment ') has compile command for Swift source files
The problem was the data model (CheckList.xcdatamodeld in my case) was in the "Compile Sources" list. The project compiled cleanly when I removed it from the list.
Open the project navigator and select the project (very first entry at the top)
Select your build target under Targets in the "Projects and Targets" pane
Select Build Phases option near the top
Expand the "Compile Sources" entry and look for your data model name. Search for "xcdatamodeld" if you have trouble finding it.
Delete the model from the compile list
Make sure the data model is included in the "Copy Bundle Resources" list. Add it if it is missing.
EDIT
As #WilliamT. explains in the comments, you need the xcdatamodeld in the compile list. Instead, go to your entities within the xcdatamodeld file. Select the models that are erroring, expand the left panel, and change the field of "Codegen" to "Manual/None".
This answer is deprecated - XCode 12 has deprecated the Legacy Build System, it will be removed in a further release
Try this as well.
Xcode->File->Project Settings-> Build System -> Legacy Build System.
If you are getting this from the Ditto command creating multiple instances of the same name (NOT the 'copy files' build phase), you may have to change the Product Module Name.
Click on your Target(s) Xcode is complaining about
Click on Build Settings
Search for Product Module Name
Change the name to something unique
We have a watch target and a few notification targets in our app, so I just put things like Extension on the end of the module name.
I found this solution originally here: https://forums.developer.apple.com/thread/103913
If you use CocoaPods you may want to try deintegrate the pods and install again. It works for me.
pod deintegrate
pod install
While checking the build log, I noticed a warning:
note: Using new build system
note: Planning build
note: Constructing build description
Build system information
warning: The Copy Bundle Resources build phase contains this target's Info.plist file '/Users/<redacted>/Repositories/Whitesmith/optimize-ios/Carthage/Checkouts/WSStatusBarNotification/Miscellaneous/Info.plist'. (in target 'JDStatusBarNotification')
So, if that's your case then just go to your target:
Build Phases
Copy Bundle Resource
Remove info.plist.
Read this answer if error message references Core Data files
Synopsis: You may have both automatically-generated and manually-generated Core Data managed object class files.
This answer applies if the first line of the error refers to a Foo+CoreDataProperties.o or Foo+CoreDataClass.o file. Example:
error: Multiple commands produce '/Users/me/Library/Developer/Xcode/DerivedData/MyApp-uebslaqdwgldkjemijpdqmizgyzc/Build/Intermediates.noindex/ MyApp /Debug-iphonesimulator/ MyApp.build/Objects-normal/x86_64/Foo+CoreDataProperties.o':
1) Target ' MyApp ' (project ' MyApp ') has compile command for Swift source files
2) Target ' MyApp ' (project ' MyApp ') has compile command for Swift source files
The root cause can be seen by expanding the Compile Swift Source Files section of the Build Transcript. For example:
<unknown>:0: error: filename "Address+CoreDataClass.swift" used twice: '/Users/myUserName/Projects/Jnky/Foo+CoreDataProperties' and '/Users/jk/myUserName/Developer/Xcode/DerivedData/MyApp-uebslaqdwgldkjemijpdqmizgyzc/Build/Intermediates.noindex/MyApp.build/Debug/MyApp.build/DerivedSources/CoreDataGenerated/Jnky/Foo+CoreDataProperties.swift'
The first file mentioned there is a source file in your project directory, which someone generated by selecting your data model in the Project Navigator and clicking in the menu Editor > Create Managed Object Subclass. This feature was added in Xcode 7 or so.
The second file is a file of the same name but which is buried in Xcode's DerivedData. This file is generated automatically by Xcode during every build if the data model (.xcdatamodeld) file is included in the target's Compile Sources build phase. This feature was added in Xcode 9 or so. Zero, one or two files are generated for each entity/class, depending on the setting of the Codegen popup. That popup is in the Data Model Inspector when you select an entity while editing your data model…
The settings are:
Manual/None No files are generated
Category/Extension One file, Foo+CoreDataProperties.m or .swift is generated, containing an Objective-C category or Swift extension.
Class Definition That same Category/Extension file is generated, and in addition a Foo+CoreDataClass.m or .swift is generated, containing class declaration and definition.
So you see the problem occurs when a developer (like me) who is accustomed to the older Xcode begins a project in a newer Xcode. We think that we need to use the Create Managed Object Subclass menu item, which we do, to create the files we can see in the Project Navigator while not realizing that our settings in the Codegen popup are causing Xcode to create duplicate files, which Apple "cleverly" does not show in the Project Navigator, because they don't trust developers to read and heed the comment in the header // This file was automatically generated and should not be edited.
Solution 1 - Use the Older Way
You can disable all automatic Codegen for a data model with just one setting:
Open the problem Target's Build Phases (In Project Navigator, select project, then in list of TARGETS which appears, select the problem target, then tab Build Phases).
Expand the Compile Sources entry and find the problem data model (.xcdatamodeld file).
Delete it from the compile list
Ensure the data model is included in the Copy Bundle Resources list.
Solution 2 - Core Data Magic For Beginners
Here, you go all in on the newer way.
Leave your data model as is in that Compile Sources.
In each Entity Inspector in your data model, set Codegen to Class Definition.
In the Project Navigator, delete and trash any Foo+CoreDataClass files, and rename any Foo+CoreDataProperties.m or .swift files to something like Foo+MyProperties.
In each Foo+MyProperties.m or .swift file, if there are properties generated by Xcode, delete these properties because they will be in the hidden files created by Codegen.
With this solution, your class definitions are generated automatically from the data model on each build. You can't even see them. It is Core Data Magic, nice and simple for beginners.
Solution 3 - For Most Real-World Apps
But Solution 2 is no good if you really want to add non-managed properties. (Objective-C does not allow properties to be added in categories, and Swift does not allow stored properties to be added in extensions.) So in most real-world apps, you probably want to go halfway between Solutions 1 and 2…
Leave your data model in the list of Compile Sources
In each Entity Inspector in your data model, set Codegen to Category/Extension.
In the Project Navigator, delete and trash any Foo+CoreDataClass.m or .swift files, and, to reduce future confusion, rename any Foo+CoreDataProperties.m or .swift files to maybe just Foo.m or .swift.
Ensure that each Foo.m or .swift file contains the class definition, to which you can add your own non-managed properties.
(Acknowledgments to the answer by Positron. My answer here explains why Positron's answer (my Solution 1) works, and adds Solution 2 and Solution 3.)
Solution 1 :
Open target ➼ Build phases ➼ Copy Bundle Resources ➼ remove info.plist from there. ➼ you will have to remove the plist from the Extensions too (if any).
Solution 2:
If you use CocoaPods you may want to try deintegrate the pods and install again.
Commands:
1) pod deintegrate
2) pod install
Solution 3:
In Xcode, go to File ➼ Project/Workspace settings.
➼ Change the build system to Legacy Build system.
I had the same problem, I had a one more helper app in main App and copy this in resource. In my case solved as :-
1) Target -> 2)Build Phases 2) Copy File (n items) 3) Remove Copy File.
The Helper app automatically copied in Xcode 10.0.
Try this Its Working :
In Xcode, go to File->Project/Workspace settings.
Change the build system to Legacy Build system.
None of the solutions proposed here worked for me. This was particularly due to CocoaPods. I was previously using Cocoapods 1.3.1. Simply upgrading to 1.5.3 didn't resolve the issue right away.
The steps I followed were:
Delete Podfile.lock
Delete Pods directory
Delete Derived Data & Clean
Exit Xcode
Update CocoaPods to 1.5.3
Run pod install
Open workspace and build
One option which solved my issue is to changing build system to legacy build system. Please follow the following steps in Xcode 10+.
Here I have written a detailed article on the problem & its solution. Xcode Error: Multiple commands produce
In my case PDFGenerator was producing an info.plist file, I just deleted it.
This answer is deprecated - Xcode 12 has deprecated the Legacy Build System, it will be removed in a further release
I'm using Xcode 11.4
Can't build old project
Xcode => File => Project Settings => Build System => Legacy Build System
Before I begin note that my project utilizes Carthage as a dependency manager.
None of the existing answers here resolved my issue. What did resolve the issue for me was the following.
First, I noticed that the build error pointed out one framework in particular. Next I filtered App Target > Build Phases for that framework. I noticed that that framework was present in both "Link Binary With Libraries" and "Embed Frameworks". Noting that none of the frameworks listed under "Embed Frameworks" were ones managed by Carthage I removed the framework in question from "Embed Frameworks". I then re-built my project and everything works fine including the functionality enabled by the framework in question.
This issue arose for me after adding a second part of the Fabric suite of SDKs to the app.
What actually happened was that the GoogleUtilies Framework was added twice to the Pods project
This would have been fine prior to Xcode 10 but Xcode 10 will complain if a file has two actions against it (in this case a copy action).
It's safe to remove the second framework.
there are some reasons that cause this error to be shown.
1- the project name is the same as a dependency that is used on the project
this error may happen when you choose a name for your project that is the same as one of the dependencies that you use on the project for example you cannot choose FirebaseAuth or GoogleSignIn as the project's name if you use them via pod or SPM.
to solve this problem you should change the project name with the following way:
choose the project from project navigator on the left sidebar, change the project name from the file inspector -> Identity and Type -> name from the right sidebar.
after you change it, XCode asks you to change all relative targets and just press rename.
2 - duplicated info.plist on the Copy Bundle resources portion
you may face this error when info.plist is added to Copy Bundle resources unwanted, choose project form project navigator -> choose target -> goto Build Phases tab -> Copy Bundle Resources and if you see info.plist there, remove it by choose info.plist like the following image
3 - pod files do not work well
sometimes you got this error because the dependencies that you use break for unexpected reasons.
1 - Delete Podfile.lock
2 - Delete Pods directory
3 - Delete Derived Data & Clean (you can find this directory from XCode menu -> Preferences... -> Locations -> Derived Data and go to the directory by clicking the arrow icon at the right of the address)
4 - Exit Xcode
5 - Update CocoaPods with [sudo] gem install cocoapods on mac terminal
6 - goto the project directory on the terminal and run pod install
7 - Open workspace and build
5- duplicated Core data
you may face this problem when you use Core data on the project
first I explain coreData codegen types:
**Class Definition: ** Choose Class Definition when you don’t need to edit the properties or functionality of the managed object subclass and properties files that Core Data generates for you.
Category/Extension: Choose Category/Extension to add additional convenience methods or business logic inside your managed object subclass.
Manual/None: Choose Manual/None to edit the properties in your managed object subclass, for example, to alter access modifiers, and to add additional convenience methods or business logic.
Choose the Manual/None and check if a copy of xcmodeldata is exist on CopyBundleRecources, remove it.
Well, in my case:
If you create two file with same name, will trigger this error.
Remove the one you recently added, will solve this problem.
Hope this helps.
I had this problem when I had a file with the same name in two different targets. For some reason one of those files I had part of both targets. So basically I had two files. And both of those files belonged to one target.
It makes sense that a target can only have one file name per target, so just unchecking the target member box for the file that wasn't related to the main target fixed the issue.
So the problem I was having is that I had accidentally included the Info.plist in the project settings -> Build Phases -> Copy Bundle Resources for my target.
Steps:
Go to Xcode File
Click to WorkSpace Settings
Build System Select as Legacy Build System
Here is another working solution : (If you are using custom Pods)
Select "Pods" from sidebar as highlights in screenshot.
Click on Build Phase. Expand "Headers" section. There are 3 options Public, Private, Project
Expand Public and check there are duplicate files. Remove it. DONE!!
Unfortunately none of these answers worked for me... here was the error I was seeing:
"Multiple commands produce '/Users/.../.../.../Frameworks/abcdef.framework"
That command depends on command ...: script phase ""
That command depends on command ...: script phase ""
Adding this line to the Podfile and doing a " Pod Install " was the ONLY thing that worked.
install! 'cocoapods', :disable_input_output_paths => true
I really hope this helps someone. I spent hours trying to fix this and finally got it.
Sometimes I just wish Xcode was as efficient as IntelliJ / Android Studio :(
Goodluck!
My error was:
duplicate output file
'/Users/home/Library/Developer/Xcode/DerivedData/myAppName-fawptgabysjowicvpeqydjniuovo/Build/Products/Debug-iphoneos/myAppName.app/GoogleMaps.bundle'
on task: PhaseScriptExecution [CP] Copy Pods Resources
/Users/home/Library/Developer/Xcode/DerivedData/myAppName-fawptgabysjowicvpeqydjniuovo/Build/Intermediates.noindex/myAppName.build/Debug-iphoneos/myAppName.build/Script-32CCC25BF727B592A1784900.sh
I focused on the problem file being GoogleMaps.bundle and the location of that file being in [CP] Copy Pods Resources, and the fact that it specified it’s a duplicate output file (I highlighted them in black above), it's the 4th step below
First create a copy of your project and make sure you first do the following steps on that copy
1- In the project navigator I went to the blue project icon
2- I choose Build phases
3- Under Build Phases I choose [CP] Copy Pods Resources
4- Under [CP] Copy Pods Resources I went to Output Files and underneath there I found the file that ended with GoogleMaps.bundle. I selected it and pressed the minus sign to delete it. Make sure you go to Output Files and NOT Input Files
5- I did a clean shift+cmmd+k and afterwards when I built the project the error was gone
The odd thing was even though the red error went away the yellow warning was still there but it worked :)
I had bunch of Multiple commands produce warnings - not limited to info.plist duplication in one target. Including localized resources and string files, headers etc.
Solution: remove all duplications in target membership.
Try all this option anyone of this 3 option will work for you, for sure
Option 1: Remove all files from
Target >> Build Phases >> Compile Sources
Target >> Build Phases >> Copy Bundle Resources
Option 2: Change the build system
Xcode->File->Project Settings-> Build System -> Legacy Build System
Option 3: remove and update existing pod
pod cache clean PromisesObjC
pod cache clean PromisesSwift
cd [your_project_dir]
rm -rf Pods/
rm Podfile.lock
pod update
I hope this will help you, Happy coding :-)
Go in Project Build Phase and Remove info.plist from the Compile Sources. It will remove that issue and project will be active again.
It's worth noting that this error can be produced after auto generation of CoreData models where the Codegen is not set to Manual/None.
To correct this in Xcode 10 double click on your xcdatamodeId file and select each of your entities and set Codegen to Manual/None under Class in your Data Model Inspector.

Xcode 8 update build number for multiple target before building

I have an app with 3 targets:
app target
message app
app extension
I'm trying to update the build number for each build, this number must be equal for all the target or App Store will invalid the build.
I've tried different approach, also this one, but I don't like it because I must hardcode the plist file names into the script, while I would prefer to keep it as general as possible.
Searching for different solutions, I've found this tool from Apple called avgtool.
I was able to create a script that updates the build number at the same value for all the target, but this seems to work only if I add it to the build phases at the top of the list. The issue is that this script must be launched before building.
In order to do that it must be added to the Edit Scheme->Build->Pre-Actions but when I write it here it doesn't have any effect.
#!/bin/bash
buildNumber=$(date +%s)
echo "$buildNumber"
xcrun agvtool new-version -all "$buildNumber"

Apple Watch pre-build action to change storyboard customModule references

I currently have a project with 3 different versions of the same app (different branding and such), which is working just fine. I've since then added 3 new Apple Watch targets (1 for each app "version"), where which 2 of them reference the files in the "master" Apple Watch target.
Everything works fine as long as i remember to change the module reference for each storyboard view, so that it maps to the correct interface controller in the corresponding watchkit extension target.
However, remembering to switch this every time i need to run/build a app version/target is a pain and not really a long term solution.
I've created the following command which I want to be run at the correct time, such that it changes the storyboard references before it is compiled.
perl -pi.bak -e 's/customModule=\"(.*?)\">/customModule=\"watchMyTarget_Extension\">/g' ${SRCROOT}/watch/Base.lproj/Interface.storyboard
I also concluded that I would probably want to reset the change after the app was compiled, since I don't want to have a file change for git to complain about. Which is why the aforementioned script creates a .bak file. So after the compilation is done and packed/run on device or whatever, I want to run
mv ${SRCROOT}/watch/Base.lproj/Interface.storyboard.bak ${SRCROOT}/watch/Base.lproj/Interface.storyboard
I've tried placing the scripts in the target's (watchTarget, not appTarget) build scheme, Build->Pre/Post Actions which didn't seem to have any effect. And I tried putting it in Run->Pre/Post Actions which worked to some degree, but it seemed like the post action kicked in before the app was pushed to the simulator and thus crashing the application ("could not run see device logs" or something like that).
So where on earth do I need to put these scripts to have them run at the correct time in the build process?
you should use add New Run Script Phase in your target's Build Phases, place it before the Compile Sources
Steps: (from Apple)
In the project editor, select the target to which you want to add a
run script build phase.
Click Build Phases at the top of the project editor.
Choose Editor > Add Build Phase > Add Run Script Build Phase.
Disclose the Run Script section in the project editor.
Configure the script in the Run Script template.
My solution is to go the Build settings of each watch extension target, setting the Product module name to the same value, for example, xxx_watch_extension. Then we should be able to choose this module for custom classes on the storyboard.
It works fine for me.

How to assign location specific BundleDisplayNames for different Targets

I have 2 Apps : Free & Full
I have 2 Languages : English & German
This results in 4 different App-Names
How can I achieve this.
Actually all my targets have the name "Free" in it, even it the the Full Version.
I know:
I have 1 InfoPlist.strings File, which is multiplied by Localization and I can assign them to targets. So for 1 App I can localize the BundleDisplayName.
But how can I assign the specific names for the second app ?
PlistBuddy is your friend here. What you need to do is update your plist in a build time.
You have to create custom scheme for every target you have (I assume you are already has that)
In your xcode go to scheme and add post action like that to custom scheme target:
Here is the code:
infoplist="$BUILT_PRODUCTS_DIR/$INFOPLIST_PATH"
/usr/libexec/PlistBuddy -c "Set CFBundleName some_new_name_here" "$infoplist"
Important thing with this approach is that you're not change nothing in your plist file but with post-actions you already changing the packet prepared for device, so you keep your code repository nice and clean.
Associate InfoPlist.strings to targets
From the Apple Developer Technical Notes:
If your app supports localization, be sure to localize CFBundleDisplayName by adding it to all your language-specific InfoPlist.strings files. Furthermore, be sure to use a name that complies with the App Review Guidelines for your app.
Prerequisites
I am assuming you already have multiple targets.
I am also assuming you already have an InfoPlist.strings file. If your do not, code > File > File... > Resource > Strings File > Next > Save As: InfoPlist > Base.lproj > Target: the english target > Create
Ensure you have enabled this in your Info.plist:
<key>LSHasLocalizedDisplayName</key>
<true/>
Step by step
In Project Navigator, select InfoPlist.strings.
In File Inspector > Localization, select all the languages. This will create and/or copy said InfoPlist.strings into their respective locations.
Would you want to add languages, you achieve this in Project Navigator > project > Project & Target List > Project > Info > Localizations > +. Again, keep Use Base Internationalization.
Unfortunately, when you execute step 2., Xcode does all the magic for you. It is worth noting that in versions of Xcode where that magic did not happen, you had to do the associations by hand. Step 5. is about reverting the magic.
Remove InfoPlist.strings from the Project Navigator. Of course, only Remove References when prompted.
In the Finder, locate en.lproj, drag it onto your Project Navigator, pick Create Groups, add to target your English, or Base target, Finish
Repeat step 6 with de.lproj, fr.lproj, each time dragging the entire .lproj from the Finder, and associating with the appropriate target.
Some .lproj may contain other localized files for which you want to enjoy the magic of step 3. Simply remove their references.
You are done.
The final setup, for say the German language, will look like this in the File Inspector:
References:
Share localization across targets:
This is the general method to achieving localization. See https://stackoverflow.com/a/33749062/218152.
Specific localization to specific targets:
This is only useful if you want to explicitly control which languages go into which target. See https://stackoverflow.com/a/33791181/218152.

Xcode has started making archives, not apps. How do I change it back?

Im' writing an phone app in Xcode 4.2. At some point in the last few days, I changed something - I don't know what, and there's nothing obvious in the git history - and although I can still run it on my device and in the simulator, when I archive the build it makes an archive instead of an app. I can't share these archives as IPA files; if I try I get told "No packager exists for this type of archive".
What did I do? How do I change it back so I can produce IPAs again?
I did the following to make it work for me:
for the three20 static library, I used cocoapods to include the files within the main project.. it just got rid of all the trouble three20 was giving me (and they are lots..) btw i tried replacing three20 with Nimbus.. but Nimbus was lacking on some of the features that my project was using three20 for.. so Nimbus wasn't helpful.
set skip install to yes under build settings for all other sub projects/static libraries and switched the copy headers from public to project under build phases
most importantly: under the sub libraries.. under build phases i ensured that copy files destination was changed from Absolute path to products directory.
and that was it!
hint: to get an idea of the offending files that's causing your archive to create an archive file rather than an ipa do this:
Select the archive and click the Distribute button.
Select the 'Save Built Products' option.
Hit Next and Save.
Browse the created directory in Finder.
The 'libraries' subdirectory will identify the libraries that you need to set the Skip Install to Yes.
in some cases usr/local/include will identify the culprit header files you need to move from Public to Project or the files that you have to change from absolute path to products directory. but that directory (ie usr/local/include) varies depending on your sublibrary directory structure
Make sure for any intermediate targets you select "Skip Install" for its build settings.
Click ont the build dropdown in the top-right of your Xcode window and select "edit scheme" and see if anything is wrong there.
If you can't see anything, try selecting "manage schemes" then delete your old schemes and press "autocreate schemes now" to make a new one.
You only want one scheme, for your app build (or one for each target if there are multiple targets). If there are other schemes (e.g. for embedded sub projects used to create static libraries used by your project) delete them.
Also, as jrtc27 says, if you have got any sub-projects that produce static libs, you need to mark them as "skip install" in the build settings. There's another question here that relates specifically to that issue and has a more detailed explanation of how to fix it:

Resources