I must admit my knowledge of swift is limited, and I cannot wrap my head around this problem.
I've defined this protocol, so I can use different auth providers in my app.
protocol AuthRepository {
associatedtype AuthData
associatedtype AuthResponseData
associatedtype RegistrationData
associatedtype RegistrationResponseData
func login(with data: AuthData) async throws -> AuthResponseData?
func register(with data: RegistrationData) async throws -> RegistrationResponseData?
}
and an implementation for my server
struct MyServerAuthData {
let email: String
let password: String
}
struct MyServerAuthResponseData {
let token: String
}
struct MyServerRegistrationData {
let email: String
let password: String
let name: String
}
actor AuthRepositoryImpl: AuthRepository {
func login(with data: MyServerAuthData) async throws -> MyServerAuthResponseData? {
...
}
func register(with data: MyServerRegistrationData) async throws -> Void? {
...
}
}
To use across the app, I've created this ViewModel
@MainActor
final class AuthViewModel<T: AuthRepository>: ObservableObject {
private let repository: T
init(repository: T) {
self.repository = repository
}
func login(data: T.AuthData) async throws -> T.AuthResponseData? {
try await repository.login(with: data)
}
func register(with data: T.RegistrationData) async throws {
try await repository.register(with: data)
}
}
defined in the app as
@main
struct MyApp: App {
@StateObject var authViewModel = AuthViewModel(repository: AuthRepositoryImpl())
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(self.authViewModel)
}
}
}
and consumed as
@EnvironmentObject private var authViewModel: AuthViewModel<AuthRepositoryImpl>
But with this code, the whole concept of having a generic implementation for the auth repository is useless, because changing the AuthRepostory will need to search and replace AuthViewModel<AuthRepositoryImpl> across all the app.
I've experienced this directly creating a MockAuthImpl to use with #Preview, and the preview crashed because it defines AuthViewModel(repository: MockAuthImpl()) but the view expects AuthViewModel.
There is a better way to do that?
Explore the various UI frameworks available for building app interfaces. Discuss the use cases for different frameworks, share best practices, and get help with specific framework-related questions.
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
As you can see in the screenshot, the verification popups that appear when making a StoreKit purchase cut off the buttons. When typing the code into the input field, the window will also flicker and stutter with random view refreshes. Is this something I can configure/change? It's not a very pleasant experience for making an in app purchase.
I'm using UIDocumentBrowserViewController. This view controller automatically creates a TabView with navigation titles and up to two trailing navigation bar items.
To visualize this, open the Files app by Apple on an iPhone.
I want to do the following:
Add a third button and place it farthest on the trailing side.
Keep all three buttons blue (the default color), but adjust the color of the navigation title to use the primary text color (it is also currently blue, by default)
Button Order
If my button is represented by C, then the order from left-to-right or leading-to-trailing should be A B C.
I tried to add it by using additionaltrailingnavigationbarbuttonitems:
class DocumentBrowserViewController: UIDocumentBrowserViewController, UIDocumentBrowserViewControllerDelegate
{
override func viewDidLoad()
{
super.viewDidLoad()
let button = UIBarButtonItem(...)
additionalTrailingNavigationBarButtonItems.append(button)
}
}
This always adds it as the leftmost trailing item. The order when the view loads is C A B, where C represents my button.
Here are some things I've tried:
Add it in viewWillAppear - same results.
Add it in viewDidAppear - same results.
Add it using rightBarButtonItems - does not show up at all.
insert it at: 0 instead of appending it - same results.
Add it with a delay using DispatchQueue.main.async - same results.
After some experimentation, I realized that the arrays referenced by additionalTrailingNavigationBarButtons and rightBarButtonItems seem to be empty, other than my own button. This is the case even if the DispatchQueue delay is so long that the view has already rendered and the two default buttons are clearly visible. So I'm not sure how to place my button relative to these, since I can't figure out where they actually are in the view controller's properties.
How do I put my button farther to the trailing/right side of these two default buttons?
Title Color
The navigation titles created by UIDocumentBrowserViewController are blue when not in their inline format. I want them to use the primary text color instead.
In viewDidLoad, I could do something like this:
UINavigationBar.appearance().tintColor = UIColor.label
This will change the title color to white or black, but it will also change the color of the buttons. I've tried various approaches like titleTextAttributes, and none of them seem to work with this view controller.
How do I change just the color of the navigation title, and not the color of the navigation bar items?
Topic:
UI Frameworks
SubTopic:
UIKit
The SwiftUI Playground code below demonstrates that a .jpeg image can be read and written to the iOS file system. While, a.png image can only be read; the writing request appears to be ignored. Can anyone please tell me how to code to save a .png image using SwiftUI to the iOS file system.
Code:
import SwiftUI
import UniformTypeIdentifiers
/*
(Copied from Playground 'Help' menu popup.)
UIImage Summary
An object that manages image data in your app.
You use image objects to represent image data of all kinds, and the UIImage class is capable of managing data for all image formats supported by the underlying platform. Image objects are immutable, so you always create them from existing image data, such as an image file on disk or programmatically created image data. An image object may contain a single image or a sequence of images for use in an animation.
You can use image objects in several different ways:
Assign an image to a UIImageView object to display the image in your interface.
Use an image to customize system controls such as buttons, sliders, and segmented controls.
Draw an image directly into a view or other graphics context.
Pass an image to other APIs that might require image data.
Although image objects support all platform-native image formats, it’s recommended that you use PNG or JPEG files for most images in your app. Image objects are optimized for reading and displaying both formats, and those formats offer better performance than most other image formats. Because the PNG format is lossless, it’s especially recommended for the images you use in your app’s interface.
Declaration
class UIImage : NSObject
UIImage Class Reference
*/
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct ImageFileDoc: FileDocument {
static var readableContentTypes = [UTType.jpeg, UTType.png]
static var writableContentTypes = [UTType.jpeg, UTType.png]
var someUIImage: UIImage = UIImage()
init(initialImage: UIImage = UIImage()) {
self.someUIImage = initialImage
}
init(configuration: ReadConfiguration) throws {
guard let data = configuration.file.regularFileContents,
let some = UIImage(data: data)
else {
throw CocoaError(.fileReadCorruptFile)
}
self.someUIImage = some
}
func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
switch configuration.contentType {
case UTType.png:
if let data = self.someUIImage.pngData() {
return .init(regularFileWithContents: data)
}
case UTType.jpeg:
if let data = self.someUIImage.jpegData(compressionQuality: 1.0) {
return .init(regularFileWithContents: data)
}
default:
break
}
throw CocoaError(.fileWriteUnknown)
}
}
struct ContentView: View {
@State private var showingExporterPNG = false
@State private var showingExporterJPG = false
@State private var showingImporter = false
@State var message = "Hello, World!"
@State var document: ImageFileDoc = ImageFileDoc()
@State var documentExtension = ""
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundColor(.accentColor)
Text(message)
Button("export") {
if documentExtension == "png" {
message += ", showingExporterPNG is true."
showingExporterPNG = true
}
if documentExtension == "jpeg" {
message += ", showingExporterJPG is true."
showingExporterJPG = true
}
}
.padding(20)
.border(.white, width: 2.0)
.disabled(documentExtension == "")
Button("import") {
showingImporter = true
}
.padding(20)
.border(.white, width: 2.0)
Image(uiImage: document.someUIImage)
.resizable()
.padding()
.frame(width: 300, height: 300)
}
// exporter .png
.fileExporter(isPresented: $showingExporterPNG, document: document, contentType: UTType.png) { result in
switch result {
case .success(let url):
message += ", .\(documentExtension) Saved to \(url.lastPathComponent)"
case .failure(let error):
message += ", Some error saving file: " + error.localizedDescription
}
}
// exporter .jpeg
.fileExporter(isPresented: $showingExporterJPG, document: document, contentType: UTType.jpeg) { result in
switch result {
case .success(let url):
message += ", .\(documentExtension) Saved to \(url.lastPathComponent)"
case .failure(let error):
message += ", Some error saving file: " + error.localizedDescription
}
}
// importer
.fileImporter(isPresented: $showingImporter, allowedContentTypes: [.png, .jpeg]) { result in
switch result {
case .failure(let error):
message += ", Some error reading file: " + error.localizedDescription
case .success(let url):
let gotAccess = url.startAccessingSecurityScopedResource()
if !gotAccess {
message += ", Unable to Access \(url.lastPathComponent)"
return
}
documentExtension = url.pathExtension
guard let fileContents = try? Data(contentsOf: url)
else {
message += ",\n\nUnable to read file: \(url.lastPathComponent)\n\n"
url.stopAccessingSecurityScopedResource()
return
}
url.stopAccessingSecurityScopedResource()
message += ", Read file: \(url.lastPathComponent)"
message += ", path extension is '\(documentExtension)'."
if let uiImage = UIImage(data: fileContents) {
self.document.someUIImage = uiImage
}else{
message += ", File Content is not an Image."
}
}
}
}
}
Topic:
UI Frameworks
SubTopic:
SwiftUI
Tags:
Files and Storage
Developer Tools
iPad
Swift Playground
I encountered an issue with UITextView on iOS 18 where, when typing Pinyin, extra Unicode characters such as U+2004 are inserted unexpectedly. This occurs when using a Chinese input method.
Steps to Reproduce:
1. Set up a UITextView with a standard delegate implementation.
2. Use a Pinyin input method to type the character “ㄨ”.
3. Observe that after the character “ㄨ” is typed, extra spaces (U+2004) are inserted automatically between the characters.
Code Example:
class ViewController: UIViewController {
@IBOutlet weak var textView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
extension ViewController: UITextViewDelegate {
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
print("shouldChangeTextIn: range \(range)")
print("shouldChangeTextIn: replacementText \(text)")
return true
}
func textViewDidChange(_ textView: UITextView) {
let currentText = textView.text ?? ""
let unicodeValues = currentText.unicodeScalars.map { String(format: "U+%04X", $0.value) }.joined(separator: " ")
print("textViewDidChange: textView.text: \(currentText)")
print("textViewDidChange: Unicode Scalars: \(unicodeValues)")
}
}
Output:
shouldChangeTextIn: range {0, 0}
shouldChangeTextIn: replacementText ㄨ
textViewDidChange: textView.text: ㄨ
textViewDidChange: Unicode Scalars: U+3128
------------------------
shouldChangeTextIn: range {1, 0}
shouldChangeTextIn: replacementText ㄨ
textViewDidChange: textView.text: ㄨ ㄨ
textViewDidChange: Unicode Scalars: U+3128 U+2004 U+3128
------------------------
shouldChangeTextIn: range {3, 0}
shouldChangeTextIn: replacementText ㄨ
textViewDidChange: textView.text: ㄨ ㄨ ㄨ
textViewDidChange: Unicode Scalars: U+3128 U+2004 U+3128 U+2004 U+3128
This issue may affect text processing, especially in cases where precise text manipulation is required, such as calculating ranges in shouldChangeTextIn.
Xcode 16.2 (16C5032a)
FB16300857
Consider the following SwiftData model objects (only the relevant portions are shown) (note that all relationships are optional because eventually this app will use CloudKit):
@Model
final public class Team {
public var animal: Animal?
public var handlers: [Handler]?
...
}
@Model
final public class Animal {
public var callName: String
public var familyName: String
@Relationship(inverse: \Team.animal) public var teams: [Team]?
...
}
@Model
final public class Handler {
public var givenName: String
@Relationship(inverse: \Team.handlers) public var teams: [Team]?
}
Now I want to display Team records in a list view, sorted by animal.familyName, animal.callName, and handlers.first.givenName.
The following code crashes:
struct TeamListView: View {
@Query<Team>(sort: [SortDescriptor(\Team.animal?.familyName),
SortDescriptor(\Team.animal?.callName),
SortDescriptor(\Team.handlers?.first?.givenName)]) var teams : [Team]
var body: some View {
List {
ForEach(teams) { team in
...
}
}
}
}
However, if I remove the sort clause from the @Query and do the sort explicitly, the code appears to work (at least in preliminary testing):
struct TeamListView: View {
@Query<Team> var teams: [Team]
var body: some View {
let sortedTeams = sortResults()
List {
ForEach(sortedTeams) { team in
...
}
}
}
private func sortResults() -> [Team] {
let results: [Team] = teams.sorted { team1, team2 in
let fam1 = team1.animal?.familyName ?? ""
let fam2 = team2.animal?.familyName ?? ""
let comp1 = fam1.localizedCaseInsensitiveCompare(fam2)
if comp1 == .orderedAscending { return true }
if comp1 == .orderedDescending { return false }
... <proceed to callName and (if necessary) handler givenName comparisons> ...
}
}
}
While I obviously have a workaround, this is (in my mind) a serious weakness in the implementation of the Query macro.
We are using the contactAccessPicker modifier connected to a Button to allow the user to change the selection of contacts that he allows for use in our app. In the two places where the iOS UI screen refers to our app:
"manage which contacts can access." on top,
and below in the explanatory text, again ,
the value of is taken probably from the app's PRODUCT_NAME. Instead, we need it to be either CFBundleName or CFDisplayBundleName.
In our case they are different (PRODUCT_NAME is legacy, reasons of rebranding, which is a very common reason in apps).
Is there a specific reason why iOS is using PRODUCT_NAME (or something similar) in the contactAccessPicker UI screen instead of the user facing CFBundleName or CFDisplayBundleName? or is this a bug?
Topic:
UI Frameworks
SubTopic:
SwiftUI
I'm creating an app which gamifies Screen Time reduction. I'm running into an issue with apples Screen Time setting where the user can disable my apps "Screen Time access" and get around losing the game.
Is there a way to detect when this setting is disabled for my app? I've tried using AuthorizationCenter.shared.authorizationStatus but this didn't do the trick. Does anyone have an ideas?
Hi,
I am in need of a solution that takes data from SwiftData classes and generates a PDF with the given data. Any guidance would be greatly appreciated.
I am able to retrieve the text in the input field by doing:
let contextBeforeInput = textDocumentProxy.documentContextBeforeInput ?? ""
let contextAfterInput = textDocumentProxy.documentContextAfterInput ?? ""
let fullText = contextBeforeInput + contextAfterInput
However, when I'm pasting text into the input field, textDocumentProxy.documentContextBeforeInput refuses to return the entire text from the input field but instead only returns the last two sentences.
I have tried this with the input fields in WhatsApp, Signal, and Telegram and it's all the same, so it doesn't seem to be caused by the specific app.
At first I thought it was a limitation imposed by Apple but other third party keyboard extensions such as Grammarly are able to pick up the whole pasted text from the input field, so how are they doing it?
Topic:
UI Frameworks
SubTopic:
UIKit
System: iOS 18.1.1
When connected to Carplay, after playing a song, check the playback page CPNowPlayingTemplate. This error appears on the BMW car, as shown in the picture:
In our project, this is achieved using the following methods:
UIImage *image1 = [UIImage imageNamed:@"imageName"];;
CPNowPlayingImageButton *button1 = [[CPNowPlayingImageButton alloc] initWithImage:image1 handler:^(__kindof CPNowPlayingButton * _Nonnull action) {
//do something
}];
UIImage *image2 = [UIImage imageNamed:@"imageName"];;
CPNowPlayingImageButton *button2 = [[CPNowPlayingImageButton alloc] initWithImage:image2 handler:^(__kindof CPNowPlayingButton * _Nonnull action) {
//do something
}];
NSArray<CPNowPlayingButton *> *buttons;
buttons = @[button1,button2];
[[CPNowPlayingTemplate sharedTemplate] updateNowPlayingButtons:buttons];
Is there any way to solve this problem?
Hi
I am developing an app for counting money. One view is for entering the intended destinations specified by a donor. Each destination has a row with a TextField that has an OnChange to keep it all numeric. The model that holds the data is a class called AllocationItems, which I recently changed from having the protocol ObservableObject to having the macro @Observable.
It mostly works the same, except that before with each key stroke the total would be updated, but now with the macro it only gets updated when I exit the current TextField by clicking on another TextField
How do I get it to update the total with each keystroke?
The code that shows the total is this:
Text(allocations.totalAllocated.fmtCurrency)
where allocations is an instance of AllocationItems
with this definition:
var totalAllocated: NSDecimalNumber { items.reduce(.zero) { $0 + $1.amountValue } }
I hope someone knows why this has changed and can suggest a simple fix.
I have a grid-like container with subviews.
I recently changed some internal details of the subviews, so that changes to the values they display animate.
Now, the behaviour of the grid container has changed: the animation duration used for the internal changes is now also used when the grid is re-ordered or subviews are added or removed.
I can see why this happens: the grid repositions the subviews, and the subview has declared an animation that applies to all of its properties however they are modified.
This doesn't seem like a good idea to me. The principle of encapsulation suggests that I should be able to make internal changes to a component without suffering "spooky action at a distance", i.e. other components unexpectedly changing their behaviour.
Is this an inherent issue with SwiftUI animations, or does it suggest that I am doing something wrong?
let home = homeViewController()
let rootNav = UINavigationController(rootViewController: home)
window?.rootViewController = rootNav
window?.makeKeyAndVisible()
let second = SecondViewController()
home.navigationController?.pushViewController(second, animated: false)
let third = ThirdViewController()
home.navigationController?.pushViewController(third, animated: false)
After the above is executed, the viewdidload and other related lifecycle methods in the SecondViewController are not executed。
Except for iOS18, other versions don't have this problem, so it's not a bug in iOS18, or iOS18 has optimized UINavigationController
Topic:
UI Frameworks
SubTopic:
UIKit
I used to be able to display all app icon assets in the UI using UIImage(named: String)
However, it seems this method has been deprecated in Xcode 16 and the iOS 18.
How can I display app icon assets in the UI now, without duplicating the assets?
When pushing a page in the navigation, changing the state of interactivePopGestureRecognizer causes the page to freeze.
Just like this:
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
CGFloat red = (arc4random_uniform(256) / 255.0);
CGFloat green = (arc4random_uniform(256) / 255.0);
CGFloat blue = (arc4random_uniform(256) / 255.0);
CGFloat alpha = 1.0; //
self.view.backgroundColor = [UIColor colorWithRed:red green:green blue:blue alpha:alpha];
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
btn.frame = CGRectMake(0, 0, 100, 44);
btn.backgroundColor = [UIColor redColor];
btn.center = self.view.center;
[btn setTitle:@"push click" forState:UIControlStateNormal];
[btn addTarget:self action:@selector(click:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:btn];
}
- (void)click:(id)sender {
[self.navigationController pushViewController:[ViewController new] animated:YES];
}
- (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
self.navigationController.interactivePopGestureRecognizer.enabled = NO;
}
- (void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
self.navigationController.interactivePopGestureRecognizer.enabled = YES;
}
@end
in ios it is not same as it in computer
there is text:"ยินดี
in computer is
but in ios it is
the fontsize is 16
I tried every font which is in ios and tried copy simsun in windows to ios and create CTFont
I draw it using UIGraphics drawString
If you add the .scaleEffect() modifier to a parent view inside of which there are children with contextMenu()-s, the context menu preview unfortunately keeps the original, unscaled size of the child view. This results in a very weird, glitchy user experience.
Unfortunately, providing a custom preview that is scaled up also does not help, since even though it is the right size, it gets cropped to the unscaled size of the child view.
Adding scaleEffect() to each child element individually (BEFORE the contextMenu() modifier!) does make the problem disappear. However, I would like to avoid this, since my use case is zooming into a complex graph with context menus on its nodes, and having to recalculate the position of each node manually seems to perform much worse than delegating that work to scaleEffect().
Tested on iOS 18.2 (device + emulator)
Is there a workaround?
Here is a minimal working example that demonstrates the problem:
struct ContentView: View {
var body: some View {
VStack(spacing: 20) {
Rectangle()
.frame(width: 100, height: 100)
.contextMenu {
Button("Test") {}
Button("Test") {}
}
Rectangle()
.frame(width: 100, height: 100)
.contextMenu {
Button("Test") {}
Button("Test") {}
}
}
.scaleEffect(1.5)
}
}
Screenshot (problem: The two squares are the same size. However, the long-tapped upper square got shrunk down before the context menu got displayed.)
Topic:
UI Frameworks
SubTopic:
SwiftUI
SwiftUI Issues on iOS 16: App Freezes, Buttons Unresponsive, and Missing Data (Works Fine on iOS 17)
Hi everyone,
I’m experiencing significant issues with my SwiftUI app when running on iOS 16. These issues are not present in iOS 17, where everything works as expected. I’m hoping someone can provide insights or suggestions on how to address these problems.
The Problems
App Freezing:
In certain views, the app becomes completely unresponsive when running on iOS 16.
There are no clear patterns or console logs pointing to the source of the freeze.
Unresponsive Buttons:
Buttons stop working in some views. Tapping them does nothing, even though the logic and bindings are correct.
Missing Data:
Data fetched from services (remote APIs) or local storage doesn’t show up in the UI.
Expected Behavior
The app should handle user interactions and display data correctly on both iOS 16 and iOS 17, without freezes or unresponsive elements.
Environment:
Xcode Version: Xcode 15.4
Deployment Target: iOS 16
Testing Devices: iPhone 14 with iOS 17.6.1, iPhone 13 with iOS 18.1.1, iPhone 8 with iOS 16.4, iPhone 12 with 16.4, iPhone 8 Plus with iOS 16.0, iPhone 8 Plus with 16.7.10
Topic:
UI Frameworks
SubTopic:
SwiftUI
I tried to create a Text View using attributedString. I want to set the line height using paragraphStyle and return the Text, but paragraphStyle is not being applied. Why is that?
extension Text {
init?(_ content: String, font: StyleType, color: Color = .ppBlack) {
var attributedString = AttributedString(content)
attributedString.font = Font.custom(font.fontWeight, fixedSize: font.fontSize)
attributedString.foregroundColor = color
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.minimumLineHeight = 16
paragraphStyle.maximumLineHeight = 16
paragraphStyle.lineSpacing = 0
attributedString.mergeAttributes(.init([.paragraphStyle: paragraphStyle]))
self = Text(attributedString)
}
}