setScreenname() only fires after logEvent() - swift

I try to use Firebase to track my app. Here's my code:
import UIKit
import Firebase
class Section1121: UIViewController {
override func loadView() {
super.loadView()
Analytics.setScreenName("Screen1.1.2.1", screenClass: "Section1121")
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
// Do any additional setup after loading the view.
Analytics.logEvent(AnalyticsEventAddToCart, parameters: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
I expect following order:
1: default screen_view event fires, without changing screen name
2: setScreenname fires and changes the screen name
3: logevent fires, sending add_to_cart event with new screen name
But actual order on debug view is 1 -> 3 -> 2, and add_to_cart is logged without new screen name.
Here's the screenshot of debug view.
In the code setScreenname is definitely before logEvent, but why the firing order has changed?
Can I make it fire before logEvent?

Following adbitx's advice, I moved setScreenName to viewDidAppear just before logEvent, and the code seems to work properly.

Related

How to Complete Async call before app loads?

I couldn't find the answer to this probably because I'm not really sure what I'm looking for since i just started programing a few weeks ago.
My storyboard entry point requires data that I get from an asynchronous session and JSON parse. Then once it gets the data it stores it to NSUserDefaults so it doesn't have to make the async call again and the app can access that data anytime.
I put my async call in the viewdidload of the storyboard entry point because as far as I know thats where the app starts. The issue is that the data isn't showing up until the app is started for a second time.
The data I'm getting from the async call only changes once every month so its not necessarily time sensitive.
How can I delay the app from getting to the storyboard entry point until the async call is finished?
Is that even the right way to go about it?
Should I switch to a synchronous call?
What if I changed the storyboard entry point to a view controller that looked like the app was loading and then when the async call finished, use a completion handler to perform segue to the view controller that requires the asynchronous call to finish?
Thanks Leo that worked.
Storing a variable when the app is first installed
I did two things here. First I created a new view controller that would execute the async call and segue to my main view controller when it finished. Then I detected if it was the first launch or not by using the above linked solution. Both of those together worked.
import Foundation
import UIKit
class FirstLoad: UIViewController {
var installedDate: NSDate? {
get {
return NSUserDefaults().objectForKey("installedDateKey") as? NSDate
}
set {
NSUserDefaults().setObject(newValue, forKey: "installedDateKey")
}
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewDidAppear(animated: Bool) {
firstLoad()
}
func firstLoad() {
if installedDate == nil {
installedDate = NSDate()
parseData(heroesDataProject) { heroesArrayFromParse in //this function gets my json and the following code is executed after completion
saveToDefaults("heroesOriginal")
print("First Run")
self.performSegueWithIdentifier("firstLoadToHomeMenu", sender: nil)
}
} else {
print("Not first run, installd on \(installedDate!)")
loadFromDefaults(userProfile)
performSegueWithIdentifier("firstLoadToHomeMenu", sender: nil)
}
}
}

How to pass a string between viewcontrollers with out the use of a SEGUE

Recently I have been making a app where you can create and quiz yourself on definitions or anything for that matter. I pass data to the next view after it the user hits the create button to make the title of the new notecard. The code I am using right now for that is:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let DestViewController: Card1 = segue.destinationViewController as! Card1
DestViewController.Content = Notetitle.text!
self.saved = self.Notetitle.text!
}
All of that works but, it will only work if I have a segue between viewcontrollers. I need to be able to pass that data with out a segue because I want the user to be able to create as many notecards as they want and the way I am trying to do that now is by using this code to make a copy of the UIView and then put in the new data (a master view). The new view can only be create using an IBAction. The prepare for segue I cannot use in the IBAction because it is it's own override function.
This is the code I am using to make a the new view:
let newCard =
self.storyboard!.instantiateViewControllerWithIdentifier("Main")
self.presentViewController(newCard, animated: true, completion:nil)
My hope is that I will be able to make a new view and then pass in the data pass in the data that the user just made to go on the notecard. (Hope this makes any sense at all)
MAIN TOPICS: -Create a new view and pass in new data Problem: Can pass data without a segue dont have one :/ -Be able to pass data between view controllers without a segue :)
I am new to all of this about 5 months. All of my code is in swift. Take it easy on me please. Feel free to ask me with any questions or comments. I have already posted a question on this but I didnt get an answer so have at it.
Thanks, Lucas Mazza
Don't use global variables unless you really need to. Making global static singleton's does not follow best practices. For more information read: What is so bad about singletons?
A better solution
You can use the protocol delegate pattern. I've actually written an article on this topic here:
https://www.codebeaulieu.com/36/Passing-data-with-the-protocol-delegate-pattern
You'll need a protocol that defines a function that will accept data. Then your other view controller will need to implement the delegate. If you need step-by-step details see the link provided above, alternatively you can simply download the project below and examine the code.
Download Working Example Project
Here's the code to make your protocol-delegate pattern work:
View Controller 1:
class ViewController: UIViewController, PresentedViewControllerDelegate {
#IBOutlet weak var textOutlet: UILabel!
#IBAction func doPresent(sender: AnyObject) {
let pvc = storyboard?.instantiateViewControllerWithIdentifier("PresentedViewController") as! PresentedViewController
pvc.data = "important data sent via delegate!"
pvc.delegate = self
self.presentViewController(pvc, animated: true, completion: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func acceptData(data: AnyObject!) {
self.textOutlet.text = "\(data!)"
}
}
View Controller 2:
import UIKit
// place the protocol in the view controller that is being presented
protocol PresentedViewControllerDelegate {
func acceptData(data: AnyObject!)
}
class PresentedViewController: UIViewController {
// create a variable that will recieve / send messages
// between the view controllers.
var delegate : PresentedViewControllerDelegate?
// another data outlet
var data : AnyObject?
#IBOutlet weak var textFieldOutlet: UITextField!
#IBAction func doDismiss(sender: AnyObject) {
if textFieldOutlet.text != "" {
self.presentingViewController!.dismissViewControllerAnimated(true, completion: nil)
}
}
override func viewDidLoad() {
super.viewDidLoad()
print("\(data!)")
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
if self.isBeingDismissed() {
self.delegate?.acceptData(textFieldOutlet.text)
}
}
}

MBCalendarKit showing black screen in basic tabbed application

I'm fairly new to swift & Xcode and I'm using a basic "tabbed" application template in XCode and I am trying to implement the MBCalendarKit to show a calendar on the first tab view. I am using Cocoapods for the MBCalendar kit. The Basic tabbed application starts with the following files:
The author of MBCalendarKit explains that :
"You can show an instance of CKCalendarView. Use this if you want to manually manage your view hierarchy or have a finer control over your calendar view." :
/*
Here's how you'd show a CKCalendarView from within a view controller.
It's just four easy steps.
*/
// 0. In either case, import CalendarKit:
#import "CalendarKit/CalendarKit.h"
// 1. Instantiate a CKCalendarView
CKCalendarView *calendar = [CKCalendarView new];
// 2. Optionally, set up the datasource and delegates
[calendar setDelegate:self];
[calendar setDataSource:self];
// 3. Present the calendar
[[self view] addSubview:calendar];
And I also followed some of the example code that was provided although most of it was in objective-c. Currently when running the program, On the first tab view, I'm just getting a black screen. How can I fix this to present the calendar itself? I have also looked at the other CalendarKit posts on stackoverflow but none seem to tackle something as specific as this.
My FirstViewController.swift (Which is basically identical to that provided by the author) :
import UIKit
import MBCalendarKit
class FirstViewController: CKCalendarViewController, CKCalendarViewDataSource {
var data : NSMutableDictionary
required init(coder aDecoder: NSCoder) {
data = NSMutableDictionary()
super.init(coder: aDecoder)
}
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
self.data = NSMutableDictionary()
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// Step 0 : Wire up the data source and delegate
self.delegate = self
self.dataSource = self
// Step 1 : Define some test events
let title : NSString = NSLocalizedString("Some random event", comment: "")
let date : NSDate = NSDate(day: 20, month: 12, year: 2015)
let event : CKCalendarEvent = CKCalendarEvent(title: title as String, andDate: date, andInfo: nil)
// Step 2 : Add the events to the cache array
self.data[date] = [event]
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//
// MARK: - CKCalendarDataSource
//
func calendarView(calendarView: CKCalendarView!, eventsForDate date: NSDate!) -> [AnyObject]! {
return self.data.objectForKey(date) as! [AnyObject]!
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
and the AppDelegate.swift:
import UIKit
import MBCalendarKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
var viewController: CKCalendarViewController = FirstViewController(nibName: nil, bundle: nil)
self.window!.rootViewController = viewController
self.window!.makeKeyAndVisible()
return true
}
func applicationWillResignActive(application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
func applicationDidEnterBackground(application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(application: UIApplication) {
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
}
Also when creating the FirstViewController() in the AppDelegate file I was a little unsure of the nibName and bundle arguments. If someone could please explain this as I tried to look it up on Apples reference documentation but it still wasn't clear, and for all I know the fact that I passe nil for both could be my issue? If anyone has experience with CalendarKit or can help me out that would be great,
Cheers

AVMIDIPlayer leaks memory

I would like to play a MIDI file on iOS 8.2 with AVMIDIPlayer.
This does work indeed, but for a very short number of times: the memory seems never to be freed, and it is eventually filled.
Below is my code in Swift 1.2, reduced to a bare minimum: everything takes place in the ViewController, there are no other classes, no delegates, no error checking, etc.
The interface has just three buttons: NEW (instantiates a new AVMIDIPlayer and prerolls), PLAY, STOP.
This memory problem happens on the Simulator and on an actual device, and the bigger the soundfont and the midifile, the worse it is.
I noticed that RAM grows when creating a new AVMIDIPlayer instance AND when playing the midifile (in the latter case you can get even +4Mb/sec).
What am I missing?
Thanks for your help.
import UIKit
import AVFoundation
class ViewController: UIViewController {
var mp:AVMIDIPlayer?
#IBAction func newButton(sender: AnyObject) {
mp = AVMIDIPlayer(contentsOfURL: NSBundle.mainBundle().URLForResource("ahVous", withExtension:"mid"),
soundBankURL: NSBundle.mainBundle().URLForResource("TimGM6mb", withExtension:"sf2"),
error: nil)
mp!.prepareToPlay()
}
#IBAction func playButton(sender: AnyObject) {
mp!.play(nil)
}
#IBAction func stopButton(sender: AnyObject) {
mp!.stop()
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

Can't dismiss keyboard, UITextField delegate being called OK

This problem has been driving me crazy. It seems no matter what I try I cannot get the keyboard to hide once it is shown in my simple Swift program.
It does not work when I resignFirstResponder() in textFieldShouldReturn nor does it work when handing a background touch by calling endEditing() from touchesBegan(...). I can see these respective methods are all being called when I set debugger break points to them, so the delegate is properly set and being called as expected.
Here are the specific steps I took:
Create a new single-view application Swift project
Drag a UITextField onto the view, wire up the IBOutlet, and set the ViewController up as UITextFieldDelegate.
Run in iPhone 6 Simulator or on an iPhone 6 device
Once the keyboard is presented, it is never dismissed!!
I am out of ideas - what am I missing???
Here is the entire contents of my ViewController:
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var myTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
myTextField.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
self.view.endEditing(true)
}
func textFieldDidBeginEditing(textField: UITextField!) {
return
}
func textFieldShouldEndEditing(textField: UITextField!) -> Bool {
return false
}
func textFieldShouldReturn(textField: UITextField!) -> Bool {
textField.resignFirstResponder()
return true
}
}
You are returning 'false' from 'textFieldShouldEndEditing'. The following is an excerpt from the 'UITextFieldDelegate' documentation:
Return Value
YES if editing should stop; otherwise, NO if the editing session
should continue
Discussion
This method is called when the text field is asked to resign the first
responder status. This might occur when your application asks the text
field to resign focus or when the user tries to change the editing
focus to another control. Before the focus actually changes, however,
the text field calls this method to give your delegate a chance to
decide whether it should.
Normally, you would return YES from this method to allow the text
field to resign the first responder status. You might return NO,
however, in cases where your delegate detects invalid contents in the
text field. By returning NO, you could prevent the user from switching
to another control until the text field contained a valid value.
Source
So, either return 'true' from it or remove the method completely, unless you really need to do something useful inside it. 'textFieldDidBeginEditing'and 'touchesBegan' should also probably be removed. I'm really surprised that the same code worked in Objective-C.