I received the following rejection message:
Your app uses or references the following non-public APIs:
removeItems:,
setSelectedSection:
setIsNew:
selectedSection
The use of non-public APIs is not permitted on the App Store because
it can lead to a poor user experience should these APIs change.
Looking through the code, I find removeItems: in a piece of code implemented by Xcode Core Data code generator (I have a substructure called items).
selectedSection and isNew are property in generated core data code.
The code passed last deployment. And the code passed beta review two or three weeks ago.
Does Apple forbid to use these function names in MY private code? Or is there currently an AppStore issue with false positives?
UPDATE: I received the following answer from Apple App Review team:
Thank you for your response. For each of the selectors at issue, we
found the following occurrences:
... 4 method uses deleted ...
To clarify, these are not necessarily direct uses of non-public APIs,
but as your app does not have these statically defined, they are name
collisions with possible private selectors, and as such revision is
appropriate.
The last section is especially interesting: Does this mean, that I cannot any longer choose the names of any methods free of fear that Apple might come up with the same method name? Or, to reduce it to a objc question: what is "statically defined"? static C-methods? class-methods?
The reason for rejection is actually really that I am using selectors that Apple decided to make introduce as private selectors.
This can happen at any time, with any version and there is no testing facility but the app review (at the time of this writing).
It seems that Apple changed policy here, earlier, they just send out a warning, nowadays the app is refused.
The solution is actually simple, but can require major efforts.
rename the selectors listed in the review report
rename properties, also dynamic properties from core-data, that match the report. In my case, all reported fields were core-data properties. This required renaming of database fields and thus I had to add a new DB schema version and rename the fields accordingly and initialize the db stack with auto-migration enabled.
Related
So my App was released to the AppStore some months ago and was free to download. I gained like 2k Users. Now I am implementing an auto renewable Subscription model, so future users can use the demo app for free and subscribe to get the full content.
How ever I want to thank my Users "from day one" by giving them free access to all content without paying the subscription.
Is there a "given way" how to implement this because it seems like a common scenario. If not, does someone with IAP experience has a smart workaround how to handle/implement it?
Thanks guys :D
The answer from Paul didnt work for me. Neither did any other solutions.
But i managed to find a solution.
In case you use CoreData as database within your app, you can use the solution i found out for my app.
The idea:
If someone downloads the app from the appstore, he will receive the latest database version in your app.
If someone updates your app, coredata will perform a migration. And this migration updates from bottom to top trough your database versions.
Through this behavior we have the opportunity to dinstinct the two cases.
Solution: We create two new database versions. One will only be updated by the former users. We will set a flag here. The new Users will never get in touch with that.
The first new one will create a new attribut field in any entity (e.g. User) e.g. isFormerUser as a Bool. You can set it per default on true.
Now we create a second new one (the latest one). Trough a Core Data mapper it will take the $source attributes if it migrates.
So by that case, all FormerUser were set on True and will keep that attribute.
Here will the default be False, so if $source didnt exist, set it on false (new Users).
This solution is nice because it gets set once by that migration/update and you dont have to carry hardcoded stuff in your app. Now just create a load/get Function from CoreData for that attribute and set a variable to use in an if-statement.
We are considering changing our rails app (which relies heavily on Nested Attributes-style data creation) to use JSONAPI instead. However, it seems that JSONAPI may not support this style of document creation/updating?
I've read around and this is sometimes referred to as "compound documents" or "sideposting". My research has been inconclusive, so posting here in hopes that someone out there knows the deal.
For example if in our app we had a Person that has many PhoneNumber, I would like to be able to send a PATCH to /person/1 that had information relating to phone numbers. RoR Nested Attributes allows for sending new phone numbers (ones without an id), modifying existing ones, and deleting existing ones. Is there an equivalent for this in JSONAPI?
If not, how would a client go about submitting this data to the server? A separate request for every object? On some of our pages that could result in 100 plus requests... and each one would need to be managed for errors indivdually?
JSON:API specification v1.0 does not support to create, update or delete more than one resource with one request. So if your server only supports that version of the specification without any custom modification the client would need to submit separate requests for each resource that should be created, updated or deleted. That means the client needs to handle errors for each one individually. This may include rolling back partial changes if one requests fails in a series of related updates.
The missing support for atomic operations is known to be a problematic limitation for some use cases. It should be resolved with the upcoming v1.1. This version is planned to add extensions to the specification. An extension can extend the specification. An extension Atomic Opersions is proposed, which would add support for creating, updating and deleting multiple resources with one request. A client would be able to provide a list of mutations that must be processed in a linear and atomic manner by matter by a server implementing that extension. This means that the modifications are processed in order and will either completely succeed or fail together.
I'm currently building a mutation that deletes a node.
I looked into the NODE_DELETE mutator configuration, but it specifies it needs a parentName, parentID and connectionName.
Why does deleting a node needs theses fields ? As Relay uses global IDs, it should be fairly easy to delete a node from all connections and/or all fields it is being referenced in.
See the documentation :
Relay NODE_DELETE documentation
Your intuition is correct. Those fields are redundant for the purpose of deleting node from data store, only config.deletedIDFieldName is used during such operation (details in writeRelayUpdatePayload module).
However you can't leave other fields unfilled, because they are still required during validation of your mutation that extends RelayMutation class. I have no idea where this inconsistency comes from, but it's worth noting that Relay team changed the direction of development and RelayMutation will get deprecated.
RelayMutation and fat/tracked queries. Future releases will deprecate
this API in favor of a static mutation API. We recommend using
RelayGraphQLMutation to ease the transition to new mutations.
source: Related issue on github
After quick look in Relay's source it seems this transitional API does not use the old method of configs validation, but I haven't got a chance to experiment with it yet, so you can confirm it yourself.
our app have been rejected by iTunes because of using non-public APIs
We found that your app uses one or more non-public APIs, which is not in compliance with the App Store Review Guidelines. The use of non-public APIs is not permissible because it can lead to a poor user experience should these APIs change.
We found the following non-public API/s in your app:
.......
If you have defined methods in your source code with the same names as the above-mentioned APIs, we suggest altering your method names so that they no longer collide with Apple's private APIs to avoid your application being flagged in future submissions.
what is the meaning of that rejection?
does it means:there are some apple interface "SomeAppleInterface"
// SomeAppleInterface.m
#implementation SomeAppleInterface
- (void)SomePrivateAPI // this api doesn't exists in SomeAppleInterface.h file
{
// ... some code
}
and in my code i call to a private api of apple class:
- (void)MyCodeMethod
{
SomeAppleInterface x;
[x SomePrivateAPI];
}
is it the meaning of this error? if yes how could i call to this private method, it is not mentioned in h file?
please help me to understand
You are asking for clarification for a rejection that is, most likely, simply an error on Apple's part. This list looks like a bunch of false positives. All they're saying is that you appear be calling a bunch of methods that they believe to be part of the private API.
In this question, you provide a straw man, showing us an invocation of a private method which will not compile. You seem to be asking "how could I have accidentally called the Apple private API?"
The thing is, it's really hard to accidentally call the private API. You'd could create a category #interface that exposed the private API. There are other ways, too, but this simply isn't something you can accidentally do. You have to consciously take steps to call a private API.
So the question becomes how one could get a false positive report from Apple. It's possible that you're not using Apple's private API at all, but happen to have methods in your own classes with the same signature. This might result in a false positive which can be resolved by simply changing the name of your method.
But in this case, I think there is something completely different going on and I don't think it's worth trying to track it down until you hear back from Apple. There's no point in getting all lathered up about it until you hear back from them.
I am going to develop an iOS app for a web application. (The web app uses code igniter)
I am going to create an API Service that the iOS app will consume.
I am thinking of creating an api version, so when the web api changes, the iOS app will know.
Concerns:
iOS app will need to be updated when web application api changes (unless I keep legacy api available..Is this a good option)
If iOS app is updated when web app api is NOT updated this will cause a problem too
Should my iOS app specify the version of the api it requires?
If iOS app api is less than web api: Display Message: Please update iOS app
If iOS app api is greater than web api: Display Message: Please update web app
Is this best practice?
Should I make an api class for every version and extend the previous version and override methods when they change?
Example
ApiV1 extends CI_Controller
{
function list_customers(){//Code}
function saveSale() {//Code}
}
ApiV2 extends ApiV1
{
function saveSale()
{
//New way of saving sale
}
}
Also what happens if I make a change to the database structure where the v1 api will no longer work? (Example, changed the name of a database table?)
In general, you want to create a fairly loose coupling between your service API and your client. As a rule, there will be multiple versions of the client always floating around in the wild, and you want to force upgrades on users as rarely as possible.
A full rev of an API version is actually somewhat rare in web services, and usually only corresponds to significant changes to the data model, security model, etc. Allowing multiple versions to coexist may require some extra work on the service, but can be worth it to allow existing clients to keep working.
To that end, think carefully in the design up front about the "model" you're using as an abstract entity independent of the current client UI needs. (If you want more specific thinking around your particular case, you may wish to post a separate question about modeling your needs.) But don't worry too much about solving all of the needs forever in advance, because requirements will inevitably change.
Once you've done this, do prepare for the future by building some notion of versioning into the service API. Some things to consider:
An explicit version as part of the URL scheme or specified initially during e.g. auth handshake. This is a way to cleanly namespacing what the client accesses. (The former would result in explicit URL routing on the service, the latter would require more gymnastics to route after cracking an auth token.)
A known error response that means "this API call is obsolete", which an earlier client can recognize to tell the user that their client requires an update
On the service, your design can be as explicit as you note, with a controller with method overrides, but at a little higher level: the saveSale method is somewhat unlikely to behave very differently between versions. It would seem more likely to have a saveSale method in V1 that does the baseline thing, and then maybe e.g. saves some extra bit of data in V2. When that happens, the code might just have conditional branching if that extra bit of data is present. All of this is another way of saying that a service API doesn't actually change incompatibly that often. list_customers could return more information over time. That doesn't necessarily mean that your API needs a new version or that old clients shouldn't just ignore any extra information they don't need.
Re: your final question about database table names. Those may change internally, but you aren't required to map those explicitly to what the client sees. An API is a stable interface that should ideally hide the implementation details of your ever-evolving service.
You'll choose to rev the API when, as a whole, you decide that the overall picture of what the API needs to do is significantly changed enough that it cannot peacefully serve the needs of existing clients. You'll choose to deprecate and obsolete certain client versions when you decide that maintaining support for them on the service is causing you more headache than the install base is worth (a very business/case specific issue).
Good luck.
I don't know if having your iOS app specify the version of the api it requires is good practice but, I would think it is a safe play; one concern though, if you frequently update your api then it won't be long before it becomes a hassle/anoying having to frequently update the app.
I would keep legacy method name(s) and add method(s) with a different name to avoid users having to update to new version of the app when you change the web api.
I would not create an api class for every version to extend the previous version of the api.
I would say changing the database structure would require changing/updating your api, unless you also want to keep legacy version of your table name or definition or data, which it is not feasible/practical/convenient in most instances/situations. In this case you want your users to update to the new app and api.
Look at this answer that points to a presentation of API design principles and practices.
I do not know as to what best practice, however I would definitely recommend that your iOS app keep track of what version of your API it is looking for, and specifically request that version. For instance, a URL of '/api/v1/....'. This way When you update your API, you can simply up it to a different version ('/api/v2/...', and leave v1 alone for the iOS app to consume. Obviously you should display a message to the iOS user to upgrade (perhaps a meta field in your response) when a newer version exists.
This approach should allow you to continue development on your API without cutting off people who haven't been able to upgrade their app.
Update
Just one more thing; if you make a change that will make a previous version inoperable (such as changing table names, schema, etc), you should have a status code for that that your iOS app will understand. Something associated with the message 'This API version has been retired. You must update'.
I would also recommend a similar header (or something) when an API is deprecated (ie, a new version exists). Obviously continue to provided the requested information/actions, however a warning that the version is not supported anymore and that they should upgrade (or even triggering something in your app to upgrade) can be helpful.