Post
Replies
Boosts
Views
Activity
Have you tried asking the people who wrote Android Studio? How about asking on the forums for that application?
As @MobileTen stated, never store passwords in UserDefaults. You need to use the Keychain.
Also, I'm not sure why you need to store something called passcode-reset and passcode-set. The very fact that a password exists means it has been set, and if you're resetting a passcode, then that should be done as part of the journey for the user, not something that needs storing anywhere. In other words, if the user wants to reset their password, ask them for a new one, and save the new password to the keychain. There's no need to store in UDs that you're resetting the password.
So, here's how to use the keychain:
let userAccount: String = "AuthenticatedUser"
let passwordKey: String = "SomeSortOfStringIdentifyingThisIsAPasswordForYourApp" // Choose a relevant value and never change it, as the password is stored against this value
func getKeychainPasscode() -> String {
return KeychainService.loadPassword(service: passwordKey, account: userAccount) ?? ""
}
// getKeychainPasscode() will tell you if a password is set. If it returns "" then it hasn't been set.
func updatePasscode(_ value: String) {
KeychainService.updatePassword(service: passwordKey, account: userAccount, data: value)
}
// Arguments for the keychain queries
let kSecClassValue = NSString(format: kSecClass)
let kSecAttrAccountValue = NSString(format: kSecAttrAccount)
let kSecValueDataValue = NSString(format: kSecValueData)
let kSecClassGenericPasswordValue = NSString(format: kSecClassGenericPassword)
let kSecAttrServiceValue = NSString(format: kSecAttrService)
let kSecMatchLimitValue = NSString(format: kSecMatchLimit)
let kSecReturnDataValue = NSString(format: kSecReturnData)
let kSecMatchLimitOneValue = NSString(format: kSecMatchLimitOne)
public class KeychainService: NSObject {
class func updatePassword(service: String, account: String, data: String) {
if let dataFromString: Data = data.data(using: String.Encoding.utf8, allowLossyConversion: false) {
// Instantiate a new default keychain query
let keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, account], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue])
let status = SecItemUpdate(keychainQuery as CFDictionary, [kSecValueDataValue : dataFromString] as CFDictionary)
if(status == errSecItemNotFound) {
// No existing passcode, so just save the new one
savePassword(service: service, account: account, data: data)
} else {
// Passcode exists, so delete it and save the new one
removePassword(service: service, account: account)
savePassword(service: service, account: account, data: data)
}
}
}
class func removePassword(service: String, account: String) {
// Instantiate a new default keychain query
let keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, account, kCFBooleanTrue ?? true], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecReturnDataValue])
// Delete any existing items
let status = SecItemDelete(keychainQuery as CFDictionary)
if(status != errSecSuccess) {
if let err = SecCopyErrorMessageString(status, nil) {
print("Remove failed: \(err)")
}
}
}
class func savePassword(service: String, account: String, data: String) {
if let dataFromString = data.data(using: String.Encoding.utf8, allowLossyConversion: false) {
// Instantiate a new default keychain query
let keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, account, dataFromString], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecValueDataValue])
// Add the new keychain item
let status = SecItemAdd(keychainQuery as CFDictionary, nil)
// Always check the status
if (status != errSecSuccess) {
if let err = SecCopyErrorMessageString(status, nil) {
print("Write failed: \(err)")
}
}
}
}
class func loadPassword(service: String, account: String) -> String? {
// Instantiate a new default keychain query
// Tell the query to return a result
// Limit our results to one item
let keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, account, kCFBooleanTrue ?? true, kSecMatchLimitOneValue], forKeys: [kSecClassValue, kSecAttrServiceValue, kSecAttrAccountValue, kSecReturnDataValue, kSecMatchLimitValue])
var dataTypeRef: AnyObject?
// Search for the keychain items
let status: OSStatus = SecItemCopyMatching(keychainQuery, &dataTypeRef)
var contentsOfKeychain: String?
if(status == errSecSuccess) {
if let retrievedData = dataTypeRef as? Data {
contentsOfKeychain = String(data: retrievedData, encoding: String.Encoding.utf8)
}
}
return contentsOfKeychain
}
}
You should probably get in touch with Meta. It's Meta who develop and release that app, not Apple, and certainly not the third-party developers who use these forums.
Cutting out the excessive nonsense in this post, what's your actual issue? Try and restrain yourself to a couple of sentences.
What app are you talking about? How do you expect anyone to understand what you'r talking about when you don't provide even the most basic of info?
If single-tapping on a text field in your app can be calamitous I'd say your app isn't designed properly. You need to revisit the user experience.
Also, since text fields exist across the entire OS and users expect them to be activated when they single-tap them, changing this just for your app will ruin a user's 'muscle memory' and give them a poor experience.
These are the Developer Forums, where developers of apps for Apple's platforms ask each other for hints and tips on coding.
Your question is more of a product support one, so I'd suggest you ask it over at the Apple Support Forums. Thanks.
You should probably raise this as a bug in the usual way. It won't really get progressed if it's only posted in these Developer Forums.
You need to raise each issue you find separately at https://feedbackassistant.apple.com/ You can post the FB numbers here if you want, so that others can link to them.
These are the Developer Forums, where developers of apps for Apple's platforms ask each other for hints and tips on coding.
Your question is more of a product support one, so I'd suggest you ask it over at the Apple Support Forums. Thanks.
This page on Citrix's website doesn't mention macOS 15 as a supported version, so it seems you'll have to wait for Citrix to release a compatible version, and I'd suggest you contact them:
https://docs.citrix.com/en-us/citrix-secure-private-access/service/spa-csa-for-client-server-apps.html
Not really an issue for us third-party app developers. You should get in touch with WhatsApp.
No problem. Just remember not to redraw the entire view when you change the degrees. You only need to change the rotationEffect of the compass marker arrow (and whatever text you put in the VStack) when the value changes.
Something like this?
This is what I wrote and use in my own app, so if you use it, please make it look a little different... or I'll sue 😉
import SwiftUI
struct ContentView: View {
var body: some View {
let windDegrees = 255.0
let frameSize = 240.0
ZStack {
LinearGradient(gradient: Gradient(colors: [Color.init(red: 91.0/255.0, green: 100.0/255.0, blue: 157.0/255.0), Color.init(red: 125.0/255.0, green: 108.0/255.0, blue: 142.0/255.0)]), startPoint: .top, endPoint: .bottom)
ZStack {
Circle()
.opacity(0.1)
Image("compassMarker")
.resizable()
.scaledToFit()
.frame(width: 10, height: 100)
.rotationEffect(Angle(degrees: windDegrees - 180))
.shadow(color: .black.opacity(0.6), radius: 1, x: 1, y: 1)
Circle()
.fill(.black.opacity(0.15))
.frame(width: frameSize/2, height: frameSize/2)
VStack {
Text(convertWindDirectionToCompassPoint(windDegrees))
.font(.system(size: 18, weight: .bold))
.foregroundStyle(.white)
.shadow(color: .black.opacity(0.6), radius: 1, x: 1, y: 1)
Text("\(String(format: "%.0f", windDegrees))º")
.font(.system(size: 12))
.foregroundStyle(.white)
.lineLimit(1)
.minimumScaleFactor(0.6)
.shadow(color: .black.opacity(0.6), radius: 1, x: 1, y: 1)
}
ForEach(CompassMarker.markers(), id: \.self) { marker in
CompassMarkerView(marker: marker, compassDegress: 0)
}
}
.frame(width: frameSize, height: frameSize)
}
.ignoresSafeArea()
}
struct CompassMarker: Hashable {
let degrees: Double
let label: String
init(degrees: Double, label: String = "") {
self.degrees = degrees
self.label = label
}
static func markers() -> [CompassMarker] {
return [
CompassMarker(degrees: 0, label: "N"),
CompassMarker(degrees: 30),
CompassMarker(degrees: 60),
CompassMarker(degrees: 90, label: "E"),
CompassMarker(degrees: 120),
CompassMarker(degrees: 150),
CompassMarker(degrees: 180, label: "S"),
CompassMarker(degrees: 210),
CompassMarker(degrees: 240),
CompassMarker(degrees: 270, label: "W"),
CompassMarker(degrees: 300),
CompassMarker(degrees: 330)
]
}
func degreeText() -> String {
return String(format: "%.0f", self.degrees)
}
}
struct CompassMarkerView: View {
let marker: CompassMarker
let compassDegress: Double
var body: some View {
VStack {
Capsule()
.frame(width: 2, height: self.capsuleHeight())
.foregroundStyle(Color.white)
.opacity(0.6)
.padding(.bottom, self.capsulePadding())
Text(marker.label)
.font(.system(size: 16, weight: .bold))
.foregroundStyle(Color.white)
.opacity(0.6)
.rotationEffect(self.textAngle())
Spacer(minLength: 97)
}
.rotationEffect(Angle(degrees: marker.degrees))
}
private func capsuleHeight() -> CGFloat {
return (marker.label != "" ? 8 : 12)
}
private func capsulePadding() -> CGFloat {
return (marker.label != "" ? -12 : -6)
}
private func textAngle() -> Angle {
return Angle(degrees: -self.compassDegress - self.marker.degrees)
}
}
func convertWindDirectionToCompassPoint(_ degrees: Double) -> String {
var degrees_: Double = fmod(degrees, 360.0)
var point: String = ""
if(degrees_ > 360) {
degrees_ = degrees_.truncatingRemainder(dividingBy: 360)
}
if((degrees_ >= 0.0 && degrees_ <= 11.25) || (degrees_ > 348.75 && degrees_ <= 360.0)) {
point = "N"
}
if(degrees_ > 11.25 && degrees_ <= 33.75) {
point = "NNE"
}
if(degrees_ > 33.75 && degrees_ <= 56.25) {
point = "NE"
}
if(degrees_ > 56.25 && degrees_ <= 78.75) {
point = "ENE"
}
if(degrees_ > 78.75 && degrees_ <= 101.25) {
point = "E"
}
if(degrees_ > 101.25 && degrees_ <= 123.75) {
point = "ESE"
}
if(degrees_ > 123.75 && degrees_ <= 146.25) {
point = "SE"
}
if(degrees_ > 146.25 && degrees_ <= 168.75) {
point = "SSE"
}
if(degrees_ > 168.75 && degrees_ <= 191.25) {
point = "S"
}
if(degrees_ > 191.25 && degrees_ <= 213.75) {
point = "SSW"
}
if(degrees_ > 213.75 && degrees_ <= 236.25) {
point = "SW"
}
if(degrees_ > 236.25 && degrees_ <= 258.75) {
point = "WSW"
}
if(degrees_ > 258.75 && degrees_ <= 281.25) {
point = "W"
}
if(degrees_ > 281.25 && degrees_ <= 303.75) {
point = "WNW"
}
if(degrees_ > 303.75 && degrees_ <= 326.25) {
point = "NW"
}
if(degrees_ > 326.25 && degrees_ <= 348.75) {
point = "NNW"
}
return point
}
}
#Preview {
ContentView()
}
Pass in your windDegrees (line 5) as a Double.
Oh, and here's an image for the compassMarker arrow:
The size of the compass is constrained by the frameSize so if you change it you'll need to alter the arrow image.
You also don't need to show the degrees and 'WSW' text (for example), and can easily put your own text in the VStack.
I've had this a few times, but the message says the compiler is unable to type-check it in a reasonable time, which AFAIK - is why you can't see where the error is: it took too long to figure it out.