You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET MAUI iOS应用重启后用户数据丢失问题求助

.NET 8 iOS应用Release模式下用户数据重启丢失

问题现象

使用Visual Studio 2022 v17.13.6开发.NET 8.0 iOS应用时,Android端Debug和Release模式均运行正常,但iOS仅Debug模式正常。重启iOS应用后,settings.xml中的用户数据会丢失,用户每次重启都需要重新登录;SecureStorage也存在相同问题。

相关代码实现

SharedSettings类(用户数据XML读写)

using System;
using System.Collections.Generic;
using System.Data.SqlTypes;
using System.Linq;
using System.Net.Http.Headers;
using System.Runtime;
using System.Text;
using System.Xml.Linq;
using System.Xml.Serialization;
using CryptoForms;

namespace MyApp
{
    public class SharedSettings : IDisposable
    {
        private static SharedSettings _instance;
        private static object _syncRoot = new Object();
        private bool _disposed = false;

        private string _userName;
        private string _userId;
        private string _passWord;

        public string UserName
        {
            get { return _userName; }
            set { _userName = value; }
        }

        public string UserId
        {
            get { return _userId; }
            set { _userId = value; }
        }

        public string Password
        {
            get { return _passWord; }
            set { _passWord = value; }
        }

        public static SharedSettings Instance
        {
            get
            {
                if (_instance == null)
                {
                    lock (_syncRoot)
                    {
                        if (_instance == null)
                        {
                            _instance = new SharedSettings();
                            _instance.Init();
                        }
                    }
                }
                return _instance;
            }
        }


        #region IDisposable Members
        public void Dispose()
        {
            if (!_disposed)
            {
                SaveSettings();
                _instance = null;
                _disposed = true;
            }
        }
        #endregion

        private void Init()
        {
            try
            {
                var loaded = _instance.LoadSettings().Result;

                if (!loaded)
                {
                    UserName = "";
                    UserId = "";
                    Password = "";

                    _instance.SaveSettings();
                }
            }
            catch (Exception e)
            {
                Console.WriteLine($"SharedSettings.Init: {e.Message}");
            }           
        }

        public async void SaveSettings()
        {
            try
            {

                var pass = "asd123";
                byte[] salt = Encoding.UTF8.GetBytes("123asd");

                var encryptedPasswordBytes = SharedCrypto.EncryptAes(Password, pass, salt);

                var encryptedPasswordString = Convert.ToBase64String(encryptedPasswordBytes);                

                var settings = new UserSettings
                {
                    UserName = UserName,
                    UserId = UserId,
                    PassWord = encryptedPasswordString
                };

                await SaveSettingsAsync(settings);                

            }
            catch (Exception e)
            {
                ResetSettings();
                Console.WriteLine($"SharedSettings.Init: {e.Message}");
            }
        }

        public async Task<bool> LoadSettings()
        {
            try
            {
                bool retVal = false;
                
                var loadedSettings = new UserSettings();
                try
                {
                    loadedSettings = await LoadSettingsAsync();
                    if (loadedSettings != null)
                    {
                        Console.WriteLine($"Loaded User: {loadedSettings.UserName}");
                        UserName = loadedSettings.UserName;
                    }
                    else
                    {
                        Console.WriteLine("No settings file found.");
                        return false;
                    }                    
                }
                catch (Exception)
                {
                    ResetSettings();

                    return false;
                }


                if (!string.IsNullOrEmpty(UserName))
                {
                    string encryptedPasswordString = "";


                    encryptedPasswordString = loadedSettings.PassWord;
                    Byte[] encryptedPassWordBytes = Convert.FromBase64String(encryptedPasswordString);
                    var pass = "asd123";
                    byte[] salt = Encoding.UTF8.GetBytes("123asd");
                    var decryptedPasswordString = SharedCrypto.DecryptAes(encryptedPassWordBytes, pass, salt);
                    Password = decryptedPasswordString;
                    
                    retVal = true;
                }
                
                return retVal;
            }
            catch (Exception ex)
            {
                Console.WriteLine($"SharedSettings.LoadSettings: {ex.Message}");
                ResetSettings();

                return false;
            }
        }

        public async Task SaveSettingsAsync(UserSettings settings)
        {
            try
            {
                string filePath = Path.Combine(FileSystem.AppDataDirectory, "settings.xml");

                using (var writer = new StreamWriter(filePath))
                {
                    XmlSerializer serializer = new XmlSerializer(typeof(UserSettings));
                    serializer.Serialize(writer, settings);
                }

                Console.WriteLine($"Settings saved successfully: {filePath}");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error saving settings: {ex.Message}");
            }
        }

        public async Task<UserSettings> LoadSettingsAsync()
        {
            try
            {
                string filePath = Path.Combine(FileSystem.AppDataDirectory, "settings.xml");
                Console.WriteLine($"Settings file path: {filePath}");
                if (!File.Exists(filePath))
                {
                    Console.WriteLine("No settings file found.");
                    return null; // No settings file exists
                }

                using (var reader = new StreamReader(filePath))
                {
                    XmlSerializer serializer = new XmlSerializer(typeof(UserSettings));
                    return (UserSettings)serializer.Deserialize(reader);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error loading settings: {ex.Message}");
                return null;
            }
        }

        public void ResetSettings()
        {
            try
            {                
                SecureStorage.Default.RemoveAll();
            }
            catch (Exception ex)
            {
                Console.WriteLine($"SharedSettings.ResetSettings: {ex.Message}");
            }

        }
    }

    public class UserSettings
    {
        public string UserName;
        public string UserId;
        public string PassWord;
    }
}

SharedCrypto加密工具类

using System.Text;
using PCLCrypto;

namespace CryptoForms
{
    /// <summary>    
    /// Common cryptographic helper    
    /// </summary>    
    public static class SharedCrypto
    {
        /// <summary>    
        /// Creates Salt with given length in bytes.    
        /// </summary>    
        /// <param name="lengthInBytes">No. of bytes</param>    
        /// <returns></returns>    
        public static byte[] CreateSalt(int lengthInBytes)
        {
            try
            {
                return WinRTCrypto.CryptographicBuffer.GenerateRandom(lengthInBytes);
            }
            catch (Exception e)
            {
                Console.WriteLine($"SharedCrypto.CreateSalt: {e.Message}");
                return null;
            }
            
        }

        /// <summary>    
        /// Creates a derived key from a combination     
        /// </summary>    
        /// <param name="password"></param>    
        /// <param name="salt"></param>    
        /// <param name="keyLengthInBytes"></param>    
        /// <param name="iterations"></param>    
        /// <returns></returns>    
        public static byte[] CreateDerivedKey(string password, byte[] salt, int keyLengthInBytes = 32, int iterations = 1000)
        {
            try
            {
                byte[] key = NetFxCrypto.DeriveBytes.GetBytes(password, salt, iterations, keyLengthInBytes);
                return key;
            }
            catch (Exception e)
            {
                Console.WriteLine($"SharedCrypto.CreateDerivedKey: {e.Message}");
                return null;
            }
            
        }

        /// <summary>    
        /// Encrypts given data using symmetric algorithm AES    
        /// </summary>    
        /// <param name="data">Data to encrypt</param>    
        /// <param name="password">Password</param>    
        /// <param name="salt">Salt</param>    
        /// <returns>Encrypted bytes</returns>    
        public static byte[] EncryptAes(string data, string password, byte[] salt)
        {
            try
            {
                byte[] key = CreateDerivedKey(password, salt);

                ISymmetricKeyAlgorithmProvider aes = WinRTCrypto.SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithm.AesCbcPkcs7);
                ICryptographicKey symetricKey = aes.CreateSymmetricKey(key);
                var bytes = WinRTCrypto.CryptographicEngine.Encrypt(symetricKey, Encoding.UTF8.GetBytes(data));
                return bytes;
            }
            catch (Exception e)
            {
                Console.WriteLine($"SharedCrypto.EncryptAes: {e.Message}");
                return null;
            }
            
        }
        /// <summary>    
        /// Decrypts given bytes using symmetric algorithm AES    
        /// </summary>    
        /// <param name="data">data to decrypt</param>    
        /// <param name="password">Password used for encryption</param>    
        /// <param name="salt">Salt used for encryption</param>    
        /// <returns></returns>    
        public static string DecryptAes(byte[] data, string password, byte[] salt)
        {
            try
            {
                byte[] key = CreateDerivedKey(password, salt);

                ISymmetricKeyAlgorithmProvider aes = WinRTCrypto.SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithm.AesCbcPkcs7);
                ICryptographicKey symetricKey = aes.CreateSymmetricKey(key);
                var bytes = WinRTCrypto.CryptographicEngine.Decrypt(symetricKey, data);
                return Encoding.UTF8.GetString(bytes, 0, bytes.Length);
            }
            catch (Exception e)
            {
                Console.WriteLine($"SharedCrypto.DecryptAes: {e.Message}");
                return null;
            }
            
        }

    }
}

Entitlements.plist配置

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>aps-environment</key>
    <string>production</string>
    <key>keychain-access-groups</key>
    <array>
        <string>$(AppIdentifierPrefix)fi.mycompany.myApp</string>
    </array>
</dict>
</plist>

问题修复

经排查,问题根源为PCLCrypto NuGet包不支持iOS平台。建议替换为.NET 8内置的加密API(如System.Security.Cryptography命名空间下的类),或使用支持iOS平台的现代加密库,以确保Release模式下加密解密逻辑正常执行,避免用户数据丢失。


内容的提问来源于stack exchange,提问作者user30393331

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 06:39:50