I am in the process of adding my company's brand font to our SwiftUI app. I am able to implement the font using the provided public APIs so that text styles / dynamic type and the font weight modifier in SwiftUI work correctly.
However we are unable to implement custom font in such a way that text styles / dynamic type, the font weight modifier, and the bold text accessibility setting all work at the same time. Am I missing an implementation detail so that all these features work correctly?
Font Setup
The font files were modified to better support SwiftUI:
The font style name metadata was modified to match the name the .fontWeight(...) modifier expects. This was done with Typelight.
The font weight value (100/200/300) was modified so that the underlying weight value matches the value the .fontWeight(...) modifier expects. See "Using custom fonts with SwiftUI" by Matthew Flint.
The font files were imported via the Info.plist.
Examples
Font Weight Comparison
San Fransisco:
Text("#100")
.font(.largeTitle)
.fontWeight(.ultraLight)
Overpass by Name:
Text("#100")
.font(.custom("Overpass-UltraLight", size: 34, relativeTo: .largeTitle))
Overpass by Weight:
Text("#100")
.fontWeight(.ultraLight)
.font(.custom("Overpass", size: 34, relativeTo: .largeTitle))
Legibility Weight Test
When using the .fontWeight(...) modifier, the custom font does not change weights when the bold text accessibility setting is enabled. Dynamic type size works as expected.
Normal legibility weight:
Bold legibility weight:
Dynamic Type Size:
Use UIFont
Using UIFont to load the custom font files and initializing a Font with the UIFont breaks dynamic type:
Bold type also does not work:
Custom Modifier
Creating a custom modifier allows us to support dynamic type and manually handle bold text. However it creates a conflicting API to SwiftUI's .fontWeight(...) modifier.
struct FontModifier: ViewModifier {
enum UseCase {
case paragraph
case headline
}
enum Weight {
case light
case regular
case heavy
}
@Environment(\.legibilityWeight) var legibilityWeight
var useCase: UseCase
var weight: Weight
init(_ useCase: UseCase, _ weight: Weight) {
self.useCase = useCase
self.weight = weight
}
var resolvedHeadlineWeight: String {
let resolvedLegibilityWeight = legibilityWeight ?? .regular
switch weight {
case .light:
switch resolvedLegibilityWeight {
case .regular:
return "Light"
case .bold:
return "Semibold"
@unknown default:
return "Light"
}
case .regular:
switch resolvedLegibilityWeight {
case .regular:
return "Regular"
case .bold:
return "Bold"
@unknown default:
return "Regular"
}
case .heavy:
switch resolvedLegibilityWeight {
case .regular:
return "Heavy"
case .bold:
return "Black"
@unknown default:
return "Heavy"
}
}
}
var resolvedParagraphWeight: Font.Weight {
switch weight {
case .light:
return .light
case .regular:
return .regular
case .heavy:
return .heavy
}
}
var resolvedFont: Font {
switch useCase {
case .paragraph:
return .system(.largeTitle).weight(resolvedParagraphWeight)
case .headline:
return .custom("Overpass-\(resolvedHeadlineWeight)", size: 34, relativeTo: .largeTitle)
}
}
func body(content: Content) -> some View {
content
.font(resolvedFont)
}
}
GridRow {
Text("Aa")
.modifier(FontModifier(.paragraph, .regular))
Text("Aa")
.modifier(FontModifier(.paragraph, .heavy))
Text("Aa")
.modifier(FontModifier(.headline, .regular))
Text("Aa")
.modifier(FontModifier(.headline, .heavy))
}
Font Environment Value
The font environment value does not contain font weight information when the fontWeight(...) modifier is used.:
struct DumpFontTest: View {
@Environment(\.font) var font
var body: some View {
Text("San Fransisco")
.onAppear {
print("------------")
dump(font)
}
}
}
Posts under iOS tag
200 Posts
Sort by:
Post
Replies
Boosts
Views
Activity
Hello.
I want to do a fetch when a view loads. In UIKit I would have used viewDidLoad to do this but in SwiftUI we only have onAppear and task. Is that by design, and if so, what is the recommended way to fetch data?
I wrote a little blog post for a workaround describing the issue and the found solution, but I presume there is a better way.
https://www.ludafux.com/post/viewdidload_doppelganger
Best Regards,
Luda
Should I update to iOS 18 beta 2 or not please reply (my device is a iPhone 12 mini)(DONT JUDGE ME IM SMALL)
Hi I'm new to Swiftui.
I want to show a sheet and pass a var to it.
In the case below when I tap on a list view it will popup then disappear. But subsequent taps will work as intended.
Main view:
struct ContentView: View {
@State private var selectedMonth = 1
@State private var selectedYear = "2024"
let months = [1,2,3,4,5,6,7,8,9,10,11,12]
@State private var isShowing = false
var body: some View {
List {
ForEach (months, id: \.self) { month in
HStack {
ViewList(month: month, year: selectedYear)
}
.onTapGesture {
isShowing = true
selectedMonth = month
}
.sheet(isPresented: $isShowing){
PopupView(month: selectedMonth, year: selectedYear)
.presentationDetents([.large])
}
}
}
.listRowSpacing(2)
.listStyle(.grouped)
}
}
ViewList:
struct ViewList: View {
var month: Int
var year: String
@State private var selectedMonthText = "Jan"
var body: some View {
VStack (alignment: .leading) {
Text(selectedMonthText + " / " + year)
.font(.headline)
}
.onAppear {
switch month {
case 01:
selectedMonthText = "Jan"
case 02:
selectedMonthText = "Feb"
case 03:
selectedMonthText = "Mar"
case 04:
selectedMonthText = "Apr"
case 05:
selectedMonthText = "May"
case 06:
selectedMonthText = "Jun"
case 07:
selectedMonthText = "Jul"
case 08:
selectedMonthText = "Aug"
case 09:
selectedMonthText = "Sep"
case 10:
selectedMonthText = "Oct"
case 11:
selectedMonthText = "Nov"
case 12:
selectedMonthText = "Dec"
default:
selectedMonthText = "All"
}
}
}
}
Then my popup:
struct PopupView: View {
@Environment(\.dismiss) var dismiss
var month: Int
var year: String
@State private var selectedMonthText = "Jan"
@State private var imageText = "plus"
@State private var items = ["Cat", "Dog", "Bird", "Snake"]
var body: some View {
Button("Dismiss"){
dismiss()
}
Text(selectedMonthText + " / " + year)
List {
ForEach(items, id: \.self) {item in
HStack {
Text(item)
Text("Fed on: ")
Text(selectedMonthText)
}
}
}
.listRowSpacing(0)
.listStyle(.inset)
.onAppear {
switch month {
case 01:
selectedMonthText = "Jan"
case 02:
selectedMonthText = "Feb"
case 03:
selectedMonthText = "Mar"
case 04:
selectedMonthText = "Apr"
case 05:
selectedMonthText = "May"
case 06:
selectedMonthText = "Jun"
case 07:
selectedMonthText = "Jul"
case 08:
selectedMonthText = "Aug"
case 09:
selectedMonthText = "Sep"
case 10:
selectedMonthText = "Oct"
case 11:
selectedMonthText = "Nov"
case 12:
selectedMonthText = "Dec"
default:
selectedMonthText = "All"
}
}
}
}
Dear Apple Developer Support,
I am seeking information regarding the implementation of in-app updates for iOS applications. Specifically, I would like to know if there is an available SDK or method that allows users to update their apps without being redirected to the App Store. This functionality is essential for enhancing user experience and streamlining the update process within the app itself.
Thank you for your assistance.
We are building an app that uses ARKit occasionally, but not always.
We would like to test the non-ARKit parts in the simulator, since it offers more debugging features (e.g. SwiftUI previews or the Thread Sanitizer).
However, we can't even build the app for the simulator, since the simulator SDK does not know about certain classes (e.g. "AnchorEntity"). This also means that none of the SwiftUI previews work, even if the views are not using ARKit.
What is the best approach to test such an app in the simulator, without using any ARKit features?
Hey,
I have a setup in my app that I am currently building, that uses @Observable router objects that hold the app's entire navigation state, so that I can easily set it globally and let SwiftUI show the appropriate views accordingly. Each view gets passed in such a router object and there is a global "app" router that the app's root view can access as an entry point:
@Observable @MainActor
final class AppRouter {
static let shared = AppRouter() // Entry point used by the app's root view
var isShowingSheet = false // Navigation state
let sheetRouter = SheetRouter() // Router that's passed to the sheet view. This router could contain other routers for sheets it will show, and so on
}
@Observable @MainActor
final class SheetRouter { // Example of a "nested" router for a sheet view
var path = NavigationPath()
var isShowingOtherSheet = false
func reset() {
path = .init()
isShowingOtherSheet = false
}
}
To open a sheet, I have a button like this:
@Bindable var appRouter = AppRouter.shared
// ...
Button("Present") {
appRouter.sheetRouter.reset() // Reset sheet router
appRouter.isShowingSheet = true // show sheet
}
This seems to work perfectly fine. However, this produces tons of "error" logs in the console, whenever I open the sheet for a second time:
Mutating observable property \SheetRouter.path after view is torn down has no effect.
Mutating observable property \SheetRouter.path after view is torn down has no effect.
Mutating observable property \SheetRouter.path after view is torn down has no effect.
Mutating observable property \SheetRouter.path after view is torn down has no effect.
Mutating observable property \SheetRouter.isShowingOtherSheet after view is torn down has no effect.
These errors appear when calling the reset() of the sheet view's router before opening the sheet. That method simply resets all navigation states in a router back to their defaults. It's as if the sheetRouter is still connected to the dismissed view from the previous sheet, causing a mutation to trigger these error logs.
Am I misusing SwiftUI here or is that a bug? It's also worth mentioning that these error logs do not appear on iOS 17. Only on iOS 18. So it might be a bug but I just want to make sure my usage of these router objects is okay and not a misuse of the SwiftUI API that the runtime previously simply did not catch/notice. Then I'd have to rewrite my entire navigation logic.
I do have an example project to demonstrate the issue. You can find it here: https://github.com/SwiftedMind/PresentationBugDemo.
I have also filed a feedback for this: FB14162780
STEPS TO REPRODUCE
Open the example project.
Open the sheet in the ContentView twice by tapping "Present"
Close that sheet
Open it again.
Then the console will show these error logs from above.
I'd appreciate any help with this.
Cheers
Hi, I have an app that uses a firestore database, throughout testing I had no problems. However, I released my app and it crashes all the time.when i distribute my app to testflight this will working on the 3 ios devices with same version but the app is not working on one device which have also a same version of ios. when i open the app the app will crash and not open.
Here is a crash report I have. I am struggling to find resources online on how to interpret it but no solution found, so any help would be appreciated.
Thanks so much for any help,
You can see the error here
Hello, I'm new here, I was developing a screen recording extension for an IOS application, I used the RPSBroadcastSampleHandler livekit as a basis, in tests a few months ago it worked, but after the long wait for publishing authorization the extension stopped working, I noticed which is not just mine but screen sharing from Google Meet, Zoom Mettings and others also don't work, I tested it on iPhone 14 pro and iPhone 6s, nothing worked, the option to select the extension appears but when clicking "start sharing" nothing happens and after a few seconds the sharing button returns to "start sharing", the same behavior in all tested apps, does anyone know what happens? Did you change the way you record and no app has updated? Internal error in IOS? Nothing logs in terminal just doesn't work.
I was taking some photos of the Milky Way while using a tripod and the stars seemed to not be in focus. I tried plenty of times to get stars in focus. I switched to other apps that have a manual focus and the problem was resolved however the results weren’t as clear as the standard app would have been. I then tried again with the standard app but while focusing on an outdoor camera on our house and yet the camera would still not focus no matter how much I tried. I’m not sure if it is a bug with the iOS 18 developer beta. Either way I figured it was best to post something I had realized. Apple please add a manual focus option.
Hello,
I‘ve started developing an App not to long ago on Xcode and I have noticed that I can’t see any progress or like preview of what I have coded so far and it’s starting to concern me. I have Xcode version 15.5 and I am coding with swift, everything works fine there is no error or anything preventing it to run but when I try to preview the only thing I see when I launch the Simulator and I open my app the only thing I see is a white page.
Can someone please help me.
As has been posted a number of times, the large title navigation bar only collapses on scrolling for the UIView listed first in an .xib or storyboard file.
In my case, I have an enclosing view containing 2 Views, a TableView and a CollectionView in an .XIB file. This is connected to a UIKit class. Tapping a button switches between the two views. If the TableView is the first view listed, the navigationbar collapses with scrolling but with the CollectionView listed second, the large title navigation bar doesn't collapse with scrolling. Alternatively, if the CollectionView is the first view listed, the navigation bar collapses with scrolling but with the TableView listed second, the large title navigation bar doesn't collapse with scrolling.
I have not been able to figure out a way to enable the collapsable large title navigation bar to work in such a scenario as mine for both views within a container view. Is there a way to do this? I have tested this capability through iOS 17.
Be warned! When developing your app in Xcode 16 be careful!!! The new SDK has changes that cause builds on Xcode Cloud and other non-beta Xcode to have bugs (EVEN WITH IOS TARGET SET LOWER).
This wasted 2 days of development time, but in WKNavigationDelegate, the webView(_:decidePolicyFor:decisionHandler:) method has a new type signature that will ONLY work in the latest SDK. The change was that the decisionHandler now has a @MainActor attribute. This causes Swift to recognize that it "almost" meets an optional requirement and suggests that you change it. If you change it, it will cause builds to not include the optional method.
Hello,
I recently encountered an issue following the transfer of my app between Apple Developer accounts and would appreciate any insights or solutions.
Here's a summary of the situation:
App Transfer: I transferred an app from one Apple Developer account to another. The app's bundle ID remained unchanged during this process.
App Update: After the transfer, I integrated a new feature into the app and pushed the updated version to TestFlight under the new account.
Installation Issue: When I installed the TestFlight version of the app from the new account on my device, which already had the app installed from the old account, the app logged out the user. It appears that the UserDefaults data was not retained, resulting in the loss of stored user data.
My hypothesis is that the transfer between accounts caused the user defaults to reset, leading to the data loss.
Has anyone else experienced this issue, and if so, are there any recommended solutions or best practices to prevent UserDefaults from resetting during such transfers?
Thank you in advance for your assistance.
We currently try to run tests on 4 physically attached iPhones all on ios 17. When tests are concurrently running we occasionally get failures that look like this:
Testing failed:
Failed to install the app on the device.
SwiftUITests-Runner encountered an error (Failed to install or launch the test runner. (Underlying Error: Failed to install the app on the device. (Underlying Error: Connection with the remote side was unexpectedly closed : { count = 1, transaction: 0, voucher = 0x0, contents =
"XPCErrorDescription" => { length = 22, contents = "Connection interrupted" }
}. Unhandled error domain IXRemoteErrorDomain, code 6))
I noticed that when relying on xcodebuild to install the target app and test runner it cannot install concurrently/parallel on all 4 devices but instead does each device sequentially causing the test to hang. The same behavior can be exhibited when trying to install an app concurrently on 4 different phones with xcrun devicectl as well.
STEPS TO REPRODUCE
Trigger a test with xcodebuild on 4 different phones at the same time where xcodebuild also needs to install the target apps.
Currently using Xcode15.2 and iOS 17.5.1
Dear community,
I am a new developer and I am building a view (called Root) that has a list of rows where clicking each row navigates to a completely different view.
I have a CaseIteratable enum and I list each enum type using ForEach and each enum case navigates to a different view using NavigationLink and NavigationDestination. But the problem is that clicking any of the rows for the first time navigates correctly to the corresponding view. But when I go back to the root view and chose another row, it navigates me to a blank view for less than a sec and automatically navigates back to the root view.
Below is the code for reference.
I would really appreciate some help and advice here. Thank you very much!
struct RootViewNavigationStack: View {
@AppStorage("items") private var items = Item.allCases
@State private var enableMove = false
@State private var rootStackPath: NavigationPath = .init()
var body: some View {
NavigationStack(path: $rootStackPath) {
VStack {
List {
ForEach(items) { item in
HStack {
NavigationLink(value: item) {
ListCell(
icon: item.icon,
title: item.title)
}
.disabled(enableMove)
if enableMove {
withAnimation {
Image(systemName: "line.3.horizontal")
.foregroundStyle(.secondary)
}
}
}
}
.onMove(perform: enableMove == true ? moveItems : nil)
}
}
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button {
enableMove.toggle()
} label: {
if enableMove {
Text("Done")
.bold()
} else {
Text("Edit")
}
}
}
}
.navigationDestination(for: Item.self) { item in
item.destinationView
}
.navigationTitle("Root")
}
}
}
and this is the Item enum for more info
Just kindly ignore the var iconName since it doesnt represent any actual SF Symbol name
enum Item: Identifiable, Codable, Hashable, CaseIterable {
case view1
case view2
case view3
case view4
case view5
var id: Item { self }
}
extension Item {
var title: String {
switch self {
case .view1:
"View1"
case .view2:
"View2"
case .view3:
"View3"
case .view4:
"View4"
case .view5:
"View5"
}
}
var iconName: String {
switch self {
case .view1:
"View1"
case .view2:
"View2"
case .view3:
"View3"
case .view4:
"View4"
case .view5:
"View5"
}
}
var icon: Image {
Image(systemName: self.iconName)
}
@ViewBuilder
var destinationView: some View {
switch self {
case .view1:
CarView()
case .view2:
HouseView()
case .view3:
MusicView()
case .view4:
ShoesView()
case .view5:
BooksView()
}
}
}
Once again, would really appreciate someone to help and many thanks 🙏!
Hello everyone,
I am trying to implementing the In-App Purchase in one of my iOS App and I am getting the following error when I test it into iPhone, but when I test the same app into the iPad I didn't get any error and it works properly.
Error Domain=SKErrorDomain Code=0 "An unknown error occurred" UserInfo={NSLocalizedDescription=An unknown error occurred, NSUnderlyingError=0x282da9860 {Error Domain=ASDErrorDomain Code=500 "(null)" UserInfo={NSUnderlyingError=0x282d02940 {Error Domain=AMSErrorDomain Code=203 "Bag Load Failed" UserInfo={NSLocalizedFailureReason=Unable to retrieve p2-product-offers-batch-limit because we failed to load the bag., NSLocalizedDescription=Bag Load Failed, NSUnderlyingError=0x282d02af0 {Error Domain=AMSErrorDomain Code=203 "Bag Load Failed" UserInfo=0x2836ca380 (not displayed)}}}}}}
Hello VisionOS Developer Community,
I am currently working on a demo application for VisionOS, tailored for the Vision Pro. In my application, I have implemented a feature to prevent the screen from sleeping using the following code:
UIApplication.shared.isIdleTimerDisabled = true
This works perfectly on iOS, ensuring the screen remains active regardless of other system settings. However, I've run into a snag with VisionOS. Even with isIdleTimerDisabled set to true, the screen still sleeps when I take off the Vision Pro.
Hello VisionOS Developer Community,
I am currently working on a demo application for VisionOS, tailored for the Vision Pro. In my application, I have implemented a feature to prevent the screen from sleeping using the following code:
UIApplication.shared.isIdleTimerDisabled = true
This works perfectly on iOS, ensuring the screen remains active regardless of other system settings. However, I've run into a snag with VisionOS. Even with isIdleTimerDisabled set to true, the screen still sleeps when I take off the Vision Pro.
We have observed for a few months that the Instruments tool in Xcode does not show correct memory allocation for the PacketTunnelProvider process on iOS 17. The memory allocation does not exceed 6-7 MB, which is not the case with iOS 16 or 15. Additionally, Instruments crashes the PacketTunnelProvider process after profiling for a few minutes.
Please note that I am not running Xcode in debugger mode for the PacketTunnelProvider process along with instruments, as this is a known issue that causes the PacketTunnelProvider to be killed when both Instruments and the Xcode debugger are running.
Is anyone else facing this issue and have a workaround?