I am trying to read CoreData out of an iMessage Extension (MSMessagesAppViewController) but as there is no AppDelegate there is also no managedContext.
Has anybody an idea if that is possible?
Is it possible to "instantiate the AppDelegate" of my App or something like that?
Update: How to access core data within an app and an extension: The solution for this is to use AppGroup container.
Using Core Data has nothing to do with whether there is an application delegate. They are two separate things. You can put them together but neither Core Data nor the application delegate requires that. It's not even very good design to do so. The app delegate's job is to respond to app life-cycle events like launching, going into the background, etc, and it's bad design to also mix in the separate job of managing the app's data.
If you create a new project in Xcode and check the "use Core Data" box, Xcode will include methods in the app delegate class to set up Core Data for the app. But that code can go in any class. There's nothing special about the app delegate that requires that code to be in that class. Just because Xcode puts that code in that class doesn't mean that it's necessary to do it that way. If you need to use Core Data in an app extension, that's what you'll do.
One common approach is to create a new class to manage Core Data. Put all your Core Data stack management code there, instead of in the app delegate. The app delegate might create an instance of this class in your app. In an app extension, the class can be instantiated wherever it's needed for the type of extension.
Related
Background: In order to make web requests to an API endpoint, I need to scrape a website and retrieve a token every 25-30 seconds. I'm doing this with a WKWebView and injecting some custom JavaScript using WKUserScript to retrieve AJAX response headers containing the token. Please focus on the question specifically and not on this background information - I'm attempting this entirely for my own educational purposes.
Goal
I will have different 'model' classes, or even just other UIViewControllers, that may need to call the shared UIViewController to retrieve this token to make an authenticated request.
Maybe I might abstract this into one "Sdk" class. Regardless, this 'model' SDK class could be instantiated and used by any other ViewController.
More info
I would like to be able to call the UIViewController of the WKWebView and retrieve some data. Unless I re-create it every 25 seconds, I need to run it in the background or share it. I would like to be able to run a UIViewController 'in the background' and receive some information from it once WKWebView has done it's thing.
I know there are multiple ways of communicating with another ViewController including delegation and segueing. However, I'm not sure that these help me keep the view containing the WKWebView existing in the background so I can call it's ViewController and have it re-perform the scrape. Delegation may work for normal code, but what about one that must have the view existing? Would I have to re-create this WKWebView dynamically each time a different model, or view controller, were to try and get this token?
One post suggests utilising ContainerViewControllers. From this, I gather that in the 'master' ViewController (the one containing the other ones), I could place the hidden WKWebView to do it's thing and communicate to the child view controllers that way via delegation.
Another post suggests using AppDelegate and making it a shared service. I'm completely against using a Singleton as it is widely considered an anti-pattern. There must be another way, even if a little more complex, that helps me do what I want without resorting to this 'cheat'.
This post talks about communicating between multiple ViewControllers, but I can't figure out how this would be useful when something needs to stay running and executing things.
How about any other ways to do this? Run something in a background thread with a strong pointer so it doesn't get discarded? I'm using Xcode 9.2, Swift 4, and iOS 11. As I'm very new to iOS programming, any small code examples on this would be appreciated.
Unfortunately, WKWebView must be in the view hierarchy to use it. You must have added it as a sub view of an on-screen view controller.
This was fine for me. I added this off-screen so it was not visible. Hidden attribute might have worked as well. Either way you must call addSubview with it to make it work.
There are some other questions and answers here which verify this.
Here is a way if you don't wish to use a singleton.
1- In the DidFinishlaunchingWithOptions, Make a timer that runs in the background and call a method inside the app delegate Called FetchNewToken.
2- In FetchNewToken, make the call needed and retrieve the new token (you can use alamofire or any 3rd library to make the call easier for you).
Up on successfully retrieving the token, save it in NSUserDefaults under the name upToDateToken
You can access this token anywhere from the application using NSUserDefaults and it will always be up to date.
My goal is to send a dictionary to the watchKit from iOS app prior to the watchKit's app launch. I'm using interactive messaging (sendMessage) to quickly transfer the dictionary.
The issue is - dictionary is created inside the MainViewController. If i declare the WCSession and activate it inside the MainViewController i can transfer the data to the watchKit on the simulator without any problem. But when i test the process on a real device - the iOS app never gets called.
Waking the app in the background is done by declaring and activating the WCSession inside the AppDelegate, but there's another blocker - i cannot create the dictionary - because multiple variables for its creation are declared inside the MainViewController.
I tried a third approach - wrapping the WCSession inside a singleton (suggested by Natasha the robot). The only drawback of this framework is that the Interactive messaging never works and wasn't ever tested by Natasha herself.
So i'm confused - what do i do to send the dictionary to the watchKit?
Thanks for any insights
You need to figure out a way to get the dictionary created outside of MainViewController. Perhaps you can write a class method in the controller that creates and returns the dictionary so that it can be used from both AppDelegate and MainViewController.
You should use a data store to hold your dictionary, then have it create its data based on the variables passed to it by the main view controller.
Once that occurs, you can use the WCSession manager to transfer the data store's dictionary.
I know Natasha covers these aspects in her tutorial. If you have a specific question as to how to do that, you'd really need to post code showing what you tried, along with a description of what's not working.
If the watch asks for data, but it has not been created yet, you need to return a "No data yet" reply so the watch can display a message telling the user to open the app and set the view controller's variables used for creating the data.
It really is better to separate and encapsulate responsibilities into these different components. The view controller shouldn't need to contain any code related to creating or transferring the dictionary.
Having said all that...
I cannot create the dictionary - because multiple variables for its creation are declared inside the MainViewController
This really sounds like an XY problem. You've been focused on the problem of "sending" this dictionary of large arrays that you have to create, when there's likely an easier way to accomplish what you're actually trying to do with this large dictionary in the first place.
For one, I'd wonder why you're sending that huge computed data set to the watch for it to do something with, instead of also handling that computation on the phone side, then sending a very small set of "results".
Perhaps you should describe the real Y problem you want to solve on the watch, instead of asking us for an X solution which may end up being unnecessary.
I'm developing an app which handles some downloads in the background. I move the file to the documents directory and save it under the original name (using downloadTask.originalRequest?.URL!.pathExtension). So far so good.
Using
URLSession(_:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:)
I can print out the progress in the console while downloading. But this doesn't help me if I'm using the App on the phone. What I'm expecting to do is to call a function to update a progress bar from here within my View that was initialising the download. But I have no idea how to call it.
Does anybody know how I can find out from which UIViewController (actually most of the time it will be a TableCellViewController) I started the download and how to send the progress to a function of that ViewController?
I 'd like to tell you a good tutorial about this question. NSURLSession Tutorial
Delegates are often used in the following situations
When a class needs to communicate some information to another class
When a class wants to allow another class to customize it
The classes don't need to know anything about each other beforehand except that the delegate class conforms to the required protocol.
I the following article you can see how to create a delegate in objective-C and Swift 2.0.
How do I create delegates in Objective-C?
I am very new to iOS. I am developing an app with data persistence. I have decided to use Realm for that purpose.
I must to create the database and load data the first time that app runs. I get data from a Web Service in JSON format. I will implement some strategy to update this database later, maybe with iOS Silent Push notifications.
I have read and I have worked about Realm, loading data from JSON... to learn about that.
Now, I need to apply this in my project but I don't know how to start. I need some clues about general idea for the app:
How can I organize my app to load data when it is installed? At what point should I create the database and load data?
I have thought to create a global Realm object y AppDelegate and use it as a global variable. Is it a good idea?
Do I need to set a path for my database? Can I user default path?
If you are looking for a place to start, you can check out the example apps of this UI component add-on for Realm: ABFRealmGridController.
The controller is a subclass of UICollectionView and the example app should demonstrate most of the functionality you are curious about. The example uses the controller to display the top news stories from the New York Times. This involves making a request to their API and loading the JSON response data into Realm.
When to load the data is dependent on how you want the app to function. If the data will be the same for each user, you could bundle the Realm file with the app pre-populated with data.
The ABFRealmGridController example loads data when the user clicks the refresh button and performs the JSON handling on a background thread; a general best-practice.
Finally, unless you have multiple Realms or need to store the file in a specific path, it is probably simplest to use the default path.
In the Core Data lecture from Stanford 193P iPhone course on iTunes, the instructor coded up a sample project with Core Data without using NSPersistentStoreCoordinator and loading it with a NSManagedObjectModel. But in looking at other code samples and the Big Nerd Ranch book on iPhone development, they are creating a NSManagedObjectModel and PersistentStoreCoordinator and setting up the NSManagedObjectContext that way.
My question is what is the purpose of doing it this way, and what are the pros and cons of both approaches?
I followed the same lecture series very closely. This particular example pulls data (Photographers and Photos) from Flickr and loads them into CoreData. It wasn't really necessary to use CoreData in this app since it needs to fetch new data from flickr on every application load, therefore there is no point in saving persistently. The prof was just using the flickr fetching app from the previous demo as a starting point since students were already familiar with it (allowing him to focus on explaining CoreData). However, as rickster mentioned, there are huge benefits to using core data without saving the context to disk.
As Paul explained in the lecture before the demo, a core database can be created (in iOS5) either by:
Clicking "use core data" for an app template when creating a new project.
Using UIManagedDocument
The idea behind the first approach is that Xcode will put a bunch of code in AppDelegate to set up your documents directory/persistent store coordinator/and model. It will then pass the managed object CONTEXT to your initial view controller (which should have an NSManagedObjectContext property in the public API) and from there you can pass the context around like a bottle of beer when you segue to other viewcontrollers. Passing the context around is correct procedure for accessing the core database.
Using UIManagedDocument is very similar, except your AppDelegate is left alone. You create a UIManagedDocument (maybe in your initial view controller) using a URL path from your app's document directory (Note: you have to manually check to see if the file already exits, exists but is not open, or does not exist). Then you can use this document's context in the same way as above.
Another Note: It's a good idea to create a pointer to your context in your AppDelegate so you are able to explicitly save your context (only when it's ready!) when the app crashes or terminates.
The persistent store coordinator is set up automatically for you and you can configure it using it's persistentStoreOptions property (and indeed you will need to in order to save the context persistently), or by subclassing UIManagedDocument and overriding desired methods.
Read the overview in UIManagedDocument documentation
http://developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UIManagedDocument_Class/Reference/Reference.html
Both methods work the same way and provide you with the same control and access. With UIManagedDocuments you can create multiple databases in multiple sqlite files, you can also wait to create / set up the database until it's needed. The "use core data" option provides you with a single core database which it sets up on application load, allows you to centralize CoreData stuff around AppDelegate, saves coding time and is good for a fast-track app. I like UIManagedDocument.
If you started you app without the core data option checked and would like to add it to AppDelegate, just create a new project with core data checked and copy all the code to your AppDelegate (should just be 3 properties and their accessors as well as a convenience method to access the documents directory). You will need to point in to your initial view controller, model, etc..
UPDATE:
Just wanted to add one other convenience. If your managed object context is stored in your appDelegate, you can access it anywhere in your app just by using
NSManagedObjectContext* context = [[(AppDelegate*) [UIApplication sharedApplication] delegate] myManagedObjectContext];
this negates having to pass it around.
For any CoreData app, if you make any changes to your model, MAKE SURE TO MANUALLY DELETE THE APP IN THE SIMULATOR before building again. Otherwise you will get an error on the next build since it will use the old file.
Without a persistent store coordinator you will be unable to save your results to a persistent area (database, file, etc)...so if you want a persistent data manager that is utterly useless, then omit NSPersistentStoreCoordinator. Are you sure the project wasn't using it? How was the professor saving the data? When you create a new Core Data project, this logic is autogenerated for you.
EDIT: I got it now, the professor is using UIManagedDocument, which uses its own persistent store coordinator internally (based on the file type) so there is no need to create an explicit one (unless you are not satisfied with the default). So, in the end, its not about whether or not to use a coordinator, it is whether or not you explicitly create it.