diff --git a/Sources/VariantsCore/Custom Types/Project/AndroidProject.swift b/Sources/VariantsCore/Custom Types/Project/AndroidProject.swift index f7482844..ee7c0974 100644 --- a/Sources/VariantsCore/Custom Types/Project/AndroidProject.swift +++ b/Sources/VariantsCore/Custom Types/Project/AndroidProject.swift @@ -94,10 +94,7 @@ class AndroidProject: Project { } private func createVariants(with configuration: AndroidConfiguration, spec: String) throws { - guard let defaultVariant = configuration.variants - .first(where: { $0.name.lowercased() == "default" }) else { - throw ValidationError("Variant 'default' not found.") - } + let defaultVariant = try configuration.defaultVariant try gradleFactory.createScript(with: configuration, variant: defaultVariant) } @@ -138,10 +135,7 @@ class AndroidProject: Project { """ if StaticPath.Fastlane.baseFolder.isDirectory { - guard let defaultVariant = configuration.variants - .first(where: { $0.name.lowercased() == "default" }) else { - throw ValidationError("Variant 'default' not found.") - } + let defaultVariant = try configuration.defaultVariant // Create 'variants_params.rb' with parameters whose // destination are set as '.fastlane' diff --git a/Sources/VariantsCore/Custom Types/Project/iOSProject.swift b/Sources/VariantsCore/Custom Types/Project/iOSProject.swift index 95270777..fc2ecd7f 100644 --- a/Sources/VariantsCore/Custom Types/Project/iOSProject.swift +++ b/Sources/VariantsCore/Custom Types/Project/iOSProject.swift @@ -83,23 +83,23 @@ class iOSProject: Project { // Create 'variants.xcconfig' with parameters whose // destination are set as '.project' + let configPath = Path(spec).absolute().parent() do { try configFactory.createConfig( - for: configuration.target, - variant: variant, - xcodeProj: configuration.xcodeproj, - configPath: Path(spec).absolute().parent()) + for: variant, + configuration: configuration, + configPath: configPath) } catch { Logger.shared.logFatal(item: error.localizedDescription) } + // Update `variants_params.rb` with custom fastlane properties var customProperties: [CustomProperty] = (variant.custom ?? []) + (configuration.custom ?? []) customProperties.append(variant.destinationProperty) - // Create 'variants_params.rb' with parameters whose - // destination are set as '.fastlane' - try? storeFastlaneParams(customProperties) + try storeFastlaneParams(customProperties) - try parametersFactory.createMatchFile(for: variant, target: configuration.target) + // Update `Matchfile` with signing configurations + try parametersFactory.createMatchFile(for: variant, configuration: configuration) } private func runPostSwitchScript(_ script: String) throws { @@ -108,19 +108,15 @@ class iOSProject: Project { } private func createVariants(with configuration: iOSConfiguration, spec: String) throws { - guard let defaultVariant = configuration.variants - .first(where: { $0.name.lowercased() == "default" }) else { - throw ValidationError("Variant 'default' not found.") - } + let defaultVariant = try configuration.defaultVariant // Create 'variants.xcconfig' with parameters whose // destination are set as '.project' let configPath = Path(spec).absolute().parent() do { try configFactory.createConfig( - for: configuration.target, - variant: defaultVariant, - xcodeProj: configuration.xcodeproj, + for: defaultVariant, + configuration: configuration, configPath: configPath) } catch { Logger.shared.logFatal(item: error.localizedDescription) @@ -166,20 +162,15 @@ class iOSProject: Project { """ if StaticPath.Fastlane.baseFolder.isDirectory { + let defaultVariant = try configuration.defaultVariant - guard let defaultVariant = configuration.variants - .first(where: { $0.name.lowercased() == "default" }) - else { - throw ValidationError("Variant 'default' not found.") - } + // Update `variants_params.rb` with custom fastlane properties var customProperties: [CustomProperty] = (defaultVariant.custom ?? []) + (configuration.custom ?? []) customProperties.append(defaultVariant.destinationProperty) - - // Create 'variants_params.rb' with parameters whose - // destination are set as '.fastlane' try storeFastlaneParams(customProperties) - try parametersFactory.createMatchFile(for: defaultVariant, target: configuration.target) + // Update `Matchfile` with signing configurations + try parametersFactory.createMatchFile(for: defaultVariant, configuration: configuration) setupCompleteMessage = """ diff --git a/Sources/VariantsCore/Factory/FastlaneParametersFactory.swift b/Sources/VariantsCore/Factory/FastlaneParametersFactory.swift index cbb4da03..98895165 100644 --- a/Sources/VariantsCore/Factory/FastlaneParametersFactory.swift +++ b/Sources/VariantsCore/Factory/FastlaneParametersFactory.swift @@ -11,7 +11,7 @@ import PathKit protocol ParametersFactory { func createParametersFile(in file: Path, renderTemplate: String, with parameters: [CustomProperty]) throws - func createMatchFile(for variant: iOSVariant, target: iOSTarget) throws + func createMatchFile(for variant: iOSVariant, configuration: iOSConfiguration) throws func render(context: [String: Any], renderTemplate: String) throws -> Data? func write(_ data: Data, using parametersFile: Path) throws } @@ -31,7 +31,7 @@ class FastlaneParametersFactory: ParametersFactory { try write(data, using: file) } - func createMatchFile(for variant: iOSVariant, target: iOSTarget) throws { + func createMatchFile(for variant: iOSVariant, configuration: iOSConfiguration) throws { // Return immediately if folder 'fastlane/' doesn't exist. guard StaticPath.Fastlane.baseFolder.exists && StaticPath.Fastlane.baseFolder.isDirectory else { return } @@ -43,9 +43,14 @@ class FastlaneParametersFactory: ParametersFactory { with: parameters) // Populate 'fastlane/Matchfile' from template - var context = [ + let extensionBundleIDs = configuration.extensions + .filter { $0.signed } + .map { $0.makeBundleID(variant: variant, target: configuration.target) } + .reduce(into: [], { $0.append($1) }) + let appBundleID = [variant.makeBundleID(for: configuration.target)] + var context: [String: Any] = [ "export_method": (variant.signing?.exportMethod ?? .appstore).rawValue, - "bundle_id": variant.makeBundleID(for: target) + "app_identifiers": appBundleID + extensionBundleIDs ] if let matchURL = variant.signing?.matchURL { diff --git a/Sources/VariantsCore/Factory/iOS/XCConfigFactory.swift b/Sources/VariantsCore/Factory/iOS/XCConfigFactory.swift index 97537ab9..9113b3c5 100644 --- a/Sources/VariantsCore/Factory/iOS/XCConfigFactory.swift +++ b/Sources/VariantsCore/Factory/iOS/XCConfigFactory.swift @@ -5,6 +5,8 @@ // Created by Arthur Alves // +// swiftlint:disable file_length + import Foundation import ArgumentParser import PathKit @@ -15,10 +17,7 @@ public typealias DoesFileExist = (exists: Bool, path: Path?) protocol XCFactory { func write(_ stringContent: String, toFile file: Path, force: Bool) -> (Bool, Path?) func writeJSON(_ encodableObject: T, toFile file: Path) -> (Bool, Path?) where T: Encodable - func createConfig(for target: iOSTarget, - variant: iOSVariant, - xcodeProj: String?, - configPath: Path) throws + func createConfig(for variant: iOSVariant, configuration: iOSConfiguration, configPath: Path) throws } class XCConfigFactory: XCFactory { @@ -62,19 +61,11 @@ class XCConfigFactory: XCFactory { } } - func createConfig(for target: iOSTarget, - variant: iOSVariant, - xcodeProj: String?, - configPath: Path) throws { - + func createConfig(for variant: iOSVariant, configuration: iOSConfiguration, configPath: Path) throws { let logger = Logger.shared - guard let xcodeProj = xcodeProj - else { - throw RuntimeError("Attempting to create \(xcconfigFileName) - Path to Xcode Project not found") - } - let xcodeProjPath = Path(xcodeProj) - let configString = target.source.config - + let xcodeProjPath = Path(configuration.xcodeproj) + let configString = configuration.target.source.config + logger.logInfo("Checking if \(xcconfigFileName) exists", item: "") let xcodeConfigFolder = Path("\(configPath)/\(configString)") guard xcodeConfigFolder.isDirectory else { @@ -89,24 +80,25 @@ class XCConfigFactory: XCFactory { _ = write("", toFile: xcodeConfigPath, force: true) logger.logInfo("Created file: ", item: "'\(xcconfigFileName)' at \(xcodeConfigPath.parent().abbreviate().description)") - populateConfig(for: target, configFile: xcodeConfigPath, variant: variant) + populateConfig(for: configuration.target, configFile: xcodeConfigPath, variant: variant) /* * If template files should be added to Xcode Project */ - addToXcode(xcodeConfigPath, toProject: xcodeProjPath, sourceRoot: configPath, target: target, variant: variant) + addToXcode(xcodeConfigPath, toProject: xcodeProjPath, sourceRoot: configPath, variant: variant, configuration: configuration) /* * Adjust signing configuration in project.pbxproj */ - updateSigningConfig(for: target, variant: variant, projectPath: xcodeProjPath) + updateSigningConfig(for: variant, configuration: configuration, projectPath: xcodeProjPath) + updateSigningConfigForExtensions(for: variant, configuration: configuration, projectPath: xcodeProjPath) /* * INFO.plist */ - let infoPath = target.source.info + let infoPath = configuration.target.source.info let infoPlistPath = Path("\(configPath)/\(infoPath)") - updateInfoPlist(with: target, configFile: infoPlistPath, variant: variant) + updateInfoPlist(with: configuration.target, configFile: infoPlistPath, variant: variant) /* * Add custom properties whose values should be read from environment variables @@ -121,8 +113,8 @@ class XCConfigFactory: XCFactory { private func addToXcode(_ xcConfigFile: Path, toProject projectPath: Path, sourceRoot: Path, - target: iOSTarget, - variant: iOSVariant) { + variant: iOSVariant, + configuration: iOSConfiguration) { let variantsFile = Path("\(xcConfigFile.parent().absolute().description)/Variants.swift") do { let path = try TemplateDirectory().path @@ -132,22 +124,31 @@ class XCConfigFactory: XCFactory { ).run() let xcodeFactory = XcodeProjFactory() - xcodeFactory.add([xcConfigFile, variantsFile], toProject: projectPath, sourceRoot: sourceRoot, target: target) - + xcodeFactory.add([xcConfigFile, variantsFile], toProject: projectPath, sourceRoot: sourceRoot, target: configuration.target) + + // Update main target let mainTargetSettings = [ "PRODUCT_BUNDLE_IDENTIFIER": "$(V_BUNDLE_ID)", "PRODUCT_NAME": "$(V_APP_NAME)", "ASSETCATALOG_COMPILER_APPICON_NAME": "$(V_APP_ICON)" ] - xcodeFactory.modify(mainTargetSettings, in: projectPath, target: target) - - xcodeFactory.modify( - [ - "TEST_HOST": "$(BUILT_PRODUCTS_DIR)/$(V_APP_NAME).app/$(V_APP_NAME)" - ], - in: projectPath, - target: target, - asTestSettings: true) + xcodeFactory.modify(mainTargetSettings, in: projectPath, targetName: configuration.target.source.info) + + // Update test target + let testTargetSettings = [ + "TEST_HOST": "$(BUILT_PRODUCTS_DIR)/$(V_APP_NAME).app/$(V_APP_NAME)" + ] + xcodeFactory.modify(testTargetSettings, in: projectPath, targetName: configuration.target.testTarget) + + // Update extensions + for targetExtension in configuration.extensions.filter({ $0.signed }) { + let bundleID = targetExtension.makeBundleID(variant: variant, target: configuration.target) + let extensionSettings = [ + "PRODUCT_BUNDLE_IDENTIFIER": "\(bundleID)" + ] + xcodeFactory.modify(extensionSettings, in: projectPath, targetName: targetExtension.name) + } + } catch { logger.logError("❌ ", item: "Failed to add Variants.swift to Xcode Project") } @@ -167,8 +168,8 @@ class XCConfigFactory: XCFactory { } private func updateSigningConfig( - for target: iOSTarget, - variant: iOSVariant, + for variant: iOSVariant, + configuration: iOSConfiguration, projectPath: Path ) { guard @@ -179,19 +180,48 @@ class XCConfigFactory: XCFactory { !teamName.isEmpty else { return } - let xcodeFactory = XcodeProjFactory() - var certType = "Development" - if exportMethod == .appstore || exportMethod == .enterprise { - certType = "Distribution" - } - let mainTargetSettings = [ + let isDistribution = exportMethod == .appstore || exportMethod == .enterprise + let certType = isDistribution ? "Distribution" : "Development" + let signingSettings = [ "PROVISIONING_PROFILE_SPECIFIER": "$(V_MATCH_PROFILE)", "CODE_SIGN_STYLE": "Manual", "CODE_SIGN_IDENTITY": "Apple \(certType): \(teamName) (\(teamID))" ] - xcodeFactory.modify(mainTargetSettings, in: projectPath, target: target) + + let xcodeFactory = XcodeProjFactory() + xcodeFactory.modify(signingSettings, in: projectPath, targetName: configuration.target.source.info) } - + + private func updateSigningConfigForExtensions( + for variant: iOSVariant, + configuration: iOSConfiguration, + projectPath: Path + ) { + let targetExtensions = configuration.extensions.filter({ $0.signed }) + guard + !targetExtensions.isEmpty, + let exportMethod = variant.signing?.exportMethod, + let teamName = variant.signing?.teamName, + let teamID = variant.signing?.teamID, + !teamID.isEmpty, + !teamName.isEmpty + else { return } + + let isDistribution = exportMethod == .appstore || exportMethod == .enterprise + let certType = isDistribution ? "Distribution" : "Development" + + let xcodeFactory = XcodeProjFactory() + for targetExtension in targetExtensions { + let bundleID = targetExtension.makeBundleID(variant: variant, target: configuration.target) + let signingSettings = [ + "PROVISIONING_PROFILE_SPECIFIER": "\(exportMethod.prefix) \(bundleID)", + "CODE_SIGN_STYLE": "Manual", + "CODE_SIGN_IDENTITY": "Apple \(certType): \(teamName) (\(teamID))" + ] + xcodeFactory.modify(signingSettings, in: projectPath, targetName: targetExtension.name) + } + } + private func updateInfoPlist(with target: iOSTarget, configFile: Path, variant: iOSVariant) { let configFilePath = configFile.absolute().description do { @@ -227,3 +257,5 @@ class XCConfigFactory: XCFactory { let xcconfigFileName: String = "variants.xcconfig" let logger: Logger } + +// swiftlint:enable file_length diff --git a/Sources/VariantsCore/Factory/iOS/XcodeProjFactory.swift b/Sources/VariantsCore/Factory/iOS/XcodeProjFactory.swift index bf8e793a..83d3ae14 100644 --- a/Sources/VariantsCore/Factory/iOS/XcodeProjFactory.swift +++ b/Sources/VariantsCore/Factory/iOS/XcodeProjFactory.swift @@ -151,21 +151,20 @@ struct XcodeProjFactory { /// - Parameters: /// - keyValue: Key/value pair to be modified /// - projectPath: Path to Xcode project - /// - target: iOSTarget on which the `buildSettings` should be changed. + /// - targetName: Name of the target on which the `buildSettings` should be changed. /// - asTestSettings: If true, add configuraiton to test/non-host targets. /// - silent: Flag to determine if final logs are necessary func modify(_ keyValue: [String: String], in projectPath: Path, - target: iOSTarget, + targetName: String, asTestSettings: Bool = false, silent: Bool = false) { do { let project = try XcodeProj(path: projectPath) logger.logInfo("Updating: ", item: projectPath) - - let matchingKey = asTestSettings ? target.testTarget : target.source.info + project.pbxproj.buildConfigurations - .filter({ ($0.buildSettings["INFOPLIST_FILE"] as? String)?.contains(matchingKey) ?? false }) + .filter({ ($0.buildSettings["INFOPLIST_FILE"] as? String)?.contains(targetName) ?? false }) .forEach { conf in keyValue.forEach { (key, value) in Logger.shared.logDebug("Item: ", item: "\(key) = \(value)", @@ -191,7 +190,7 @@ private extension XcodeProjFactory { target: iOSTarget ) throws -> PBXGroup? { let groupName = "Variants" - let currentVariantsGroup = project.pbxproj.groups.first(where: { $0.name == groupName }) + let currentVariantsGroup = project.pbxproj.groups.first(where: { $0.path == groupName || $0.name == groupName }) guard currentVariantsGroup == nil else { return currentVariantsGroup } let sourceGroup = project.pbxproj.groups.first(where: { $0.path == target.name }) diff --git a/Sources/VariantsCore/Schemas/Android/AndroidConfiguration.swift b/Sources/VariantsCore/Schemas/Android/AndroidConfiguration.swift index 25bce463..bf494593 100644 --- a/Sources/VariantsCore/Schemas/Android/AndroidConfiguration.swift +++ b/Sources/VariantsCore/Schemas/Android/AndroidConfiguration.swift @@ -6,6 +6,7 @@ // import Foundation +import ArgumentParser public struct AndroidConfiguration: Codable { let path: String @@ -14,6 +15,14 @@ public struct AndroidConfiguration: Codable { let variants: [AndroidVariant] let custom: [CustomProperty]? + var defaultVariant: AndroidVariant { + get throws { + guard let defaultVariant = variants.first(where: { $0.name.lowercased() == "default" }) + else { throw ValidationError("Variant 'default' not found.") } + return defaultVariant + } + } + enum CodingKeys: String, CodingKey { case path = "path" case appName = "app_name" diff --git a/Sources/VariantsCore/Schemas/iOS/iOSConfiguration.swift b/Sources/VariantsCore/Schemas/iOS/iOSConfiguration.swift index ea37a30f..54442778 100644 --- a/Sources/VariantsCore/Schemas/iOS/iOSConfiguration.swift +++ b/Sources/VariantsCore/Schemas/iOS/iOSConfiguration.swift @@ -5,17 +5,18 @@ // Created by Arthur Alves // -// swiftlint:disable type_name - import Foundation +import ArgumentParser internal extension CodingUserInfoKey { static let bundleID = CodingUserInfoKey(rawValue: "bundle_id")! } +// swiftlint:disable:next type_name public struct iOSConfiguration: Codable { let xcodeproj: String let target: iOSTarget + let extensions: [iOSExtension] let variants: [iOSVariant] let custom: [CustomProperty]? @@ -25,12 +26,21 @@ public struct iOSConfiguration: Codable { var pbxproj: String { return xcodeproj + "/project.pbxproj" } - + + var defaultVariant: iOSVariant { + get throws { + guard let defaultVariant = variants.first(where: { $0.name.lowercased() == "default" }) + else { throw ValidationError("Variant 'default' not found.") } + return defaultVariant + } + } + public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.xcodeproj = try container.decode(String.self, forKey: .xcodeproj) self.target = try container.decode(iOSTarget.self, forKey: .target) + self.extensions = try container.decodeIfPresent([iOSExtension].self, forKey: .extensions) ?? [] let globalCustomProperties = try? container.decode([CustomProperty].self, forKey: .custom) self.custom = globalCustomProperties @@ -47,5 +57,3 @@ public struct iOSConfiguration: Codable { globalSigning: globalSigning, globalPostSwitchScript: globalPostSwitchScript) } } } - -// swiftlint:enable type_name diff --git a/Sources/VariantsCore/Schemas/iOS/iOSExtension.swift b/Sources/VariantsCore/Schemas/iOS/iOSExtension.swift new file mode 100644 index 00000000..4105df5a --- /dev/null +++ b/Sources/VariantsCore/Schemas/iOS/iOSExtension.swift @@ -0,0 +1,81 @@ +// +// Variants +// +// Copyright (c) Backbase B.V. - https://www.backbase.com +// Created by Gabriel Rodrigues Minucci on 24/01/2025. +// + +import Foundation + +// swiftlint:disable:next type_name +public struct iOSExtension: Codable { + let name: String + let bundleNamingOption: BundleNamingOption + let signed: Bool + + enum CodingKeys: String, CodingKey { + case name + case bundleID = "bundle_id" + case bundleSuffix = "bundle_suffix" + case signed + } + + enum BundleNamingOption: Codable, Equatable { + case explicit(String) + case suffix(String) + + static func == (lhs: Self, rhs: Self) -> Bool { + switch (lhs, rhs) { + case let (.explicit(lhsValue), .explicit(rhsValue)): + return lhsValue == rhsValue + case let (.suffix(lhsValue), .suffix(rhsValue)): + return lhsValue == rhsValue + default: + return false + } + } + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + name = try container.decode(String.self, forKey: .name) + signed = try container.decode(Bool.self, forKey: .signed) + + let bundleID = try container.decodeIfPresent(String.self, forKey: .bundleID) + let bundleSuffix = try container.decodeIfPresent(String.self, forKey: .bundleSuffix) + + if let bundleID, bundleSuffix == nil { + bundleNamingOption = .explicit(bundleID) + } else if let bundleSuffix, bundleID == nil { + bundleNamingOption = .suffix(bundleSuffix) + } else { + throw RuntimeError( + """ + Target extension "\(name)" have "bundle_suffix" and "bundle_id" configured at the same time or no \ + configuration were provided to any of them. Please provide only one of them per target extension. + """) + } + } + + public func encode(to encoder: any Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(name, forKey: .name) + try container.encode(signed, forKey: .signed) + + switch bundleNamingOption { + case .explicit(let bundleID): + try container.encode(bundleID, forKey: .bundleID) + case .suffix(let bundleSuffix): + try container.encode(bundleSuffix, forKey: .bundleSuffix) + } + } + + func makeBundleID(variant: iOSVariant, target: iOSTarget) -> String { + switch bundleNamingOption { + case .explicit(let bundleID): + return bundleID + case .suffix(let bundleSuffix): + return variant.makeBundleID(for: target).appending(".\(bundleSuffix)") + } + } +} diff --git a/Sources/VariantsCore/Schemas/iOS/iOSSigning.swift b/Sources/VariantsCore/Schemas/iOS/iOSSigning.swift index 2401d9ab..068b7fd0 100644 --- a/Sources/VariantsCore/Schemas/iOS/iOSSigning.swift +++ b/Sources/VariantsCore/Schemas/iOS/iOSSigning.swift @@ -5,10 +5,9 @@ // Created by Arthur Alves // -// swiftlint:disable type_name - import Foundation +// swiftlint:disable:next type_name struct iOSSigning: Codable { let teamName: String? let teamID: String? @@ -92,5 +91,3 @@ extension iOSSigning { return signing } } - -// swiftlint:enable type_name diff --git a/Sources/VariantsCore/Schemas/iOS/iOSTarget.swift b/Sources/VariantsCore/Schemas/iOS/iOSTarget.swift index d145d48b..b1e9ff1e 100644 --- a/Sources/VariantsCore/Schemas/iOS/iOSTarget.swift +++ b/Sources/VariantsCore/Schemas/iOS/iOSTarget.swift @@ -5,10 +5,9 @@ // Created by Arthur Alves // -// swiftlint:disable type_name - import Foundation +// swiftlint:disable:next type_name public struct iOSTarget: Codable { let name: String let app_icon: String @@ -25,10 +24,9 @@ public struct iOSTarget: Codable { } } +// swiftlint:disable:next type_name public struct iOSSource: Codable { let path: String let info: String let config: String } - -// swiftlint:enable type_name diff --git a/Sources/VariantsCore/Schemas/iOS/iOSVariant.swift b/Sources/VariantsCore/Schemas/iOS/iOSVariant.swift index 6463f087..5ea0e56f 100644 --- a/Sources/VariantsCore/Schemas/iOS/iOSVariant.swift +++ b/Sources/VariantsCore/Schemas/iOS/iOSVariant.swift @@ -5,10 +5,9 @@ // Created by Arthur Alves // -// swiftlint:disable type_name - import Foundation +// swiftlint:disable:next type_name public struct iOSVariant: Variant { let name: String let versionName: String @@ -107,12 +106,11 @@ public struct iOSVariant: Variant { } else if let globalSigning = globalSigning { return try globalSigning ~ nil } else { - Logger.shared.logWarning(item: + throw RuntimeError( """ Variant "\(name)" doesn't contain a 'signing' configuration. \ Create a global 'signing' configuration or make sure all variants have this property. """) - return nil } } @@ -232,5 +230,3 @@ extension iOSVariant { variantPostSwitchScript: unnamediOSVariant.postSwitchScript) } } - -// swiftlint:enable type_name diff --git a/Templates/ios/matchfile_template.rb b/Templates/ios/matchfile_template.rb index 83898992..42509e12 100644 --- a/Templates/ios/matchfile_template.rb +++ b/Templates/ios/matchfile_template.rb @@ -2,7 +2,7 @@ git_url("{{ git_url }}") {% else %} -# Sample: "git@github.com:backbase/match.git" + git_url(YOUR_MATCH_GIT_URL) {% endif %} @@ -10,12 +10,24 @@ storage_mode("git") {% if export_method %} -# appstore, development, adhoc, enterprise + type("{{ export_method }}") {% endif %} -{% if bundle_id %} -app_identifier("{{ bundle_id }}") +{% if app_identifiers %} + +{% if app_identifiers.count == 1 %} + +app_identifier("{{ app_identifiers[0] }}") + +{% else %} + +app_identifier([ +{% for identifier in app_identifiers %} + "{{ identifier }}"{% if not forloop.last %},{% endif %} +{% endfor %}]) + +{% endif %} {% endif %} diff --git a/Templates/ios/variants-template.yml b/Templates/ios/variants-template.yml index 2620544e..fb8bc059 100644 --- a/Templates/ios/variants-template.yml +++ b/Templates/ios/variants-template.yml @@ -14,6 +14,16 @@ ios: path: {{ SOURCE }} info: {{ INFO_PLIST }} config: {{ SOURCE }} + # ---------------------------------------------------------------------- + # This can be used to add a list of target extensions to be included in + # the signing phase, both in the Matchfile and Xcode target + # + # Comment or delete section below if necessary. + # ---------------------------------------------------------------------- + #extensions: + # - name: MyAppExtension + # bundle_id: com.myApp.MyAppExtension + # signed: true variants: # Default variant is mandatory, do not remove default: diff --git a/Tests/VariantsCoreTests/Mocks/MockFastlaneFactory.swift b/Tests/VariantsCoreTests/Mocks/MockFastlaneFactory.swift index bd41f3b2..b7d8c2c8 100644 --- a/Tests/VariantsCoreTests/Mocks/MockFastlaneFactory.swift +++ b/Tests/VariantsCoreTests/Mocks/MockFastlaneFactory.swift @@ -11,18 +11,18 @@ import PathKit class MockFastlaneFactory: ParametersFactory { var createParametersCache: [(file: Path, renderTemplate: String, parameters: [CustomProperty])] = [] - var createMatchFileCache: [(variant: iOSVariant, target: iOSTarget)] = [] + var createMatchFileCache: [(variant: iOSVariant, configuration: iOSConfiguration)] = [] var renderCache: [[String: Any]] = [] var writeCache: [(data: Data, parametersFile: Path)] = [] func createParametersFile(in file: Path, renderTemplate: String, with parameters: [CustomProperty]) throws { createParametersCache.append((file: file, renderTemplate: renderTemplate, parameters: parameters)) } - - func createMatchFile(for variant: iOSVariant, target: iOSTarget) throws { - createMatchFileCache.append((variant: variant, target: target)) + + func createMatchFile(for variant: iOSVariant, configuration: iOSConfiguration) throws { + createMatchFileCache.append((variant: variant, configuration: configuration)) } - + func render(context: [String: Any], renderTemplate: String) throws -> Data? { renderCache.append(context) return nil diff --git a/Tests/VariantsCoreTests/Mocks/MockXCcodeConfigFactory.swift b/Tests/VariantsCoreTests/Mocks/MockXCcodeConfigFactory.swift index 68b9cfe0..4f3bb08a 100644 --- a/Tests/VariantsCoreTests/Mocks/MockXCcodeConfigFactory.swift +++ b/Tests/VariantsCoreTests/Mocks/MockXCcodeConfigFactory.swift @@ -12,11 +12,8 @@ import PathKit class MockXCcodeConfigFactory: XCFactory { var writeContentCache: [(content: String, file: Path, force: Bool)] = [] var writeJSONCache: [(encodableObject: Encodable, file: Path)] = [] - var createConfigCache: [(target: iOSTarget, - variant: iOSVariant, - xcodeProj: String?, - configPath: Path)] = [] - + var createConfigCache: [(variant: iOSVariant, configuration: iOSConfiguration, configPath: Path)] = [] + init(logLevel: Bool = false) { logger = Logger(verbose: logLevel) } @@ -31,14 +28,12 @@ class MockXCcodeConfigFactory: XCFactory { return (true, file) } - func createConfig(for target: iOSTarget, - variant: iOSVariant, - xcodeProj: String?, - configPath: Path) throws { - createConfigCache.append((target: target, - variant: variant, - xcodeProj: xcodeProj, - configPath: configPath)) + func createConfig(for variant: iOSVariant, configuration: iOSConfiguration, configPath: Path) throws { + createConfigCache.append(( + variant: variant, + configuration: configuration, + configPath: configPath + )) } var xcconfigFileName: String = "variants.xcconfig" diff --git a/Tests/VariantsCoreTests/Resources/valid_variants.yml b/Tests/VariantsCoreTests/Resources/valid_variants.yml index 1faa8e1a..5e3edb8e 100644 --- a/Tests/VariantsCoreTests/Resources/valid_variants.yml +++ b/Tests/VariantsCoreTests/Resources/valid_variants.yml @@ -59,6 +59,16 @@ ios: path: Sources info: Sources/Info.plist config: Sources + extensions: + - name: VariantsWidgetExtension + bundle_suffix: VariantsWidgetExtension + signed: true + - name: VariantsWidgetExtension2 + bundle_id: com.backbase.frank.io.VariantsWidgetExtension2 + signed: true + - name: OtherExtension + bundle_id: com.variantsTest.OtherExtension + signed: false variants: default: version_name: 0.0.1 diff --git a/Tests/VariantsCoreTests/Resources/variants-template.yml b/Tests/VariantsCoreTests/Resources/variants-template.yml index 84196404..43328c2d 100644 --- a/Tests/VariantsCoreTests/Resources/variants-template.yml +++ b/Tests/VariantsCoreTests/Resources/variants-template.yml @@ -14,6 +14,16 @@ ios: path: {{ SOURCE }} info: {{ INFO_PLIST }} config: {{ SOURCE }} + # ---------------------------------------------------------------------- + # This can be used to add a list of target extensions to be included in + # the signing phase, both in the Matchfile and Xcode target + # + # Comment or delete section below if necessary. + # ---------------------------------------------------------------------- + #extensions: + # - name: MyAppExtension + # bundle_id: com.myApp.MyAppExtension + # signed: true variants: # Default variant is mandatory, do not remove default: diff --git a/Tests/VariantsCoreTests/iOSTargetExtensionTests.swift b/Tests/VariantsCoreTests/iOSTargetExtensionTests.swift new file mode 100644 index 00000000..39913bd3 --- /dev/null +++ b/Tests/VariantsCoreTests/iOSTargetExtensionTests.swift @@ -0,0 +1,81 @@ +// +// Variants +// +// Copyright (c) Backbase B.V. - https://www.backbase.com +// Created by Gabriel Rodrigues Minucci on 27/01/2025. +// + +// swiftlint:disable line_length +// swiftlint:disable type_name + +import XCTest +@testable import VariantsCore + +class iOSTargetExtensionTests: XCTestCase { + private let validSigning = iOSSigning(teamName: "Signing Team Name", teamID: "AB12345CD", exportMethod: .appstore, matchURL: "git@github.com:sample/match.git") + private let target = iOSTarget(name: "Target Name", app_icon: "AppIcon", bundleId: "com.Company.ValidName", testTarget: "ValidNameTests", source: iOSSource(path: "", info: "", config: "")) + + func testTargetExtensionCreationWithBundleSuffix() { + guard let variant = try? iOSVariant( + name: name, versionName: "1.0.0", versionNumber: 0, appIcon: nil, appName: nil, storeDestination: "appStore", + idSuffix: "beta", bundleID: nil, globalCustomProperties: nil, variantCustomProperties: nil, + globalSigning: validSigning, variantSigning: nil, globalPostSwitchScript: nil, variantPostSwitchScript: nil) + else { + return XCTFail("Failed to initialize iOSVariant with provided parameters") + } + + let extensionsJsonString = """ + {"name": "TestExtension", "bundle_suffix": "TestExtension", "signed": true} + """ + guard let targetExtension = try? JSONDecoder().decode(iOSExtension.self, from: Data(extensionsJsonString.utf8)) + else { return XCTFail("Failed to decode JSON for extensions data") } + + XCTAssertEqual(targetExtension.name, "TestExtension") + XCTAssertEqual(targetExtension.signed, true) + XCTAssertEqual(targetExtension.bundleNamingOption, .suffix("TestExtension")) + + let generatedBundleForTarget = targetExtension.makeBundleID(variant: variant, target: target) + XCTAssertEqual(generatedBundleForTarget, "com.Company.ValidName.beta.TestExtension") + } + + func testTargetExtensionCreationWithBundleID() { + guard let variant = try? iOSVariant( + name: name, versionName: "1.0.0", versionNumber: 0, appIcon: nil, appName: nil, storeDestination: "appStore", + idSuffix: "beta", bundleID: nil, globalCustomProperties: nil, variantCustomProperties: nil, + globalSigning: validSigning, variantSigning: nil, globalPostSwitchScript: nil, variantPostSwitchScript: nil) + else { + return XCTFail("Failed to initialize iOSVariant with provided parameters") + } + + let extensionsJsonString = """ + {"name": "TestExtension", "bundle_id": "com.test.App.TestExtension", "signed": true} + """ + + guard let targetExtension = try? JSONDecoder().decode(iOSExtension.self, from: Data(extensionsJsonString.utf8)) + else { return XCTFail("Failed to decode JSON for extensions data") } + + XCTAssertEqual(targetExtension.name, "TestExtension") + XCTAssertEqual(targetExtension.signed, true) + XCTAssertEqual(targetExtension.bundleNamingOption, .explicit("com.test.App.TestExtension")) + + let generatedBundleForTarget = targetExtension.makeBundleID(variant: variant, target: target) + XCTAssertEqual(generatedBundleForTarget, "com.test.App.TestExtension") + } + + func testTargetExtensionCreationWithBundleIDAndBundleSuffix() { + let extensionsJsonString = """ + {"name": "TestExtension", "bundle_suffix": "TestExtension", "bundle_id": "com.test.App.TestExtension", "signed": true} + """ + + XCTAssertThrowsError(try JSONDecoder().decode(iOSExtension.self, from: Data(extensionsJsonString.utf8))) + } + + static var allTests = [ + ("testTargetExtensionCreationWithBundleSuffix", testTargetExtensionCreationWithBundleSuffix), + ("testTargetExtensionCreationWithBundleID", testTargetExtensionCreationWithBundleID), + ("testTargetExtensionCreationWithBundleIDAndBundleSuffix", testTargetExtensionCreationWithBundleIDAndBundleSuffix) + ] +} + +// swiftlint:enable line_length +// swiftlint:enable type_name diff --git a/Tests/VariantsCoreTests/iOSVariantTests.swift b/Tests/VariantsCoreTests/iOSVariantTests.swift index e85bb579..61f2cdca 100644 --- a/Tests/VariantsCoreTests/iOSVariantTests.swift +++ b/Tests/VariantsCoreTests/iOSVariantTests.swift @@ -263,7 +263,7 @@ class iOSVariantTests: XCTestCase { globalSigning: nil, variantSigning: nil, globalPostSwitchScript: nil, variantPostSwitchScript: nil) } - XCTAssertNoThrow(try makeiOSVariant()) + XCTAssertThrowsError(try makeiOSVariant()) } func testGetDefaultValuesForTargetWithoutSigning() { diff --git a/Variants.xcodeproj/project.pbxproj b/Variants.xcodeproj/project.pbxproj index b74e7dfa..5693a29f 100644 --- a/Variants.xcodeproj/project.pbxproj +++ b/Variants.xcodeproj/project.pbxproj @@ -32,7 +32,9 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ + 2D72CCA82D478A6000B01883 /* iOSTargetExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D72CCA72D478A6000B01883 /* iOSTargetExtensionTests.swift */; }; 2D99DF062820856A004A36E1 /* iOSVariantTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D99DF052820856A004A36E1 /* iOSVariantTests.swift */; }; + 2DFF30112D438AEF00F8CF7B /* iOSExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DFF30102D438AEF00F8CF7B /* iOSExtension.swift */; }; 3907EE7026FDF9CE00311EE6 /* XcodeProjFactoryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3907EE6F26FDF9CE00311EE6 /* XcodeProjFactoryTests.swift */; }; 3940C5A127074B1A00FEA51D /* LogData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3940C5A027074B1A00FEA51D /* LogData.swift */; }; 397811EF26F397A900643F91 /* Data+WriteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 397811EE26F397A900643F91 /* Data+WriteTests.swift */; }; @@ -172,7 +174,9 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 2D72CCA72D478A6000B01883 /* iOSTargetExtensionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSTargetExtensionTests.swift; sourceTree = ""; }; 2D99DF052820856A004A36E1 /* iOSVariantTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSVariantTests.swift; sourceTree = ""; }; + 2DFF30102D438AEF00F8CF7B /* iOSExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSExtension.swift; sourceTree = ""; }; 3907EE6F26FDF9CE00311EE6 /* XcodeProjFactoryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XcodeProjFactoryTests.swift; sourceTree = ""; }; 3940C5A027074B1A00FEA51D /* LogData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogData.swift; sourceTree = ""; }; 397811EE26F397A900643F91 /* Data+WriteTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Data+WriteTests.swift"; sourceTree = ""; }; @@ -338,6 +342,7 @@ 8EDC54E125554B8C00A9CDFF /* CustomProperty+EnvironmentVarTests.swift */, BEAA71EC255A012900E9D4D9 /* AndroidProjectTests.swift */, 8EDC550B25592F5800A9CDFF /* iOSProjectTests.swift */, + 2D72CCA72D478A6000B01883 /* iOSTargetExtensionTests.swift */, 2D99DF052820856A004A36E1 /* iOSVariantTests.swift */, 8E6ABBAF25C03F05006A62FE /* VariantsFileFactoryTests.swift */, 8E1B9C0E254AB51300DD0204 /* Resources */, @@ -539,6 +544,7 @@ 8EE24234256BA98C00F66F61 /* iOSSigning.swift */, OBJ_59 /* iOSTarget.swift */, OBJ_60 /* iOSVariant.swift */, + 2DFF30102D438AEF00F8CF7B /* iOSExtension.swift */, ); path = iOS; sourceTree = ""; @@ -825,6 +831,7 @@ E3BE3E742820336700A31096 /* Errors.swift in Sources */, 3940C5A127074B1A00FEA51D /* LogData.swift in Sources */, 8E1BA0C3254BFF6400DD0204 /* Switch.swift in Sources */, + 2DFF30112D438AEF00F8CF7B /* iOSExtension.swift in Sources */, 8E1B9D9D254AC26F00DD0204 /* Platform.swift in Sources */, 8E1B9EFC254AC2A900DD0204 /* iOSConfiguration.swift in Sources */, 8E1B9E3B254AC28F00DD0204 /* XCConfigFactory.swift in Sources */, @@ -873,6 +880,7 @@ 7C460E7F281C1DDE00BBF15D /* MockVariant.swift in Sources */, 8E8A48CA255307B20056F79F /* GradleScriptFactoryTests.swift in Sources */, 8E8A491025543F920056F79F /* SpecHelperTests.swift in Sources */, + 2D72CCA82D478A6000B01883 /* iOSTargetExtensionTests.swift in Sources */, 7C460E83281C30B900BBF15D /* CommandTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/docs/USAGE.md b/docs/USAGE.md index 7bbe31a4..dfdc4186 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -17,6 +17,9 @@ Commands: * [1. Initialize](#initialize) * [Variants Spec](#variants-spec) + * [Environment variables injection](#enviromental-variables-injection) + * [iOS: Bundle ID](#configuring-bundleid) + * [iOS: Signing App Extensions](#signing-extensions) * [Custom configuration](#custom-configuration) * [Signing configuration (iOS only)](#signing-configuration) * [2. Setup](#setup-multiple-build-variants-with-full-fastlane-integration) @@ -191,6 +194,38 @@ For example: Target BundleID is `com.sample.App` and variant `bundle_id` is `com *Note: `id_suffix` and `bundle_id` are not compatible and must not be provided at the same time. Only one of the configurations can be provided per each variant.* +#### Signing extensions + +Variants can also help signing extensions via Match. In order to do so simply include the extensions in the `variants.yml` as the following: + +```yaml +ios: + xcodeproj: SampleProject.xcodeproj + target: + ... + extensions: + - name: TestWidgetExtension + bundle_suffix: TestWidgetExtension + signed: true + - name: AnotherTestWidgetExtension + bundle_id: com.test.MyApp.AnotherTestWidgetExtension + signed: true + variants: + ... +``` + +The `bundle_id` will be generated for each extension marked with `signed: true` and added to the `app_identifier` property in the Matchfile for Match to sign the application. + +There are two ways to configure the Bundle ID generation: + +If a `id_suffix` is provided in the extension config the BundleID will be generated based on the selected variant BundleID and the suffix provided. +For example: Variant BundleID is `com.sample.App.beta` and extension `id_sufix` is `TestWidgetExtension`, the generated BundleID will be `com.sample.App.beta.TestWidgetExtension` + +If a `bundle_id` is provided in the extension config the BundleID will be generated based on the bundle ID provided. +For example: Variant BundleID is `com.sample.App.beta` and extension `bundle_id` is `com.test.MyApp.AnotherTestWidgetExtension`, the generated BundleID will be `com.test.MyApp.AnotherTestWidgetExtension` + +*Note: `id_suffix` and `bundle_id` are not compatible and must not be provided at the same time. Only one of the configurations can be provided per each extension.* + #### Custom configuration Configuration through custom properties can bring a lot of value to your variants, such as defining different API base URLs, or credentials using environment variables. This allows us to also define its destination. Certain properties should not be available to the project but to fastlane and vice-versa. diff --git a/samples/ios/VariantsTestApp/VariantsTestApp copy-Info.plist b/samples/ios/VariantsTestApp/VariantsTestApp copy-Info.plist deleted file mode 100644 index f2addf35..00000000 --- a/samples/ios/VariantsTestApp/VariantsTestApp copy-Info.plist +++ /dev/null @@ -1,43 +0,0 @@ - - - - - CFBundleDisplayName - $(V_APP_NAME) - CFBundleExecutable - $(V_APP_NAME) - CFBundleIdentifier - $(V_BUNDLE_ID) - CFBundleName - $(V_APP_NAME) - CFBundleShortVersionString - $(V_VERSION_NAME) - CFBundleVersion - $(V_VERSION_NUMBER) - OTHER_SWIFT_FLAGS - $(OTHER_SWIFT_FLAGS) - SAMPLE_FASTLANE_PROPERTY - $(SAMPLE_FASTLANE_PROPERTY) - UIApplicationSceneManifest - - UIApplicationSupportsMultipleScenes - - UISceneConfigurations - - UIWindowSceneSessionRoleApplication - - - UISceneConfigurationName - Default Configuration - UISceneDelegateClassName - $(PRODUCT_MODULE_NAME).SceneDelegate - UISceneStoryboardFile - Main - - - - - custom_global_property - $(custom_global_property) - - diff --git a/samples/ios/VariantsTestApp/VariantsTestApp.xcodeproj/project.pbxproj b/samples/ios/VariantsTestApp/VariantsTestApp.xcodeproj/project.pbxproj index 980f0dad..5b3cfca6 100644 --- a/samples/ios/VariantsTestApp/VariantsTestApp.xcodeproj/project.pbxproj +++ b/samples/ios/VariantsTestApp/VariantsTestApp.xcodeproj/project.pbxproj @@ -64,9 +64,8 @@ 2DFD1E722D3FE3B400349BF3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 2DFD1E7E2D3FE6ED00349BF3 /* VariantsTestAppTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = VariantsTestAppTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 2DFD1E802D3FE6ED00349BF3 /* VariantsTestAppTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VariantsTestAppTests.swift; sourceTree = ""; }; - 2DFD1E992D3FEB9000349BF3 /* VariantsTestApp copy-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "VariantsTestApp copy-Info.plist"; path = "/Users/gabriel.minucci/Documents/Backbase/variants/samples/ios/VariantsTestApp/VariantsTestApp copy-Info.plist"; sourceTree = ""; }; - 78DBE119A2B49BE740D07F6E /* variants.xcconfig */ = {isa = PBXFileReference; explicitFileType = text.xcconfig; lastKnownFileType = text.xcconfig; name = variants.xcconfig; path = variants.xcconfig; sourceTree = ""; }; - 8E00D0E329967BD4009F995B /* .app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = .app; sourceTree = BUILT_PRODUCTS_DIR; }; + 78DBE119A2B49BE740D07F6E /* variants.xcconfig */ = {isa = PBXFileReference; explicitFileType = text.xcconfig; path = variants.xcconfig; sourceTree = ""; }; + 8E00D0E329967BD4009F995B /* .app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; name = .app; path = VariantsTestApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; 8E00D0E629967BD4009F995B /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 8E00D0E829967BD4009F995B /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; 8E00D0EA29967BD4009F995B /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; @@ -74,7 +73,7 @@ 8E00D0EF29967BD5009F995B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 8E00D0F229967BD5009F995B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 8E00D0F429967BD5009F995B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - B512AE731F1EAB7155E4C339 /* Variants.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; lastKnownFileType = sourcecode.swift; name = Variants.swift; path = Variants.swift; sourceTree = ""; }; + B512AE731F1EAB7155E4C339 /* Variants.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; path = Variants.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -132,16 +131,6 @@ path = VariantsTestAppTests; sourceTree = ""; }; - 3C00DF7AC3E9535AC87DE841 /* Variants */ = { - isa = PBXGroup; - children = ( - 78DBE119A2B49BE740D07F6E /* variants.xcconfig */, - B512AE731F1EAB7155E4C339 /* Variants.swift */, - ); - name = Variants; - path = Variants; - sourceTree = ""; - }; 8E00D0DA29967BD4009F995B = { isa = PBXGroup; children = ( @@ -150,7 +139,6 @@ 2DFD1E7F2D3FE6ED00349BF3 /* VariantsTestAppTests */, 2DFD1E662D3FE3B200349BF3 /* Frameworks */, 8E00D0E429967BD4009F995B /* Products */, - 2DFD1E992D3FEB9000349BF3 /* VariantsTestApp copy-Info.plist */, ); sourceTree = ""; }; @@ -174,11 +162,20 @@ 8E00D0EF29967BD5009F995B /* Assets.xcassets */, 8E00D0F129967BD5009F995B /* LaunchScreen.storyboard */, 8E00D0F429967BD5009F995B /* Info.plist */, - 3C00DF7AC3E9535AC87DE841 /* Variants */, + EAFEB4948D49564FD23C7C18 /* Variants */, ); path = VariantsTestApp; sourceTree = ""; }; + EAFEB4948D49564FD23C7C18 /* Variants */ = { + isa = PBXGroup; + children = ( + 78DBE119A2B49BE740D07F6E /* variants.xcconfig */, + B512AE731F1EAB7155E4C339 /* Variants.swift */, + ); + path = Variants; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -377,9 +374,9 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; CODE_SIGN_IDENTITY = "Apple Distribution: Backbase B.V. (ABC1234567D)"; - CODE_SIGN_STYLE = Automatic; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = R22WT7DX79; + DEVELOPMENT_TEAM = ""; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; GENERATE_INFOPLIST_FILE = YES; @@ -394,8 +391,9 @@ ); LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.backbase.VariantsTestApp.beta.VariantsWidgetExtension; + PRODUCT_BUNDLE_IDENTIFIER = com.backbase.VariantsTestApp.VariantsWidgetExtension; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = "match AppStore com.backbase.VariantsTestApp.VariantsWidgetExtension"; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; SWIFT_EMIT_LOC_STRINGS = YES; @@ -411,9 +409,9 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; CODE_SIGN_IDENTITY = "Apple Distribution: Backbase B.V. (ABC1234567D)"; - CODE_SIGN_STYLE = Automatic; + CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = R22WT7DX79; + DEVELOPMENT_TEAM = ""; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; GENERATE_INFOPLIST_FILE = YES; @@ -428,8 +426,9 @@ ); LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.backbase.VariantsTestApp.beta.VariantsWidgetExtension; + PRODUCT_BUNDLE_IDENTIFIER = com.backbase.VariantsTestApp.VariantsWidgetExtension; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = "match AppStore com.backbase.VariantsTestApp.VariantsWidgetExtension"; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; diff --git a/samples/ios/VariantsTestApp/VariantsTestApp.xcodeproj/xcshareddata/xcschemes/VariantsTestApp.xcscheme b/samples/ios/VariantsTestApp/VariantsTestApp.xcodeproj/xcshareddata/xcschemes/VariantsTestApp.xcscheme index d8849f33..4b5e74fc 100644 --- a/samples/ios/VariantsTestApp/VariantsTestApp.xcodeproj/xcshareddata/xcschemes/VariantsTestApp.xcscheme +++ b/samples/ios/VariantsTestApp/VariantsTestApp.xcodeproj/xcshareddata/xcschemes/VariantsTestApp.xcscheme @@ -1,7 +1,7 @@ + version = "1.3"> diff --git a/samples/ios/VariantsTestApp/fastlane/Matchfile b/samples/ios/VariantsTestApp/fastlane/Matchfile index c8d2a6ec..e77a5f20 100644 --- a/samples/ios/VariantsTestApp/fastlane/Matchfile +++ b/samples/ios/VariantsTestApp/fastlane/Matchfile @@ -1,5 +1,7 @@ git_url("git@github.com:sample/match.git") storage_mode("git") -# appstore, development, adhoc, enterprise type("appstore") -app_identifier("com.backbase.VariantsTestApp") \ No newline at end of file +app_identifier([ + "com.backbase.VariantsTestApp", + "com.backbase.VariantsTestApp.VariantsWidgetExtension" +]) \ No newline at end of file diff --git a/samples/ios/VariantsTestApp/variants.yml b/samples/ios/VariantsTestApp/variants.yml index 2b036133..a3b4a99f 100644 --- a/samples/ios/VariantsTestApp/variants.yml +++ b/samples/ios/VariantsTestApp/variants.yml @@ -13,6 +13,13 @@ ios: path: VariantsTestApp info: VariantsTestApp/Info.plist config: VariantsTestApp + extensions: + - name: VariantsWidgetExtension + bundle_suffix: VariantsWidgetExtension + signed: true + - name: OtherExtension + bundle_id: com.variantsTest.OtherExtension + signed: false variants: # Default variant is mandatory, do not remove default: @@ -47,24 +54,24 @@ ios: # Sample variant, "beta". # Only `version_name` and `version_number` are mandatory fields # - BETA: - id_suffix: beta - # If app_icon isn't specified, the value fallbacks to target.app_icon - app_icon: AppIconYellow - version_name: 0.0.1 - version_number: 1 - # 'store_destination' can be: AppStore, TestFlight or AppCenter - store_destination: AppCenter - - custom: - - name: OTHER_SWIFT_FLAGS - value: $(inherited) - env: false - destination: project - - name: SAMPLE_FASTLANE_PROPERTY - value: This will be available to fastlane on Beta variant - env: false - destination: fastlane + BETA: + id_suffix: beta + # If app_icon isn't specified, the value fallbacks to target.app_icon + app_icon: AppIconYellow + version_name: 0.0.1 + version_number: 1 + # 'store_destination' can be: AppStore, TestFlight or AppCenter + store_destination: AppCenter + + custom: + - name: OTHER_SWIFT_FLAGS + value: $(inherited) + env: false + destination: project + - name: SAMPLE_FASTLANE_PROPERTY + value: This will be available to fastlane on Beta variant + env: false + destination: fastlane signing: # 'match_url' isn't mandatory, only if you use Match to sign your app