Determining If CKRecord Was Created By Current User - ios

I'm new to CloudKit and I was trying to figure out if a Record was created by the current user. I have researched this topic and have come about two methods to do this. I'm not sure which one is right or better and I don't even quite understand how the second method works.
The first way is using the following method to get the current user and then comparing it to the user who created the record:
func fetchUserRecordID(
completionHandler: (recordID: CKRecordID?, error: CKError?) -> Void
)
The second way involves an extension on CKRecord:
extension CKRecord{
var wasCreatedByThisUser: Bool{
return (creatorUserRecordID == nil) || (creatorUserRecordID?.recordName == "__defaultOwner__")
}
}

The first method is making another call to the server to fetch an additional record. The downside is that costs time, it counts against your monthly traffic quotas, and you have yet another async callback function that your code flow will have to account for. If you wind up calling this check a lot, you would generate a lot of unnecessary traffic to the server.
The second method is checking a value, creatorUserRecordID, that came with the record you already fetched. So at the time you check its value, it's all local data, no additional calls to the server and no async processing required.
Per the answer here: creatorUserRecordID.recordName contains "__defaultOwner__" instead of UUID shown in Dashboard, __defaultOwner__ is a synonym for the local user.
The second method looks to be the better choice for most scenarios I can think of.

Related

Realm Swift: Question about Query-based public database

I’ve seen all around the documentation that Query-based sync is deprecated, so I’m wondering how should I got about my situation:
In my app (using Realm Cloud), I have a list of User objects with some information about each user, like their username. Upon user login (using Firebase), I need to check the whole User database to see if their username is unique. If I make this common realm using Full Sync, then all the users would synchronize and cache the whole database for each change right? How can I prevent that, if I only want the users to get a list of other users’ information at a certain point, without caching or re-synchronizing anything?
I know it's a possible duplicate of this question, but things have probably changed in four years.
The new MongoDB Realm gives you access to server level functions. This feature would allow you to query the list of existing users (for example) for a specific user name and return true if found or false if not (there are other options as well).
Check out the Functions documentation and there are some examples of how to call it from macOS/iOS in the Call a function section
I don't know the use case or what your objects look like but an example function to calculate a sum would like something like this. This sums the first two elements in the array and returns their result;
your_realm_app.functions.sum([1, 2]) { sum, error in
if let err = error {
print(err.localizedDescription)
return
}
if case let .double(x) = result {
print(x)
}
}

Prevent redundant operations in RxSwift

I'm starting my adventure with RxSwift, having small experience with React in js already. I think that my problem is common, but I'm not sure how to describe it in concise abstract way, so instead I will describe it on the example.
I'm building iOS app showing some charts. The part of interest consist of ChartAreaController, ChartInfoController, both embedded in ChartController. First controller is the area showing some graph(based on rx chartData property), and the second one among others will have a slider for user to restrict show x-value (rx selectedXRange property) which is restricted to be between some min and max. The min/max value is defined by the current chart data.
Behavior when slider change updates chart is defined in ChartController:
override func viewDidLoad() {
super.viewDidLoad()
(...)
chartInfoController.selectedXRange.asObservable()
.subscribe(onNext: { [unowned self] selectedXRange in
(...)
let chartData = self.filterChartData(from: self.rawChartData, in: selectedXRange)
self.chartAreaController.chartData.accept(chartData)
}).disposed(by: disposeBag)
The filterChartData() method just filters out data that is not in the range, but for the sake of the argument we can assume it is very costly and I don't want it to run twice when it is not necessary.
When user changes the chart he or she wants to show, the new data arrives from server (again ChartController):
private func handleNewData(_ rawChartData: ChartData) {
self.rawChartData = rawChartData
guard let allowedXRange = rawChartData.xRange() else { return }
let selectedXRange = chartInfoController.selectedXRange.value
let newSelectedXRange = calculateSelectedXRange(currentSelectedDays: selectedDaysRange, availableDaysRange: daysRange)
let chartData = filterChartData(from: rawChartData, in: selectedXRange)
self.chartInfoController.allowedXRange = allowedXRange //this line is not crucial
self.chartInfoController.selectedXRange.accept(newSelectedXRange)
self.chartAreaController.chartData.accept(rawChartData)
}
So upon new chart data arrival it may be the case that the currently selected xRange must be trimmed because of the new min/max values of the data. So the side effect of the method will be changing the selectedXRange and in turn running the subscription I pasted earlier. So when new data arrives the chartData is updated twice and I don't want it to happen.
Of course I can comment out last line of the handleNewData() method, but I don't like it very much, since main reason for existence of the handleNewData() is to set chartData, and with the line commented out it's goal would be achieved because of the side effect of the method (which is updating the slider). Not acceptable.
To chartData I added throttle anyways, because fast moving slider will result in many updates and this solves my problem partially(chartData updated only once). But as you may remember the filterChartData() method is costly, and this part will still be running twice.
So the one question is, if my general layout of tackling the problem is OK, or should it be handled way different? At this point I came to conclusion that I'm looking for some way of temporary disabling particular subscription on selectedXRange (without damaging other subscriptions to that variable). Temporary meaning:
(...)
//disable subscription
self.chartInfoController.selectedXRange.accept(newSelectedXRange)
self.chartAreaController.chartData.accept(rawChartData)
//enable subscription
(...)
This seem legit to me, since ChartController as an owner of the subscription and changer of the values may want to disable the subscription whenever it suits him(it?).
Does RxSwift support something like this? If not, then I think I can achieve it myself e.g. via bool property in ChartController, or via adding the subscription to separate disposeBag, which I would dispose and then recreate the subscription. But if it's good thing to do? For example bool solution may be prone to be ill handled when there is some error, and dispose/recreate may be somehow costly, and it may be the case that disposal was not intended to be used like that.
Is there a better practice to handle such situations? As I said I think the problem is common so I hope there is a canonical solution to it :) Thanks for any answer, sorry for the lengthy post.
So the one question is, if my general layout of tackling the problem is OK, or should it be handled way different?
A properly written UI input element observable will only fire when the user makes a change to the UI, not when the program makes a change. For example:
textField.rx.text.orEmpty.subscribe(onNext: { print($0) }) will only print a value when the user types in the textField, not when you call textField.text = "foo" or from a binding .bind(to: textfield.rx.text).
If you wrote the ChartInfoController, I suggest you modify it to work the way the other UI elements do. If you didn't write it, submit an issue to the developer/maintainer.
Does RxSwift support something like [temporarily disabling particular subscription]?
It depends on what you mean by "temporarily disabling". It doesn't support silently unsubscribing and resubscribing but there are plenty of operators that will filter out some events they receive while passing others along. For example filter, throttle, debounce, ignoreElements... There's a lot of them that do that.
Is there a better practice to handle such situations?
Then best solution is mentioned above.
When We have multiple subscriptions to the same Observable, it will re-execute for each subscription.
To stop re-execute for each subscription. RxSwift has several operators for this: share(), replay(), replayAll(), shareReplay(), publish(), and even shareReplayLatestWhileConnected().
read more at (RxSwift: share vs replay vs shareReplay)

Why Hybris modelService.save() doesn't work inside the ifPresent() method?

private void doSomething(someProcessModel process){
CustomerModel customer = process.getCustomerModel();
customer.getFoos().stream()
.filter(foo -> foo.getCountryCode().equals(process.getCountryCode()))
.findFirst()
.ifPresent(foo -> {
if(foo.getSomeNumber() == null){
foo.setSomeNumber("1234567");
modelService.save(foo);
}
});
}
As seen in the code snippet above, I have a 'CustomerModel' that has an attribute 'Foos'. It's a one-to-many relationship. As you can see I have done some filtering and in the end, I want to update the value of 'someNumber' attribute of 'Foo' if it is null. I've confirmed that everything is working as the "someNumber" attribute's value is updated during the debugging. It doesn't save at all as I have done my checking in the HMC. I have also validated that the Interceptor doesn't have any condition that would throw an error. There is nothing being shown in the log either.
I am wondering is it a legal approach to do the "modelService.save()' inside the 'ifPresent()' method? What could be the possible issue here?
I have found the root cause now as I have face the same issue again.
Context to my original question
To give more context to my original question, the #doSomething method resides in a Hybris Business Process action class and I have ended the action prematurely while I am debugging it (by stopping the debugging) once the #doSomething method is ran.
Root cause
The mentioned problem happened when I was debugging the action class. I assumed that the ModelService#save will persist the current state of the business process once it has been ran. However, the Hybris OOTB business process will do a rollback if there is any error (and I believe it was caused by me stopping the debugging half-way).
SAP Commerce Documentation:
All actions are performed inside their own transaction. This means that changes made inside the action bean run method are rolled back in case of an error.
Solution
Let the action runs completely!
Good to know
Based on the SAP Documentation and this blog post, there will be times that we will need to bypass a business process rollback even though there is an exception being thrown and there are ways to achieve that. More info can be found in this SAP Commerce Documentation and the mentioned blog post.
However, in some circumstances it may be required to let a business exception reach the outside but also commit the transaction and deal with the exception outside. Therefore, it is possible to make the task engine not roll back the changes made during a task which failed.
You have to be cautious with a list in models as they are immutable, you have to set the whole new list. Also you called save only on a particular model, which changes its Jalo reference, that's why your list is not updated. Mutating stream and collecting it in the end will create new list that's why you can stream over the list directly from the model.
private void doSomething(someProcessModel process){
CustomerModel customer = process.getCustomerModel();
ArrayList<FooModel> foos = doSomethingOnFoos(customer.getFoos());
customer.setFoos(foos);
modelService.saveAll(foos, customer);
}
//compare the value you know exists with something that might be NULL as equals can handle that, but not the other way around
private ArrayList<FooModel> doSomethingOnFoos(ArrayList<FooModel> fooList) {
return fooList.stream()
.filter(Objects::nonNull)
.filter(foo -> process.getCountryCode().equals(foo.getCountryCode()))
.filter(foo -> Objects.isNull(foo.getSomeNumber()))
.map(foo -> foo.setSomeNumber(1234))
.collect(toList());
}

Parse SaveInBackgroundWithBlock takes a lot of time

I have a parse class(named "Story") with following columns:
contents - Array of Pointers to Media class
creator - User pointer
lastPosted - Date
title - String
users - array of pointers to User class
I am trying to update a PFObject from "Story" class like this
storyObject.addUniqueObjectsFromArray(selectedFriends, forKey: "users")
storyObject.setObject(createStoryTextField.text, forKey: "title")
storyObject.saveInBackgroundWithBlock({ (succeed: Bool, error: NSError?) -> Void in
hud.hide(true)
})
In some cases the completion block takes a lot of time to complete. By debugging, I have found that the more objects in the "contents" column, the longer it takes. But I haven't even updated the "contents". All the values in the code are not nil. I checked for that too. Any help would be appreciated. Thanks.
This is potentially expected behavior. In general when you put something on a background thread (like saveInBackgroundWithBlock is almost definitely doing) it will take a lot longer to complete than if you call it on the main thread. If you need it to return quickly and don't mind blocking the UI, you can use the save: method.
You also may want to check to see how many requests you are making simultaneously as this may slow down each one.
Finally, if you want to customize the network behavior of Parse more than you already do, you can just use the REST API and make all your own network calls (using NSURLSession or a third party library like AFNetworking.)

Using Stackmob getLoggedInUserOnSuccess:^(NSDictionary *result) onFailure:^(Error *error) method

When I implement the getLoggedInUserOnSuccess:onFailure method (or the loginWithUsername: password: onSuccess:^(NSDictionary *results)...method in xcode, the results array does not become available until after all of my code has finished running. (If I NSLog the results, they will show up correctly.)
There is one other question that mentions this in Stackoverflow:
How to get the sm_owner User object using StackMob as backend
However, the Stackmob Evangelist in the answer here does not suggest that there is any requirement for a completion block or something of this nature. (And in fact, in his own code, it appears to work without such a block or any sort of "waiting.") This was my first hunch as to what might be going wrong.
(Without posting a ton of code, I am trying to use this function to get the sm_owner which then serves as the predicate in the FetchedResultsController's getter, to ensure the user only sees their own creations and not those of other users, when in one view; in another fetch, they might be able to see the creations of users they follow.)
Has anyone else tried to use one of these methods with a results dictionary returned to write the predicate on a FetchedResultsController or similar and been able to make it work?
None of the Stackmob tutorials appear to limit data returned from a database based on its creator as far as I can tell.
If you set your schema's read permissions to "Allow to sm_owner", then you don't need to place a predicate on the fetch. Doing a generic fetch will return only those objects owned by the current logged in user.

Resources