Writing in the front

This article is mainly based on the need to generate customizations using the same set of code without package names, different developer certificates, different features, different component dependencies, different extensions, multiple targets, etc.

Environmental installation

  • Install Cocoapods. If none of this is installed, you don’t need to look behind you.
  • Install the Ruby library XcodeProj, see XcodeProj Github for details.

Custom packaging

Load the configuration of xxx.project

project_path = 'xxx.xcodeproj'
project = Xcodeproj::Project.open(project_path)
Copy the code

2. Modify the build setting of a Target of the project

 project.targets.each do |target|
    #your target name
    if target.name == 'Scene'
        target.build_configurations.each do |config|
         # Debug / Release
        if config.name == 'Release'
             Get build Settings
            build_settings = config.build_settings
            #build_settings is a hash that contains individual configurations
            build_settings.each do |key,value|
                print key, "= =", value, "\n"
                # you can set the certificate here.
                Change the bundle ID and test it
                # build_settings[key] = "com.test.demo"
                Set the license file
                # build_settings["CODE_SIGN_ENTITLEMENTS"] = "xxx.entitlements"
                Set signature iPhone Distribution/iPhone Developer
                build_settings["PROVISIONING_PROFILE_SPECIFIER"] = "PROFILENAME"
                build_settings["PRODUCT_BUNDLE_IDENTIFIER"] = "your BUNDLEID"
                build_settings["DEVELOPMENT_TEAM"] = "your TEAMID"
                #... Others look up the API as required
            end
        end
    end
 end
Copy the code

3. Rely on and unrely on Extension

def updateDependecies(mode, project, app_target_name, ext)
        require 'xcodeproj'
        
        # Find project and app target
        puts mode
        xc = project
        app_target = xc.targets.find { |t| t.name == app_target_name }
        
        # Find app extensions' target objects
        target = xc.targets.find { |t| t.name == ext }
        return "Couldn't find a '#{ext}' target in '#{app_target_name}'" if target.nil?
        return "'#{ext}' doesn't seem to be an application extension target" unless target.product_type.include? 'app-extension'
        target
        
        # Find .appex product files
        appex = target.product_reference
        
        # Add or remove dependency on extension targets
        dependency = app_target.dependency_for_target(target)
        puts app_target.dependencies
        if mode == 'add'
            if dependency
                puts "[WARN] App already has dependency on #{target.name}"
                else
                app_target.add_dependency(target)
            end
            else
            if dependency
                app_target.dependencies.delete(dependency)
                else
                puts "[WARN] Couldn't find dependency on #{target.name}"
            end
        end
        
        # Add or remove .appex copy jobs
        embed_extensions_phase = app_target.copy_files_build_phases.find do |copy_phase|
            copy_phase.symbol_dst_subfolder_spec == :plug_ins
        end
        
        return "Couldn't find 'Embed App Extensions' phase" if embed_extensions_phase.nil?
        appex_included = embed_extensions_phase.files_references.include? appex
        
        if mode == 'add'
            if appex_included
                puts "[WARN] App already embeds #{appex.display_name}"
                else
                build_file = embed_extensions_phase.add_file_reference(appex)
                build_file.settings = { "ATTRIBUTES"= > ['RemoveHeadersOnCopy'] }
            end
            else
            if appex_included
                embed_extensions_phase.remove_file_reference(appex)
                else
                puts "[WARN] App doesn't seem to embed #{appex.display_name}"
            end
        end
         return Update dependency '#{ext}' target in '#{app_target_name}'"
    end
Copy the code

Add/remove framework to Target

  • The relevant knowledge Refer to the address

Link binary with libraries system libraries, link them.

Embed Frameworks 3rd party libraries, embed them.

  • Add or remove a system framework
def exsit_framework? (build_phase,name)# next ! if build_phase.nil?
        build_phase.files_references.each do |ref|
            if ref.name == "#{name}"
                puts "Existing #{name}"
                return true
            end
        end
        puts "There is no #{name}"
        return false
end

def updateSystemFramework(project,target,name, command)
        require 'xcodeproj'
        puts "#{command} #{name} to #{target}"
        build_phase = target.frameworks_build_phase
        framework_group = project.frameworks_group
        if command == :add
            ifself.exsit_framework? (build_phase,name)return
            end
            systempath = "System/Library/Frameworks/"
            path = "#{systempath}#{name}"
            file_ref = framework_group.new_reference(path)
            file_ref.name = "#{name}"
            file_ref.source_tree = 'SDKROOT'
            build_file = build_phase.add_file_reference(file_ref)
        else
            build_phase.files_references.each do |ref|
                if ref.name == "#{name}"
                    puts Delete "# {name}"
                    build_phase.remove_file_reference(ref)
                    framework_group.children.delete(ref)                    
                    break
                end
            end 
        end
    end
Copy the code
  • Add or remove custom framework and lib
def updateCustomFramework(project,target, path,name,command)

        # Get useful variables
        frameworks_group = project.groups.find { |group| group.display_name == 'Frameworks' }
        frameworks_build_phase = target.build_phases.find { |build_phase| build_phase.to_s == 'FrameworksBuildPhase' }
        embed_frameworks_build_phase = nil
        target.build_phases.each do |phase|
            if phase.display_name == 'Embed Frameworks'
                embed_frameworks_build_phase = phase
            end
            # puts phase
        end
        if command == :add
            # Add new "Embed Frameworks" build phase to target
            if embed_frameworks_build_phase.nil?
                embed_frameworks_build_phase = project.new(Xcodeproj::Project::Object::PBXCopyFilesBuildPhase)
                embed_frameworks_build_phase.name = 'Embed Frameworks'embed_frameworks_build_phase.symbol_dst_subfolder_spec = :frameworks target.build_phases << embed_frameworks_build_phase  end# Add framework search path to target
            ['Debug'.'Release'].each do |config|
                paths = ['$(inherited)', path]
                orgpath = target.build_settings(config)['FRAMEWORK_SEARCH_PATHS']
                if orgpath.nil?
                    puts "Path is empty ----------"
                    target.build_settings(config)['FRAMEWORK_SEARCH_PATHS'] = paths
            else
                    puts "Path not empty ----------"
                    paths.each do |p|
                        if! orgpath.include? (p) orgpath << p end end end end# Add framework to target as "Embedded Frameworks"
            framework_ref = frameworks_group.new_file("#{path}#{name}")
            exsit = false
            embed_frameworks_build_phase.files_references.each do |ref|
                if ref.name = name
                    exsit = true
                end
            end
            if! exsit build_file = embed_frameworks_build_phase.add_file_reference(framework_ref) frameworks_build_phase.add_file_reference(framework_ref) build_file.settings = {'ATTRIBUTES'= > ['CodeSignOnCopy'] }
            end
        else
            frameworks_build_phase.files_references.each do |ref|

                if ref.name == "#{name}"
                    puts Delete "# {name}"
                    frameworks_build_phase.remove_file_reference(ref)
                    embed_frameworks_build_phase.remove_file_reference(ref)
                    break
                end
            end
        end

    end
Copy the code

Enable SystemCapabilities

For example: AccessWiFi

# an argument passed in from outside
wifiFlag = ARGV[0]
project.objects.each do |obj|
    if obj.isa == "PBXProject"
        systemCapabilities = obj.attributes["TargetAttributes"][ta.uuid]["SystemCapabilities"]
        puts systemCapabilities
        systemCapabilities["com.apple.AccessWiFi"] = {"enabled":"#{wifiFlag}"}
    end
end
Copy the code

Dynamically modify component dependencies

  • The original idea is to use the sed command to replace the string directly as follows:
target 'Scene' do
    pod 'iOSExtension/Widgets',:path => '.. / '
    pod 'XXX'
    pod 'YYY'
    pod 'AAA'
    pod 'BBB'
end
Copy the code

Use the sed command to delete the specified line of package name XXX

$sed -i ' ' "/XXX.*/d" filePath
Copy the code
  • Although the above specified we achieve the effect, but some scenes below, it seems a little ugly. For example, in some circumstances, our pod library for a module would look like this if we followed the script above:
$sed -i ' ' "/XXX.*/d" filePath
$sed -i ' ' "/YYY.*/d" filePath
$sed -i ' ' "/AAA.*/d" filePath
$sed -i ' ' "/BBB.*/d" filePath
Copy the code

In fact, the Podfile itself is a Ruby file, so you can define functions in it. In this case, to solve the problem of removing multiple POD libraries, you can define a function

def myPodModule(flag)
    if flag == 0
        puts "No -- install myPodModule"
        return
    end
    pod 'XXX'
    pod 'YYY'
    pod 'AAA'
    pod 'BBB'
    puts "Install myPodModule"
end
target 'Scene' do
    pod 'iOSExtension/Widgets',:path => '.. / '
    # call
    myPodModule(1)
end
Copy the code

The script is as follows:

# dependent scripts
$sed -i "" 's/myPodModule(0)/myPodModule(1)/g' xxx/Podfile
# undependent scripts
$sed -i "" 's/myPodModule(1)/myPodModule(0)/g' xxx/Podfile
Copy the code

Multitarget component dependencies

The structure of the original Podfile is as follows:

target 'Scene' do
    pod 'iOSExtension/Widgets',:path => '.. / '
end
target 'Widgets' do
    pod 'my/Wiget',:path => '.. / '
end
Copy the code

The script at this point makes me sick. In fact, you can also use the above ideas to encapsulate

def widgets(flag)
    if flag == 0
        puts "No -- install dependencies for Widgets Target"
        return
    end
    target 'Widgets' do
        pod 'my/Wiget',:path => '.. / '
    end
end
target 'Scene' do
    pod 'iOSExtension/Widgets',:path => '.. / '
end
# call
widgets(1)
Copy the code

The script is as follows:

# dependent scripts
$sed -i "" 's/widgets(0)/widgets(1)/g' xxx/Podfile
# undependent scripts
$sed -i "" 's/widgets(1)/widgets(0)/g' xxx/Podfile
Copy the code

More Podfile SAO operations

  • hook Podfile
post_install do |installer|
    project = installer.aggregate_targets.first.user_project
    
    project.targets.each do |target|
        puts target.name,'Name:'
        end
end

Copy the code

For more information

Xcodeproj making address

Xcodeproj API documentation

The last

fuck996