xcodebuild is not compiling the project unless it is opened using Xcode atleast only once for cocoapods integrated project xcodebuild is not compiling the project unless it is opened using Xcode atleast only once for cocoapods integrated project shell shell

xcodebuild is not compiling the project unless it is opened using Xcode atleast only once for cocoapods integrated project


Why it happens ?

A quick diffMerge tool analysis between a project opened by Xcode Vs a same project not opened by Xcode so far

Diff Merge tool analysis

From this we can see lots of scheme related files being created once opened by Xcode inside .xcodeproj

so xcodebuild creates .app by using the scheme related meta data but as there is no scheme to build against its failing

How to solve this ?

As there is lack of meta data it couldn't build and so it requires Xcode to be opened so that the files get automactically created by Xcode and so there will be some schemes to build against.

But when you open the Xcode this Scheme related files gets created under xcuserdata which is for particular user. i.e each user gets their own file that saves state folders opened,last file opened etc...

Its not wise idea to keep this file with us.

The problem can be solved by checking the Shared Check box under Manage Schemes

This moves schemes out from under your individual xcuserdata into a shared folder that can be committed via source control and you can safely ignore xcuserdata and keep the shared folder in source control

Manage schemes

Now we can build the code without opening the Xcode even for only one time.

Branding(UI,Build settings and functional)

  • UI
    • App icon & other icons
    • iTunes Artwork
  • Build settings
    • App Name
    • Bundle Identifier
    • Provisioning profile
    • Code signing identity
  • Functional
    • Brand specific URLs(login,logout,resource-fetch etc...)

Using Terminal

Branding.sh

   #Author: Durai Amuthan(h.duraiamuthan@gmail.com)#This is to achieve multiple branding of an iOS app by configuring the variables below#************ Configuring the brand starts ************#Directory path where .xcworkspace or .xcodeproj existsPathOfProjectDirectory=/Users/Shared/Jenkins/Documents/JenkinsTestNuu/New/#Path where info.plist existsPathOfInfoPlist=$PathOfProjectDirectory/XxYyZz#Path to icons where new iTunesArtwork and application icon exixts#Note: Make sure proper naming conventions of file has been followedPathOfNewIcons=/Users/Shared/Jenkins/Documents/icons-two#Path to asset resource where you have kept your application icon.PathOfAppIconSet=$PathOfProjectDirectory/XxYyZz/Icon.xcassets/AppIcon.appiconset#Path where do you want the .app file has to be keptPathToApp=/Users/Shared/Jenkins/Documents/JenkinsTestNuu/app#Path where do you want the .ipa file has to keptPathToIpa=/Users/Shared/Jenkins/Documents/JenkinsTestNuu/ipa#Cocoapods project or project that involves more than one modules are scheme basedisWorkspaceBased=true#Path of the Project (.xcodeproj) - applicable for workspace(.xcworkspace) based projectPathofProjectFile=$PathOfProjectDirectory/XxYyZz.xcodeproj#Path of the Workspace (.xcworkspace)PathofWorkspaceFile=$PathOfProjectDirectory/XxYyZz.xcworkspace#Name of the target - applicable only for non-workspace(.xcodeproj)  based projectsTarget=XxYyZz#Scheme of the iOS appScheme=XxYyZz#To ascertain Cocoapods has been used or notisCocoaPodsBased=true#Configuration of the app (Debug -(Development) or Release(Adhoc or Distribution))Config=Release#For giving access to signing idetity found in KeyChainLoginKeychainPath=/Users/Shared/Jenkins/Library/Keychains/login.keychainLoginKeyChainPassword=xxyyzz#Name of the code signing identity.You can find the name in Keychain or xcode build settingCodeSigningIdentity='iPhone Distribution: Xx Yy Zz Limited (3Z5MHUYJ2L)'#Path of the provisioning profilePathToMobileProvision=/Users/Shared/Jenkins/Desktop/BrandingTest.mobileprovision#UUID value found inside Provisioning profile has to be given#Do not forget to install provisiong profile in the systemProvisioningProfileIdentity=6e6506e9-8233-4886-9084-zf21e8f8bbae#Bundle identifier of the appBundleIdentifier=com.xxyy.zz#AppVersion of the appAppVersion=2.2.2#App NameAppname=Two#************ Configuring the brand ends ************#** Creatting the build based on configuration starts **cd $PathOfInfoPlistecho "****************** Setting App Name ******************"/usr/libexec/PlistBuddy -c "Set :CFBundleName $Appname" info.plist/usr/libexec/PlistBuddy -c "Set :CFBundleDisplayName $Appname" info.plistecho "app name has been set as $Appname"cd $PathOfProjectDirectoryecho "****************** Setting AppVersion ******************"/usr/bin/agvtool new-marketing-AppVersion $AppVersion/usr/bin/agvtool new-AppVersion -all $AppVersionecho "****************** Changing app icons & iTunes Artwork ******************"cp -R $PathOfNewIcons/*.png $PathOfAppIconSetecho "App icons has been changed at $PathOfNewIcons"cp -R $PathOfNewIcons/iTunesArtwork@2x $PathOfProjectDirectory/XxYyZzcp -R $PathOfNewIcons/iTunesArtwork $PathOfProjectDirectory/XxYyZzecho "iTunesArtwork has been changed at $PathOfProjectDirectory"#Unlock login keychainsecurity unlock-keychain -p $LoginKeyChainPassword $LoginKeychainPathif $isCocoaPodsBased == 'true'thenecho "****************** Installing Cocoapods **********************"/usr/local/bin/pod installecho "Cocoapods has been installed"fiecho "****************** Creating .app ******************"if $isWorkspaceBased == 'true'then/usr/bin/xcodebuild -scheme $Scheme -workspace $PathofWorkspaceFile -configuration $Config clean build CONFIGURATION_BUILD_DIR=$PathToApp "CODE_SIGN_IDENTITY=$CodeSigningIdentity" "PRODUCT_BUNDLE_IDENTIFIER=$BundleIdentifier" "PROVISIONING_PROFILE=$ProvisioningProfileIdentity"else/usr/bin/xcodebuild -target $Target -project $PathofProjectFile -configuration $Config clean build CONFIGURATION_BUILD_DIR=$PathToApp "CODE_SIGN_IDENTITY=$CodeSigningIdentity" "PRODUCT_BUNDLE_IDENTIFIER=$BundleIdentifier" "PROVISIONING_PROFILE=$ProvisioningProfileIdentity"fiecho ".app has been generated at $PathToApp"echo "****************** Creating .ipa *******************"/usr/bin/xcrun -sdk iphoneos PackageApplication -v $PathToApp/XxYyZz.app -o $PathToIpa/$Appname.ipa --embed $PathToMobileProvision --sign "$CodeSigningIdentity"echo "$Appname.ipa has been generated at $PathToIpa"#** Creatting the build based on configuration ends **

The file is self-descriptive you can understand easily.Just configure the values of variable in the file and call it like below

sh Branding.sh

FYI:

If you want some other icons also to be changed besides App Icon and iTunesArtworkuse cp command e.g

cp path/to/source path/to/destination

To know more info do cp man

With the above file you can do Branding for UI and Build Settings.

For functional branding , you have to keep

  • Brand specific URLs

  • Other inputs respective to a brand

in a separate plist file so that this things also can be changed according to respective brand while building the app

Plist file

In coding side you can customise your application to read the values from plist like this

Function defintion:

func getPlistFile()->Dictionary<String,AnyObject>? {        var dictPlistFile:Dictionary<String,AnyObject>?        if let path = NSBundle.mainBundle().pathForResource("plistfile", ofType: "plist") {            if let dictValue = NSDictionary(contentsOfFile: path) as? Dictionary<String, AnyObject> {                dictPlistFile=dictValue            }        }        return dictPlistFile    }

Function calling:

var Value=getPlistFile()?["Key"]

You can change the values of the key according to brand using the PlistBuddy while building the app

Here is the syntax

/usr/libexec/PlistBuddy -c "Set :Key Value" plistfile.plist

Using Jenkins

We can effectively re-use the shell script here in jenkins

1.You have to parameterise all the variables in shell script in jenkins using Add Parameter... like in the below screenshot I have done for one variable like that you have to do it for all others

Paramterization

2.Choose Execute shell in the Build Step

Build Step

3.Copy the script that is there in between Creating the build based on configuration starts and Creating the build based on configuration ends and paste it in Execute ShellExecute Shell

Note:

  • Resource Rules

    There is a known bug Regarding ResourceRules of Xcode in some versions while building and packaging the app through non-xcode interface.

    So it has to be run once to deactivate a validation for resource rules path in xcode.The resource rules path is deprecated feature and apple doesn't accept apps that comes with resource rules but if we build an app without using Xcode the validation error saying resource rules has not been found will arise to counter that we have to run the script only once.

xcode_fix_PackageApplicationResourceRules.sh

#!/bin/sh# A script to patch xcrun PackageInstallation so that it doesn't use the deprecated --resource-rules# See "Do not use the --resource-rules flag or ResourceRules.plist. They have been obsoleted and will be rejected."#   under https://developer.apple.com/library/mac/technotes/tn2206/_index.html#//apple_ref/doc/uid/DTS40007919-CH1-TNTAG205# Reported as Apple bug #19384243# # should be run as a user who can modify the PackageApplication filexcodedir=$1function usage {        # FIXME we cannot parse args properly because 2 are optional...        echo "USAGE: $0 xcodedir"        echo "  xcodedir: an install dir like /Application/Xcode6.1.1.app"}if [[ $# -ne 1 ]]; then        echo "ERROR: invalid number of arguments"        usage        exit -1 fipi="$xcodedir/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/PackageApplication"piorig="$piOrig"if [[ ! -f "$pi" ]]; then    echo "$pi file not found. Invalid argument ?"    usage    exit -1figrep resource-rules "$pi" if [[ $? -ne 0 ]]; then    echo "PackageApplication doesn't use resource-rules. Skipping"    exit 0fiif [[ -f "$piorig" ]]; then    echo "Backup file $piorig already exist. Aborting"    exit -1fiperl  -p -i'Orig' -e 'BEGIN{undef $/;} s/,resource-rules(.*sign}).*ResourceRules.plist"/$1/smg' "$pi" echo $?
  • Unlock keychain

    Whenever you run Branding.sh in terminal it will prompt username and password as its accessing system keychain

    Whenever you run the Job in jenkins you will get "User Interaction Is Not Allowed" error

    so to tackle this you have to follow the below steps

    • Open the Keychain Access
    • Right click on the private key
    • Select "Get Info"
    • Select "Access Control" tab
    • Click "Allow all applications to access this item"
    • Click "Save Changes"
    • Enter your password
  • Provisioning profile

    if you ever get "No Matching Provisioning Profile Found" make sure you have double clicked and installed it via Xcode.

    The moment you install you'll see UUID.mobileprovision in ~/Library/MobileDevice/Provisioning Profiles/

    This UUID is the value inside mobile provision that means the provisioning profile is installed.

I hope this helps you


You need to run pod install before building the project so that CocoaPods fetches the Pods specified in your Podfile within the Jenkins workspace.