Introduction
Have you ever used a mobile app that remembered your login information or preferences, even after you closed the app? This is thanks to the magic of SharedPreferences
in Android and UserDefaults
in iOS.
👀 In this article, we'll explore how these two features work to store and retrieve key-value data in a cross-platform Swift app. 🔍 We'll start with an overview of what SharedPreferences and UserDefaults are and how they differ from each other. 💡 Then, we'll dive into some examples of how to use them in your Android and iOS apps and show you how to make your app's data persistent even when the user closes the app.
Whether you're a seasoned mobile developer or just getting started with cross-platform development, understanding how to use SharedPreferences
and UserDefaults
is an essential skill. So, let's get started and explore how these features can make your app more user-friendly and efficient! 🙌
SharedPreference & UserDefaults
First, let's talk about SharedPreferences, which is a lightweight and efficient way to store key-value pairs in Android. To use SharedPreferences in a cross-platform Swift app, you can use a library such as SwiftyUserDefaults or UserDefaultsWrapper. These libraries provide a convenient and type-safe way to access SharedPreferences in Swift, using syntax similar to that of UserDefaults in iOS.
Speaking of UserDefaults, this is a built-in mechanism in iOS that allows apps to store and retrieve user preferences and other small amounts of data. To use UserDefaults in a cross-platform Swift app, you can create a wrapper class that abstracts away the platform-specific details and provides a unified API for accessing user defaults.
Prerequisite
If you haven’t installed the SCADE IDE, download the SCADE IDE and install it on your macOS system. The only prerequisite for SCADE is the Swift language (at least basics). Also, please ensure the Android emulator or physical device is running if you want to run SCADE apps on an Android device.
We will try to implement saving the key-value pairs in Cache for both Android & iOS specific environments. One of the common uses of persistence is saving the login result after successful login. We will build a simple login screen and try to save the result if the authentication is successful.
Source Code
Design a SCADE Page
Let’s quickly design a screen with input fields User’s name & email. There will be a button to save the key-value pairs. Similarly, there will be other two labels and a button to fetch the key-value pairs stored in Cache.
Code for iOS
For the iOS platform, we will use UserDefaults in the Foundation
package to store key-value pairs. UserDefaults is a built-in tool in iOS that provides a simple way to store small amounts of data persistently between app launches. It is commonly used to store user preferences, settings, or small pieces of data.
You can access the UserDefaults
object anywhere in your SCADE IDE. To get started, you should create an instance of UserDefaults.
Let’s save the current name and email string text to some variables.
var currEmailText = ""
var currNameText = ""
self.email_tb.onTextChange.append(
SCDWidgetsTextChangeEventHandler { ev in
currEmailText = ev!.newValue
})
self.name_tb.onTextChange.append(
SCDWidgetsTextChangeEventHandler { ev in
currNameText = ev!.newValue
})
Then, attach the onClick
listener to the Save button and call the saveTocache()
method which saves the key-value pairs to persistence.
self.save_btn.onClick { _ in
print("Email: \(currEmailText) Name: \(currNameText)")
if currEmailText.isEmpty || currEmailText.isEmpty {
print("Name or Email is empty")
return
}
self.saveToCache(email: currEmailText, name: currNameText)
}
As per the code snippet, it uses UserDefaults
to save the persistence. Here, the keys are “email” and “name” which stores the values.
func saveToCache(email: String, name: String) {
#if os(iOS)
UserDefaults.standard.set(email, forKey: "email")
UserDefaults.standard.set(name, forKey: "name")
#endif
}
Now let’s fetch the Key-Value pairs
Now we will fetch the values for the keys stored in the Cache. Let’s write generic methods to get the values for the keys as Integer, String, and boolean values. It will use the same UserDefaults.standard.*
property to get the values.
func retrieveIntegerFromCache(key: String) -> Int {
#if os(iOS)
return UserDefaults.standard.integer(forKey: key) ?? 0
#endif
return 0
}
func retrieveStringFromCache(key: String) -> String {
#if os(iOS)
return UserDefaults.standard.string(forKey: key) ?? ""
#endif
return ""
}
func retrieveBoolFromCache(key: String) -> Bool {
#if os(iOS)
return UserDefaults.standard.bool(forKey: key) ?? false
#endif
return false
}
Code for Android
We will now add the Android code in Swift to implement SharedPreferences
. You can refer here for details about how the Android SDK SharedPreferences classes are mapped to the Swift equivalent.
Now let’s add the Android Swift library to the Package.swift
.
// swift-tools-version:5.3
import Foundation
import PackageDescription
let SCADE_SDK = ProcessInfo.processInfo.environment["SCADE_SDK"] ?? ""
let package = Package(
name: "ScadeProject",
platforms: [
.macOS(.v10_14)
],
products: [
.library(
name: "ScadeProject",
type: .static,
targets: [
"ScadeProject"
]
)
],
dependencies: [
.package(
name: "ScadeExtensions", url: "https://github.com/scade-platform/ScadeExtensions",
.branch("main")),
.package(
name: "Android", url: "https://github.com/scade-platform/swift-android.git",
.branch("android/24")),
],
targets: [
.target(
name: "ScadeProject",
dependencies: [
.product(name: "Android", package: "Android", condition: .when(platforms: [.android])),
.product(name: "AndroidOS", package: "Android", condition: .when(platforms: [.android])),
.product(name: "AndroidApp", package: "Android", condition: .when(platforms: [.android])),
.product(
name: "AndroidContent", package: "Android", condition: .when(platforms: [.android])),
],
exclude: ["main.page"],
swiftSettings: [
.unsafeFlags(["-F", SCADE_SDK], .when(platforms: [.macOS, .iOS])),
.unsafeFlags(["-I", "\(SCADE_SDK)/include"], .when(platforms: [.android])),
]
)
]
)
As the next step, import the Android packages and use the SharedPreferences methods to save/retrieve the key-value pairs.
#if os(Android)
import Android
import AndroidContent
import AndroidApp
import AndroidOS
import Foundation
import Java
#endif
We will now use the SharedPreferencesEditor
which will be used to update/fetch the key-value pairs from the SharedPreferences
.
#if os(Android)
private var currentActivity: Activity? { Application.currentActivity }
var delegate: SharedPreferences?
var delegateEditor: AndroidContent.SharedPreferencesEditor?
#endif
Then, initialize the variables delegate
and delegateEditor
using the activity instance. Here, we will use the mode as 0(MODE_PRIVATE
) to allow both read and write operations privately by application with the keyword-defined “SCADE” instance.
#if os(Android)
delegate = Application.currentActivity?.getSharedPreferences(name: "SCADE", mode: 0)
delegateEditor = delegate?.edit()
#endif
Let’s write separate methods for saving and fetching the values using the delegateEditor
instance.
Saving the Key-Value pairs
public func putInt(value: Int32, key: String) {
let val32:Int32 = value
delegateEditor!.putInt(key: key, value: val32)
delegateEditor!.commit()
}
public func putBoolean(value: Bool, key: String) {
delegateEditor!.putBoolean(key: key, value: value)
delegateEditor!.commit()
}
public func putString(value: String, key: String) {
delegateEditor!.putString(key: key, value: value)
delegateEditor!.commit()
}
public func putFloat(value: Float, key: String) {
delegateEditor!.putFloat(key: key, value: value)
delegateEditor!.commit()
}
Fetching the Key-Value pairs
public func getInt(key: String) -> Int32 {
let intValue:Int32 = (delegate?.getInt(key: key, defValue: 0)) ?? 0
return intValue
}
public func getString(key: String) -> String {
let strValue:String = delegate?.getString(key: key, defValue: "DEFAULT_VALUE_STRING") ?? ""
return strValue
}
public func getBoolean(key: String) -> Bool {
let boolValue:Bool = delegate?.getBoolean(key: key, defValue: false) ?? false
return boolValue
}
public func getFloat(key: String) -> Float {
let floatValue:Float = delegate?.getFloat(key: key, defValue: 0.0) ?? 0.0
return floatValue
}
public func containsKey(key: String) -> Bool {
return delegate!.contains(key: key)
}
Now we will use the above methods wherever required to save and fetch the values for the keys: “email” & “name”. Hence the below method is used to update the values.
func retrieveStringFromCache(key: String) -> String {
#if os(iOS)
return UserDefaults.standard.string(forKey: key) ?? ""
#endif
#if os(Android)
return self.getString(key: key)
#endif
return ""
}
After fetching the values stored in the application’s cache, update the text for name & email labels.
self.fetch_btn.onClick { _ in
print(
"retrieve from cache: \(self.retrieveStringFromCache(key: "email")) \(self.retrieveStringFromCache(key: "name"))"
)
self.email_fetch.text = "Email: \(self.retrieveStringFromCache(key: "email"))"
self.name_fetch.text = "Name: \(self.retrieveStringFromCache(key: "name"))"
}
Run the App on iOS/Android
In order to run the app on iOS/Android devices, please ensure the physical devices or simulator/emulator are up and running. SCADE apps can be run on SCADE emulators, iOS & Android devices as well as Apple and Android emulators.
You can also build the app for Android (APK/AAB) or iOS (IPA) to publish them to the respective app stores. You need to click on the App Name button and choose the device target accordingly.
iOS
Android
Voila 🎉! It was really interesting to create an iOS or Android app using the SCADE IDE by implementing Cache. It is really cool. We encourage you to implement SharedPreferences
with SCADE😊.
Top comments (0)