Post

Replies

Boosts

Views

Activity

How do I get the branch name for a manual build in XCode Cloud?
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.
0
0
478
Feb ’24
How to upload .ipa files to external services with XCode Cloud?
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.
1
0
1.3k
Feb ’24
How to use XCode Cloud with multiple app versions?
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?
0
0
520
Feb ’24
Accessibility Identifiers showing up in inspector, but not Appium
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?
0
0
715
Jan ’24
SwiftUI ForEach Animation inside border
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?
1
0
1.1k
Nov ’23
XCode errors when using Link in SwiftUI
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?
1
0
645
Aug ’23
UIKit UITableViewCell holds space for items removed from embedded SwiftUI LazyVGrid
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)               })             }           }         }       }     }   } }
1
0
878
Jun ’22