Posts

Post not yet marked as solved
0 Replies
225 Views
The available environment variables have this: CI_BRANCH The name of the source branch that Xcode Cloud checked out for the current build, for example, main. However, this is only available for builds started by a branch change. I tried using it in a manual build, and it was just an empty string. Is there a way to get the branch name for manually started workflows? You have to choose a branch to start one, so I'd assume that info is available somewhere.
Posted
by KBartlett.
Last updated
.
Post marked as solved
1 Replies
417 Views
I'm setting up my XCode Cloud workflows based on an existing CICD pipeline. One of the things I'd really like the workflow to do is upload an IPA file to BrowserStack, which is the tool we use for our testing. I have a curl command which will do that: curl -u "<username>:<api key>" -X POST "https://api-cloud.browserstack.com/app-live/upload" -F "file=@/path/to/app/file/Application-debug.ipa" So I added this to the ci_post_xcodebuild script: if [[ $CI_XCODEBUILD_ACTION = "archive" ]]; then curl -u "$BROWSERSTACK_USERID:$BROWSERSTACK_APIKEY" -X POST "https://api-cloud.browserstack.com/app-live/upload" -F "file=@$CI_ARCHIVE_PATH" curl -u "$BROWSERSTACK_USERID:$BROWSERSTACK_APIKEY" -X POST "https://api-cloud.browserstack.com/app-automate/upload" -F "file=@$CI_ARCHIVE_PATH" fi But I got an "incorrect extension" error. Once I echoed the $CI_ARCHIVE_PATH, it was obvious why - it was leading to a file called build.xcarchive. Is there any way to get access to the IPA file? There's a different curl command that will upload an IPA file from a URL, so I could try getting the URL for the build from App Store Connect? It has to be a public URL, though, so I'm not sure BrowserStack would be able to access our builds.
Posted
by KBartlett.
Last updated
.
Post not yet marked as solved
0 Replies
265 Views
I'm investigating converting our pipeline that uses Gitlab runners and Fastlane to XCode Cloud. I think I got the basic workflow set up ok, but the problem comes in when trying to archive to a different version - we generally have the next release in our develop branch, the following release in a delta branch and/or feature branches, and sometimes switch to a far-future version to test things when we don't want a build on the current release to confuse our testing team. With our current setup, we have a Fastlane lane that checks the version in our project, checks the latest build number for that version, and increments it, like so: lane:checkAndIncrementBuildNumber do # Increment the build number localProjectVersion = get_version_number_from_plist( xcodeproj: PROJECT_NAME, target: TARGET_NAME, build_configuration_name: BUILD_CONFIGURATION, plist_build_setting_support: true) current_build_number = latest_testflight_build_number(api_key_path: API_KEY_PATH,version: localProjectVersion) increment_build_number( build_number: current_build_number + 1 ) incrementedBuildNumber = get_info_plist_value(path: INFO_PLIST_PATH, key: "CFBundleVersion") File.write(VERSION_NUMBER_FILE_NAME, localProjectVersion) File.write(BUILD_NUMBER_FILE_NAME, incrementedBuildNumber) File.write(VERSION_BUILD_FILE_NAME, localProjectVersion + " (#{incrementedBuildNumber})") File.write(IPA_FILE_NAME_FILE, IPA_FILE_NAME_START + localProjectVersion.tr(".","") + "b" + incrementedBuildNumber + ".ipa") end Can I just call this lane or something like it in the ci_pre_xcodebuild script? Or is there a way to replicate this functionality without having to set up new workflows every time we switch releases?
Posted
by KBartlett.
Last updated
.
Post not yet marked as solved
0 Replies
357 Views
I'm working on converting an app to SwiftUI, and I have a menu that used to be several table cells in a storyboard, but I moved it to an embedded SwiftUI view instead. Here's the old way (from override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell ): cellReuseID = "BillingToolsCell" let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseID, for: indexPath) if let billingToolsCell = cell as? BillingToolsCell { billingToolsCell.billingToolsOptions.text = billingTools[indexPath.row].title // Accessibility billingToolsCell.isAccessibilityElement = true billingToolsCell.accessibilityIdentifier = "Billing_\(billingTools[indexPath.row].title.replacingOccurrences(of: " ", with: ""))" } return cell And here's the new way I'm creating the cell: cellReuseID = "BillingToolsSwiftUI" if let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseID, for: indexPath) as? SwiftUIHostTableViewCell<BillingToolsView> { let view = BillingToolsView(billingToolVM: BillingToolViewModel()) { segueID in self.performSegue(segueID: segueID) } cell.host(view, parent: self) return cell } Here's the swiftUI view: struct BillingToolsView: View { @StateObject var billingToolVM: BillingToolViewModel var navigationCallback: (String) -> Void var body: some View { VStack { VStack{ ForEach(self.billingToolVM.billingToolList, id: \.self) { tool in Button { navigationCallback(tool.segueID) } label: { BillingToolsRowView(toolName: tool.title) Divider().foregroundColor(AFINeutral800_SwiftUI) } .accessibilityIdentifier("Billing_\(tool.title.replacingOccurrences(of: " ", with: ""))") } } .padding(.vertical) .padding(.leading) .background(AFINeutral0_SwiftUI) } } } If I check the accessibility inspector, I can see the identifier - here it is showing Billing_PaymentHistory: But when the testers try to run their tests in Appium, they don't see any identifier at all: Did I mess up setting up the accessibility identifier somehow? Or do the testers need to update their script?
Posted
by KBartlett.
Last updated
.
Post not yet marked as solved
1 Replies
668 Views
I'm having issues animating views inside a ForEach list that's inside a border. Here's my code: struct ContentView: View { @State var showNewMethodOptions = false var body: some View { VStack { Button { showNewMethodOptions.toggle() } label: { HStack { Image(systemName: showNewMethodOptions ? "chevron.up" : "plus") .resizable() .scaledToFit() .frame(width: 32) .foregroundColor(.blue) Text("Add new payment method") .foregroundColor(.gray) .padding(.leading, 6) Spacer() } .padding(.horizontal) .padding(.top, 20) .padding(.bottom, showNewMethodOptions ? 10 : 20) } if showNewMethodOptions { ForEach(PaymentMethodType.allCases) { method in NavigationLink { } label: { ZStack(alignment: .center) { RoundedRectangle(cornerRadius: 5) .stroke(.blue) Text(method.rawValue) .font(.title3) .foregroundColor(.gray) } .frame(height: 45.0) .background() .padding(.horizontal) .padding(.vertical, 5) .transition(.slide) } } } } .animation(.spring, value: showNewMethodOptions) .padding(.bottom, showNewMethodOptions ? 16 : 0) .padding(.horizontal) .overlay( RoundedRectangle(cornerRadius: 4) .stroke(.gray, lineWidth: 1) .padding(.vertical, 4) .padding(.horizontal) ) } } enum PaymentMethodType: String, CaseIterable, Identifiable { var id: Self { return self } case checking = "Checking" case savings = "Savings" case creditOrDebitCard = "Card" } When the animation happens, the "add a new payment method" HStack animates nicely enough, but the payment method options fade in while it's still sliding up, which doesn't look great because it looks like everything's on top of each other. The options also don't seem to follow the transition animation I apply to them - it's always just a fade. Is there a way to get these elements to animate together?
Posted
by KBartlett.
Last updated
.
Post marked as solved
1 Replies
354 Views
I'm trying to use a Link in a swiftUI view, and like every tutorial online, I'd like to use this one: Link(destination: URL, label: () -> View) It pops up as one of the autocomplete options, but as soon as I add it to my code, I get these three errors: Extra arguments at positions #1, #2 in call Missing arguments for parameters 'rel', 'href', 'method' in call Static method 'buildExpression' requires that 'Link' conform to 'View' It looks like XCode wants to force me to use this Link instead: Link(rel: String, href: String, method: String) but I don't understand why XCode won't let me use the first one. Does anyone know why this is happening?
Posted
by KBartlett.
Last updated
.
Post marked as solved
1 Replies
676 Views
I'm having an issue where I have a grid with a number of items that I'm retrieving from an API. It's a SwiftUI LazyVGrid embedded in a UIKit storyboard TableView as a UITableViewCell. The user has the ability to dismiss the items on a different screen, and then the main screen refreshes to reflect the change. Everything looks fine until the user dismisses enough items to reduce the number of rows. The items themselves still go away, but it looks like the hosting controller isn't responding to the changing height of the grid, so it leaves a weird-looking empty space. Looking through the view hierarchy, I see that every time an item is removed, the updated grid view is stacked on top of the old view instead of replacing it, so I thought that could be causing the issue, if the constraints are still taking the old view into account. Is there any way to stop this extra space from showing? Here's the table cell that's hosting the SwiftUI view:   required init?(coder: NSCoder) {     fatalError("init(coder:) has not been implemented")   }   override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {     super.init(style: .default, reuseIdentifier: "opportunityGridCell")   }       func host(_ view: Content, parent: UIViewController) {     let hostingController = UIHostingController(rootView: view)     hostingController.view.backgroundColor = .lightGray           layoutIfNeeded()           parent.addChild(hostingController)     contentView.addSubview(hostingController.view)           hostingController.view.translatesAutoresizingMaskIntoConstraints = false     hostingController.view.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true     hostingController.view.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true     hostingController.view.leftAnchor.constraint(equalTo: contentView.leftAnchor).isActive = true     hostingController.view.rightAnchor.constraint(equalTo: contentView.rightAnchor).isActive = true     hostingController.didMove(toParent: parent)     hostingController.view.layoutIfNeeded()   } } And the SwiftUI view: struct OpportunityGridView: View {   @ObservedObject var opportunityViewModel: OpportunityViewModel   let goToDetailsPage: (PolicyOpportunity, OpportunityContent) -> Void       let columns = [     GridItem(.flexible(), alignment: .top),     GridItem(.flexible(), alignment: .top),     GridItem(.flexible(), alignment: .top)   ]       var body: some View {     VStack(alignment: .leading) {       Spacer()           ScrollView {         LazyVGrid(columns: columns, spacing: 20) {           ForEach(opportunityViewModel.opportunities, id: \.self) { opportunity in             if let opportunityContent = opportunityViewModel.getOpportunityContent(opportunity: opportunity) {               Button(action: {                 goToDetailsPage(opportunity, opportunityContent)               }, label: {                 OpportunityView(opportunityImage: Image(opportunityContent.opportunityIconName), opportunityLabel: opportunityContent.cellTitle)               })             }           }         }       }     }   } }
Posted
by KBartlett.
Last updated
.