diff --git a/.swiftlint.yml b/.swiftlint.yml index da61a583..8864cf49 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -25,6 +25,7 @@ excluded: - ios/epac/cabinet-positions.json - ios/epacTests/__Snapshots__ - ios/fastlane + - ios/epacUITests/SnapshotHelper.swift included: - ios diff --git a/docs/marketing/screenshots/README.md b/docs/marketing/screenshots/README.md index 595c168a..8a2b21db 100644 --- a/docs/marketing/screenshots/README.md +++ b/docs/marketing/screenshots/README.md @@ -1,37 +1,56 @@ # App Store Screenshots -EPAC-109 screenshot exports live here. +## Capture pipeline -Generate source captures with the App Store screenshot UI test: +Screenshots are captured natively on each target device using Fastlane Snapshot. +The Snapfile at `ios/fastlane/Snapfile` lists 4 devices: -```sh -APPSTORE_SCREENSHOT_DIR=/tmp/epac-appstore-screenshots \ -xcodebuild test \ - -project epac.xcodeproj \ - -scheme epac \ - -destination 'platform=iOS Simulator,name=EPAC App Store 16 Pro Max' \ - -derivedDataPath ~/Library/Developer/Xcode/DerivedData/epac-afnetyeysumivgfzkpjhhfcnazhs \ - -only-testing:epacUITests/epacUITests/testCaptureAppStoreScreenshotSources -``` +- iPhone 16 Pro Max (6.9-inch, 1320×2868) +- iPhone 16 (6.1-inch, 1290×2796) +- iPad Pro 13-inch M4 (2064×2752) +- iPad Pro 11-inch M4 (1668×2388) -Render App Store-ready assets: +Each device captures 6 scenes via `AppStoreScreenshotTests.testCaptureAppStoreScreenshotSources`, +producing 24 PNGs total in `ios/fastlane/screenshots/en-CA/`. + +### Running a capture ```sh -scripts/marketing/render_app_store_screenshots.sh /tmp/epac-appstore-screenshots docs/marketing/screenshots +cd ios && bundle exec fastlane snapshot ``` -The capture route uses `Evidence.ScreenshotPlan`, the app's `-AppStoreScreenshots` launch argument, and official-record sample data from the House of Commons Hansard fixture already committed in the test suite. Resizing is delegated to `evidence resize` through `scripts/marketing/render_app_store_screenshots.sh`. +This launches the showcase UI (`AppStoreScreenshotShowcaseView`) with the +`-AppStoreScreenshots` and `-AppStoreScreenshotPage <0-5>` launch arguments on +each device. The showcase is fully offline — it uses bundled `NSLocalizedString` +keys and Color literals with no backend dependency. + +On iPad simulators the showcase automatically renders a two-column layout +(headline/subtitle on the left, content card on the right) via +`horizontalSizeClass == .regular`, producing screenshots that are visually +distinct from the phone captures. + +### Output -The render script writes: +Fastlane writes PNGs named `-.png` into +`ios/fastlane/screenshots/en-CA/`. The `clear_previous_screenshots(true)` +Snapfile directive removes stale files before each run. -- 6 iPhone 6.9-inch screenshots using the base scene filenames. -- 6 iPad Pro 13-inch screenshots prefixed with `APP_IPAD_PRO_3GEN_129_`. -- 6 iPad Pro 12.9-inch screenshots prefixed with `APP_IPAD_PRO_129_`. +### Refreshing the App Store upload set -To refresh the App Store upload directory from the committed marketing set: +Copy the Fastlane output into the delivery directory: ```sh -scripts/marketing/render_app_store_screenshots.sh docs/marketing/screenshots ios/fastlane/screenshots/en-CA +cp -f ios/fastlane/screenshots/en-CA/*.png docs/marketing/screenshots/ ``` -See `docs/marketing/evidence-workflows.md` for the full local workflow. +### Manual single-device capture + +To capture a single device outside Fastlane: + +```sh +xcodebuild test \ + -project ios/epac.xcodeproj \ + -scheme epac \ + -destination 'platform=iOS Simulator,name=iPad Pro 13-inch (M4)' \ + -only-testing:epacUITests/AppStoreScreenshotTests/testCaptureAppStoreScreenshotSources +``` diff --git a/ios/epac/Views/ContentView.swift b/ios/epac/Views/ContentView.swift index 1fc58a08..92503b59 100644 --- a/ios/epac/Views/ContentView.swift +++ b/ios/epac/Views/ContentView.swift @@ -645,11 +645,22 @@ private enum AppStoreScreenshotSpec { static let sourceBadgeFontSize: CGFloat = 11 static let sourceBadgeHorizontalPadding: CGFloat = 12 static let sourceBadgeVerticalPadding: CGFloat = 8 + + // iPad two-column layout + static let iPadHeadlineFontSize: CGFloat = 44 + static let iPadSubtitleFontSize: CGFloat = 18 + static let iPadColumnSpacing: CGFloat = 40 + static let iPadContentPadding: CGFloat = 48 + static let iPadTopPadding: CGFloat = 64 + static let iPadPhoneFrameMaxWidth: CGFloat = 420 } private struct AppStoreScreenshotShowcaseView: View { + @Environment(\.horizontalSizeClass) private var horizontalSizeClass @State private var selection = Self.initialSelection() + private var isIPad: Bool { horizontalSizeClass == .regular } + private static func initialSelection() -> Int { let arguments = ProcessInfo.processInfo.arguments guard let index = arguments.firstIndex(of: "-AppStoreScreenshotPage"), @@ -716,15 +727,30 @@ private struct AppStoreScreenshotShowcaseView: View { } var body: some View { + Group { + if isIPad { + iPadBody + } else { + phoneBody + } + } + .dynamicTypeSize(.large) + .preferredColorScheme(.light) + } + + private var phoneBody: some View { TabView(selection: $selection) { ForEach(Array(pages.enumerated()), id: \.offset) { index, page in - AppStoreScreenshotPageView(page: page) + AppStoreScreenshotPageView(page: page, isIPad: false) .tag(index) } } .tabViewStyle(.page(indexDisplayMode: .never)) - .dynamicTypeSize(.large) - .preferredColorScheme(.light) + } + + private var iPadBody: some View { + let page = pages[selection] + return AppStoreScreenshotPageView(page: page, isIPad: true) } } @@ -746,6 +772,7 @@ private struct AppStoreScreenshotPage { private struct AppStoreScreenshotPageView: View { let page: AppStoreScreenshotPage + var isIPad = false private func t(_ key: String) -> String { NSLocalizedString(key, comment: "") @@ -764,31 +791,59 @@ private struct AppStoreScreenshotPageView: View { ) .ignoresSafeArea() - VStack(alignment: .leading, spacing: AppStoreScreenshotSpec.pageVerticalSpacing) { - VStack(alignment: .leading, spacing: AppStoreScreenshotSpec.titleVerticalSpacing) { - Text(page.headline) - .font(.system(size: AppStoreScreenshotSpec.headlineFontSize, weight: .heavy, design: .rounded)) - .foregroundStyle(.primary) - .minimumScaleFactor(AppStoreScreenshotSpec.headlineMinimumScaleFactor) - .lineLimit(AppStoreScreenshotSpec.headlineLineLimit) - .fixedSize(horizontal: false, vertical: true) - .accessibilityIdentifier("appStoreScreenshotHeadline") + if isIPad { + iPadContent + } else { + phoneContent + } + } + } - Text(page.subtitle) - .font(.system(size: AppStoreScreenshotSpec.subtitleFontSize, weight: .medium, design: .rounded)) - .foregroundStyle(.secondary) - .lineLimit(AppStoreScreenshotSpec.subtitleLineLimit) - .fixedSize(horizontal: false, vertical: true) - } + private var phoneContent: some View { + VStack(alignment: .leading, spacing: AppStoreScreenshotSpec.pageVerticalSpacing) { + titleSection(headlineSize: AppStoreScreenshotSpec.headlineFontSize, subtitleSize: AppStoreScreenshotSpec.subtitleFontSize) .padding(.horizontal, AppStoreScreenshotSpec.titleHorizontalPadding) - showcaseContent - .padding(.horizontal, AppStoreScreenshotSpec.showcaseHorizontalPadding) + showcaseContent + .padding(.horizontal, AppStoreScreenshotSpec.showcaseHorizontalPadding) - Spacer(minLength: AppStoreScreenshotSpec.bottomSpacerMinLength) + Spacer(minLength: AppStoreScreenshotSpec.bottomSpacerMinLength) + } + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) + .padding(.top, AppStoreScreenshotSpec.pageTopPadding) + } + + private var iPadContent: some View { + HStack(alignment: .top, spacing: AppStoreScreenshotSpec.iPadColumnSpacing) { + VStack(alignment: .leading, spacing: AppStoreScreenshotSpec.pageVerticalSpacing) { + titleSection(headlineSize: AppStoreScreenshotSpec.iPadHeadlineFontSize, subtitleSize: AppStoreScreenshotSpec.iPadSubtitleFontSize) + Spacer() } - .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) - .padding(.top, AppStoreScreenshotSpec.pageTopPadding) + .frame(maxWidth: .infinity, alignment: .topLeading) + + showcaseContent + .frame(maxWidth: AppStoreScreenshotSpec.iPadPhoneFrameMaxWidth) + } + .padding(AppStoreScreenshotSpec.iPadContentPadding) + .padding(.top, AppStoreScreenshotSpec.iPadTopPadding) + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) + } + + private func titleSection(headlineSize: CGFloat, subtitleSize: CGFloat) -> some View { + VStack(alignment: .leading, spacing: AppStoreScreenshotSpec.titleVerticalSpacing) { + Text(page.headline) + .font(.system(size: headlineSize, weight: .heavy, design: .rounded)) + .foregroundStyle(.primary) + .minimumScaleFactor(AppStoreScreenshotSpec.headlineMinimumScaleFactor) + .lineLimit(AppStoreScreenshotSpec.headlineLineLimit) + .fixedSize(horizontal: false, vertical: true) + .accessibilityIdentifier("appStoreScreenshotHeadline") + + Text(page.subtitle) + .font(.system(size: subtitleSize, weight: .medium, design: .rounded)) + .foregroundStyle(.secondary) + .lineLimit(AppStoreScreenshotSpec.subtitleLineLimit) + .fixedSize(horizontal: false, vertical: true) } } diff --git a/ios/epacUITests/AppStoreScreenshotTests.swift b/ios/epacUITests/AppStoreScreenshotTests.swift new file mode 100644 index 00000000..5968cc83 --- /dev/null +++ b/ios/epacUITests/AppStoreScreenshotTests.swift @@ -0,0 +1,38 @@ +import XCTest + +final class AppStoreScreenshotTests: XCTestCase { + + private let sceneNames = [ + "01-parliament-in-your-pocket", + "02-see-how-your-mp-votes", + "03-your-mp-everything-they-do", + "04-track-a-bill-start-to-finish", + "05-know-whos-influencing-your-mp", + "06-contact-them-in-one-tap" + ] + + override func setUpWithError() throws { + continueAfterFailure = false + } + + func testCaptureAppStoreScreenshotSources() throws { + let app = XCUIApplication() + for (index, name) in sceneNames.enumerated() { + app.launchArguments = [ + "-AppStoreScreenshots", + "-AppStoreScreenshotPage", "\(index)", + "-UIAnimationsDisabled", "YES" + ] + setupSnapshot(app) + app.launch() + + let headline = app.staticTexts["appStoreScreenshotHeadline"] + XCTAssertTrue( + headline.waitForExistence(timeout: 10), + "Showcase headline should appear for page \(index)" + ) + + snapshot(name, timeWaitingForIdle: 2) + } + } +} diff --git a/ios/epacUITests/SnapshotHelper.swift b/ios/epacUITests/SnapshotHelper.swift new file mode 100644 index 00000000..fa355c2a --- /dev/null +++ b/ios/epacUITests/SnapshotHelper.swift @@ -0,0 +1,301 @@ +// +// SnapshotHelper.swift +// Example +// +// Created by Felix Krause on 10/8/15. +// + +// ----------------------------------------------------- +// IMPORTANT: When modifying this file, make sure to +// increment the version number at the very +// bottom of the file to notify users about +// the new SnapshotHelper.swift +// ----------------------------------------------------- + +import Foundation +import XCTest + +var deviceLanguage = "" +var locale = "" + +func setupSnapshot(_ app: XCUIApplication, waitForAnimations: Bool = true) { + Snapshot.setupSnapshot(app, waitForAnimations: waitForAnimations) +} + +func snapshot(_ name: String, waitForLoadingIndicator: Bool) { + if waitForLoadingIndicator { + Snapshot.snapshot(name) + } else { + Snapshot.snapshot(name, timeWaitingForIdle: 0) + } +} + +/// - Parameters: +/// - name: The name of the snapshot +/// - timeout: Amount of seconds to wait until the network loading indicator disappears. Pass `0` if you don't want to wait. +func snapshot(_ name: String, timeWaitingForIdle timeout: TimeInterval = 20) { + Snapshot.snapshot(name, timeWaitingForIdle: timeout) +} + +enum SnapshotError: Error, CustomDebugStringConvertible { + case cannotFindSimulatorHomeDirectory + case cannotRunOnPhysicalDevice + + var debugDescription: String { + switch self { + case .cannotFindSimulatorHomeDirectory: + return "Couldn't find simulator home location. Please, check SIMULATOR_HOST_HOME env variable." + case .cannotRunOnPhysicalDevice: + return "Can't use Snapshot on a physical device." + } + } +} + +@objcMembers +open class Snapshot: NSObject { + static var app: XCUIApplication? + static var waitForAnimations = true + static var cacheDirectory: URL? + static var screenshotsDirectory: URL? { + return cacheDirectory?.appendingPathComponent("screenshots", isDirectory: true) + } + + open class func setupSnapshot(_ app: XCUIApplication, waitForAnimations: Bool = true) { + + Snapshot.app = app + Snapshot.waitForAnimations = waitForAnimations + + do { + let cacheDir = try getCacheDirectory() + Snapshot.cacheDirectory = cacheDir + setLanguage(app) + setLocale(app) + setLaunchArguments(app) + } catch let error { + NSLog(error.localizedDescription) + } + } + + class func setLanguage(_ app: XCUIApplication) { + guard let cacheDirectory = self.cacheDirectory else { + NSLog("CacheDirectory is not set - probably running on a physical device?") + return + } + + let path = cacheDirectory.appendingPathComponent("language.txt") + + do { + let trimCharacterSet = CharacterSet.whitespacesAndNewlines + deviceLanguage = try String(contentsOf: path, encoding: .utf8).trimmingCharacters(in: trimCharacterSet) + app.launchArguments += ["-AppleLanguages", "(\(deviceLanguage))"] + } catch { + NSLog("Couldn't detect/set language...") + } + } + + class func setLocale(_ app: XCUIApplication) { + guard let cacheDirectory = self.cacheDirectory else { + NSLog("CacheDirectory is not set - probably running on a physical device?") + return + } + + let path = cacheDirectory.appendingPathComponent("locale.txt") + + do { + let trimCharacterSet = CharacterSet.whitespacesAndNewlines + locale = try String(contentsOf: path, encoding: .utf8).trimmingCharacters(in: trimCharacterSet) + } catch { + NSLog("Couldn't detect/set locale...") + } + + if locale.isEmpty && !deviceLanguage.isEmpty { + locale = Locale(identifier: deviceLanguage).identifier + } + + if !locale.isEmpty { + app.launchArguments += ["-AppleLocale", "\"\(locale)\""] + } + } + + class func setLaunchArguments(_ app: XCUIApplication) { + guard let cacheDirectory = self.cacheDirectory else { + NSLog("CacheDirectory is not set - probably running on a physical device?") + return + } + + let path = cacheDirectory.appendingPathComponent("snapshot-launch_arguments.txt") + app.launchArguments += ["-FASTLANE_SNAPSHOT", "YES", "-ui_testing"] + + do { + let launchArguments = try String(contentsOf: path, encoding: String.Encoding.utf8) + let regex = try NSRegularExpression(pattern: "(\\\".+?\\\"|\\S+)", options: []) + let matches = regex.matches(in: launchArguments, options: [], range: NSRange(location: 0, length: launchArguments.count)) + let results = matches.map { result -> String in + (launchArguments as NSString).substring(with: result.range) + } + app.launchArguments += results + } catch { + NSLog("Couldn't detect/set launch_arguments...") + } + } + + open class func snapshot(_ name: String, timeWaitingForIdle timeout: TimeInterval = 20) { + if timeout > 0 { + waitForLoadingIndicatorToDisappear(within: timeout) + } + + NSLog("snapshot: \(name)") // more information about this, check out https://docs.fastlane.tools/actions/snapshot/#how-does-it-work + + if Snapshot.waitForAnimations { + sleep(1) // Waiting for the animation to be finished (kind of) + } + + #if os(OSX) + guard let app = self.app else { + NSLog("XCUIApplication is not set. Please call setupSnapshot(app) before snapshot().") + return + } + + app.typeKey(XCUIKeyboardKeySecondaryFn, modifierFlags: []) + #else + + guard self.app != nil else { + NSLog("XCUIApplication is not set. Please call setupSnapshot(app) before snapshot().") + return + } + + let screenshot = XCUIScreen.main.screenshot() + #if os(iOS) && !targetEnvironment(macCatalyst) + let image = XCUIDevice.shared.orientation.isLandscape ? fixLandscapeOrientation(image: screenshot.image) : screenshot.image + #else + let image = screenshot.image + #endif + + guard var simulator = ProcessInfo().environment["SIMULATOR_DEVICE_NAME"], let screenshotsDir = screenshotsDirectory else { return } + + do { + // The simulator name contains "Clone X of " inside the screenshot file when running parallelized UI Tests on concurrent devices + let regex = try NSRegularExpression(pattern: "Clone [0-9]+ of ") + let range = NSRange(location: 0, length: simulator.count) + simulator = regex.stringByReplacingMatches(in: simulator, range: range, withTemplate: "") + + let path = screenshotsDir.appendingPathComponent("\(simulator)-\(name).png") + try image.pngData()?.write(to: path, options: .atomic) + } catch let error { + NSLog("Problem writing screenshot: \(name) to \(screenshotsDir)/\(simulator)-\(name).png") + NSLog(error.localizedDescription) + } + #endif + } + + class func fixLandscapeOrientation(image: UIImage) -> UIImage { + if #available(iOS 10.0, *) { + let format = UIGraphicsImageRendererFormat() + format.scale = image.scale + let renderer = UIGraphicsImageRenderer(size: image.size, format: format) + return renderer.image { context in + image.draw(in: CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height)) + } + } else { + return image + } + } + + class func waitForLoadingIndicatorToDisappear(within timeout: TimeInterval) { + #if os(tvOS) + return + #endif + + guard let app = self.app else { + NSLog("XCUIApplication is not set. Please call setupSnapshot(app) before snapshot().") + return + } + + let networkLoadingIndicator = app.otherElements.deviceStatusBars.networkLoadingIndicators.element + let networkLoadingIndicatorDisappeared = XCTNSPredicateExpectation(predicate: NSPredicate(format: "exists == false"), object: networkLoadingIndicator) + _ = XCTWaiter.wait(for: [networkLoadingIndicatorDisappeared], timeout: timeout) + } + + class func getCacheDirectory() throws -> URL { + let cachePath = "Library/Caches/tools.fastlane" + // on OSX config is stored in /Users//Library + // and on iOS/tvOS/WatchOS it's in simulator's home dir + #if os(OSX) + let homeDir = URL(fileURLWithPath: NSHomeDirectory()) + return homeDir.appendingPathComponent(cachePath) + #elseif arch(i386) || arch(x86_64) || arch(arm64) + guard let simulatorHostHome = ProcessInfo().environment["SIMULATOR_HOST_HOME"] else { + throw SnapshotError.cannotFindSimulatorHomeDirectory + } + let homeDir = URL(fileURLWithPath: simulatorHostHome) + return homeDir.appendingPathComponent(cachePath) + #else + throw SnapshotError.cannotRunOnPhysicalDevice + #endif + } +} + +private extension XCUIElementAttributes { + var isNetworkLoadingIndicator: Bool { + if hasAllowListedIdentifier { return false } + + let hasOldLoadingIndicatorSize = frame.size == CGSize(width: 10, height: 20) + let hasNewLoadingIndicatorSize = frame.size.width.isBetween(46, and: 47) && frame.size.height.isBetween(2, and: 3) + + return hasOldLoadingIndicatorSize || hasNewLoadingIndicatorSize + } + + var hasAllowListedIdentifier: Bool { + let allowListedIdentifiers = ["GeofenceLocationTrackingOn", "StandardLocationTrackingOn"] + + return allowListedIdentifiers.contains(identifier) + } + + func isStatusBar(_ deviceWidth: CGFloat) -> Bool { + if elementType == .statusBar { return true } + guard frame.origin == .zero else { return false } + + let oldStatusBarSize = CGSize(width: deviceWidth, height: 20) + let newStatusBarSize = CGSize(width: deviceWidth, height: 44) + + return [oldStatusBarSize, newStatusBarSize].contains(frame.size) + } +} + +private extension XCUIElementQuery { + var networkLoadingIndicators: XCUIElementQuery { + let isNetworkLoadingIndicator = NSPredicate { (evaluatedObject, _) in + guard let element = evaluatedObject as? XCUIElementAttributes else { return false } + + return element.isNetworkLoadingIndicator + } + + return self.containing(isNetworkLoadingIndicator) + } + + var deviceStatusBars: XCUIElementQuery { + guard let app = Snapshot.app else { + fatalError("XCUIApplication is not set. Please call setupSnapshot(app) before snapshot().") + } + + let deviceWidth = app.windows.firstMatch.frame.width + + let isStatusBar = NSPredicate { (evaluatedObject, _) in + guard let element = evaluatedObject as? XCUIElementAttributes else { return false } + + return element.isStatusBar(deviceWidth) + } + + return self.containing(isStatusBar) + } +} + +private extension CGFloat { + func isBetween(_ numberA: CGFloat, and numberB: CGFloat) -> Bool { + return numberA...numberB ~= self + } +} + +// Please don't remove the lines below +// They are used to detect outdated configuration files +// SnapshotHelperVersion [1.29] diff --git a/ios/fastlane/Snapfile b/ios/fastlane/Snapfile index e4919dec..f61e0579 100644 --- a/ios/fastlane/Snapfile +++ b/ios/fastlane/Snapfile @@ -1,7 +1,9 @@ # Devices to capture (required App Store sizes) devices([ - "iPhone 16 Pro Max", # 6.9" — required - "iPhone 16", # 6.1" — required for older devices + "iPhone 16 Pro Max", # 6.9" — required + "iPhone 16", # 6.1" — required for older devices + "iPad Pro 13-inch (M4)", # 13" iPad — required + "iPad Pro 11-inch (M4)", # 11" iPad — required ]) languages(["en-CA"]) diff --git a/ios/fastlane/screenshots/en-CA/APP_IPAD_PRO_129_01-parliament-in-your-pocket.png b/ios/fastlane/screenshots/en-CA/APP_IPAD_PRO_129_01-parliament-in-your-pocket.png deleted file mode 100644 index cc8663e1..00000000 Binary files a/ios/fastlane/screenshots/en-CA/APP_IPAD_PRO_129_01-parliament-in-your-pocket.png and /dev/null differ diff --git a/ios/fastlane/screenshots/en-CA/APP_IPAD_PRO_129_02-see-how-your-mp-votes.png b/ios/fastlane/screenshots/en-CA/APP_IPAD_PRO_129_02-see-how-your-mp-votes.png deleted file mode 100644 index fb9d32a8..00000000 Binary files a/ios/fastlane/screenshots/en-CA/APP_IPAD_PRO_129_02-see-how-your-mp-votes.png and /dev/null differ diff --git a/ios/fastlane/screenshots/en-CA/APP_IPAD_PRO_129_03-your-mp-everything-they-do.png b/ios/fastlane/screenshots/en-CA/APP_IPAD_PRO_129_03-your-mp-everything-they-do.png deleted file mode 100644 index 8ebb457b..00000000 Binary files a/ios/fastlane/screenshots/en-CA/APP_IPAD_PRO_129_03-your-mp-everything-they-do.png and /dev/null differ diff --git a/ios/fastlane/screenshots/en-CA/APP_IPAD_PRO_129_04-track-a-bill-start-to-finish.png b/ios/fastlane/screenshots/en-CA/APP_IPAD_PRO_129_04-track-a-bill-start-to-finish.png deleted file mode 100644 index a785f238..00000000 Binary files a/ios/fastlane/screenshots/en-CA/APP_IPAD_PRO_129_04-track-a-bill-start-to-finish.png and /dev/null differ diff --git a/ios/fastlane/screenshots/en-CA/APP_IPAD_PRO_129_05-know-whos-influencing-your-mp.png b/ios/fastlane/screenshots/en-CA/APP_IPAD_PRO_129_05-know-whos-influencing-your-mp.png deleted file mode 100644 index 7ac9a5b4..00000000 Binary files a/ios/fastlane/screenshots/en-CA/APP_IPAD_PRO_129_05-know-whos-influencing-your-mp.png and /dev/null differ diff --git a/ios/fastlane/screenshots/en-CA/APP_IPAD_PRO_129_06-contact-them-in-one-tap.png b/ios/fastlane/screenshots/en-CA/APP_IPAD_PRO_129_06-contact-them-in-one-tap.png deleted file mode 100644 index 47df3af7..00000000 Binary files a/ios/fastlane/screenshots/en-CA/APP_IPAD_PRO_129_06-contact-them-in-one-tap.png and /dev/null differ diff --git a/ios/fastlane/screenshots/en-CA/APP_IPAD_PRO_3GEN_129_01-parliament-in-your-pocket.png b/ios/fastlane/screenshots/en-CA/APP_IPAD_PRO_3GEN_129_01-parliament-in-your-pocket.png deleted file mode 100644 index 09a403f5..00000000 Binary files a/ios/fastlane/screenshots/en-CA/APP_IPAD_PRO_3GEN_129_01-parliament-in-your-pocket.png and /dev/null differ diff --git a/ios/fastlane/screenshots/en-CA/APP_IPAD_PRO_3GEN_129_02-see-how-your-mp-votes.png b/ios/fastlane/screenshots/en-CA/APP_IPAD_PRO_3GEN_129_02-see-how-your-mp-votes.png deleted file mode 100644 index ae779eb1..00000000 Binary files a/ios/fastlane/screenshots/en-CA/APP_IPAD_PRO_3GEN_129_02-see-how-your-mp-votes.png and /dev/null differ diff --git a/ios/fastlane/screenshots/en-CA/APP_IPAD_PRO_3GEN_129_03-your-mp-everything-they-do.png b/ios/fastlane/screenshots/en-CA/APP_IPAD_PRO_3GEN_129_03-your-mp-everything-they-do.png deleted file mode 100644 index febb78a3..00000000 Binary files a/ios/fastlane/screenshots/en-CA/APP_IPAD_PRO_3GEN_129_03-your-mp-everything-they-do.png and /dev/null differ diff --git a/ios/fastlane/screenshots/en-CA/APP_IPAD_PRO_3GEN_129_04-track-a-bill-start-to-finish.png b/ios/fastlane/screenshots/en-CA/APP_IPAD_PRO_3GEN_129_04-track-a-bill-start-to-finish.png deleted file mode 100644 index fd6720a3..00000000 Binary files a/ios/fastlane/screenshots/en-CA/APP_IPAD_PRO_3GEN_129_04-track-a-bill-start-to-finish.png and /dev/null differ diff --git a/ios/fastlane/screenshots/en-CA/APP_IPAD_PRO_3GEN_129_05-know-whos-influencing-your-mp.png b/ios/fastlane/screenshots/en-CA/APP_IPAD_PRO_3GEN_129_05-know-whos-influencing-your-mp.png deleted file mode 100644 index 308facfe..00000000 Binary files a/ios/fastlane/screenshots/en-CA/APP_IPAD_PRO_3GEN_129_05-know-whos-influencing-your-mp.png and /dev/null differ diff --git a/ios/fastlane/screenshots/en-CA/APP_IPAD_PRO_3GEN_129_06-contact-them-in-one-tap.png b/ios/fastlane/screenshots/en-CA/APP_IPAD_PRO_3GEN_129_06-contact-them-in-one-tap.png deleted file mode 100644 index 5f1b8fa2..00000000 Binary files a/ios/fastlane/screenshots/en-CA/APP_IPAD_PRO_3GEN_129_06-contact-them-in-one-tap.png and /dev/null differ