Interacting with Firebase in SwiftUI with Combine
- Tram Ho
So true to the title, today I will show you how we can read and process data in firebase with a combine guy. Then let’s get started.
Configure FireBase for App
Then Firebase is a real-time database service provided by Google and operating on the cloud platform. It helps developers to quickly develop mobile applications by simplifying database operations. And in today’s post, I will use the realtime database guy, it is: the guy that stores and synchronizes real-time user data. Applications that support this feature can store and retrieve data from the server very quickly. The data is stored in a database system that supports NoSQL and is located on the cloud server platform. Data is written and read with the lowest time in milliseconds.
And to configure, you must first go to /settings/general/ of your firebase. To download GoogleService-Info.plist and then put it in our app. Then it’s a bit different from the UIKit side, on the SwiftUI side we will call the configure function in the init of the total view like this.
So we’re done with the firebase configuration setup. Next, we will start creating a simple application to demo today’s lesson.
Create a service for the firebase guy
Get all the data of a reference
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <span class="token keyword">func</span> getAllDataOfChildNote<span class="token operator"><</span>T<span class="token punctuation">:</span> <span class="token builtin">Decodable</span><span class="token punctuation">,</span> E<span class="token punctuation">:</span> <span class="token builtin">Error</span><span class="token operator">></span><span class="token punctuation">(</span>child<span class="token punctuation">:</span> <span class="token builtin">String</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">Future</span><span class="token operator"><</span><span class="token punctuation">[</span>T<span class="token punctuation">]</span><span class="token punctuation">,</span> E<span class="token operator">></span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token function">Future</span><span class="token punctuation">(</span><span class="token punctuation">{</span> promise <span class="token keyword">in</span> <span class="token builtin">Database</span><span class="token punctuation">.</span><span class="token function">database</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">reference</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">child</span><span class="token punctuation">(</span>child<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">observeSingleEvent</span><span class="token punctuation">(</span>of<span class="token punctuation">:</span> <span class="token punctuation">.</span>value<span class="token punctuation">)</span> <span class="token punctuation">{</span> snapshot <span class="token keyword">in</span> <span class="token keyword">var</span> data<span class="token punctuation">:</span> <span class="token punctuation">[</span>T<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token keyword">for</span> note <span class="token keyword">in</span> snapshot<span class="token punctuation">.</span>children <span class="token punctuation">{</span> <span class="token keyword">guard</span> <span class="token keyword">let</span> snap <span class="token operator">=</span> note <span class="token keyword">as</span><span class="token operator">?</span> <span class="token builtin">DataSnapshot</span><span class="token punctuation">,</span> <span class="token keyword">let</span> value <span class="token operator">=</span> snap<span class="token punctuation">.</span>value <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token punctuation">}</span> <span class="token keyword">do</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> model <span class="token operator">=</span> <span class="token keyword">try</span> <span class="token function">FirebaseDecoder</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">decode</span><span class="token punctuation">(</span>T<span class="token punctuation">.</span><span class="token keyword">self</span><span class="token punctuation">,</span> from<span class="token punctuation">:</span> value<span class="token punctuation">)</span> data<span class="token punctuation">.</span><span class="token function">append</span><span class="token punctuation">(</span>model<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token keyword">let</span> error <span class="token punctuation">{</span> <span class="token function">print</span><span class="token punctuation">(</span><span class="token string">"ERROR: <span class="token interpolation"><span class="token delimiter variable">\(</span>error<span class="token delimiter variable">)</span></span>"</span><span class="token punctuation">)</span> <span class="token function">promise</span><span class="token punctuation">(</span><span class="token punctuation">.</span><span class="token function">failure</span><span class="token punctuation">(</span>error <span class="token keyword">as</span><span class="token operator">!</span> E<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token function">promise</span><span class="token punctuation">(</span><span class="token punctuation">.</span><span class="token function">success</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> |
Then, at a glance, we can see that I’m using Future, this is a special puller that will emit a single value, then terminate or fail. It will execute a Promise. It’s a closure with type Result, so there will be 1 of 2 cases:
Success : emitting Output
Failure : emitting Error
So when it only returns data after all the data has been retrieved. As for decoding data in firebase, I will use https://github.com/alickbass/CodableFirebase it will help you decode and encode quickly and easily.
Handling the data of a reference
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | <span class="token keyword">func</span> addDateChildNote<span class="token operator"><</span>E<span class="token punctuation">:</span> <span class="token builtin">Error</span><span class="token operator">></span><span class="token punctuation">(</span>note<span class="token punctuation">:</span> <span class="token builtin">Note</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">Future</span><span class="token operator"><</span><span class="token builtin">Void</span><span class="token punctuation">,</span> E<span class="token operator">></span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token builtin">Future</span> <span class="token punctuation">{</span> promise <span class="token keyword">in</span> <span class="token keyword">let</span> docData <span class="token operator">=</span> <span class="token keyword">try</span><span class="token operator">!</span> <span class="token function">FirebaseEncoder</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">encode</span><span class="token punctuation">(</span>note<span class="token punctuation">)</span> <span class="token builtin">Database</span><span class="token punctuation">.</span><span class="token function">database</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">reference</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">child</span><span class="token punctuation">(</span><span class="token string">"note"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">childByAutoId</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">setValue</span><span class="token punctuation">(</span>docData<span class="token punctuation">)</span> <span class="token punctuation">{</span> error<span class="token punctuation">,</span> <span class="token number">_</span> <span class="token keyword">in</span> <span class="token keyword">if</span> <span class="token keyword">let</span> error <span class="token operator">=</span> error <span class="token punctuation">{</span> <span class="token function">promise</span><span class="token punctuation">(</span><span class="token punctuation">.</span><span class="token function">failure</span><span class="token punctuation">(</span>error <span class="token keyword">as</span><span class="token operator">!</span> E<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token function">promise</span><span class="token punctuation">(</span><span class="token punctuation">.</span><span class="token function">success</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">func</span> removeDataChildNote<span class="token operator"><</span>E<span class="token punctuation">:</span> <span class="token builtin">Error</span><span class="token operator">></span><span class="token punctuation">(</span>element<span class="token punctuation">:</span> <span class="token builtin">Note</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">Future</span><span class="token operator"><</span><span class="token builtin">Void</span><span class="token punctuation">,</span> E<span class="token operator">></span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token builtin">Future</span> <span class="token punctuation">(</span><span class="token punctuation">{</span> promise <span class="token keyword">in</span> <span class="token builtin">Database</span><span class="token punctuation">.</span><span class="token function">database</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">reference</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">child</span><span class="token punctuation">(</span><span class="token string">"note"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">observeSingleEvent</span><span class="token punctuation">(</span>of<span class="token punctuation">:</span> <span class="token punctuation">.</span>value<span class="token punctuation">)</span> <span class="token punctuation">{</span> snapshot <span class="token keyword">in</span> <span class="token keyword">for</span> note <span class="token keyword">in</span> snapshot<span class="token punctuation">.</span>children <span class="token punctuation">{</span> <span class="token keyword">guard</span> <span class="token keyword">let</span> snap <span class="token operator">=</span> note <span class="token keyword">as</span><span class="token operator">?</span> <span class="token builtin">DataSnapshot</span><span class="token punctuation">,</span> <span class="token keyword">let</span> value <span class="token operator">=</span> snap<span class="token punctuation">.</span>value <span class="token keyword">as</span><span class="token operator">?</span> <span class="token punctuation">[</span><span class="token builtin">String</span><span class="token punctuation">:</span> <span class="token builtin">AnyObject</span><span class="token punctuation">]</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token keyword">let</span> content <span class="token operator">=</span> value<span class="token punctuation">[</span><span class="token string">"content"</span><span class="token punctuation">]</span> <span class="token keyword">as</span><span class="token operator">?</span> <span class="token builtin">String</span><span class="token punctuation">,</span> element<span class="token punctuation">.</span>content <span class="token operator">==</span> content <span class="token punctuation">{</span> <span class="token builtin">Database</span><span class="token punctuation">.</span><span class="token function">database</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">reference</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">child</span><span class="token punctuation">(</span><span class="token string">"note"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">child</span><span class="token punctuation">(</span>snap<span class="token punctuation">.</span>key<span class="token punctuation">)</span><span class="token punctuation">.</span>removeValue <span class="token punctuation">{</span> error<span class="token punctuation">,</span> <span class="token number">_</span> <span class="token keyword">in</span> <span class="token keyword">if</span> <span class="token keyword">let</span> error <span class="token operator">=</span> error <span class="token punctuation">{</span> <span class="token function">promise</span><span class="token punctuation">(</span><span class="token punctuation">.</span><span class="token function">failure</span><span class="token punctuation">(</span>error <span class="token keyword">as</span><span class="token operator">!</span> E<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token function">promise</span><span class="token punctuation">(</span><span class="token punctuation">.</span><span class="token function">success</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">break</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> |
That’s as I said above, when the jobs are done, it will send us a data to report that the data interaction has been successful. Since here is the post method, we do not need it to return any data, so here Future<Void, E>.
MVVM with Combine
ViewModel side
Here because it is an MVVM model, it will have two guys, input and output
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <span class="token keyword">struct</span> <span class="token builtin">Input</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> loadTrigger<span class="token punctuation">:</span> <span class="token builtin">AnyPublisher</span><span class="token operator"><</span><span class="token builtin">Void</span><span class="token punctuation">,</span> <span class="token builtin">Never</span><span class="token operator">></span> <span class="token keyword">let</span> contentNoteTrigger<span class="token punctuation">:</span> <span class="token builtin">AnyPublisher</span><span class="token operator"><</span><span class="token builtin">String</span><span class="token punctuation">,</span> <span class="token builtin">Never</span><span class="token operator">></span> <span class="token keyword">let</span> addNoteTrigger<span class="token punctuation">:</span> <span class="token builtin">AnyPublisher</span><span class="token operator"><</span><span class="token builtin">Void</span><span class="token punctuation">,</span> <span class="token builtin">Never</span><span class="token operator">></span> <span class="token keyword">let</span> removeNoteTrigger<span class="token punctuation">:</span> <span class="token builtin">AnyPublisher</span><span class="token operator"><</span><span class="token builtin">Note</span><span class="token punctuation">,</span> <span class="token builtin">Never</span><span class="token operator">></span> <span class="token punctuation">}</span> <span class="token keyword">struct</span> <span class="token builtin">Output</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> notes<span class="token punctuation">:</span> <span class="token builtin">AnyPublisher</span><span class="token operator"><</span><span class="token punctuation">[</span><span class="token builtin">Note</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token builtin">Never</span><span class="token operator">></span> <span class="token keyword">let</span> addValueSuccess<span class="token punctuation">:</span> <span class="token builtin">AnyPublisher</span><span class="token operator"><</span><span class="token builtin">Void</span><span class="token punctuation">,</span> <span class="token builtin">Never</span><span class="token operator">></span> <span class="token keyword">let</span> removeValueSuccess<span class="token punctuation">:</span> <span class="token builtin">AnyPublisher</span><span class="token operator"><</span><span class="token builtin">Void</span><span class="token punctuation">,</span> <span class="token builtin">Never</span><span class="token operator">></span> <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function">bind</span><span class="token punctuation">(</span><span class="token number">_</span> input<span class="token punctuation">:</span> <span class="token builtin">Input</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">Output</span> <span class="token punctuation">{</span> |
On it I am using AnyPublisher, it is Type-erased publisher With AnyPublisher, it is not possible to call the send() function. This class has wrapped and hidden many methods & properties of Publisher. And to convert the user input into the desired output, we will use the following:
1 2 3 4 5 6 | input<span class="token punctuation">.</span>loadTrigger <span class="token punctuation">.</span>flatMap <span class="token punctuation">{</span> usecase<span class="token punctuation">.</span><span class="token function">getAllDataList</span><span class="token punctuation">(</span>child<span class="token punctuation">:</span> <span class="token string">"note"</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">.</span><span class="token function">eraseToAnyPublisher</span><span class="token punctuation">(</span><span class="token punctuation">)</span> |
flatMap, it will transform one publisher into another puller. But on the other side of rxSwift, when transformed, the data type it returns becomes a very lumpy lump. So then we call a function eraseToAnyPublisher(). Then it will turn our existing data into the value type for the new object AnyPublisher. And that data will be returned to the View for processing.
View side
1 2 3 4 5 6 | @<span class="token builtin">State</span> <span class="token keyword">private</span> <span class="token keyword">var</span> list<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token builtin">Note</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token punctuation">.</span><span class="token function">onReceive</span><span class="token punctuation">(</span>output<span class="token punctuation">.</span>notes<span class="token punctuation">)</span> <span class="token punctuation">{</span> notes <span class="token keyword">in</span> list <span class="token operator">=</span> notes <span class="token punctuation">}</span> |
It will then subscribe to the stream’s data returned data notifications. Then when the output side has return data, we will use it to capture that returned data, then when it’s done, it just needs to be assigned to the list. Because our list variable is State, when its data changes, it will automatically update the views that are using that state variable.
Then come here, you have got yourself all the tools to network with all kinds of api, not just the firebase guy anymore.
Here is the project link for your reference : https://github.com/thanduchuy/SwiftUI-FirebaseAppList