Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MultiBranding the same source code without using xcode

I have an app and I’d like to brand it as 3 different apps with different UI.

I don’t want to do using Xcode directly.

So far I found so many solutions that says create different targets and drag and drop the image like that

But I intend to do this branding work outside of Xcode



Help me out !

like image 469
Rajesh Vekariya Avatar asked Oct 30 '22 00:10

Rajesh Vekariya


1 Answers

Now we can build the code without opening the Xcode

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([email protected])
#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 exists
PathOfProjectDirectory=/Users/Shared/Jenkins/Documents/JenkinsTestNuu/New/

#Path where info.plist exists
PathOfInfoPlist=$PathOfProjectDirectory/XxYyZz

#Path to icons where new iTunesArtwork and application icon exixts
#Note: Make sure proper naming conventions of file has been followed
PathOfNewIcons=/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 kept
PathToApp=/Users/Shared/Jenkins/Documents/JenkinsTestNuu/app

#Path where do you want the .ipa file has to kept
PathToIpa=/Users/Shared/Jenkins/Documents/JenkinsTestNuu/ipa

#Cocoapods project or project that involves more than one modules are scheme based
isWorkspaceBased=true

#Path of the Project (.xcodeproj) - applicable for workspace(.xcworkspace) based project
PathofProjectFile=$PathOfProjectDirectory/XxYyZz.xcodeproj

#Path of the Workspace (.xcworkspace)
PathofWorkspaceFile=$PathOfProjectDirectory/XxYyZz.xcworkspace

#Name of the target - applicable only for non-workspace(.xcodeproj)  based projects
Target=XxYyZz

#Scheme of the iOS app
Scheme=XxYyZz

#To ascertain Cocoapods has been used or not
isCocoaPodsBased=true

#Configuration of the app (Debug -(Development) or Release(Adhoc or Distribution))
Config=Release

#For giving access to signing idetity found in KeyChain
LoginKeychainPath=/Users/Shared/Jenkins/Library/Keychains/login.keychain
LoginKeyChainPassword=xxyyzz

#Name of the code signing identity.You can find the name in Keychain or xcode build setting
CodeSigningIdentity='iPhone Distribution: Xx Yy Zz Limited (3Z5MHUYJ2L)'

#Path of the provisioning profile
PathToMobileProvision=/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 system
ProvisioningProfileIdentity=6e6506e9-8233-4886-9084-zf21e8f8bbae

#Bundle identifier of the app
BundleIdentifier=com.xxyy.zz

#AppVersion of the app
AppVersion=2.2.2

#App Name
Appname=Two

#************ Configuring the brand ends ************

#** Creatting the build based on configuration starts **

cd $PathOfInfoPlist
echo "****************** Setting App Name ******************"
/usr/libexec/PlistBuddy -c "Set :CFBundleName $Appname" info.plist
/usr/libexec/PlistBuddy -c "Set :CFBundleDisplayName $Appname" info.plist
echo "app name has been set as $Appname"

cd $PathOfProjectDirectory
echo "****************** Setting AppVersion ******************"
/usr/bin/agvtool new-marketing-AppVersion $AppVersion
/usr/bin/agvtool new-AppVersion -all $AppVersion

echo "****************** Changing app icons & iTunes Artwork ******************"
cp -R $PathOfNewIcons/*.png $PathOfAppIconSet
echo "App icons has been changed at $PathOfNewIcons"
cp -R $PathOfNewIcons/iTunesArtwork@2x $PathOfProjectDirectory/XxYyZz
cp -R $PathOfNewIcons/iTunesArtwork $PathOfProjectDirectory/XxYyZz
echo "iTunesArtwork has been changed at $PathOfProjectDirectory"

#Unlock login keychain
security unlock-keychain -p $LoginKeyChainPassword $LoginKeychainPath
if $isCocoaPodsBased == 'true'
then
echo "****************** Installing Cocoapods **********************"
/usr/local/bin/pod install
echo "Cocoapods has been installed"
fi

echo "****************** 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"
fi
echo ".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 iTunesArtwork use 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 Shell Execute 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 file

xcodedir=$1

function 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 
fi

pi="$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 -1
fi

grep resource-rules "$pi" 
if [[ $? -ne 0 ]]; then
    echo "PackageApplication doesn't use resource-rules. Skipping"
    exit 0
fi

if [[ -f "$piorig" ]]; then
    echo "Backup file $piorig already exist. Aborting"
    exit -1
fi

perl  -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

like image 77
Durai Amuthan.H Avatar answered Nov 15 '22 06:11

Durai Amuthan.H