I have an infinite week scroller implemented using a TabView's page styling.
basically when you scroll to the next week, it pre-loads the week after so that you can scroll infinitely.
Since iOS 17.4, it seems to partially scroll two pages ahead. Scrolling backwards works fine.
I made a radar: FB13718482
Here is a simplified implementation that has the issue reproduced. It uses the swift ordered collections library.
Video of the issue: https://youtu.be/JW8dHqawURA
import Foundation
import OrderedCollections
import SwiftUI
struct ContentView: View {
private let calendar: Calendar
private let dateFormatter: DateFormatter
@State var weeks: OrderedDictionary<String, WeekView.Week>
@State var selectedWeek: WeekView.Week.ID
init() {
let calendar = Calendar.autoupdatingCurrent
self.calendar = calendar
let formatter = DateFormatter()
formatter.calendar = calendar
formatter.dateFormat = "MMM d"
dateFormatter = formatter
// Setup initial week
let currentDate = Date()
let weekIdentifier = Self.weekIdentifier(for: currentDate, calendar: calendar)
let weeks: OrderedDictionary<WeekView.Week.ID, WeekView.Week> = [
weekIdentifier: Self.createWeek(for: currentDate, calendar: calendar)
]
self._weeks = .init(initialValue: weeks)
self._selectedWeek = .init(initialValue: weekIdentifier)
}
var body: some View {
NavigationStack {
TabView(selection: $selectedWeek) {
ForEach(weeks.values) { week in
WeekView(week: week)
.tag(week.id)
}
}
.onChange(of: selectedWeek, initial: true) { oldValue, newValue in
createNextWeekIfRequired(for: weeks[newValue]!)
}
.tabViewStyle(.page(indexDisplayMode: .always))
.indexViewStyle(.page(backgroundDisplayMode: .always))
.navigationTitle(selectedWeek)
}
.environment(\.dateFormatter, dateFormatter)
}
private func createNextWeekIfRequired(for week: WeekView.Week) {
guard let finalWeek = weeks.values.last, week.id == finalWeek.id, let day = finalWeek.days.first else {
return
}
let nextWeek = calendar.date(byAdding: .weekOfYear, value: 1, to: day)!
let identifier = Self.weekIdentifier(for: nextWeek, calendar: calendar)
guard weeks[identifier] == nil else {
return
}
weeks[identifier] = Self.createWeek(for: nextWeek, calendar: calendar)
}
static func weekIdentifier(for date: Date, calendar: Calendar) -> WeekView.Week.ID {
let year = calendar.component(.yearForWeekOfYear, from: date)
let week = calendar.component(.weekOfYear, from: date)
return "\(year)-\(week)"
}
static func createWeek(for date: Date, calendar: Calendar) -> WeekView.Week {
let startOfDay = calendar.startOfDay(for: date)
let weekOfYear = calendar.component(.weekOfYear, from: startOfDay)
let startOfWeek = calendar.nextDate(
after: startOfDay + 1,
matching: .init(hour: 0, minute: 0, second:0, nanosecond: 0, weekday: 1, weekOfYear: weekOfYear),
matchingPolicy: .nextTime,
direction: .backward
)!
var dates: [Date] = []
calendar.enumerateDates(
startingAfter: startOfWeek - 1,
matching: .init(hour: 0, minute: 0, second:0, nanosecond: 0),
matchingPolicy: .nextTime
) { result, exactMatch, stop in
guard let result, calendar.component(.weekOfYear, from: result) == weekOfYear else {
stop = true
return
}
dates.append(result)
}
return WeekView.Week(id: weekIdentifier(for: date, calendar: calendar), days: dates)
}
}
#Preview {
ContentView()
}
import SwiftUI
struct WeekView: View {
struct Week: Identifiable {
var id: String
var days: [Date]
}
var week: Week
private let columnDefinition = [GridItem](
repeating: GridItem(.flexible(minimum: 10, maximum: 200), alignment: .center),
count: 7
)
var body: some View {
LazyVGrid(columns: columnDefinition, alignment: .center) {
ForEach(week.days, id: \.timeIntervalSinceReferenceDate) { date in
DayView(date: date)
}
}
.frame(maxWidth: .infinity)
}
}
import SwiftUI
struct DayView: View {
@Environment(\.dateFormatter) private var dateFormatter
let date: Date
var body: some View {
VStack {
Text(date, formatter: dateFormatter)
Image(systemName: "calendar")
.foregroundStyle(Color.blue)
}
}
}
#Preview {
DayView(date: Date())
}
import Foundation
import SwiftUI
struct DateFormatterEnvironmentKey: EnvironmentKey {
static var defaultValue: DateFormatter = {
let formatter = DateFormatter()
formatter.calendar = .autoupdatingCurrent
formatter.dateFormat = "MMM d"
return formatter
}()
}
extension EnvironmentValues {
var dateFormatter: DateFormatter {
get { self[DateFormatterEnvironmentKey.self] }
set { self[DateFormatterEnvironmentKey.self] = newValue }
}
}
Post
Replies
Boosts
Views
Activity
We’re looking to bump our minimum version target to 16. If we were to release one final iOS 15 version of our app with a bunch of bug fixes, will users be able to download that version in the future after we bump to iOS 16?
Or will they just be cut off from downloading our app entirely?
here is my post clone scrip:
#!/bin/zsh
brew install swift-format
And here is the errors im getting in the Xcode Cloud logs:
Showing All Messages
cd /Volumes/workspace/repository/ci_scripts && /bin/zsh /Volumes/workspace/repository/ci_scripts/ci_post_clone.sh
==> Downloading https://ghcr.io/v2/homebrew/portable-ruby/portable-ruby/blobs/sha256:02180ca8b8295422ae84921bcf034b7ee8ce5575488bd5e6a37a192e53cd5d34
#=#=#
##O#-#
### 5.5%
##################################################### 74.3%
######################################################################## 100.0%
==> Pouring portable-ruby-3.1.4.el_capitan.bottle.tar.gz
Running `brew update --auto-update`...
To restore the stashed changes to /Users/local/Homebrew, run:
cd /Users/local/Homebrew && git stash pop
==> Homebrew collects anonymous analytics.
Read the analytics documentation (and how to opt-out) here:
https://docs.brew.sh/Analytics
No analytics have been recorded yet (nor will be during this `brew` run).
==> Homebrew is run entirely by unpaid volunteers. Please consider donating:
https://github.com/Homebrew/brew#donations
Error: swift-format: the bottle needs the Apple Command Line Tools to be installed.
You can install them, if desired, with:
xcode-select --install
If you're feeling brave, you can try to install from source with:
brew install --build-from-source swift-format
It is expected behaviour that most formulae will fail to build from source.
It is expected behaviour that Homebrew will be buggy and slow when building from source.
Do not create any issues about failures building from source on Homebrew's GitHub repositories.
Do not create any issues building from source even if you think this message is unrelated.
Any opened issues will be immediately closed without response.
Do not ask for help from Homebrew or its maintainers on social media.
You may ask for help building from source in Homebrew's discussions but are unlikely to receive a response.
If building from source fails, try to figure out the problem yourself and submit a fix as a pull request.
We will review it but may or may not accept it.
Run command: 'cd /Volumes/workspace/repository/ci_scripts && /bin/zsh /Volumes/workspace/repository/ci_scripts/ci_post_clone.sh'
==> Downloading https://ghcr.io/v2/homebrew/portable-ruby/portable-ruby/blobs/sha256:02180ca8b8295422ae84921bcf034b7ee8ce5575488bd5e6a37a192e53cd5d34
#=#=#
##O#-#
### 5.5%
##################################################### 74.3%
######################################################################## 100.0%
==> Pouring portable-ruby-3.1.4.el_capitan.bottle.tar.gz
Running `brew update --auto-update`...
To restore the stashed changes to /Users/local/Homebrew, run:
cd /Users/local/Homebrew && git stash pop
==> Homebrew collects anonymous analytics.
Read the analytics documentation (and how to opt-out) here:
https://docs.brew.sh/Analytics
No analytics have been recorded yet (nor will be during this `brew` run).
==> Homebrew is run entirely by unpaid volunteers. Please consider donating:
https://github.com/Homebrew/brew#donations
Error: swift-format: the bottle needs the Apple Command Line Tools to be installed.
You can install them, if desired, with:
xcode-select --install
If you're feeling brave, you can try to install from source with:
brew install --build-from-source swift-format
It is expected behaviour that most formulae will fail to build from source.
It is expected behaviour that Homebrew will be buggy and slow when building from source.
Do not create any issues about failures building from source on Homebrew's GitHub repositories.
Do not create any issues building from source even if you think this message is unrelated.
Any opened issues will be immediately closed without response.
Do not ask for help from Homebrew or its maintainers on social media.
You may ask for help building from source in Homebrew's discussions but are unlikely to receive a response.
If building from source fails, try to figure out the problem yourself and submit a fix as a pull request.
We will review it but may or may not accept it.
Command exited with non-zero exit-code: 1
The ci_post_clone.sh is not executable and was run using zsh (default shell on macOS). To make sure your script runs correctly, make the file executable using `chmod +x` and add an appropriate shebang line.
I feel like Xcode Cloud can't possibly be missing Xcode command line tools. I also can't imagine they forgot to run chmod +x on the default run script files
Any Ideas here?
I am trying to build a text editor that shrinks to its content size. The closest I have been able to get has been to add the .scrollDisabled(true) and .fixedSize(horizontal: false, vertical: true) modifiers.
This almost achieves what I need. There are two problems though:
long single line text gets cut off at the end
creating line breaks causes the text editor to grow vertically as expected (uncovering the cut off text in point 1 above). However, when you delete the line breaks, the TextEditor does not shrink again.
I have had a radar open for some time: FB13292506. Hopefully opening a thread here will get more visibility.
And here is some sample code to easily reproduce the issue:
import SwiftUI
struct ContentView: View {
@State var text = "[This is some long text that will be cut off at the end of the text editor]"
var body: some View {
TextEditor(text: $text)
.scrollDisabled(true)
.fixedSize(horizontal: false, vertical: true)
}
}
#Preview {
ContentView()
}
Here is a gif of the behavior:
We have a fairly large app with dozens of dependencies.
SwiftUI Previews take ages to build, even though they are only using a fraction of our code.
I managed to speed them up a bit by disabling most of my custom build phases. But they are still super slow due to how long it takes to build things like Firebase.
Anyone have any ideas here? Is there a way to ensure that previews are only compiling the files that they need to work?
It seems UINavigationControllers do not play nicely with UIHostingController.
When presenting a UIHostingController, the UINavigationBar does not update with the nav bar title until after the SwiftUI view has finished appearing on screen.
This only happens the first time the hosting controller loads its view. If you were to store the hosting controller somewhere, then try to present it a second time, everything works as expected.
Feedback: FB13287789
Pretty easy to reproduce:
Scene Delegate:
import UIKit
import SwiftUI
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
window = UIWindow(windowScene: windowScene)
let navigationController = UINavigationController()
let contentView = RootView(navController: navigationController) { navController in
navController.pushViewController(UIHostingController(rootView: SearchView()), animated: true)
}
let firstController = UIHostingController(rootView: contentView)
navigationController.setViewControllers([firstController], animated: false)
window?.rootViewController = navigationController
window?.makeKeyAndVisible()
}
func sceneDidDisconnect(_ scene: UIScene) {}
func sceneDidBecomeActive(_ scene: UIScene) {}
func sceneWillResignActive(_ scene: UIScene) {}
func sceneWillEnterForeground(_ scene: UIScene) {}
func sceneDidEnterBackground(_ scene: UIScene) {}
}
SwiftUI Views:
import UIKit
import SwiftUI
struct RootView: View {
var navController: UINavigationController
var didTapButton: (UINavigationController) -> ()
var body: some View {
Button {
didTapButton(navController)
} label: {
Text("Tap this to show broken search bar animation")
}
.navigationTitle("First Page")
.navigationBarTitleDisplayMode(.inline)
}
}
struct SearchView: View {
let items: [Int] = {
(0...100).map {$0}
}()
@State var searchText: String = ""
var body: some View {
List {
ForEach(items, id: \.self) { item in
Text("\(item)")
}
}
.listStyle(.plain)
.navigationTitle("Search Page")
.navigationBarTitleDisplayMode(.inline)
.searchable(text: $searchText, placement: .navigationBarDrawer(displayMode: .always), prompt: "Search")
}
}
So I have been trying to figure out how to solve an issue all day today. Basically, I have two async streams and I am using async-algorithms combineLatest to merge them, and then .map to select a value from one of the streams.
Unfortunately, there is no "AnyAsyncSequence" so I can't type erase. As such, I have wrapped this sequence in an AsyncStream.
I noticed that by doing this, the observer of this stream seems to cancel early. When in reality it should only be ending when my class deinitializes.
Here is the code that does not work:
var values: AsyncStream<MyValue> {
let stream = combineLatest(self.myFirstStream, self.mySecondStream)
.map(self.selectValueToReturn)
return AsyncStream {
for await value in stream { // For some reason, only a few values get returned here and then the stream gets cancelled.
return value
}
return nil
}
}
Strangely enough, if I switch to a continuation based AsyncStream. It works fine:
var values: AsyncStream<FeatureConfig> {
return AsyncStream { continuation in
Task { [weak self] in
guard let self else {
return
}
let stream = combineLatest(self.myFirstStream, self.mySecondStream)
.map(self.selectValueToReturn)
for await value in stream {
continuation.yield(value)
}
continuation.finish()
}
}
}
So I've added swift-format as one of my dependencies to my own swift package so that I can use their lint swift package plugin.
Here is my Package.swift:
// swift-tools-version: 5.7
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "EssentialFeed",
platforms: [
.iOS(.v16),
.macOS(.v13),
.macCatalyst(.v16),
.tvOS(.v16),
.watchOS(.v9)
],
products: [
// Products define the executables and libraries a package produces, and make them visible to other packages.
.library(
name: "EssentialFeed",
targets: ["EssentialFeed"]),
.library(
name: "EssentialFeedTestHelpers",
targets: ["EssentialFeedTestHelpers"]),
],
dependencies: [
.package(url: "https://github.com/apple/swift-format", .upToNextMajor(from: "0.50700.1")),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages this package depends on.
.target(
name: "EssentialFeed",
dependencies: [],
plugins: [
.plugin(name: "LintPlugin", package: "swift-format"),
.plugin(name: "FormatPlugin", package: "swift-format"),
]
),
.target(
name: "EssentialFeedTestHelpers",
dependencies: []),
.testTarget(
name: "EssentialFeedTests",
dependencies: ["EssentialFeed", "EssentialFeedTestHelpers"]),
.testTarget(
name: "EssentialFeedAPIEndToEndTests",
dependencies: ["EssentialFeed", "EssentialFeedTestHelpers"]),
]
)
I get the following error when pulling this package:
product 'LintPlugin' required by package 'essentialfeed' target 'EssentialFeed' not found in package 'swift-format'.
But if you look at swift-format's Package.swift, you can see the plugins exist.
I keep getting the following error when trying to build my app:
2022-07-08 19:59:03.837 ibtoold[65032:257043] DEBUG: Added to environment: {
TMPDIR = "/var/folders/n1/mn972l7d7t53rk8kghkt9b4h0000gp/T/61CA0AE1-7540-4D8C-94B2-3BF37B7EB31C";
}
/* com.apple.actool.errors */
: error: Failed to launch AssetCatalogSimulatorAgent via CoreSimulator spawn
Failure Reason: Failed to spawn AssetCatalogSimulatorAgent on IBSimDeviceTypeiPad2x (3E230588-1FA3-45F9-AB2E-C02394631AA7, (null), Shutdown)
Underlying Errors:
Description: Invalid device: The specified device is not available for spawning processes.
This is happening with Xcode 13.4.1
This started after installing the Xcode 14 beta along side Xcode 13.4.1 (I never actually opened or used the beta before this started happening).
Things I've tried:
deleting DerrivedData
changing the DerrivedData directory
uninstalling Xcode 13 and 14 beta, and only reinstalling Xcode 13.
restarting my computer.
Anyone else see this?
Also a side note:
I can't seem to add iOS 15 simulators after this started happening. I wonder if its related.
EDIT: Upon further digging im seeing this error message in my CoreSimulator.log file:
Jul 8 20:06:10 bmifsud-mbp CoreSimulatorService[2918] <Notice>: Could not locate default .GlobalPreferences.plist for iOS 15.5 (15.5 - 19F70) - com.apple.CoreSimulator.SimRuntime.iOS-15-5
Is there any way to reset this preference?
App never reaches the AppDelegate didFinishingLaunching method.
Is there some sort of build setting that I need to set to make an iOS app run on M1?
I have been working with UIViewRepresentable a lot recently and noticed something that seems to be quite the flaw:
There doesn't seem to be a clean way of passing data back to your swiftUI views in a performant way.
Here is an example:
struct MapWrapper: UIViewRepresentable {
		@Binding var centerCoordinate: CLLocationCoordinate2D
		init(centerCoordinate: Binding<CLLocationCoordinate2D>) {
				self._centerCoordinate = centerCoordinate
		}
		func makeUIView(context: Context) -> MKMapView {
				var mapView = MKMapView()
				mapView.delegate = context.coordinator
				return mapView
		}
		
		func updateUIView(uiView: MKMapView, context: Context) {
				// Updating the maps center coordinate triggers mapViewDidChangeVisibleRegion, which thus causes updateUIView to trigger again.
				uiView.centerCoordinate = centerCoordinate
		}
		func makeCoordinator() -> Coordinator {
				return Coordinator(parent: self)
		}
		class Coordinator: MKMapViewDelegate {
				var parent: MapWrapper
				init(parent: MapWrapper) {
						self.parent = parent
				}
				func mapViewDidChangeVisibleRegion(_ mapView: MKMapView) {
						// updating this variable causes a view reload, thus calling updateUIView, and eventually causing this delegate method to trigger again.
						parent.centerCoordinate = mapView.centerCoordinate
}
}
As you can see from the above code, dragging the map anywhere would cause a loop.
The only workaround I have found for this is to give your coordinator a "shouldUpdateState" Boolean variable. and set that prior to updating your bindings.
Has this ever been addressed by apple anywhere? Or are we just expected to only modify our view state from the outside of a UIViewRepresentable?
I've been following the part 1 tutorial until the point where they deploy the widgets to the simulator.
I noticed that my widget was completely blank.
Checked other widgets and noticed that the apple news widgets on the simulator is like this as well.
Heres the code:
//
// EmojiRangerWidget.swift
// EmojiRangerWidget
//
// Created by Brent Mifsud on 2020-06-24.
// Copyright © 2020 Apple. All rights reserved.
//
import WidgetKit
import SwiftUI
import Intents
struct Provider: IntentTimelineProvider {
public func snapshot(for configuration: ConfigurationIntent, with context: Context, completion: @escaping (SimpleEntry) -> ()) {
let entry = SimpleEntry(date: Date(), character: .panda)
completion(entry)
}
public func timeline(for configuration: ConfigurationIntent, with context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
var entries: [SimpleEntry] = [
SimpleEntry(date: Date(), character: .panda)
]
// Generate a timeline consisting of five entries an hour apart, starting from the current date.
let currentDate = Date()
for hourOffset in 0 ..< 5 {
let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)!
let entry = SimpleEntry(date: entryDate, character: .panda)
entries.append(entry)
}
let timeline = Timeline(entries: entries, policy: .atEnd)
completion(timeline)
}
}
struct SimpleEntry: TimelineEntry {
public let date: Date
let character: CharacterDetail
}
struct PlaceholderView : View {
var body: some View {
Text("Placeholder View")
}
}
struct EmojiRangerWidgetEntryView : View {
var entry: Provider.Entry
var body: some View {
AvatarView(entry.character)
}
}
@main
struct EmojiRangerWidget: Widget {
private let kind: String = "EmojiRangerWidget"
public var body: some WidgetConfiguration {
IntentConfiguration(kind: kind, intent: ConfigurationIntent.self, provider: Provider(), placeholder: PlaceholderView()) { entry in
EmojiRangerWidgetEntryView(entry: entry)
}
.configurationDisplayName("Emoji Ranger Detail")
.description("Keep track of your favorite emoji ranger.")
}
}
struct EmojiRangerWidget_Previews: PreviewProvider {
static var previews: some View {
AvatarView(.panda)
.previewContext(WidgetPreviewContext(family: .systemSmall))
}
}
Is the custom view/modifier backwards compatible with SwiftUI on iOS 13? Or does it require iOS 14
Does the new iPadOS 14 scribble api work with swiftUI?