IAP-Enabled Apps Get Rejected from iOS Store

Original Forum Post With Additional Info Here

We have an issue where our App is repeatedly rejected from the iOS Store because it apparently does not work with In-App Purchases over an IPv6 Network. According to 4.12 release notes, IPv6 is now supported - but something in our project is clearly disabling it or preventing it from working.

We have two versions of our game, the full edition which has Game-Center Support but no In-App Purchases, and we are now trying to publish a ‘Lite’ edition which is free, but contains ads and in-app purchases. This version is already available on the Google store and works just fine.

The issue is that Apple are continuosly rejecting our App, and are also now rejecting updates to the full version of the game for the same reason despite it having no in-app purchase support at all. It’s almost as if a system / environment variable has been set somewhere - and our machine is building out incorrectly. This still however doesn’t solve why the Lite version is being rejected in the first place.

If you check the forum post, you can see what steps we have already taken to try and resolve the issue.

Set the bCompileForSize option to false in your IOSEngine.ini file. If you didn’t have this file before, you are now building the executable in a different way (all of those settings in the BuildSettings section). We’ve had troubles with compile for size with recent updates to the clang compiler which we are still finding. Removing that will increase the size of the executable, but you didn’t have it before anyway so it shouldn’t be an issue.

Do you have access to an IPv6 network for testing? If so, after the build, try testing the IAP to see if you get a connection to the store. If not, then try submitting again and see if it passes.

-Pete

Ok, discount this answer. It turns out that while IPv6 was enabled in 4.12, it wasn’t properly setup for iOS. There is a fix for this, but it is pretty involved and will require modifying several file. Further, it doesn’t auto detect whether the network is IPv4 vs. IPv6. Pulled from a UDN post:

To those interested in getting IPv6 sockets working:

For IOS to use IPv6, have the /sockets/IOS/SocketSubsystemIOS.h derive from SocketSubsystemBSDIPv6:

SocketSubsystemIOS.h:

 #include "BSDIPv6Sockets/SocketSubsystemBSDIPv6.h"
 ...
 class FSocketSubsystemIOS : public FSocketSubsystemBSDIPv6
 {
 ...
 };

SocketSubsystemIOS.cpp:

 FSocket* FSocketSubsystemIOS::CreateSocket(const FName& SocketType, const FString& SocketDescription, bool bForceUDP)
 {
 	FSocketBSDIPv6* NewSocket = (FSocketBSDIPv6*)FSocketSubsystemBSDIPv6::CreateSocket(SocketType, SocketDescription, bForceUDP);
 	if (NewSocket)
 	{
 		// disable the SIGPIPE exception
 		int bAllow = 1;
 		setsockopt(NewSocket->GetNativeSocket(), SOL_SOCKET, SO_NOSIGPIPE, &bAllow, sizeof(bAllow));
 	}
 	return NewSocket;
 }

However, this isn’t the only change needed. There is a bug that happens when you try to connect to the same URL twice (maybe consecutive games?), where the first time successfully resolves to an IPv6 IP, but the second time comes back as an IPv4 IP which has been forced into IPv6 notation. This is due to the constructor for FResolveInfoCached, specifically FResolveInfoCached(const FInternetAddr& InAddr). It only takes the uint32 portion of the IP from the InAddr.

This fix is a bit more involved, since in the current system FResolveInfoCached is only defined in IPAddress.h. Here are the steps used to resolve this issue:

in IPAddress.h:

 class FResolveInfoCached : public FResolveInfo
 {
 protected:
 
 	/** Hidden on purpose */
 	FResolveInfoCached() {}
 	/** The address that was resolved */
 	TSharedPtr<FInternetAddr> Addr;
 
 ...
 };

Next, add in SocketSubsystem.h in ISocketSubsystem:

 virtual FResolveInfoCached *CreateResolveInfoCached(TSharedPtr<FInternetAddr> Addr) const;

in SocketSubsystem.cpp:

add:

 FResolveInfoCached *ISocketSubsystem::CreateResolveInfoCached(TSharedPtr<FInternetAddr> Addr) const
 {
 	return new FResolveInfoCached(*Addr);
 }
 
 and modify FResolveInfo* ISocketSubsystem::GetHostByName(const ANSICHAR* HostName)
 {
    ...
    if (GetHostByNameFromCache(HostName, Addr))
    {
 	Result = CreateResolveInfoCached(Addr);
    }
    ...
 }

Now we need to add the BSDIPv6 support:

In IPAddressBSDIPv6.h:
add the following class

 class FResolveInfoCachedBSDIPv6 : public FResolveInfoCached
 {
 	FResolveInfoCachedBSDIPv6();
 public:
 	FResolveInfoCachedBSDIPv6(const FInternetAddr& InAddr)
 	{
 		const FInternetAddrBSDIPv6 *InAddrAsIPv6 = static_cast< const FInternetAddrBSDIPv6* >(&InAddr);
 		if(InAddrAsIPv6)
 		{
 			Addr = ISocketSubsystem::Get()->CreateInternetAddr();
 			FInternetAddrBSDIPv6 *AddrAsIPv6 = static_cast<FInternetAddrBSDIPv6*>(Addr.Get());
 			if(AddrAsIPv6)
 			{
 				AddrAsIPv6->SetPort(InAddr.GetPort());
 				in6_addr temp;
 				InAddrAsIPv6->GetIpv6(temp);
 				AddrAsIPv6->SetIp(temp);
 			}
 
 		}
 		else
 		{
 			uint32 IpAddr;
 			InAddr.GetIp(IpAddr);
 			Addr = ISocketSubsystem::Get()->CreateInternetAddr(IpAddr, InAddr.GetPort());
 		}
 	}
 };

And finally, modify SocketSubsystemBSDIPv6.h, adding:

 virtual FResolveInfoCached *CreateResolveInfoCached(TSharedPtr<FInternetAddr> Addr) const override;

to FSocketSubsystemBSDIPv6.

In SocketSubsystemBSDIPv6.cpp:

 FResolveInfoCached *FSocketSubsystemBSDIPv6::CreateResolveInfoCached(TSharedPtr<FInternetAddr> Addr) const
 {
 	return new FResolveInfoCachedBSDIPv6(*Addr);
 }

This should allow you to use BSD ipv6 sockets in IOS.

(Courtesy of Nik of iNiS Corporation).

Ignore this answer and utilize my second answer.

Awesome, thanks for such a detailed answer. I’ve just skimmed through and this all looks fine but now that 4.13 is out, would it be easier for us to just transition over to that instead?

The reason I ask is because we’re on Binary build for simplicities sake, and switching to source would be a bit tricky.

Unfortunately, this is not in 4.13 yet, but will be in the 4.13.1 patch after I do some more testing. That will probably be a couple of weeks as we are also waiting on the final release of Xcode and iOS 10.

Amazing. Thanks so much for taking the time, I’ll keep an eye on 4.13 patch notes!

Hi Ravlek,

I follow your steps and got some errors when rebuild the engine. But now we resolve the errors an add comment here to help other one who meet the same issue.

  • In SocketSubsystem.h, add class before FResolveInfoCached.

virtual class FResolveInfoCached *CreateResolveInfoCached(TSharedPtr Addr) const;

  • In IPAddressBSDIPv6.h:

remove #if 0 at line 194.

remove #endif at line 204.

and add a public function

SOCKET GetNativeSocket()
{
	return Socket;
}

Finally, you can pass the build process without error. Please correct me if i am wrong. :slight_smile:

Cheers.