Vapor: route return before all modification done - future

I have following route:
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().map { releases in
var result: [Page] = []
for p in pages {
let page = p
var pageEvents: [Event] = []
for e in p.events {
let event = e
if let release = releases.first(where: { $0.fbId == e.id }) {
event.inProgress = release.inProgress
event.purpose = release.purpose
_ = try release.testPrices.query(on:req).all().map { testP in
event.testPrices = testP // <--- this line is not applied
}
} else {
event.inProgress = false
}
pageEvents.append(event)
}
page.events = pageEvents
result.append(page)
}
return result
}
}
Unfortunatelly event.testPrices = testP is not applied, it will e not part of the response. What can I do? At some cases I do not need to postpone "return". How can I dissolve scheduling issue?

I do a ~~ operation on TestPrice also as for Release before.
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().map { testPrices in
var result: [Page] = []
for p in pages {
let page = p
var pageEvents: [Event] = []
for e in p.events {
let event = e
if let release = releases.first(where: { $0.fbId == e.id }) {
event.inProgress = release.inProgress
event.purpose = release.purpose
event.testPrices = testPrices.compactMap({testPrice in
if testPrice.release.parentID == release.id {
return testPrice
} else {
return nil
}
})
} else {
event.inProgress = false
}
pageEvents.append(event)
}
page.events = pageEvents
result.append(page)
}
return result
}
}
}

Related

Async await swift ui

I have the following code, I would like to use async-await for the two calls.
At the moment inside the function I used a kind of check variable, which when both are set the code is executed.
How can I do?
....
.onAppear {
DispatchQueue.global(qos: .background).async {
getRemoteHead(url: repoUrlStr)
getRemoteBranch(url: repoUrlStr)
}
}
func getRemoteHead(url: String) {
do {
let branch = try Remote().getRemoteHEAD(url: url)
if branch[0].contains("fatal:") {
Log.warning("Error: getRemoteHead")
activeSheet = .error("Error: getRemoteHead")
} else {
self.mainBranch = branch[0]
self.selectedBranch = branch[0]
self.check += 1
if check == 2 {
check = 0
activeSheet = .select
}
}
} catch {
Log.error("Failed to find main branch name.")
}
}
func getRemoteBranch(url: String) {
do {
let branches = try Remote().getRemoteBranch(url: url)
if branches[0].contains("fatal:") {
Log.warning("Error: getRemoteBranch")
activeSheet = .error("Error: getRemoteBranch")
} else {
self.arrayBranch = branches
self.check += 1
if check == 2 {
check = 0
activeSheet = .select
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
// Force a UI Update.
allBranches.toggle()
}
}
}
} catch {
Log.error("Failed to find branches.")
}
}

How to combine two foreach to one in Swift?

I have one function which is having some logic which have 2 foreach loop but i want to make code compact so I am trying to use compactmap
func getData() -> [String] {
var ids = [String]()
self.item?.connections?.forEach { connection in
connection.validLine?.forEach { line in
if let _ = line.connection?.links[LinkKey.dataGroups],
let dataGroups = line.dataGroupsCache, dataGroups.isContinue {
ids += checkinGroups.connections?.compactMap { $0.id } ?? []
}
}
}
return ids
}
so instead of 2 foreach i am trying to make in one by using self.item?.connections?.compactMap({ $0.validline }) but I am getting error saying "Type of expression is ambiguous without more context"
I don't see how you can do it without to forEach or compactMap. Here is a possible solution:
func getData() -> [String] {
return item?.connections?.compactMap { connection in
connection.validLine?.compactMap { line in
guard let _ = line.connection?.links[LinkKey.dataGroups], line.dataGroupsCache?.isContinue == true else { return nil }
return checkinGroups.connections?.compactMap(\.id)
}
}
}
Here's a translation of your post into something that is compilable and a direct translation into a version that doesn't use forEach.
I changed connectionIds to ids in your example because otherwise, you might as well just return [].
class Example {
func getData() -> [String] {
var ids = [String]()
self.item?.connections?.forEach { connection in
connection.validLine?.forEach { line in
if let _ = line.connection?.links[LinkKey.dataGroups],
let dataGroups = line.dataGroupsCache, dataGroups.isContinue {
ids += checkinGroups.connections?.compactMap { $0.id } ?? []
}
}
}
return ids
}
func getDataʹ() -> [String] {
guard let connections = item?.connections else { return [] }
let numberOfProperLines = connections.flatMap { $0.validLine ?? [] }
.filter { line in
if let _ = line.connection?.links[LinkKey.dataGroups],
let dataGroups = line.dataGroupsCache, dataGroups.isContinue {
return true
} else {
return false
}
}
.count
return (0..<numberOfProperLines).flatMap { _ in checkinGroups.connections?.compactMap(\.id) ?? [] }
}
var checkinGroups: CheckInGroups!
var item: Item!
}
enum LinkKey: Int {
case dataGroups
}
struct Item {
let connections: [Connection]?
}
struct Connection {
let id: String?
let validLine: [Line]?
let links: [LinkKey: Void]
}
struct Line {
let dataGroupsCache: DataGroups?
let connection: Connection?
}
struct DataGroups {
let isContinue: Bool
}
struct CheckInGroups {
let connections: [Connection]?
}

Validating appRecieptStoreURL returning status code 21002 on the watchOS

I'm making app for both iPhone and watch. And when I validating receipt on iPhone, it is successfull and possible to get all the previous transitions. But on the watch when I validating the receipt it returns a status code 21002. I use the same code. What should I do to prevent this issue ?
There is the code:
extension IAPManagaer {
public func checkTheReciept(_ complition: #escaping ( _ success: Bool) -> Void = { _ in}) {
self.loadReciept()
guard let receipt = UserDefaultsValues.receipt else {
self.setIsAnyPurchaseOnFalse()
return
}
self.postSubscription(inputModel: SubscriptionModel(receipt: receipt, secret: String(shareSecret), phone: true, watch: true, trial: true), complition)
}
private func loadReciept() {
guard let url = Bundle.main.appStoreReceiptURL else { return }
do {
if UserDefaultsValues.receipt == nil {
recieptRefreshRequest.start()
}
let data = try Data(contentsOf: url)
UserDefaultsValues.receipt = data.base64EncodedString()
} catch let error {
print(error.localizedDescription)
DispatchQueue.main.async {
UserDefaultsValues.isWatchSubPurchased = false
UserDefaultsValues.isPhoneSubPurchased = false
}
}
}
private func setIsAnyPurchaseOnFalse() {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
self.isDataLoded = true
}
if UserDefaultsValues.subscriptionInfoModel != nil {
DispatchQueue.main.async {
self.isPhoneSubPurchased = !(Date().timeIntervalSince1970 - (UserDefaultsValues.subscriptionInfoModel!.expirePhone ?? 0)/1000 > 0)
self.isWatchSubPurchased = !(Date().timeIntervalSince1970 - (UserDefaultsValues.subscriptionInfoModel!.expireWatch ?? 0)/1000 > 0)
self.isPhoneFreeTrialStarted = UserDefaultsValues.subscriptionInfoModel!.isPhoneFreeTrial
self.isWatchFreeTrialStarted = UserDefaultsValues.subscriptionInfoModel!.iswatchFreeTrial
}
} else {
DispatchQueue.main.async {
UserDefaultsValues.subscriptionInfoModel = SubscriptionInfoModel(expirePhone: 0.0, expireWatch: 0.0, isPhoneFreeTrial: false, iswatchFreeTrial: false, isPhonePurchased: false, isWatchPurchased: false)
self.isPhoneSubPurchased = UserDefaultsValues.subscriptionInfoModel?.isPhonePurchased ?? false
self.isWatchSubPurchased = UserDefaultsValues.subscriptionInfoModel?.isWatchPurchased ?? false
self.isWatchFreeTrialStarted = UserDefaultsValues.subscriptionInfoModel?.isWatchPurchased ?? false
self.isPhoneFreeTrialStarted = UserDefaultsValues.subscriptionInfoModel?.isPhonePurchased ?? false
}
}
}
private func postSubscription(inputModel: SubscriptionModel,_ complition: #escaping (_ succes : Bool) -> Void = { _ in }) {
subscriptionAPI?.postSubscription(inputModel: inputModel, completion: { subscribers, status, error in
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
self.isDataLoded = true
}
print("STATUS - \(status?.description)")
guard let subscribers = subscribers, error == nil else {
self.setIsAnyPurchaseOnFalse()
complition(false)
return
}
var isPhoneSubPurchased = false
var isWatchSubPurchased = false
var isPhoneFreeTrial = false
var isWatchFreeTrial = false
var expireDatePhone: Double?
var expireDateWatch: Double?
subscribers.forEach { transaction in
guard let productId = transaction.product_id, let sub = Subscription(rawValue: productId) else { return }
let expireDate = Double(transaction.expires_date_ms ?? "0")!
let isPurchase: Bool = !(Date().timeIntervalSince1970 - expireDate/1000 > 0)
let isTrial = Bool(transaction.is_trial_period ?? "false") ?? false
switch sub.subGroup {
case .iphone :
if !isPhoneSubPurchased {
expireDatePhone = expireDate
}
isPhoneSubPurchased = isPhoneSubPurchased || isPurchase
isPhoneFreeTrial = isPhoneFreeTrial || isTrial
case.watch :
if !isWatchSubPurchased {
expireDateWatch = expireDate
}
isWatchSubPurchased = isWatchSubPurchased || isPurchase
isWatchFreeTrial = isWatchFreeTrial || isTrial
case .both : break
}
}
DispatchQueue.main.async {
UserDefaultsValues.subscriptionInfoModel = SubscriptionInfoModel(expirePhone: expireDatePhone, expireWatch: expireDateWatch, isPhoneFreeTrial: isPhoneFreeTrial, iswatchFreeTrial: isWatchFreeTrial, isPhonePurchased: isPhoneSubPurchased, isWatchPurchased: isWatchSubPurchased)
self.isPhoneFreeTrialStarted = UserDefaultsValues.subscriptionInfoModel!.isPhoneFreeTrial
self.isWatchFreeTrialStarted = UserDefaultsValues.subscriptionInfoModel!.iswatchFreeTrial
self.isPhoneSubPurchased = UserDefaultsValues.subscriptionInfoModel!.isPhonePurchased
self.isWatchSubPurchased = UserDefaultsValues.subscriptionInfoModel!.isWatchPurchased
}
complition(true)
})
}
}

Confused about LiveData Observe

My situation is When user enter loading fragment, check LoggedIn, true go straight to MainFragment, false jump to LoginFramgnet.
here is LoadingFragment:
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
Logger.t(LoadingFragment::class.java.simpleName).i("onCreateView")
val binding = LoadingFragmentBinding.inflate(inflater, container, false)
subscribeUi()
return binding.root
}
fun subscribeUi(){
val factory: LoadingViewModelFactory = InjectorUtils.provideLoadingViewModelFactory()
val viewModel: LoadingViewModel = ViewModelProviders.of(this, factory).get(LoadingViewModel::class.java)
Logger.t(LoadingFragment::class.java.simpleName).i("viewModel = " + viewModel.toString())
viewModel.checkLogin()
viewModel.isToLogin.observe(viewLifecycleOwner, Observer {
if (it){
findNavController().navigate(R.id.action_loading_fragment_to_garden_fragment)
}else{
Logger.t(LoadingFragment::class.java.simpleName).i("to start login")
findNavController().navigate(R.id.start_login)
}
})
}
here is LoadingViewModel:
class LoadingViewModel(
private val loadingRepository: LoadingRepository
) : ViewModel() {
val isToLogin: MediatorLiveData<Boolean> = MediatorLiveData()
fun checkLogin(){
isToLogin.addSource(loadingRepository.checkLogin()) {
isToLogin.value = it
}
}
}
here is the Loadingrepository:
fun checkLogin() : MutableLiveData<Boolean> {
val data: MutableLiveData<Boolean> = MutableLiveData()
api.httpGet(SDBUrl.CHECK_LOGIN).enqueue(object : Callback<Map<String, Any>>{
override fun onFailure(call: Call<Map<String, Any>>, t: Throwable) {
data.value = false
}
override fun onResponse(call: Call<Map<String, Any>>, response: Response<Map<String, Any>>) {
val result = response.body()
if (result != null && result.containsKey("success")){
val isLogin = result["success"] as Boolean
data.value = isLogin
}else{
data.value = false
}
}
})
return data
}
when logged in, popbackto LoadingFragment,isToLogin observe execute else immediately, LoginFragment start agagin. when I debug, wait a while on LoginFragment popBackStack, then goback to Loading Fragment,isToLogin observe execute true.so I am very confused, how can I fix this.
Finally, I solved this problem as follow.
class LoadingViewModel(
private val loadingRepository: LoadingRepository
) : ViewModel() {
fun checkLogin(): MediatorLiveData<Boolean> {
val isToLogin : MediatorLiveData<Boolean> = MediatorLiveData()
isToLogin.addSource(loadingRepository.checkLogin()) {
Logger.t(LoadingViewModel::class.java.simpleName).i("it = $it")
isToLogin.value = it
}
return isToLogin
}
}
then in LoadingFragment,:
loadingViewModel.checkLogin().observe(viewLifecycleOwner, Observer {
Logger.i("isToLogin = $it")
if (it) {
findNavController().navigate(R.id.action_loading_fragment_to_garden_fragment)
} else {
findNavController().navigate(R.id.start_login)
}
})

Return statement in nested if function

I have been struggling with something for a while.
How can I return something in a nested if function?
The function below has the task of finding out if the userProfile has a verified card or not, if verified == 1 (true) then return true, else return false.
func userHasVerfifiedCard() -> Bool{
let userDocument = users.documentWithID(Meteor.userID!)
if let card = userDocument.valueForKey("profile")!["card"] {
print("has card")
if let verified = card!["verified"] as? Int {
print("card.verified as Int")
if verified == 1{
print("card.verified == 1")
lastFourCreditCardLbl.text = card!["last4"] as? String
return true
}else {
return false
}
}
}
your method won't return anything, if if let card won't work. But it must return a bool in any case.
func userHasVerfifiedCard() -> Bool {
let userDocument = users.documentWithID(Meteor.userID!)
if let card = userDocument.valueForKey("profile")!["card"] {
print("has card")
if let verified = card!["verified"] as? Int {
print("card.verified as Int")
if verified == 1 {
print("card.verified == 1")
lastFourCreditCardLbl.text = card!["last4"] as? String
return true
}
}
}
return false
}
Try this and let me know if it's helps..!
func userHasVerfifiedCard() -> Bool{
let userDocument = users.documentWithID(Meteor.userID!)
if let card = userDocument.valueForKey("profile")!["card"], verified = card!["verified"] as? Int where verified == 1 {
return true
} else {
return false
}
}

Resources