How can i set json array into firebase remote config? - firebase-remote-config

I'll try to put a json array into a remote config variable but it seems imposible.
I'll do casting the json into string, but cant find how to without cast.
Any idea?
Tries:
template.parameters["name"] = { defaultValue: { value: {"a":"A", "b": "B"}}} // this works if json (aA, bB) is a string.
template.parameters["name"] = { defaultValue: {"a":"A", "b": "B"}} // crass
template.parameters = { name: {"a":"A", "b": "B"}}. // crash
...
Only I can do successful when:
template.parameters["name"] = { defaultValue: { value: '{"a":"A", "b": "B"}}'} // and it saved as string
Firebase reference: https://firebase.google.com/docs/remote-config/automate-rc#node.js_2

Related

Successful Firestore Write Not Reflected in Data

I am expecting data for a document field to be updated via a setData (merge) write and it does not despite completing successfully. An updateData works, but I don't understand why setData doesn't work in this instance.
I am performing the following writes to Firestore and the following paths:
setData to firstCollection/uniqueDoc
batched setData to secondCollection/uniqueDoc1 (between 1-5 writes in batch)
transaction updateData to thirdCollection/uniqueDoc
No error prints out, all data and syntax appear correct, and #2 plus #3 writes reflect in the data successfully - #1 is the one that does not.
Any guidance would be appreciated.
Here's my code for #1...
func first() {
//oldData is not empty and is the same type as var updatedData
//oldData = [
// "a": ["name":"aName", "dateAdded": dateValue, "imageUrl": "aImageUrl"]
// "b": ["name":"bName", "dateAdded": dateValue, "imageUrl": "bImageUrl"]
// ]
guard let oldData = oldData else { return }
var updatedData: [String: NSDictionary] = [:]
updatedData = oldData
updatedData["a"] = nil
db.collection(firstCollection).document(uniqueDocName).setData(["data": updatedData],
merge: true) { error in
guard error == nil else {
print("Error:" , error)
return
}
self.runOtherFunc()
}
}

Vapor 3: transform array of Future object to an array of Future other objects

I tried to make the most basic example that I could think of for my problem. I have a Course model and a many-to-many table to User that also stores some extra properties (the progress in the example below).
import FluentPostgreSQL
import Vapor
final class Course: Codable, PostgreSQLModel {
var id: Int?
var name: String
var teacherId: User.ID
var teacher: Parent<Course, User> {
return parent(\.teacherId)
}
init(name: String, teacherId: User.ID) {
self.name = name
self.teacherId = teacherId
}
}
struct CourseUser: Pivot, PostgreSQLModel {
typealias Left = Course
typealias Right = User
static var leftIDKey: LeftIDKey = \.courseID
static var rightIDKey: RightIDKey = \.userID
var id: Int?
var courseID: Int
var userID: UUID
var progress: Int
var user: Parent<CourseUser, User> {
return parent(\.userID)
}
}
Now, when I return a Course object, I want the JSON output to be something like this:
{
"id": 1,
"name": "Course 1",
"teacher": {"name": "Mr. Teacher"},
"students": [
{"user": {"name": "Student 1"}, progress: 10},
{"user": {"name": "Student 2"}, progress: 60},
]
}
Instead of what I would normally get, which is this:
{
"id": 1,
"name": "Course 1",
"teacherID": 1,
}
So I created some extra models and a function to translate between them:
struct PublicCourseData: Content {
var id: Int?
let name: String
let teacher: User
let students: [Student]?
}
struct Student: Content {
let user: User
let progress: Int
}
extension Course {
func convertToPublicCourseData(req: Request) throws -> Future<PublicCourseData> {
let teacherQuery = self.teacher.get(on: req)
let studentsQuery = try CourseUser.query(on: req).filter(\.courseID == self.requireID()).all()
return map(to: PublicCourseData.self, teacherQuery, studentsQuery) { (teacher, students) in
return try PublicCourseData(id: self.requireID(),
name: self.name,
teacher: teacher,
students: nil) // <- students is the wrong type!
}
}
}
Now, I am almost there, but I am not able to convert studentsQuery from EventLoopFuture<[CourseUser]> to EventLoopFuture<[Student]>. I tried multiple combinations of map and flatMap, but I can't figure out how to translate an array of Futures to an array of different Futures.
The logic you're looking for will look like this
extension Course {
func convertToPublicCourseData(req: Request) throws -> Future<PublicCourseData> {
return teacher.get(on: req).flatMap { teacher in
return try CourseUser.query(on: req)
.filter(\.courseID == self.requireID())
.all().flatMap { courseUsers in
// here we should query a user for each courseUser
// and only then convert all of them into PublicCourseData
// but it will execute a lot of queries and it's not a good idea
}
}
}
}
I suggest you to use the SwifQL lib instead to build a custom query to get needed data in one request πŸ™‚
You could mix Fluent's queries with SwifQL's in case if you want to get only one course, so you'll get it in 2 requests:
struct Student: Content {
let name: String
let progress: Int
}
extension Course {
func convertToPublicCourseData(req: Request) throws -> Future<PublicCourseData> {
return teacher.get(on: req).flatMap { teacher in
// we could use SwifQL here to query students in one request
return SwifQL.select(\CourseUser.progress, \User.name)
.from(CourseUser.table)
.join(.inner, User.table, on: \CourseUser.userID == \User.id)
.execute(on: req, as: .psql)
.all(decoding: Student.self).map { students in
return try PublicCourseData(id: self.requireID(),
name: self.name,
teacher: teacher,
students: students)
}
}
}
}
If you want to get a list of courses in one request you could use pure SwifQL query.
I simplified desired JSON a little bit
{
"id": 1,
"name": "Course 1",
"teacher": {"name": "Mr. Teacher"},
"students": [
{"name": "Student 1", progress: 10},
{"name": "Student 2", progress: 60},
]
}
first of all let's create a model to be able to decode query result into it
struct CoursePublic: Content {
let id: Int
let name: String
struct Teacher:: Codable {
let name: String
}
let teacher: Teacher
struct Student:: Codable {
let name: String
let progress: Int
}
let students: [Student]
}
Ok now we are ready to build a custom query. Let's build it in some request handler function
func getCourses(_ req: Request) throws -> Future<[CoursePublic]> {
/// create an alias for student
let s = User.as("student")
/// build a PostgreSQL's json object for student
let studentObject = PgJsonObject()
.field(key: "name", value: s~\.name)
.field(key: "progress", value: \CourseUser.progress)
/// Build students subquery
let studentsSubQuery = SwifQL
.select(Fn.coalesce(Fn.jsonb_agg(studentObject),
PgArray(emptyMode: .dollar) => .jsonb))
.from(s.table)
.where(s~\.id == \CourseUser.userID)
/// Finally build the whole query
let query = SwifQLSelectBuilder()
.select(\Course.id, \Course.name)
.select(Fn.to_jsonb(User.table) => "teacher")
.select(|studentsSubQuery| => "students")
.from(User.table)
.join(.inner, User.table, on: \Course.teacherId == \User.id)
.join(.leftOuter, CourseUser.table, on: \CourseUser.teacherId == \User.id)
.build()
/// this way you could print raw query
/// to execute it in postgres manually
/// for debugging purposes (e.g. in Postico app)
print("raw query: " + query.prepare(.psql).plain)
/// executes query with postgres dialect
return query.execute(on: req, as: .psql)
/// requests an array of results (or use .first if you need only one first row)
/// You also could decode query results into the custom struct
.all(decoding: CoursePublic.self)
}
Hope it will help you. There may be some mistakes in the query cause I wrote it without checking πŸ™‚ You can try to print a raw query to copy it and execute in e.g. Postico app in postgres directly to understand what's wrong.

How to create folder and files in google drive using swift

I am using google drive SDK for folder creation, but unable to create. I am able to login and get all files and folder but unable to create it.
I am using swift and used this code
let metaData = GTLRDrive_File()
metaData.name = "xyz"
metaData.mimeType = "application/vnd.google-apps.folder"
let querys = GTLRDriveQuery_FilesCreate.query(withObject: metaData, uploadParameters: nil)
querys.fields = "id"
//service.executeQuery(querys, delegate: self, didFinish: nil)
self.service.executeQuery(querys) { (ticket:GTLRServiceTicket, object:Any?, error:Error?) in
// Put your completion code here
}
But unable to create folder. Can anyone help me out. Thanks in advance.
func chilkatTest() {
var success: Bool = true
// It requires the Chilkat API to have been previously unlocked.
// See Global Unlock Sample for sample code.
// This example uses a previously obtained access token having permission for the
// Google Drive scope.
let gAuth = CkoAuthGoogle()
gAuth.AccessToken = "GOOGLE-DRIVE-ACCESS-TOKEN"
let rest = CkoRest()
// Connect using TLS.
var bAutoReconnect: Bool = true
success = rest.Connect("www.googleapis.com", port: 443, tls: true, autoReconnect: bAutoReconnect)
// Provide the authentication credentials (i.e. the access token)
rest.SetAuthGoogle(gAuth)
// -------------------------------------------------------------------------
// A multipart upload to Google Drive needs a multipart/related Content-Type
rest.AddHeader("Content-Type", value: "multipart/related")
// Specify each part of the request.
// The 1st part is JSON with information about the file.
rest.PartSelector = "1"
rest.AddHeader("Content-Type", value: "application/json; charset=UTF-8")
let json = CkoJsonObject()
json.AppendString("name", value: "testHello.txt")
json.AppendString("description", value: "A simple file that says Hello World.")
json.AppendString("mimeType", value: "text/plain")
// To place the file in a folder, we must add a parents[] array to the JSON
// and list the folder id's. It's possible for a file to be in multiple folders at once
// if it has more than one parent. If no parents are specified, then the file is created
// in the My Drive folder.
// Note: We'll assume we already have the id if the folder. It is the id's that are specified here,
// not the folder names.
var parents: CkoJsonArray? = json.AppendArray("parents")
var folderId: String? = "0B53Q6OSTWYolY2tPU1BnYW02T2c"
parents!.AddStringAt(-1, value: folderId)
parents = nil
rest.SetMultipartBodyString(json.Emit())
// The 2nd part is the file content, which will contain "Hello World!"
rest.PartSelector = "2"
rest.AddHeader("Content-Type", value: "text/plain")
var fileContents: String? = "Hello World!"
rest.SetMultipartBodyString(fileContents)
var jsonResponse: String? = rest.FullRequestMultipart("POST", uriPath: "/upload/drive/v3/files?uploadType=multipart")
if rest.LastMethodSuccess != true {
print("\(rest.LastErrorText)")
return
}
// A successful response will have a status code equal to 200.
if rest.ResponseStatusCode.integerValue != 200 {
print("response status code = \(rest.ResponseStatusCode.integerValue)")
print("response status text = \(rest.ResponseStatusText)")
print("response header: \(rest.ResponseHeader)")
print("response JSON: \(jsonResponse!)")
return
}
// Show the JSON response.
json.Load(jsonResponse)
// Show the full JSON response.
json.EmitCompact = false
print("\(json.Emit())")
// A successful response looks like this:
// {
// "kind": "drive#file",
// "id": "0B53Q6OSTWYoldmJ0Z3ZqT2x5MFk",
// "name": "Untitled",
// "mimeType": "text/plain"
// }
// Get the fileId:
print("fileId: \(json.StringOf("id"))")
}
Link for libraries needed:-
Download libraries
Include CkoAuthGoogle, CkoRest and CkoJsonObject header files in your project.
It is basically due to the scope, I have to give kGTLRAuthScopeDriveFile in scope area
private let scopes = [kGTLRAuthScopeDriveReadonly,kGTLRAuthScopeDriveFile]
and rest same in google
func folder(){
let metadata = GTLRDrive_File()
metadata.name = "eBilling"
metadata.mimeType = "application/vnd.google-apps.folder"
let querys = GTLRDriveQuery_FilesCreate.query(withObject: metadata, uploadParameters: nil)
querys.fields = "id"
self.service.executeQuery(querys, completionHandler: {(ticket:GTLRServiceTicket, object:Any?, error:Error?) in
if error == nil {
self.listFiles()
}
else {
print("An error occurred: \(error)")
}
})
}
Swift 5
If you are looking to just create a folder without uploading a file with it,
I was able to create a drive folder using Google's REST endpoint like this.
This function takes the auth token and a filename and parameters to create a URLRequest that can then be sent off within a URLSession.
func createFolderRequest(authToken: String, folderName: String) -> URLRequest {
let headers = [
"Content-Type": "multipart/related; boundary=123456789",
"Authorization": "Bearer " + authToken
]
let body =
"""
--123456789
Content-Type: application/json
{
"name": "\(folderName)",
"mimeType": "application/vnd.google-apps.folder"
}
--123456789--
"""
var request = URLRequest(url: URL(string: "https://www.googleapis.com/upload/drive/v3/files")!)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = body.data(using: .utf8)
request.addValue(String(body.lengthOfBytes(using: .utf8)), forHTTPHeaderField: "Content-Length")
return request
}
I referenced the google docs for multipart file uploads here

Deleting Realm Object entry if its deleted in API Response

First response in this I get two user i.e abc#gmail.com & xyz#gmail.com
[{
"email": "abc#gmail.com",
"type": "primary_email",
"linked_to": {
"_id": "DAS44564dasdDASd",
"image": null,
"company": null,
"designation": null,
"name": null
},
"active_platforms": [
"asd",
"qwe"
]
},
{
"email": "xyz#gmail.com",
"type": "primary_email",
"linked_to": {
"_id": "DAS44564dasdDASd",
"image": null,
"company": null,
"designation": null,
"name": null
},
"active_platforms": [
"asd",
"qwe"
]
}]
Now if abc#gmail.com is deleted if I do API call again then still I get abc#gmail.com in my object as it is not deleted from my realm. So how to handle such situation ?
// write request result to realm database
let entries = json["data"]
realm.beginWrite()
for (_, subJson) : (String, JSON) in entries {
let entry: AppUsers = Mapper<AppUsers>().map(JSONObject: subJson.dictionaryObject!)!
realm.add(entry, update: true)
}
do {
try realm.commitWrite()
} catch {
}
Update your logic as below. This is one of method to this.
Add one extra bool field to AppUsers model say 'active'. Update your code as below
// write request result to realm database
let entries = json["data"]
realm.beginWrite()
//Fetch all realm AppUsers objects
let allAppUsers = //TODO fetch all AppUsers objects here
for user in allAppUsers {
user.active = false
}
for (_, subJson) : (String, JSON) in entries {
let entry: AppUsers = Mapper<AppUsers>().map(JSONObject: subJson.dictionaryObject!)!
entry.active = true
realm.add(entry, update: true)
}
for user in allAppUsers {
if !user.active {
realm.delete(user)
}
}
do {
try realm.commitWrite()
} catch {
}
This sounds like an issue where the data in your Realm database has become stale as the contents no longer match what is on the server.
The Realm API realm.add(_, update: true) will update any objects that were passed to it, but simply not passing an object does not imply it should be deleted (More that you just didn't want to update it).
There's no way for Realm to automatically know if an object needs to be deleted. You'll need to be in charge of that logic yourself.
Since your mechanism for checking if an object is deleted is via its email address, you could capture the email addresses of each object you've updated, and then delete any other objects whose email address is not in there.
// write request result to realm database
realm.beginWrite()
let entries = json["data"]
var updatedEmails = [String]()
for (_, subJson) : (String, JSON) in entries {
let entry: AppUsers = Mapper<AppUsers>().map(JSONObject: subJson.dictionaryObject!)!
// Save the email we just processed
updatedEmails.append(entry.email)
realm.add(entry, update: true)
}
// Delete all objects not in the updated emails list
let realmEntries = realm.objects(AppUsers.self)
for entry in realmEntries {
if !updatedEmails.contains(entry.email) {
realm.delete(entry)
}
}
do {
try realm.commitWrite()
} catch {
}
If your REST API brings down all of your objects in their complete form each time, a much quicker solution would also be to simply empty the Realm file each time and just add the objects as new objects each time too.

How to parse a optional JSON object using JSONJoy?

https://github.com/daltoniam/JSONJoy-Swift
For example :
JSON1 = {
"message": "Sorry! Password does not match.",
"code": "4"
}
JOSN2 = {
"data": {
"id": 21
},
"message": "Signup Successful.",
"code": "1"
},
Here json key β€œdata” is optional. Then how I can handle both response using the same model object??
JSONJoy natively sets not found elements to nil, you just have to declare them optional and then check for nil before using them.
From the docs
This also has automatic optional validation like most Swift JSON libraries.
//some randomly incorrect key. This will work fine and the property
will just be nil.
firstName = decoder[5]["wrongKey"]["MoreWrong"].string
//firstName is nil, but no crashing!
Here is my example that my be illustrative. I have a complex object set where my top level object (UserPrefs) has arrays of secondary objects (SmartNetworkNotification and SmartNotificationTime).
Note that notifications and times are both declared as optional. What I do is check for nil after attempting to parse the secondary object arrays. Without the nil check the attempt to iterate on the parsed list fails since its nil. With the nil check it just moves past it if its empty.
This works for me but isn't deeply tested yet. YMMV! Curious how others are handling it.
struct UserPrefs: JSONJoy {
var notifications: [SmartNetworkNotification]?
var times: [SmartNotificationTime]?
init(_ decoder: JSONDecoder) throws {
// Extract notifications
let notificationsJson = try decoder["notifications"].array
if(notificationsJson != nil){
var collectNotifications = [SmartNetworkNotification]()
for notificationDecoder in notificationsJson! {
do {
try collectNotifications.append(SmartNetworkNotification(notificationDecoder))
} catch let error {
print("Error.. on notifications decoder")
print(error)
}
}
notifications = collectNotifications
}
// Extract time of day settings
let timesJson = try decoder["times"].array
if(timesJson != nil){
var collectTimes = [SmartNotificationTime]()
for timesDecoder in timesJson! {
do {
try collectTimes.append(SmartNotificationTime(timesDecoder))
} catch let error {
print("Error.. on timesJson decoder")
print(error)
}
}
times = collectTimes
}
}

Resources