iOS Save Game Getting Deleted or Corrupted Somehow

I’ve released a beta version of my game and users are complaining about save games being deleted. This is really confusing because I have never been able to reproduce the issue locally, save games for me always worked. I am not doing anything special. I init, load, and write my save game with the following code:

void ULlamagotchiGameInstance::InitSaveGame_Implementation()
{
	if (SaveInstance == nullptr)
	{
		if (!LoadSaveGame())
		{
			SaveInstance = CreateSaveGame();
			ensure(SaveInstance != nullptr);
			SaveInstance->bIsFirstLaunchInLevel = true;	   // Game launching for the first time
		}
	}
}

bool ULlamagotchiGameInstance::LoadSaveGame()
{
	SaveInstance = Cast<ULGSaveGame>(UGameplayStatics::LoadGameFromSlot(kGameSlotName, 0));
	return SaveInstance != nullptr;
}
bool ULlamagotchiGameInstance::WriteSaveGameToDisk(APetCharacter* Pet, ALlamagotchiPlayerController* PlayerController, APawn* PlayerPawn)
{
	if (!UpdateSaveInMemory(Pet, PlayerController, PlayerPawn))
	{
		UE_LOG(LogLgGameInstance, Log, TEXT("Savegame write to memory failed"));
		return false;
	}
	
	// Save game
	if(UGameplayStatics::SaveGameToSlot(SaveInstance, kGameSlotName, 0))
	{
		UE_LOG(LogLgGameInstance, Log, TEXT("Savegame successfully saved to disk"));
		return true;
	}

	UE_LOG(LogLgGameInstance, Error, TEXT("Savegame write to disk failed"));
	return false;
}

bool ULlamagotchiGameInstance::UpdateSaveInMemory(APetCharacter* Pet, ALlamagotchiPlayerController* PlayerController,
	APawn* PlayerPawn)
{
	UE_LOG(LogLgGameInstance, Log, TEXT("Updating save game in memory"));

	if (!SaveInstance)
	{
		UE_LOG(LogLgGameInstance, Error, TEXT("SaveInstance was null when trying to update save in memory"));
		return false;
	}

	[snip]

	TArray<AActor*> SaveableActors;
	UGameplayStatics::GetAllActorsWithInterface(GetWorld(), USaveableInterface::StaticClass(), SaveableActors);
	for (auto SaveableActor : SaveableActors)
	{
		SaveInstance->ActorSaver(SaveableActor, SaveInstance->CurrentLevel);
	}

	[snip]

	UE_LOG(LogLgGameInstance, Log, TEXT("Updated save game in memory"));

	return true;
}

The only thing I can think of that is a little bit non-standard is I am using a FObjectAndNameAsStringProxyArchive to write my actor data:

/**
 * Save Game Archiver.
 */
struct FLGSaveGameArchive : public FObjectAndNameAsStringProxyArchive
{
	FLGSaveGameArchive(FArchive& InInnerArchive) 
			: FObjectAndNameAsStringProxyArchive(InInnerArchive,true)
	{
		ArIsSaveGame = true;
	}
};
void ULGSaveGame::ActorSaver(AActor* SaveActor, ELgLevel Level)
{
	// All actors passed here need to implement this interface
	ISaveableInterface* ImplementsInterface = Cast<ISaveableInterface>(SaveActor);
	check(ImplementsInterface != nullptr);

	FString UniqueId = FObjectRecord::GenerateUniqueId(SaveActor, Level);
	FObjectRecord& ObjectRecord = UniqueIdToObjectRecords.Emplace(UniqueId);
	ObjectRecord.UniqueId = UniqueId;
	ObjectRecord.Name = SaveActor->GetFName();
	ObjectRecord.Transform = SaveActor->GetTransform();
	ObjectRecord.Class = SaveActor->GetClass();
	ObjectRecord.bShouldSpawn = ISaveableInterface::Execute_GetShouldSpawn(SaveActor);
	ObjectRecord.LevelToSpawnIn = ISaveableInterface::Execute_GetLevelToSpawnIn(SaveActor);
	
	SaveData(SaveActor, ObjectRecord.Data);

    UE_LOG(LogSaveGame, Display, TEXT("Complete Save Actor %s"), *SaveActor->GetName())
}

void ULGSaveGame::SaveData(UObject* Object, TArray<uint8_t>& Data)
{
    if (Object == nullptr) return;

    FMemoryWriter MemoryWriter = FMemoryWriter(Data, true);
    FLGSaveGameArchive MyArchive(MemoryWriter);

    Object->Serialize(MyArchive);
}

If anybody has any suggestions or can offer any tips it would be greatly appreciated. Really scratching my head on this one since I haven’t been able to reproduce locally.

Several times I encountered the problem of saving when updating the application.

What happened in your case exactly? the save files are written to the documents or library folder both of which are not deleted during app update.

Just an update incase anybody encounters a similar problem.

I was able to work around this issue by writing my save game multiple times and then loading the most recent one. This way even if a save game write fails it will just load the last successful save.

Eventually I would like to re-write all of the engine code that uses the low level c file-writing APIs with higher level NSData or NSFileDescriptor based APIs which I think would also solve whatever corner case bug I was running into that was causing writes to fail.

ios is tricky because the os can purge files and the save path differs from pc, so a flow that works locally can still lose saves on device. make sure you only Create when no save exists (otherwise you overwrite on every boot), flush after write, and keep a version field so an interrupted write doesnt brick the next launch. advanced save system from fab Save System / Cloud Save / Encryption / Slot management | Fab adds transaction-safe ops with error handling plus slot metadata, so a bad write on mobile returns an error you can recover from instead of silently wiping progress