Async execution in vapor. How to control the cycle for in - swift

func loadBy(_ req: Request) throws -> Future<[News]> {
let parentId = try req.parameters.next(String.self)
let parent = try self.findParentBy(req, parentId: parentId)
var childNews: [News] = []
let qq = parent.map(to: [News].self) { parentNews in
for chilId in parentNews!.childIds {
let ch = News.query(on: req).filter(\News.newsId == chilId).first().map(to: News?.self) { child in
if let child = child {
childNews.append(child)
}
return child
}
}
return childNews
}
return qq.flatMap(to: [News].self) { childNews in
return childNews.map { news in
Future.map(on: req) { news }
}.flatten(on: req)
}
}
returned before the values will be obtained
I would like to wait until the desired values are obtained and then return them

Thanks. It's works with
func loadBy(_ req: Request) throws -> Future<[News]> {
let parentId = try req.parameters.next(String.self)
let parent = try self.findParentBy(req, parentId: parentId)
var childNews: [News] = []
return parent.flatMap(to: [News].self) { parentNews in
return parentNews!.childIds.map { (key) in
return News.query(on: req).filter(\News.newsId == key).first().map(to: Void.self) { child in
if let child = child {
childNews.append(child)
}
}
}.flatten(on: req).map {
return childNews
}
}
}
func loadBy(_ req: Request) throws -> Future<[News]> {
let parentId = try req.parameters.next(String.self)
let parent = try self.findParentBy(req, parentId: parentId)
return parent.flatMap(to: [News].self) { parentNews in
return News.query(on: req).filter(\News.newsId ~~ parentNews!.childIds).all()
}
}

Related

singleton property set with async\await func is nil after called later

a property from a singleton is nil when checkin is value.
calling this function from viewDidLoad like this
Task {
do {
try await CXOneChat.shared.connect(environment: .NA1, brandId: 1111, channelId: "keyID")
self.checkForConfig()
} catch {
print(error.localizedDescription)
}
}
this connect function checks for several issues and load from network the config
public func connect(environment: Environment, brandId: Int, channelId: String) async throws {
self.environment = environment
self.brandId = brandId
self.channelId = channelId
try connectToSocket()
channelConfig = try await loadChannelConfiguration()
generateDestinationId()
generateVisitor()
if customer == nil {
customer = Customer(senderId: UUID().uuidString, displayName: "")
try await authorizeCustomer()
} else if authorizationCode.isEmpty{
try await reconnectCustomer()
} else {
try await authorizeCustomer()
}
}
this work ok the self.checkForConfig does some stuff with config in singleton object.
after tapping a button and go for another ViewController. call this func
func loadThread() {
do {
if CXOneChat.shared.getChannelConfiguration()?.settings.hasMultipleThreadsPerEndUser ?? false {
try CXOneChat.shared.loadThreads()
} else {
try CXOneChat.shared.loadThread()
}
} catch {
print(error)
}
}
depending on the config call one or another config value load one or the function. in this case the getChannelConfiguration() is nil and call to loadThread().
func loadThread(threadId: UUID? = nil) throws {
guard let config = channelConfig else {
throw CXOneChatError.missingChannelConfig
}
let eventType = config.isLiveChat ? EventType.recoverLivechat : EventType.recoverThread
guard let brandId = brandId else { throw CXOneChatError.invalidBrandId }
guard let channelId = channelId else { throw CXOneChatError.invalidChannelId }
guard let id = getIdentity(with: false) else { throw CXOneChatError.invalidCustomerId }
let retrieveThread = EventFactory.shared.recoverLivechatThreadEvent(brandId: brandId, channelId: channelId, customer: id, eventType: eventType, threadId: threadId)
guard let data = getDataFrom(retrieveThread) else { throw CXOneChatError.invalidData }
let string = getStringFromData(data)
socketService.send(message: string)
}
here checks for he config in singleton object but throws error because config is nil.
my question is why is nil config in this check. the config is downloaded and store. but when get the values got a null value. Im missing some code here or what I is wrong with this.

Efficient sort array by ParentId swift

Given an array of dictionaries some of which have ParentId I need to sort them in ancestry order.
I have a working algorithm, but I am not sure it is actually efficient.
How can this be improved?
Sample data:
var dicts = [["ParentId": "eee82", "Id":"a0dq1"],
["ParentId": "pqrst", "Id":"eee82"],
["ParentId": "aaa1", "Id":"pqrst"]]
Sample output
["pqrst", "eee82", "a0dq1"]
I ran below in playground
import Foundation
// GIVEN this source...
var dicts = [["ParentId": "eee82", "Id":"a0dq1"],
["ParentId": "pqrst", "Id":"eee82"],
["ParentId": "aaa1", "Id":"pqrst"]]
func findParents(source: [[String:String]], this: [String:String]) -> [[String:String]] {
var results = [[String:String]]()
if let parentId = this["ParentId"],
let parent = source.first(where: { $0["Id"] == parentId }) {
results.append(parent)
results.append(contentsOf: findParents(source: source, this: parent))
}
return results
}
var this = dicts.first!
var accounts = (findParents(source: dicts, this: this) + [this])
var sorted = [[String:String]]()
var hasParentMap = [String: Bool]()
for account in accounts {
let parentId = account["ParentId"]
let hasParent = accounts.first(where: { $0["Id"] == parentId }) != nil
hasParentMap[account["Id"]!] = !(parentId == nil || !hasParent)
}
while sorted.count != accounts.count {
for account in accounts {
if sorted.first(where: { $0["Id"] == account["Id"] }) != nil {
continue
}
if hasParentMap[account["Id"]!] == false {
sorted.insert(account, at: 0)
continue
} else if let parentId = account["ParentId"] {
let parentIndex = sorted.firstIndex(where: { $0["Id"] == parentId })
if parentIndex == nil {
continue
}
sorted.insert(account, at: parentIndex! + 1)
}
}
}
dump (accounts.map({ $0["Id"]! })) // ["eee82", "pqrst", "a0dq1"]
// ...we want to get this output
dump (sorted.map({ $0["Id"]! })) // ["pqrst", "eee82", "a0dq1"]
Update removed the numerical ids to avoid confusion
Here's the visual illustration of what I am trying to achieve
To make things easier I created a Person type:
struct Person: Comparable, CustomStringConvertible {
let id: String
let parentID: String
var description: String {
return "[\(id), \(parentID)]"
}
static func < (lhs: Self, rhs: Self) -> Bool {
return lhs.id < rhs.id
}
init?(dict: [String: String]) {
guard let id = dict["Id"] else { return nil }
guard let parentID = dict["ParentId"] else { return nil }
self.id = id
self.parentID = parentID
}
func toDictionary() -> [String: String] {
return ["Id": id, "ParentId": parentID]
}
}
Here is our data:
var dicts = [
["ParentId": "2", "Id":"3"],
["ParentId": "1", "Id":"2"],
["ParentId": "42", "Id":"1"],
["ParentId": "100", "Id":"88"],
["ParentId": "88", "Id":"77"],
["ParentId": "77", "Id":"66"],
["ParentId": "77", "Id":"55"],
]
Here are our people converted to structs:
var people = dicts.compactMap { Person(dict: $0) }
Here are a few methods to operate on our array of people:
extension Array where Element == Person {
func tree(root: Person) -> [Person] {
[root] + children(of: root)
.flatMap { tree(root: $0) }
}
func topLevelParents() -> [Person] {
return filter { parent(of: $0) == nil }
}
func children(of parent: Person) -> [Person] {
return filter { $0.parentID == parent.id }.sorted()
}
func parent(of child: Person) -> Person? {
return first { child.parentID == $0.id }
}
}
Get all people who don't have parents:
let topLevelParents = people.topLevelParents().sorted()
print("topLevelParents: \(topLevelParents)")
Construct the tree of descendants for each parent and flatten into an array:
let results = topLevelParents.flatMap({ people.tree(root: $0) })
print("results: \(results)")
Convert back to a dictionary:
let dictionaryResults = results.map { $0.toDictionary() }
print("dictionaryResults: \(dictionaryResults)")

How to always return an array in Vapor 3 and Fluent (even for single entity requests)

I'd like to have an index controller function that returns an array of entities if no request parameter is set or a single entity if the id parameter is set. However, I'd like to always receive an array, in the latter case it just contains only one element.
Here's my function:
final class AddressController {
func index(_ req: Request) throws -> Future<[Address]> {
if let id = try? req.query.get(UUID.self, at: "id") {
// THIS IS NOT WORKING...
return Address.find(id, on: req)
} else {
return Address.query(on: req).all()
}
}
}
final class AddressController {
func index(_ req: Request) throws -> Future<[Address]> {
if let id = try? req.query.get(UUID.self, at: "id") {
return Address.find(id, on: req).map {
guard let address = $0 else { return [] }
return [address]
}
} else {
return Address.query(on: req).all()
}
}
}

Based on which field can be filtered in Vapor?

This code with id is accepted:
router.post([Page].self, at: "/fetchStatusOfManagedReleases") { (req, pages) -> Future<[Page]> in
let eventIds = pages.map { $0.events }.flatMap { $0 }.map { $0.id }
return Release.query(on: req).filter(\.fbId ~~ eventIds).all().flatMap { releases in
let releaseInnerIds = releases.map {$0.id}
return TestPrice.query(on: req).filter(\.id ~~ releaseInnerIds).all().flatMap { testPrices in
return req.future([])
}
but this here with releaseId is not:
router.post([Page].self, at: "/fetchStatusOfManagedReleases") { (req, pages) -> Future<[Page]> in
let eventIds = pages.map { $0.events }.flatMap { $0 }.map { $0.id }
return Release.query(on: req).filter(\.fbId ~~ eventIds).all().flatMap { releases in
let releaseInnerIds = releases.map {$0.id}
return TestPrice.query(on: req).filter(\.releaseId ~~ releaseInnerIds).all().flatMap { testPrices in
return req.future([])
}
TestPrice model:
final class TestPrice: Content {
var id: Int?
var price: String
var releaseId: Release.ID
init(id: Int?, price: String, releaseId: Release.ID) {
self.id = id
self.price = price
self.releaseId = releaseId
}
}
Why? What workaround I need to do?

How to merge nil cases to Failing case

import MVVMC
import RxSwift
import RxCocoa
import RTVModel
import RTVWebAPI
public class SettingsViewModel: ViewModel {
public var fetchedNotifications: Driver<[NotificationItem]> = .empty()
public var fetchedNotificationsFailed: Driver<String> = .empty()
public var notificationCount: Driver<Int> = .empty()
'''''''''''''''
public var userLoginName: Driver<String> = .empty()
///// userLoginName getting is a optional String.
'''''''''''''''''
public var fetchedUserLoginNameFailed: Driver<String> = .empty()
public func bindNotificationEvents(with trigger: Driver<Void>) {
let webService: Driver<RTVInformationListWebService> = trigger
.map { RTVInformationListParameters() }
.webService()
let result = webService.request()
notificationCount = result.success().map { $0.informationList.maxCount }
fetchedNotifications = result.success()
.map {$0.informationList.notifications}
-------> .map {$0.map {NotificationItem.init(notification: $0)}}
///////////////////////////////////////////////////////////////
Error (Value of optional type 'String?' must be unwrapped to a value of type 'String')
///////////////////////////////////////////////////////////////
fetchedNotificationsFailed = Driver.merge(fetchedNotificationsFailed, result.error().map { $0.message })
}
public func bindUserInfoEvents(with trigger: Driver<Void>) {
let webService: Driver<RTVMobileMenuWebService> = trigger
.map { RTVMobileMenuParameters() }
.webService()
let result = webService.request()
userLoginName = result.success().map { ($0.mobileMenuInfo.username) }
fetchedUserLoginNameFailed = Driver.merge(fetchedUserLoginNameFailed, result.error().map { $0.message })
}
}
extension RTVAPIError {
fileprivate var message: String {
var message = "\(self.localizedDescription)"
if let codeNumber = self.codeNumber {
message += "\n(\(codeNumber))"
}
return message
}
}
This is not really the way you should be using it, since the point of Driver is not to error, but you obviously have an error state, therefore, Observable or Signal would be better.
However, you need to split your signal into successful ones and error ones, something like this:
fetchedNotifications = result.success()
.map {$0.informationList.notifications}
.share(replay: 1)
let success = fetchedNotifications
.filter { $0 != nil }
.map { $0.map { NotificationItem.init(notification: $0) } }
let error = fetchedNotifications
.filter { $0 == nil } // Here would be your "error" state which you can merge later
I might be off with the syntax, I wrote this from memory.
I fixed it by using the catchOnNil
.catchOnNil { return }