Swift function exits when instantiating object subclass - ios

I have a very simple subclass of GMSMarker and in it I have added a couple of properties to make point markers more identifiable. In my subclass I have the following:
import UIKit
class PointMarker: GMSMarker {
var layerID:String?
var featureID:String?
}
In one class that uses the subclass, I have a function that renders map features.
func addRemoveMapPoint(featureDict:Dictionary<String, AnyObject>, visible:Bool){
if let properties = featureDict["properties"] as? Dictionary<String, AnyObject> {
let marker:PointMarker = PointMarker()
print(properties)
// Code to render on the map ...
}
}
This function can be called directly from an object instance or it can be called from an NSNotification. The weird behavior I am experiencing is that when called directly from code (not from a notification) all works as expected. However, when called from the NSNotification, the function exits on the line that creates an instance of PointMarker.
let marker:PointMarker = PointMarker()
When code reaches this line, the function simply exits and no more code is executed. It behaves as if there is a break or return statement there. I receive no errors and the app does not crash. I have verified that the incoming parameter featureDict is identical whether called from an object or from a notification.
Can anyone offer an explanation as to why this is happening? Thanks!

Related

How would I change the value of a UILabel from a different class in swift?

I am a student trying to make an app in Xcode, and I have run into an issue to which I cannot figure out the solution. I want to access a string I have stored in another class and make it the label text of my ViewController.
In my ViewController, I call a different class, which then calls through an API and finds me a string detailing the weather in a certain place. This is what my call looks like from my ViewController class
func getWeather() {
manager = DataManager()
manager.fetchData(query: getQueryString();
}
And this is what the call looks like from the DataManager Class
func rainyWeatherPopulate(str: String){
LoadEvents.loadEvents(type: EventType.rain, str: str)
}
in the loadEvents function, I get a string detailing the type of weather that I want to put as text of a UILabel in my ViewController class, and I am not sure how to do so
Is there any way that I can pass an instance of my ViewController to my loadEvents static function or any other way in which I can set the text of a UILabel in my ViewController from the LoadEvents class?
There are many potential ways to solve this problem, but one is by passing a closure to be used upon completion of the call.
So, it might look like this in your use case:
class DataManager {
func fetchData(query: String, onComplete: #escaping (String) -> Void) {
LoadEvents.loadEvents(type: .rain, str: query, onComplete: onComplete)
}
}
enum EventType {
case rain
}
class LoadEvents {
static func loadEvents(type: EventType, str: String, onComplete: (String) -> Void) {
//load the data
//then, when it's done, call the completion
onComplete("returnedData")
}
}
func getWeather() {
let manager = DataManager()
let uiLabel = UILabel()
manager.fetchData(query: "queryString", onComplete: { result in
uiLabel.text = result
})
}
I had to mock out some stuff (like EventType) and I had no information about the types of events you're loading, so I just used a generic String for the return type, but the concept is here.
How it works:
In getWeather, there's a closure called onComplete that will get data back once everything has completed. result holds (obviously) the result -- in this case a string, and it sets uiLabel's text property to that value.
LoadEvents does whatever it needs to and then upon finishing, calls onComplete and sends the results back through the closure.
I'd say jnpdx's solution of passing a closure to your data manager is the best answer.
Another way you could do this is to add a delegate property to your data manager. The only thing you know about the delegate is that it conforms to a specific protocol. You'd then invoke methods on the delegate from the data manager that would tell the delegate about data that was available for display. The delegate would decide what to do with that data.
I mention this as an alternative method, but would suggest using closures as jnpdx recommends in their answer.

Memory Leak Kotlin Native library in iOS

I'm building a Kotlin library to use in my iOS app using Kotlin/Native. After I call some methods in the library from Swift, which works, I also want to call methods in Swift from the library. To accomplish this I implemented an interface in the library:
class Outbound {
interface HostInterfaceForTracking {
fun calcFeatureVector(bitmap: Any?): Array<Array<FloatArray>>?
}
var hostInterface: HostInterfaceForTracking? = null
fun registerInterface(hostInterface: HostInterfaceForTracking) {
this.hostInterface = hostInterface
instance.hostInterface = hostInterface
}
}
This is implemented on the Swift side like this:
class HostInterfaceForTracking : OutboundHostInterfaceForTracking {
var t : Outbound? = nil
init() {
TrackingWrapper.instance?.runOnMatchingLibraryThread {
self.t = Outbound()
self.t!.registerInterface(hostInterface: self)
}
}
func calcFeatureVector(bitmap: Any?) -> KotlinArray<KotlinArray<KotlinFloatArray>>? {
do {
var test : Any? = (bitmap as! Bitmap).bitmap
return nil
} catch {
return nil
}
}
}
The TrackingWrapper looks like this:
class TrackingWrapper : NSObject {
static var instance: TrackingWrapper? = nil
var inbound: Inbound? = nil
var worker: Worker
override init() {
self.worker = Worker()
super.init()
initInboundInterface()
}
func initInboundInterface() {
runOnMatchingLibraryThread {
TrackingWrapper.instance = self
self.inbound = Inbound()
HostInterfaceForTracking()
}
}
func runOnMatchingLibraryThread(block: #escaping() -> Void) {
worker.enqueue {
block()
}
}
}
The function runOnMatchingLibraryThread is needed because every call to the TrackingLibrary needs to be called from the exact same thread, so the Worker class initializes a thread and enqueues every method to that thread.
The Bitmap in this case is simply a wrapper for an UIImage, which I already accessed with the .bitmap call, so I've tried to access the wrapped UIImage and save it in the test variable. The library gets the current camera frame from the Swift side every few frames and sends the current image wrapped as a Bitmap to the method calcFeatureVector depicted here.
Problem: My memory load starts increasing as soon as the app starts until the point it crashes. This is not the case if I don't access the wrapped UIImage (var test : Any? = (bitmap as! Bitmap)). So there is a huge memory leak, just by accessing the wrapped variable on the Swift side. Is there anything I've missed or is there any way to release the memory?
Looks like you have a circular dependency here:
TrackingWrapper.instance?.runOnMatchingLibraryThread {
self.t = Outbound()
self.t!.registerInterface(hostInterface: self)
}
You are asking a property inside HostInterfaceForTracking to maintain a strong reference to the same instance of HostInterfaceForTracking. You should be using [weak self] to avoid the circular reference.
EDIT:
Ok after seeing the rest of you code theres a lot to unpack. There is a lot of unnecessary bouncing back and forth between classes, functions and threads.
There is no need to use runOnMatchingLibraryThread to just create an instance of something. You only need to use that for the code processing the image itself (I would assume, I haven't seen anything so far that requires being split off into another thread). Inside TrackingWrapper, you can create a singleton more easily, and matching the swift pattern by simply doing this as the first line:
static let shared = TrackingWrapper()
And everywhere you want to use it, you can just call TrackingWrapper.shared. This is more common and will avoid one of the levels of indirection in the code.
I'm not sure what Worker or Inbound are, but again these can and should be created inside the TrackingWrapper init, rather than branching Inbound's init, to use another thread.
Inside initInboundInterface you are creating an instance of HostInterfaceForTracking() which doesn't get stored anywhere. The only reason HostInterfaceForTracking is continuing to stay in memory after its creation, is because of the internal circular dependency inside it. This is 100% causing some form of a memory issue for you. This should probably also be a property on TrackingWrapper, and again, its Init should not be called inside runOnMatchingLibraryThread.
Having HostInterfaceForTracking's init, also using runOnMatchingLibraryThread is problematic. If we inline all the code whats happening is this:
TrackingWrapper
init() {
self.runOnMatchingLibraryThread {
TrackingWrapper.instance = self
self.inbound = Inbound()
TrackingWrapper.instance?.runOnMatchingLibraryThread {
self.t = Outbound()
self.t!.registerInterface(hostInterface: self)
}
}
}
Having all these classes unnecessarily keep coming back to TrackingWrapper is going to cause issues.
Inside HostInterfaceForTracking 's init, no need to be creating Outbound on a separate thread. First line in this class can simply be:
var t : Outbound = OutBound()
Or do it in the init if you prefer. Either way will also remove the issue of needing to unwrap Outbound before using it.
Inside Outbound you are storing 2 references to the hostInterface instance:
this.hostInterface = hostInterface
instance.hostInterface = hostInterface
I would have imagined there should only be 1. If there are now multiple copies of a class that has a circular dependency, which has multiple calls to separate threads. This again will cause issues.
I'm still not sure on the differences between Swift and Kotlin. In Swift when passing self into a function to be stored, the class storing it would mark the property as weak, like so:
weak var hostInterface: ......
Which will avoid any circular dependency from forming. A quick google says this isn't how things work in Kotlin. It might be better to look into the swift side passing in a closure (lambda on kotlin) and the kotlin side executing that. This might avoid the need to store a strong reference. Otherwise you need to be looking into some part of your code setting hostInterface back to null. Again its a bit hard to say only seeing some of the code and not knowing how its working.
In short, it looks like the code is very over complicated, and needs to be simplified, so that all these moving pieces can be tracked easier.

Is there a way to have a custom class act on a UIView without having the ViewController passed (as reference) upon its initialization?

Example: I have a SpeechSynthesizer class that needs to update something in my UIView when it’s done uttering a piece of text. Since the SpeechSynthesizer class conforms to protocol AVSpeechSynthesizerDelegate, it is the one that receives the didFinish signal when the uttering has been completed. The idea here is to keep the ViewController from having too many delegate methods and a long list of protocols to conform to. The workaround I found was to have the ViewController passed in as a SpeechSynthesizer initialization parameter. This way I get to access the ViewController connected to the UIView I want to update from inside the SpeechSynthesizer class. The thing I don’t like about it is that it looks kind of ugly to have the ViewController passed in as a parameter to every single class that needs to use it. So I wonder, which other way I could accomplish this.
I suppose another way to ask the question is: How can I make the function
private func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinish utterance: AVSpeechUtterance)
return something to a ViewController since it's not "called" by it?
I added a reply on Quora. Copying it here:
After doing some research and testing on code of my own here are 2 solutions to this problem.
Solution 1: The Delegate Pattern:
Create a custom delegate protocol in the ViewController
protocol ViewControllerDelegate:class {
func getViewLayer() -> CALayer
}
The ViewController must conform to this newly created protocol and therefore implement all the functions defined by it, so somewhere in the class ViewController you add:
public func getViewLayer() -> CALayer {
return self.view.layer
}
Then on my custom class, ReadTextMachine, I added a variable of the ViewControllerDelegate type
private weak var viewControllerDelegate: ViewControllerDelegate?
The variable must be weak and protocol must be of type class in order to solve a “retain cycle” problem (since both the custom class and the ViewController will point to each other)
You’ll notice now that the function call inside the ViewController is already “callable” from the custom class, so in my ReadTextMachine I added:
let viewLayer = self.viewControllerDelegate?.getViewLayer()
self.cameraPreview = CameraPreview(session: self.camera.getSession(), container: viewLayer!)
self.cameraPreview?.addPreview()
In the above case, my CameraPreview (yes, a 3rd class in this example) simply adds a camera preview layer on the UIView. For that it needed access to the main View’s layer.
The above code still doesn’t work because our original viewController’s instance hasn’t been passed as reference anywhere in our code. For that we add the following function in ReadTextMachine:
public func setViewControllerDelegate(viewController: ViewController) { // call this from the ViewController so that ViewController can be accessed from here.
self.viewControllerDelegate = viewController
}
The above piece of code will have to be called from the ViewController, after we instantiate our custom class (ReadTextMachine), so that the viewControllerDelegate inside it points to the ViewController. So in our ViewController.swift:
operatingMode = ReadTextMachine()
operatingMode.setViewControllerDelegate(viewController: self)
Another example and explanation can be found in this video from LetsBuildThatApp. I derived my solution mostly from it.
My current app in development applying the above solution can be found here: agu3rra/World-Aloud
Solution 2: Notifications and Observers pattern
This one is a bit easier to understand and follow. The general idea is to have your custom class broadcast a message which triggers a function call on your ViewController since it has an observer setup, waiting to hear that message.
So to give an example, in the context I used it, I have a CameraCapture class which uses AVFoundation to capture a photo. The capture photo trigger cannot immediately return an image, since iOS has a set of steps to execute before actually generating an image. I wanted my ReadTextMachine to resume an activity after CameraCapture had a photo available. (To apply this in the context of the CustomClass triggers ViewController event is basically the same, since both are actual classes in an iOS app as well).
So the 1st thing I did was create a broadcast function since I would use it in many places in my app. I simply placed it in a Utilities.swift file in the Xcode project.
public func broadcastNotification(name: String) {
let notification = Notification.Name(rawValue: name)
NotificationCenter.default.post(name: notification, object: nil)
}
The above function takes a string, which must be a unique notification identifier, and broadcasts it thru NotificationCenter.
In my CameraCapture class, I added a static constant to reference the unique identifier of the message:
static let NOTIFY_PHOTO_CAPTURED = "agu3rra.worldAloud.photo.captured"
For those who know AVFoundation, a photo is available when event didFinishProcessingPhoto gets executed, so at the end of that I added:
broadcastNotification(name: CameraCapture.NOTIFY_PHOTO_CAPTURED)
The above is a call to my previously defined utility function.
For my ReadTextMachine class to be able to catch that notification, I added the following on its init() and deinit routines:
override init() {
super.init()
// Setup event observers
let notification1 = Notification.Name(rawValue: CameraCapture.NOTIFY_PHOTO_CAPTURED)
NotificationCenter.default.addObserver(self,
selector: #selector(self.processingDoneTakingPhoto),
name: notification1,
object: nil)
}
deinit {
NotificationCenter.default.removeObserver(self) // cleanup observer once instance no longer exists
}
Removing the observer is important at deinit so that when your object is deallocated from memory, the observer isn’t left lingering around. The above configured observer triggers a function call inside ReadTextMachine:
#IBAction private func processingDoneTakingPhoto() {
// does my stuff
}
That’s it! Again, the entire Xcode project can be downloaded from my project’s Git repository: agu3rra/World-Aloud
Hope this can be of use to others.
Cheers!

iOS Delegate is returning nil (Swift)

I have a feeling there is more than one problem with this code, but my first issue is that my delegate returns nil and I do not know why. First, is my delegate:
import UIKit
//delegate to move information to next screen
protocol userEnteredDataDelegate {
func userDidEnterInformation(info:NSArray)
}
Next, I have a var defined for the delegate and I believe the ? makes it an optional variable? This is defined inside the class
var dataPassDelegate:userEnteredDataDelegate? = nil
Now, after my user has entered information into the fields in the view, I want to add those field values to an array and then pass that array on to the next view where it will be added to. I have pieced this code together from some YouTube examples but I think I am missing a needed part. When do I assign some kind of value to the dataPassDelegate var so it is not nil when the if statement comes? Do I even need that if statement?
if blankData != 1 {
//add code to pass data to next veiw controller
enteredDataArray = [enterDate.text, enterSeason.text, enterSport.text, enterDispTo.text]
//println(enteredDataArray)
self.appIsWorking ()
if (dataPassDelegate != nil) {
let information: NSArray = enteredDataArray
println(information)
dataPassDelegate!.userDidEnterInformation(information)
self.navigationController?.popViewControllerAnimated(true)
} else {
println ("dataPassDelegate = nil")
}
//performSegueWithIdentifier("goToDispenseScreenTwo", sender: self)
activityIndicator.stopAnimating()
UIApplication.sharedApplication().endIgnoringInteractionEvents()
}
blankData = 0
}
Your help is appreciated.
A delegate is a pointer to another object that conforms to a particular protocol. Often you use delegates to refine the behavior of your class, or to send back status information o the results of an async network request
When you set your dataPassDelegate delegate is up to you.
What is the object that has the dataPassDelegate property? What object will be serving as the delegate?
You need to create 2 objects (the object that will be serving as the delegate, and the object that has the dataPassDelegate property) and link them up.
We can't tell you when to do that because we don't know what you're trying to do or where these objects will be used.

Accessing application delegate variable delays view loading in swift

I am a newbie to Swift and i have started my new project with Swift. I am facing a delay issue while loading a viewcontroller.
On the application delegate i have a variable
var allTerms: [Dictionary<String, AnyObject>]?
This allTerms is populated with data from a local json file of 900Kb. The total json data count is 800.
So far i have a home screen and a second view. From the home screen when i navigate to second screen i need to access this allTerms from the application delegate. Referring to great tutorials,i was able to access the allTerms variable from the application delegate
let appDelegate = UIApplication.sharedApplication().delegate as AppDelegate!
self.tableData = (appDelegate.allTerms! as NSArray) as? Array
However doing so this is causing a noticeable delay in loading the secondview , which doesnot happen if i comment the line
self.tableData = (appDelegate.allTerms! as NSArray) as? Array
Appreciate any suggestions!
You might want to create a separate data manager class instead of storing it in the app delegate. You could use something like this:
class DataManager {
var allTerms: [[String:AnyObject]]?
class var sharedInstance: DataManager {
struct Singleton {
static let instance = DataManager()
}
return Singleton.instance
}
// You can access allTerms by calling DataManager.sharedInstance.allTerms
}
This probably won't solve your lag, but it's a good practice to make a DataManager class to store things. I also rewrote your allTerms declaration to use the short form for the dictionary.

Resources