I can't wrap my head around this. I read everywhere (rightly) that Audiokit lifecyle should be managed in a singleton and kept away from the View Controllers lifecyle. Then I checked all the example project's code and found a bit confusing that
AudioKit.start()
is always called in ViewDidload. But anyway thats not my real issue..
I have multiple ViewControllers that uses the AKMicrophone in a different way. For instance they need different categories, different AKSettings, and some must be plain microphones while some needs a long chain of initialization with effects and mixers (Just like the "Recorder" example project).
I have two related questions here:
I read on Github that when we change the routing or category, we need to reinitialize the AudioKit. But in this case where do we really do it? In my case I really only can think of "ViewWillAppear". (if Mic needs to start listening without needing a button click, with a different setup)
And real question is how do I do this correctly at all? For example in my project AKMicrophone is set up just like the "Recorder" example but in a Singleton class. And when I switch to another ViewController which requires a "plain" AKMicrohone right after view appears. How do I remove all those mixers and delays from the chain? This is not only a matter of changing some AKSetting and restarting the engine.
Related
Context:
My app needs the user's current location in several unrelated view controllers and, as of now, I'm creating a CLLocationManager in each of these controllers and requesting the current location over and over.
However, it doesn't look right to me. It's duplicated code and inneficient.
I've seen several references on the internet mentioning a singleton location manager. And I also heard to avoid singletons as much as possible.
Question:
Should I create a singleton class that manages the location? If not, what other solution would you suggest to avoid this duplicated code?
And I also heard to avoid singletons as much as possible
The above statement is correct in case that some dealoocated cycle of the app run needs that data so after it finishes the singleton becomes a problem from memory perspective as it stays alive all the app life cycle , in your case ( all vcs inside the app needs the location ) is the best fit for a singleton to avoid the duplication - less-efficient code and high memory issues
Needless to say 90% of apple classes uses singletons e.x
URLSession.shared
NSNotificationCenter.default
UNUserNotificationCenter.current
UserDefaults.standard
FileManager.default
There is no hard and fast rule. Apple is quite clear that it's fine to instantiate more than one location manager. However, if you do that, you might confuse yourself, because each will need a delegate, each will need its own settings, and so forth. Thus, you are right to be wary of that approach.
It is a standard strategy to pick an instance that persists throughout the life of your app — your app delegate, or your root view controller, for example — and initialize an instance property with a location manager. There is no reason whatever to add the Singleton pattern to the mix; you simply make one once and keep hold of it.
Patterns are not evil. It depends on the user. They are solutions for a specific problems. It depends on how you see your problem and what you want to achieve.
In your case, you need a location manager instance through out your app and you use it in multiple places, so you need a wrapper manager. If you only need one configuration, then it makes sense to use singleton.
Apple is recommending to have a strong reference of the manager as long as you need it. CLLocationManager - Apple Documentation
Create an instance of the CLLocationManager class and store a strong reference to it somewhere in your app.
Keeping a strong reference to the location manager object is required until all tasks involving that object are complete. Because most location manager tasks run asynchronously, storing your location manager in a local variable is insufficient.
If you create a singleton location manager it has to do things differently than other singletons. You can't use the normal delegate pattern for it to inform other objects about the location updates and errors because the normal delegate pattern is a one-to-one relationship. You have to use multiple delegates (the singleton has an array of interested objects and it sends the same message, e.g. location update, to each one). Here's another question about why that's difficult to use:
Multiple Delegates in iOS
You can get around that with notifications but personally I think that's a bad pattern (it decouples things too much and makes it hard to follow paths of responsibility).
The singleton also has to keep track of whether any of its interested objects asked it to stop or start. If they've all asked it to stop then it should power down the updates. If just one wants updates it has to power them back up. Search for all the people building frameworks just to do the same task with the network indicator to see how much trouble this is.
If there's an error with requesting location you have to save that error and when (some later time) an object wants location, you have to retransmit the error. So really you want to have all interested objects connected and listening from the start to avoid that scenario.
I'm sure I could think of more hairy cases that you'd have to deal with.
The in-between option is to create a location manager class with your special setup, error checking and so on, and instantiate one of those whenever you need it. Use the delegate pattern to get messages from it (most will just be passing the messages along directly).
I have some complex networking in my app( I don't use any third party dependencies, because of project requirements). For instance, I send three network requests in parallel after first two requests provide results. All my networking is done in separate models, known as networkClients(following MVC-S pattern) and are called directly from repository, not from ViewControllers. However, I need the last request to notify my viewController after I get response from network. How should I do that? I don't think notification center would be right solution because it can cause memory leaks and I have not found correct approach to complex problem like this. Please provide some prominent solutions. It should conform to good design pattern like MVVM or MVC and should not be some workaround or hack. Maybe delegates would work? I know that rxSwift would solve my issue, because I could start observing for results after initializing viewController and after data would be updated from repository my viewController would also be notified...
The right design doesn't have VCs observing the network clients directly. Those network operations should be assembling parts of a model, which is what the VC really cares about. Have the VC observe that singular model.
It can do this observing using one of the well known patterns for loosely coupled communication between objects. The OP correctly mentions delegates. Notification center and KVO are others. There's plenty of discussion on SO about which to use and how to implement. (I'd go with NSNotificationCenter as an easy and rational start).
So the order of operation is like this:
allocate the model
launch the network requests and setup those request completions (completion blocks, probably) to update that model with their responses. (the model can launch the requests, which is a reasonable practice).
create the view controller(s) that setup model observation when they initialize (probably, in viewWillAppear or later)
What about the fact that >1 requests are in flight simultaneously? A commenter above points out correctly that GCD offers a way to group those async operations into a single one. But you can do this yourself straight-forwardly: the model decides when it's completely built. The completion code for each request will change some condition in the model to the "ready" state. Each request completion can check to see whether all of the ready conditions are met, and only then post a "ready" notification for observers to see.
Another niggling issue: what if those requests all run very, very fast? Maybe there's some cached response that's ready early, making the model "ready" before the VC has had a chance to setup observation? Handle this straight-forwardly in the VC: before observing the model, check to see if it's ready already and run the same update code that runs on the notification.
The app I'm developing requires dynamically adding/removing/rearranging components in the sound chain.
So far, I have mostly been using the .disconnectOutput() method on most components, then reconnecting everything. This works most of the time, but occasionally it seems that a node is connected at multiple points in the sound chain, and I also get crashes if the node is connected to AudioKit.output.
AudioKit provides a number of public methods such as .detach(), .disconnectInput(), .disconnect() and I'm not really clear on what is cleanest or safest way to modify the sound chain. What is the best way to do this?
Also, is there some way to keep track of which nodes are connected to which?
Use the detach() method on an AKNode to remove it from the chain.
The disconnect() and disconnect(nodes: ) methods of AKNode are deprecated. Use AKNode.detach() and AudioKit.detach(nodes: ) instead.
I agree this terminology is very unclear and not explained in the existing documentation. I am still struggling with the lifecycle and runtime chain dynamics as I learn the API, so I can't convey best practices. In general, you don't want to break your object graph. I'm using AKMixer objects and then dynamically attaching child nodes using the .connect(input:bus:) and .disconnectInput(bus:) methods and internal tracking of the associated busses, but I am still running into crashes with this approach :(
Apple's parent AVAudioEngine documentation page provides a couple rules of thumb for dynamic chaining practices: https://developer.apple.com/documentation/avfoundation/avaudioengine
I am writing automation testing for my iOS app and I am trying to figure out how to detect in the javascript script when a view controller fully loaded and is on screen...
So right now for example the script taps on a button:
target.frontMostApp().mainWindow().buttons()["loginButton"].tap();
Then, once the app logs in (which could take a few seconds or less) I need to press another button.
Right now, I made it work by simply putting a delay:
target.delay(3);
But I want to be able to detect when the next view controller is loaded so that I know I can access the elements on the new screen just loaded.
Any suggestions?
There are some ways to achieve that:
Tune Up Library
https://github.com/alexvollmer/tuneup_js
I would really recommend to check this out. They have some useful wrapper function to write more advanced test. Some methods is written to extend the UIAElement class. They have waitUntilVisible() method.
But there might be possibility that the element itself is nil. Its not the matter of visibility but its just not in the screen yet.
UIATargetInstance.pushTimeout(delay) and UIATargetInstance.popTimeout()
Apple documentation says :
To provide some flexibility in such cases and to give you finer
control over timing, UI Automation provides for a timeout period, a
period during which it will repeatedly attempt to perform the
specified action before failing. If the action completes during the
timeout period, that line of code returns, and your script can
proceed. If the action doesn’t complete during the timeout period, an
exception is thrown. The default timeout period is five seconds, but
your script can change that at any time.
You can also use UIATarget.delay(delay); function, but this delays the execution of the next statement, not nescessarily waiting to find the element. setTimeout(delay) can also be used to globally set the timeout before UIAutomation decides it could not find the element.
There is Apple's guide explaining more understanding towards the framework here. http://developer.apple.com/library/IOS/#documentation/DeveloperTools/Conceptual/InstrumentsUserGuide/UsingtheAutomationInstrument/UsingtheAutomationInstrument.html
In most cases simply identifying an element specific to the next view controller should be sufficient. The description of the element must be something unique to the new UI however.
i.e.
target.frontmostApp().mainWindo().navigationBar()["My Next View"];
The script should delay until the element can be identified or the timeout has been reached (see pushTimeout popTimout).
In some cases it might be necessary to specify that the element is visible.
.withValueForKey(1, "isVisible")
It's also possible to use waitForInvalid to do the reverse (wait for something in the previous UI to go away).
Search for tuneup library here
Refer function waitUntilVisible for the same. you may include other files to for supporting functions in it.
Hope it help.
I recently inherited maintenance of a relatively small iOS application. The app was created by external contractors with very little guidance and oversight. Needless to say it needs "a little" cleaning - I am evaluating whether to redo the entire thing or not.
One thing that got me stumped is a crash in the app whereby the debugger shows "Applications are expected to have a root view controller at the end of application launch". In the module where this occurs, I researched all UIViews to make sure they are created with a parent (addSubView sets the root view controller, right?) - this seems all prim and proper.
Being rather new to XCode, I am not familiar with facilities that help me figure out what might be going on here. E.g., how can I quickly see/investigate the status of all UIViews created by a module? How can I 'watch' a variable just to be alerted when it changes? And in general, is there a best strategy to use to tackle issues like the one I described above?
Sorry to stay a bit vague but I don't think that publishing a bunch of ugly code helps you to understand the problem better :-)
That particular problem happens when the application's UIWindow doesn't have a value for its rootViewController property by the time it finishes launching. Take a look in your application delegate file - usually, the root view controller is set on the window there.
With regards to your more general questions: there are a variety of ways to inspect the state of your program as it runs. A very basic way to dump some info is to use an NSLog statement - you can print out messages to the console in much the same way as a C printf would. You can also set breakpoints in your application and use the debugger to inspect different variables - take a look at the lldb documentation for more info.
Whether you have "dangling" UIViews or not has not much to do with having a root view controller or not.
Instead, you should make sure that your app's UIWindow has a rootViewController at the end of your app delegates appDidFinishLaunching method.