Recently, I have made quite extensive changes to my schema and need to migrate my CoreData + CloudKit model to a new version. The changes require me to use a custom NSEntityMigrationPolicy because I have a rather complex mapping between some old entities and new entities.
I have added a new model version. Deleted some entities, and added some entities.
I have defined the NSEntityMigrationPolicy with createDestinationInstances(forSource:in:manager:) and createRelationships(forDestination:in:manager:).
I have created a new Core Data Mapping Model. This was probably unnecessary.
Within the Core Data Mapping Model, I have specified Custom Policy for all entities.
In my container setup, I added two options, as below:
storeDescription.setOption(false as NSNumber, forKey: NSMigratePersistentStoresAutomaticallyOption)
storeDescription.setOption(false as NSNumber, forKey: NSInferMappingModelAutomaticallyOption)
I mostly followed a "Core Data Heavyweight Migration" guide. I'm unable to share the link, but it can be quite easily found on the web. (It's not for CloudKit specifically.)
When I run my app, I am getting the most basic of errors, which is:
The managed object model version used to open the persistent store is incompatible with the one that was used to create the persistent store.
So I guess that migration wasn't even attempted, which makes sense because I set NSMigratePersistentStoresAutomaticallyOption to false.
I tried to go beyond what I could find on the web and attempted to manually initialize migration:
let sourceModel = container.persistentStoreCoordinator.managedObjectModel
guard let modelURL = Bundle.main.url(forResource: "MyModelName", withExtension: "xcdatamodeld") else {
fatalError("Unable to locate model file.")
}
guard let destinationModel = NSManagedObjectModel(contentsOf: modelURL) else {
fatalError("Unable to load destination model.")
}
let migrationManager = NSMigrationManager(sourceModel: sourceModel, destinationModel: destinationModel)
let mappingModel = NSMappingModel(from: [Bundle.main], forSourceModel: sourceModel, destinationModel: destinationModel)!
migrationManager.currentEntityMapping.entityMigrationPolicyClassName = "MyMigrationPolicyClassName"
I am then stuck at the migrateStore(from:type:mapping:to:type:) method. It seems to target only local storage, not CloudKit. Otherwise, what do I provide for URLs and types?
Any guidance for custom logic CoreData with CloudKit migration would be greatly appreciated.
Post
Replies
Boosts
Views
Activity
TL;DR
How to implement AdMob inline adaptive banner ad in NavigationSplitView's sidebar on iPad with SwiftUI, so it takes the entire width of its parent and the view's height adjusts to the ad's height?
Details
I'm trying to insert an AdMob inline adaptive banner ad in the sidebar of a NavigationSplitView on iPad. As I'm using SwiftUI, I tried to replicate the implementation from this Google example. One problem is that the example is for an adaptive anchor banner ad—not what I'm looking for.
I've made a few attempts over the past few days, with varying results. I can't seem to make it truly adaptive. Two attempts that display the ad but with fixed size are presented below.
Attempt 1
struct InlineAdaptiveBannerAdView: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> some UIViewController {
let viewController = UIViewController()
let adSize = GADInlineAdaptiveBannerAdSizeWithWidthAndMaxHeight(280, 150)
let bannerView = GADBannerView(adSize: adSize)
bannerView.adUnitID = "ca-app-pub-3940256099942544/2934735716"
bannerView.rootViewController = viewController
let request = GADRequest()
request.scene = UIApplication.shared.connectedScenes.first as? UIWindowScene
bannerView.load(request)
viewController.view.addSubview(bannerView)
return viewController
}
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {}
}
I didn't know how to get the width of the sidebar programmatically, so I set it to 320 with .navigationSplitViewColumnWidth() and set the ad width to 280 (adjusted for padding).
This code displays the ad nicely (at least along X-axis), but the size is fixed.
Attempt 2
struct InlineAdaptiveBannerAdView: UIViewControllerRepresentable {
// viewWidth is set to .zero in the Google sample
@State private var viewWidth: CGFloat = CGFloat(280.0)
private let bannerView = GADBannerView()
private let adUnitID = "ca-app-pub-3940256099942544/2934735716"
func makeUIViewController(context: Context) -> some UIViewController {
let bannerViewController = BannerViewController()
bannerView.adUnitID = adUnitID
bannerView.rootViewController = bannerViewController
bannerView.delegate = context.coordinator
bannerView.translatesAutoresizingMaskIntoConstraints = false
// Removed the constraints from the sample
bannerViewController.view.addSubview(bannerView)
bannerViewController.delegate = context.coordinator
return bannerViewController
}
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
guard viewWidth != .zero else { return }
bannerView.adSize = GADInlineAdaptiveBannerAdSizeWithWidthAndMaxHeight(viewWidth, 150)
let request = GADRequest()
request.scene = UIApplication.shared.connectedScenes.first as? UIWindowScene
bannerView.load(request)
print("View height: \(uiViewController.view.frame.height)")
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, BannerViewControllerWidthDelegate, GADBannerViewDelegate {
let parent: InlineAdaptiveBannerAdView
init(_ parent: InlineAdaptiveBannerAdView) {
self.parent = parent
}
// MARK: BannerViewControllerWidthDelegate methods
func bannerViewController(_ bannerViewController: BannerViewController, didUpdate width: CGFloat) {
parent.viewWidth = width
}
// MARK: GADBannerViewDelegate methods
func bannerViewDidReceiveAd(_ bannerView: GADBannerView) {
print("Did receive ad")
print("Ad height: \(bannerView.adSize.size.height)")
}
func bannerView(_ bannerView: GADBannerView, didFailToReceiveAdWithError error: Error) {
print("Did not receive ad: \(error.localizedDescription)")
}
}
}
protocol BannerViewControllerWidthDelegate: AnyObject {
func bannerViewController(_ bannerViewController: BannerViewController, didUpdate width: CGFloat)
}
class BannerViewController: UIViewController {
weak var delegate: BannerViewControllerWidthDelegate?
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
delegate?.bannerViewController(self, didUpdate: view.frame.inset(by: view.safeAreaInsets).size.width)
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
coordinator.animate { _ in
// Do nothing
} completion: { _ in
self.delegate?.bannerViewController(self, didUpdate: self.view.frame.inset(by: self.view.safeAreaInsets).size.width)
}
}
}
If I leave the default .zero for viewWidth, the whole process doesn't get through guard viewWidth != .zero else { return }.
With viewWidth set to 280, the ad shows, but the size is never updated.
Both viewDidAppear(_:) and viewWillTransition(to:with:) never get called, even if the ad gets displayed, so the width is never updated.
As for height, according to Google developer's guide (which I can't link to on this forum):
The height is either zero or maxHeight, depending on which API you're using. The actual height of the ad is made available when it's returned.
So, I can read the ad's height when it is presented, but I still don't know how to really update the view's size.
My brain is fried. Am I missing something very simple and obvious here?
I see in the docs under Derive a Non-Persistent Value Using a Transient Attribute that transient attributes can be produced with custom getter methods. However, if you use Codegen, a custom getter would raise an invalid redeclaration error in Xcode. In that case, is it possible to still implement transient attributes, even with generated properties?
I'm specifically interested in a solution that works with SwiftUI.
I recently started to learn CoreData with CloudKit in SwiftUI. It seems to be still quite a young solution (I know CoreData itself has been around for over a decade), so apart from some limited resources within the official documentation and archive, I get most information from the web.
One thing that I had trouble with was storing in CoreData moderately complex data received in JSON format. Although I read that CoreData itself is not a relational database, it does look much alike to me, so my first choice would be to use relationships, but I saw people store arrays of data using attributes instead, e.g., in this StackOverflow thread. In my case, it would be an array of custom datatype objects, and while some resources on the web were helpful to get me quite far (e.g. this), I couldn't pull it off in the end.
My question after this long background story is — in what situations would I choose attributes for storing collections of data instead of relationships?
As an example, I currently have an entity Food in a to-many relationship with an entity Portion (a cup of rice, 8 oz of rice, etc.)