iosswiftkeyboardios-app-extensionios-app-group

Passing data and variables between targets


Overview
I'm trying tot understand app groups so that I can pass data and variables from my main app to my extension and viceversa.

What I found
It's very hard for me to understand a few concepts because all I could find were other SO questions that only showed how to store and retrieve data.
I even watched this tutorial (and read some more on medium) but I'm still confused.

What I'm doing
First of all I enabled app groups both in my container app and my app extension and i set the app group name to group.grop.

I have 3 files:

  1. Shared.swift
  2. KeyboardViewController
  3. ViewController

In my Shared.swift file I put the following code:

let sharedUserdefaults = UserDefaults(suiteName: SharedDefault.suitName)
struct SharedDefault {
    static let suitName = "group.grop"
    struct Keys{
        static let letters = "letters"
    }
}

Here the struct contains my suitName and the key.

Now, my keyboard extension looks like this:

enter image description here in my keyboardViewController, over the view did load, I put the following code:

    var lept: [Int] = sharedUserdefaults?.array(forKey: SharedDefault.Keys.letters) ?? []
    
    @IBOutlet weak var alabel: UILabel!
    @IBOutlet weak var blabel: UILabel!
    @IBOutlet weak var clabel: UILabel!
    
    @IBOutlet weak var abtn: UIButton!
    @IBOutlet weak var bbtn: UIButton!
    @IBOutlet weak var cbtn: UIButton!

inside of my viewDidLoad:

let nib = UINib(nibName: "keygrop", bundle: nil)
              let objects = nib.instantiate(withOwner: self, options: nil)
              view = objects[0] as? UIView

        
        sharedUserdefaults?.set(lept, forKey: SharedDefault.Keys.letters)
        guard let sharedWords = sharedUserdefaults?.array(forKey: SharedDefault.Keys.letters) else {return}
        print ("My words: \(sharedWords)")

below my viewDidLoad:

//I have one of these for every button.
 @IBAction func aaction(_ sender: Any) {
        
       lept.append(1)
        sharedUserdefaults?.set(lept, forKey: SharedDefault.Keys.letters)
       print(lept)
    }

In my ViewController I have another button named dbutton, two labels and this code inside of my view controller:

 guard let sharedWordss = sharedUserdefaults?.array(forKey: SharedDefault.Keys.letters) else {return}
        
        print ("My words: \(sharedWordss)")

Now what I want to happen is:

  1. The user presses a button from the keyboard.
  2. A number gets stored inside of an array.
  3. The user opens the app and if he presses the dbutton another value gets stored inside the array.
  4. A label displays what's inside the array and another one displays the number of the elements inside of it.

But using this method I can't access the lept variable from my ViewController. So I can't add anything to the array and I can't call lept.count, for example.

Question
How do I access (get the value/ add items/ edit...) a variable from a target in another target?
If you have anything I could use to take a better look into the subject, please send me the links!


Solution

    1. Modify your lept to be a getter, and always get the latest value from the sharedDefaults:
    var lept: [Int] {
        return sharedUserdefaults?.array(forKey: SharedDefault.Keys.letters) as? [Int] ?? [Int]()
    }
    
    1. Inside the viewDidLoad for your keyboard extension, remove the code which saves the lept array to the sharedDefaults:

      sharedUserdefaults?.set(lept, forKey: SharedDefault.Keys.letters)

    2. Create a function which will do the same thing for each button (below I will write some optimizations regarding all this code):

    func appendValue() {  
        var currentLept = self.lept
        currentLept.append(1)
        sharedUserdefaults?.set(currentLept, forKey: SharedDefault.Keys.letters)
       //print(currentLept) - you can print for debug purposes
    }
    
    1. Call the function each time you press a button:
    @IBAction func aaction(_ sender: Any) {
            
       appendValue()
    }
    

    This is the basic logic for sending any data from the main target to an extension.

    Optimization hints

    You should consider using a UICollectionView which will hold all your labels and buttons, and each cell would act as a button or label in your scenario. This way, you will create a custom UICollectionViewCell and implement everything just once, instead of handling each button separately.

    As a rule of thumb, if you have anything which is repeating at least twice, it is either a tableView, if you need just vertical scrolling, or it should be a collectionView if you need horizontal scrolling, or cells which occupy only a portion of the screen's width.