Main app sends data to watch, and watch sets the picker index by using:
[picker setSelectedItemIndex:val];
This in turn, will fire up the picker action. However my picker action sends data to phone (using sendMessage) which in turn replies back to watch... and that goes on forever.
How can I cancel the picker action for setSelectedItemIndex:? WKInterfacePicker doesn't have a removeTarget: method.
You don't want to remove the action. Instead, you have a few options to choose from to simply stop the cycle from happening by:
Only setting the picker's value when the initial message is received, not when the response is received.
If the watch says, "The picker index is 3," and the phone replies, "Roger, the picker was set to 3," why would you want your watch code to set the picker based on the response to something the watch initially sent? This seems to be the crux of the issue, and would be the optimum solution.
Use a different key for the response, if necessary, to help any shared code distinguish between the types of messages.
Not setting (or sending) the picker's value, when it already matches the picker's current selected index.
You'd have to maintain a property to keep track of the current value, as the picker's properties are write-only and can't be read.
Using a different WCSession method (such as updateApplicationContext) which is designed not to resend data when it matches the most recent applicationContext.
Related
There are several view controllers in my app where I need to sync the local contents with server using a method running in a background thread. Sometimes I need to insert data to my database on server if user has created any. The approach I am using here is to set a flag(something like isSynced = NO) on objects that I need to sync with server (there objects are in Core Data). When the syncing is complete my method will get rid of the flag(e.g. isSynced = YES) so it won't be sent again next time.
Now the problems is that the syncing method takes very long to complete(1 or 2seconds.). If now user pops out this particular view controller and swiftly comes back the previous call is still in progress and next one will be kicked off. The consequence is that there might be duplication in database.
My approach now is the make the syncing method to be called by a Singleton object:
#property (nonatomic) BOOL isSyncing;
//every time before syncing. check if object is available for syncing
if (!isSyncing) {
isSyncing = YES;
// sync server
// when complete
isSyncing = NO;
// post notification to view controller to reload table
} else {
// cancel because previous call is not finished
}
My concern is that if the call is cancelled my view controller will not be able to receive the notification is waiting for. I can fix this by posting another notification in the event of cancelation. I am wondering if this is the right to do this because I think that this problem should be pretty common in iOS development and there should be a standard way to deal with it
Your singleton approach may not be necessary. I don't see the harm in sending a database insert for each new object. You will still need to ensure each object is synched. That is, update the "isSynched" flag. Keep each object that needs to be synced in a "need to synch" list.
Then, update the "isSynced" flag by performing a background query on the database to check if the object exits. Then, use the result of the query to set the isSynched flag.
If the query result indicates the object is not in the database you then resend the object and leave it's "isSynced" flag set to NO.
If the query result indicates the object is in the database, set the "isSynced" flag to YES and remove it from your "need to synch" list.
An approach for preventing duplicate database entries is to make a unique key. For example, tag each with a hash based on the time and date. Then configure the table to ensure each key is unique.
Similar to the iPhone Facebook app search function, I am implementing search as you type functionality into my application although I have a problem when decoding the data into JSON format.
Basically what happens is because some searches take longer than others, they return at different intervals and this causes some small visual issues when the data is presenting on the screen.
I have set an NSLOG after each decode using NSJSONSerialization for the keyword 'industry'
2013-04-09 23:38:18.941 Project Name [42836:1d03] http://fooWebAddress/json/?method=search&limit=10&q=indus
2013-04-09 23:38:19.776 Project Name [42836:3e07] http://fooWebAddress/json/?method=search&limit=10&q=indu
2013-04-09 23:38:20.352 Project Name [42836:8803] http://fooWebAddress/json/?method=search&limit=10&q=indust
2013-04-09 23:38:21.814 Project Name [42836:4e03] http://fooWebAddress/json/?method=search&limit=10&q=industr
2013-04-09 23:38:23.434 Project Name [42836:8803] http://fooWebAddress/json/?method=search&limit=10&q=ind
2013-04-09 23:38:24.070 Project Name [42836:7503] http://fooWebAddress/json/?method=search&limit=10&q=industry
As you can see it is all out of order.
Does anyone have any way of stopping NSJSONSerialization for the previous connection.
Or possibly any other way to go about this problem?
Steps up to NSJSONSerialization...
NSURLRequest (initwithURL)
NSOperationQueue
NSURLConnection (asynchronous)
NSJSONSerialization
Thanks in advance.
When the user starts typing more text, you could cancel your previous connections and ignore any further delegate callbacks you receive from them. Then make the new request for the current text.
You can do this by maintaining some sort of lastRequest or lastOperation reference. When the user starts typing, call [self.lastRequestOrOperation cancel] and ignore any further notifications from that request with a check like if (request != self.lastRequest) { return; } in whatever callbacks you have.
However this has the problem that if the user keeps typing for a while you are constantly cancelling requests and they may not see any results until they have stopped typing.
A better solution would be to add sequencing so that each request is associated with an increasing sequence ID. You then only parse the result and update the UI when the sequence of the response is higher than the last one you received. If you receive any out-of-band responses from earlier, you just ignore them.
This is a much more complex issue than just being able to cancel the NSJSONSerialization. My suggestion is to use NSFetchedResultsController to populate your table view that shows the search results. Use the search term as one of the predicate variable in the NSFetchRequest attached to NSFetchedResultsController. And then, when you parse the results using NSJSONSerialization, store the results with the search term associated with that request. As soon as the search term changed (which you can detect when the user types more characters), re-create the NSFetchedResultsController and reload your table view. In addition, you can also try to cancel the call to parse the previous results if you launched it using performSelector:withObject:afterDelay. Beware that this cannot be always relied upon as the call may have been initiated by the time you are trying to cancel.
Kinda basic, but you could always maintain an nsdictionary of sub-classed NSURLRequests (sub-classed to provide a tag).
Start request - add request to dicationary with tag = array.count - 1, with key matching tag
Connection returns - is the request the most recent request, if so, parse json
Parse JSON - is the request the most recent request, if so, show results, if not, only display if there are no previous results displayed
Request handling - remove key from dictionary
most recent request = does the dictionary contain an object with a higher key value
Currently what you are doing is, you type each character and calling web-service. Why to call web-service for each letter you type. If user is type continuously, then it will increase the load, so call the web-service only when user stops for a particular interval of time. and then pass that string to call web-service or what ever method you are calling.
[NSObject cancelPerformSelectorsWithTarget:self]; // This will cancel your all req which is going to make when user typing without stopping
[self performSelector:#selector(sendSearchRequest) withObject:searchText afterDelay:0.1f]; // This will pass the string to call a web-service method, on which user hold for some time.
It seems my base configuration of GATracker is working because I am able to see my device in the real-time section. However, even after a day or so of firing events via:
- (BOOL)send:(NSString *)trackType params:(NSDictionary *)parameters
I have not seen any events under Engagement > Events > Overview.
I have verified:
I can see traffic in the real-time pane ensuring base functionality
the tracker object is non-nil when using above method
the return value is YES for above method (i.e. it think's it was successfully queued at some point to send)
I even implemented the dispatch call on [GAI sharedInstance] as a sanity check to ensure the events were getting flushed
I've heard reports the b3/b4 version of the 2.0 SDK have had issues sending events but not seeing any at all seems very unusual.
Thanks!
Google Analytics and Mixpanel are different tools and have different ways for sending data.
For Google Analytics events have only 4 fields:
Category (string)
Action (string)
Label (string)
Value (number)
Similarly Pageviews are another type of hit, but they only have 1 field, that is the page url. On mobile apps this is renamed to view instead but it works almost the same way.
These fields have names for reference but you can actually use them for whatever you want. You will be able to drill down from one to the other in the interface.
You can also "add" more fields by using Custom Dimensions/Metrics. If you set a page type custom dimension before the event, that custom dimension/metric will be bound to the event.
Note that it's important to understand and use the right scope for each custom dimension. If you set a session scope for example the only value you will see in GA is the last one set for that specific session, it overrides previous values. You have to configure these through the interface.
I am getting the same issue.
When I use:
[[GAI sharedInstance].defaultTracker sendEventWithCategory:message
withAction:nil
withLabel:nil
withValue:nil];
the result is logged right away.
but, when I use:
[[GAI sharedInstance].defaultTracker send:message params:eventParams];
NO result gets logged at all
Not sure how to best explain this - If I use blocks to load images for UITableViewCells, how best can I ensure that when an image actually finishes loading - it is the correct image for the cell. Say I'm making an app with user icons - so cell 5 is for John and it starts loading John's image. Say that request gets hung up and by the time it returns, that cell has been reused for Bill at cell 23. We obviously don't want to load John's icon - we just want Bill's.
This is a simplified explanation. I know that we can check if a cell is visible in the block before setting the image - but say in a different scenario I have a method that makes a remote request when called. When the remote request is done, it calls a block defined in that method that updates the UI. If I call it and before it returns I call it again and only want the most recent call's block executed. Is there a good pattern for doing this?
Currently, I try to store a variable that has some state in the method so that when it returns I can check if that state is still valid. Just thought I'd see if there was a better way. Thanks.
Just checking if any updates on this.
I have a managed object which acts as a playlist, it has a to-many relationship with the playlist items. There can be multiple playlists, but only one "active" playlist. The active playlist is indicated by a boolean attribute on the managed object.
I have the number of items in the active playlist displayed as the badge on a tab bar item. The view controller that the tab bar item represents is listening for a specific notification which is fired when the contents of the active playlist are updated.
I have implemented this in what I feel is a clumsy way and would like to do it better. It does work at the moment but I'm not happy with it.
Currently, each playlist object, on awakeFromFetch, checks to see if it is the active one, and if so registers itself (using key value observing) as an observer for the key path which is the key for the relationship. When it observes a change, it fires the notification, which causes the tab bar item to update itself.
If the playlist loses or gains active status it stops or starts observing itself appropriately, so notifications are only fired from the active playlist.
I would like to drop all of the self-observing KVO code as I am concerned about the various entry and exit points and when to add and remove observers appropriately. It seems too dirty.
I would like to just override didChangeValueForKey:, check and send my notification there if necessary, then call the super implementation. But this is expressly forbidden in the documentation:
didChangeValueForKey:
Invoked to inform the receiver that the value of a given property has changed.
-(void)didChangeValueForKey:(NSString *)key
Parameters
key
The name of the property that changed. Discussion For more details, see
Key-Value Observing Programming Guide.
You must not override this method.
So, what can I do?
I've just read the same documentation, but if you look at the top of the NSManagedObject documentation, it actually says "You are strongly discouraged..."
I guess it all depends on your implementation details. For example, I do the following on a data model that I can modify locally, and sync with a server:
- (void)didChangeValueForKey:(NSString *)key
{
[super didChangeValueForKey:key]; // MUST CALL THIS!
if ([key isEqualToString:NSStringFromSelector(#selector(name))] ||
[key isEqualToString:NSStringFromSelector(#selector(text))] ||
[key isEqualToString:NSStringFromSelector(#selector(filename))]
)
{
self.lastModified = [NSDate date];
}
}
I'm not sure why this would be considered bad. It's just saying "Do what you normally do. In addition, I'd like to set another property that depends on that change."
What you want is Key Value Observing. You should be able to register for that specific key, and get notified when it changes. Check this out too: Using KVO to observe changes to a property on an object inside a collection in Objective-C
I have solved this by making a separate object (a singleton I am using to manage my core data stack) the observer instead. All of the self-observing complexity has now gone and I do not need to worry about adding or removing observers on awakeFromFetch and so on.