Cache Implementation in Cross-Platform Swift Apps
Tags: android, swift, ios, scade
Published: 2023-04-21 14:20:55

---
title: Cache Implementation in Cross-Platform Swift Apps
published: true
description: 
tags: android, swift, ios, scade
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b6xdvyo0h2coj5o3a0iz.png
# Use a ratio of 100:42 for best results.
# published_at: 2023-04-21 14:10 +0000
---



## 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](https://www.scade.io/download/) the SCADE IDE and [install](https://docs.scade.io/docs/installation) 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](https://github.com/6vedant/ScadeCacheImplmentation)

## 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.


![Design page for Cache SCADE Example](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e78hjyu0ivuqeuqwilf6.png) 


## 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.


```swift
    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. 



```swift
    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.


```swift
  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.


```swift
 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](https://github.com/scade-platform/swift-android/tree/android/24) 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
// 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.


```swift
#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`.


```swift
  #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.


```swift
    #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


```swift
    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


```swift
    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.


```swift
  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.


```swift
    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](https://docs.scade.io/docs/build-file) 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



![iOS Cache SCADE](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/41dmzztwmf067p143h88.gif)


### Android



![Android Cache SCADE](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a1cn03v2pjsioikze35x.gif)


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😊.