I am learning memory and ARC since a while and managed to use the Leaks instrument more often in order to produce quality code. Having that said, please consider my lack of experience in this zone.
Problem: I built a parent view A that presents a view B .
B contains a login form built using a TextField
, a SecureField
and a Button
. I also have a @FocusState private var isFocused: Bool
property that helps me hide the keyboard in order to bypass the "AttributeGraph: cycle detected" console error that shows up once I disable the textfield on button press having the keyboard on screen. ( Get `AttributeGraph: cycle detected` error when changing disabled state of text field )
I noticed that when I use the @FocusState
property, once I dismiss B , the Leaks instrument detects two "Malloc 32 Bytes" leaks just like in the picture below.
If I don't use the @FocusState
property, the leaks will no longer show up. Am I doing something wrong or is this some kind of bug / false positive from Swift?
This view is partially extracted from my file so it doesn't have all it's properties and methods here .
struct AuthenticationLoginView: View {
@StateObject var viewModel = AuthenticationLoginViewModel()
@FocusState private var isFocused: Bool
var body: some View {
VStack {
TextField(text: $viewModel.username) {
Text("placeholder.")
}
.tag(AuthenticationLoginField.username)
.textInputAutocapitalization(.never)
.focused($isFocused)
.disabled(viewModel.isLoggingIn)
SecureField(text: $viewModel.password) {
Text("Password")
}
// .focused($isFocused)
.disabled(viewModel.isLoggingIn)
.tag(AuthenticationLoginField.password)
}
}
}
Without more code it is hard to tell what else you are doing that may have caused the leak.
My gut is that having a single focus boolean when you have two edit fields is an anti-pattern compared to Apple's way . When something is evolving like SwiftUI, try to follow their example styles more closely. Use a boolean only when there's just one focusable field.
This similar Hacking with Swift sample shows using an optional and changing the focus as fields submitted.
@FocusState private var focusedField: FocusedField?
@State private var username = "Anonymous"
@State private var password = "sekrit"
var body: some View {
VStack {
TextField("Enter your username", text: $username)
.focused($focusedField, equals: .username)
SecureField("Enter your password", text: $password)
.focused($focusedField, equals: .password)
}
.onSubmit {
if focusedField == .username {
focusedField = .password
} else {
focusedField = nil
}
}
I found this problem. In my case, it caused a massive retain loop which fed all the way back to coordinator with a bunch of objects.
Just showing a TextField with focus (either the bool way, or the enum way) caused a retain.
My fix was to use the introspect library and just set first responder manually
@State private var textField:UITextField?
@State private var secureTextField:UITextField?
var body: some View {
ZStack {
TextField(prompt, text: $text)
.opacity(isSecure ? 0 : 1)
.introspectTextField { field in
textField = field
}
SecureField(prompt,text: $text)
.opacity(isSecure ? 1 : 0)
.introspectTextField { field in
secureTextField = field
}
}
.animation(.easeInOut, value: isSecure)
.overlay(alignment: .trailing) {
Button {
isSecure.toggle()
setFocus()
} label: {
Image(isSecure ? "eyeClose" : "eyeOpen")
}
.opacity(canBeSecure ? 1 : 0)
}
.onAppear {
DispatchQueue.main.async {
setFocus()
}
}
}
func setFocus() {
if isSecure {
secureTextField?.becomeFirstResponder()
}
else {
textField?.becomeFirstResponder()
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.