How to create a share with CloudKit's CKShare? - ios

I'm studying the new CKShare that Apple released this year and I have some questions with it. I was trying to follow the WWDC's "What's new in CloudKit" video but part of the code isn't working anymore in Xcode.
What I'm trying to do is: the user will enter his name and phone and after clicking a UIButton, will share with a specific person. Here is the code:
class ViewController: UIViewController, UICloudSharingControllerDelegate {
#IBOutlet weak var nome: UITextField!
#IBOutlet weak var telefone: UITextField!
#IBOutlet weak var button: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func send(_ sender: AnyObject) {
print("Send button was clicked.\n")
let elder = CKRecord(recordType: "Elder")
elder["name"] = self.name.text as CKRecordValue?
elder["telephone"] = self.telephone.text as CKRecordValue?
let ckContainer = CKContainer.default()
let shareRecord = CKShare(rootRecord: elder)
shareRecord[CKShareTitleKey] = "Elder" as CKRecordValue?
let shareController = UICloudSharingController(share: shareRecord, container: ckContainer)
shareController.delegate = self
shareController.availablePermissions = [.allowReadOnly]
shareController.popoverPresentationController?.sourceView = self.button
self.present(shareController, animated: true)
}
func cloudSharingController(_ csc: UICloudSharingController, failedToSaveShareWithError error: Error) {
print("PROBLEM SAVING SHARE")
}
func cloudSharingControllerDidSaveShare(_ csc: UICloudSharingController) {
print("SHARE SAVED")
}
func itemTitle(for csc: UICloudSharingController) -> String? {
return "a" //don't know what this is for
}
}
My app keeps printing "PROBLEM SAVING SHARE" even though the CloudSharingController appears. Also, the CKRecord elder isn't appearing in CloudKit's dashboard.

So, I figured out what was wrong... I was using the default zone that cloud kit creates for you and you CANNOT do that when using CKShare.
All you have to do is create a private custom CKZone and save the CKRecord and CKShare in it!

I was told that you can use the initializer that you have chosen to use only if the share has already been created. For new shares, use the alternate init
Try this code:
let csc = UICloudSharingController { controller,
preparationCompletionHandler in
let share = CKShare(rootRecord: elder)
let mro = CKModifyRecordsOperation(
recordsToSave: [elder, share],
recordIDsToDelete: nil)
mro.modifyRecordsCompletionBlock = {
records, recordIDs, error in
if error != nil {
//handle error
}
preparationCompletionHandler(share,CKContainer.default(), error)
}
myDB.add(mro)
}
csc.delegate = self
self.present(csc, animated:true, completion:nil)

Related

How to keep label results on secondViewController?

I am currently working on an app and I am stuck on the following: I have my mainVC (ReceiveInputVC), which after I enter an input, it goes to the secondVC (TimeLeftVC) and it updates all of its labels with results from the inputs received from the mainVC. My question is: How can I, after clicking on the arrow to go back to the mainVC or even if I close the app, when I click on the arrow from the mainVC to go to the secondVC have my labels showing the same values as before the user closed the application or returned to the main screen?
import UIKit
extension UIViewController {
func hideKeyboard() {
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
view.addGestureRecognizer(tap)
}
#objc func dismissKeyboard() {
view.endEditing(true)
}
}
class ReceiveInputVC: UIViewController {
#IBOutlet weak var hourglassButton: UIButton!
#IBOutlet weak var whatIsYourAgeField: UITextField!
#IBOutlet weak var ageToDieField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.hideKeyboard()
}
#IBAction func arrowBtnPressed(_ sender: Any) {
// When pressed should show go to TimeLeftVC and show last result from the first time user entered the inputs, if nothing has been typed yet and no data has been saved an alert should pop up asking the user to enter an input on both fields
}
#IBAction func hourglassBtnPressed(_ sender: Any) {
let checkAgeField: Int? = Int(whatIsYourAgeField.text!)
let checkDyingAgeField: Int? = Int(ageToDieField.text!)
if (whatIsYourAgeField.text == "" || ageToDieField.text == "") || (whatIsYourAgeField.text == "" && ageToDieField.text == "") {
alert(message: "You must enter an input on both fields")
} else if checkAgeField! < 1 || checkDyingAgeField! > 100 {
alert(message: "You must enter an age higher than 1 and a dying age lower than 100")
} else if (checkAgeField! > checkDyingAgeField!) || (checkAgeField! == checkDyingAgeField!) {
alert(message: "You must enter an age lower than a dying age")
} else {
performSegue(withIdentifier: "goToSecondScreen", sender: self)
}
}
func alert(message: String, title: String = "Alert") {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Try Again", style: UIAlertAction.Style.default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
// Passing the data entered from ReceiveInputVC to TimeLeftVC
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToSecondScreen" {
let destinationTimeLeftVC = segue.destination as! TimeLeftVC
destinationTimeLeftVC.ageReceived = whatIsYourAgeField.text
destinationTimeLeftVC.ageToDieReceived = ageToDieField.text
}
}
}
import CircleProgressBar
class TimeLeftVC: UIViewController {
var ageReceived: String! // receive whatIsYourAgeField data from ReceiveInputVC
var ageToDieReceived: String! // receive ageToDieField data from ReceiveInputVC
#IBOutlet weak var yearsLeftLabel: UILabel!
#IBOutlet weak var daysLeftLabel: UILabel!
#IBOutlet weak var hoursLeftLabel: UILabel!
#IBOutlet weak var progressBar: CircleProgressBar!
override func viewDidLoad() {
super.viewDidLoad()
createResults()
}
func createResults() {
if let userAge = Int(ageReceived), let dyingAge = Int(ageToDieReceived) {
let yearsLeft = dyingAge - userAge
let daysLeft = yearsLeft * 365
let hoursLeft = daysLeft * 24
// Update UI
yearsLeftLabel.text = "\(yearsLeft)"
daysLeftLabel.text = "\(daysLeft)"
hoursLeftLabel.text = "\(hoursLeft)"
let percentage = (CGFloat(yearsLeft) / CGFloat(dyingAge)) * 100
let formatted = String(format: "%.1f", percentage)
// Update Circle Progress Bar
progressBar.setHintTextGenerationBlock { (progress) -> String? in
return String.init(format: "\(formatted)%%", arguments: [progress])
}
progressBar.setProgress(percentage/100, animated: true, duration: 4.0)
}
}
Project on GitHub: https://github.com/mvvieira95/Time-Life.git
You can use Coredata or another data base or user default
User default implementation:
#IBAction func arrowBtnPressed(_ sender: Any) {
UserDefaults.standard.set("your input values from text field or ...", forKey: "key")
}
In second view controller get it with
UserDefaults.standard.string(forKey: "key")
You can save and restore states with these methods
application:shouldSaveApplicationState and application:shouldRestoreApplicationStat.
Example:
func application(_ application: UIApplication,
shouldSaveApplicationState coder: NSCoder) -> Bool {
// Save the current app version to the archive.
coder.encode(11.0, forKey: "MyAppVersion")
// Always save state information.
return true
}
func application(_ application: UIApplication,
shouldRestoreApplicationState coder: NSCoder) -> Bool {
// Restore the state only if the app version matches.
let version = coder.decodeFloat(forKey: "MyAppVersion")
if version == 11.0 {
return true
}
// Do not restore from old data.
return false
}
You can explore the document in https://developer.apple.com/documentation/uikit/view_controllers/preserving_your_app_s_ui_across_launches?language=objc
Thanks guys, I came up with a solution:
class ReceiveInputVC: UIViewController {
#IBAction func arrowBtnPressed(_ sender: Any) {
let defaults = UserDefaults.standard
if let _ = defaults.object(forKey: "yearsSaved"), let _ = defaults.object(forKey: "daysSaved"), let _ = defaults.object(forKey: "hoursSaved") {
performSegue(withIdentifier: "goToSecondScreen", sender: self)
} else {
alert(message: "You must first enter an input")
}
}
class TimeLeftVC: UIViewController {
var ageReceived: String! // receive whatIsYourAgeField data from ReceiveInputVC
var ageToDieReceived: String! // receive ageToDieField data from ReceiveInputVC
#IBOutlet weak var yearsLeftLabel: UILabel!
#IBOutlet weak var daysLeftLabel: UILabel!
#IBOutlet weak var hoursLeftLabel: UILabel!
#IBOutlet weak var progressBar: CircleProgressBar!
override func viewDidLoad() {
super.viewDidLoad()
let defaults = UserDefaults.standard
yearsLeftLabel.text = defaults.object(forKey: "yearsSaved") as? String
daysLeftLabel.text = defaults.object(forKey: "daysSaved") as? String
hoursLeftLabel.text = defaults.object(forKey: "hoursSaved") as? String
}
override func viewWillAppear(_ animated: Bool) {
createResults()
}
func createResults() {
if let userAge = Int(ageReceived), let dyingAge = Int(ageToDieReceived) {
let yearsLeft = dyingAge - userAge
let daysLeft = yearsLeft * 365
let hoursLeft = daysLeft * 24
// Update UI
yearsLeftLabel.text = "\(yearsLeft)"
daysLeftLabel.text = "\(daysLeft)"
hoursLeftLabel.text = "\(hoursLeft)"
// Store Data
let defaults = UserDefaults.standard
defaults.set(yearsLeftLabel.text, forKey: "yearsSaved")
defaults.set(daysLeftLabel.text, forKey: "daysSaved")
defaults.set(hoursLeftLabel.text, forKey: "hoursSaved")
// Update Circle Progress Bar
let percentage = (CGFloat(yearsLeft) / CGFloat(dyingAge)) * 100
let formatted = String(format: "%.1f", percentage)
progressBar.setHintTextGenerationBlock { (progress) -> String? in
return String.init(format: "\(formatted)%%", arguments: [progress])
}
progressBar.setProgress(percentage/100, animated: true, duration: 4.0)
}
}
Having troubles now updating that progressBar when I go back to the view...

IOS - Limit user to choose at most 8 images and upload multiple images to Firebase using a For-loop

I am new to IOS. I am building an image sharing app with Xcode and Swift
I am stuck in images uploading part.
How can I limit users to choose 8 images at most and then upload the chosen pictures to Firebase using a For-loop?
I would like to use For-loop since I want to set the first picture as an icon picture.
Below is what I am able to do now
I created a SignUp page and allow a user to use ImagePicker to select 1 image and upload it to Firebase. Below is my code.
import UIKit
import Firebase
class SignUpViewController: UIViewController,UIImagePickerControllerDelegate,UINavigationControllerDelegate
{
#IBOutlet weak var iconfield: UIImageView!
#IBOutlet weak var usernamefield: UITextField!
#IBOutlet weak var emailfield: UITextField!
#IBOutlet weak var passwordfield: UITextField!
#IBOutlet weak var confirmpasswordfield: UITextField!
#IBOutlet weak var signupbutton: UIButton!
let picker = UIImagePickerController()
var userStorage: StorageReference!
var ref: DatabaseReference!
override func viewDidLoad()
{
super.viewDidLoad()
picker.delegate = self
let storage = Storage.storage().reference(forURL: "gs://whatisit-8484a.appspot.com/")
ref = Database.database().reference()
userStorage = storage.child("users")
// Do any additional setup after loading the view.
iconfield.layer.cornerRadius=iconfield.frame.size.width/2
iconfield.clipsToBounds = true
}
#IBAction func selectionpressed(_ sender: Any)
{
picker.allowsEditing = true
picker.sourceType = .photoLibrary
present(picker,animated:true,completion:nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any])
{
if let image = info[UIImagePickerControllerEditedImage] as? UIImage
{
self.iconfield.image = image
signupbutton.isHidden = false
}
self.dismiss(animated:true,completion:nil)
}
#IBAction func confirmsignup(_ sender: Any)
{
guard usernamefield.text != "",emailfield.text != "",passwordfield.text != "", confirmpasswordfield.text != "" else {return}
if passwordfield.text == confirmpasswordfield.text
{
Auth.auth().createUser(withEmail: emailfield.text!, password: passwordfield.text!, completion: { (user, error) in
if let error = error
{
print(error.localizedDescription)
}
if let user = user
{
let changeRequest = Auth.auth().currentUser!.createProfileChangeRequest()
changeRequest.displayName = self.usernamefield.text!
changeRequest.commitChanges(completion: nil)
let imageRef = self.userStorage.child("\(user.uid).jpg")
let data = UIImageJPEGRepresentation(self.iconfield.image!, 0.5)
let uploadTask = imageRef.putData(data!, metadata: nil, completion: { (metadata, err) in
if err != nil{
print(err!.localizedDescription)
}
imageRef.downloadURL(completion: { (url, er) in
if er != nil{
print(er!.localizedDescription)
}
if let url = url
{
let userInfo: [String: Any] = ["uid": user.uid,"E-mail":self.emailfield.text,"username": self.usernamefield.text,"urlToImage": url.absoluteString]
self.ref.child("users").child(user.uid).setValue(userInfo)
let vc = UIStoryboard(name: "Main",bundle:nil).instantiateViewController(withIdentifier: "homeview")
self.present(vc,animated:true,completion:nil)
}
})
})
uploadTask.resume()
}
})
}
Your question is quite broad and actually covers a few different questions. Really, you should break the problem down and only post a question for each, but I will give you some guidance.
How to select multiple images using a UIImagePickerController - Basically you can't, you either need to use a library or do your own implementation using the PhotoLibrary
How to upload an image to Firebase should be fairly straight forward.
Multiple uploads can be a bit more complex. You could use a NSOperationQueue to manage a queue of items to process and specify how many to handle at once, you could use a SerialQueue to process items one at a time or a DispatchQueue to fire all of the requests at once and it will let you know when it finishes. Once you reach this point, firstly give it a try, look into the mentioned methods. If you get stuck, raise another question and explain your approach, how you want it to work and what is going wrong.
This other answer may help with the multiple uploads, if not have a look for Grand Central Dispatch online or check here

How to properly implement Stripe in iOS project?

Im developing an iOS app and its includes selling goods outside of the app so I downloaded Stripe using Cocoapods and placing it in my poodle. I know I have correctly installed in my app and input my test API key, because it import Stripe line works just fine. However I am having trouble creating a token. Every time I try to run my code in the simulator and try to make a purchase it prints there was an error. I don't know if its because I implemented the wrong line of code somewhere or left some out, so I was wondering if anybody could help me figure out why the purchase isn't going through? I know I don't have a backend set up but I'm not trying to send the token to the server yet I'm just trying to make sure my app is connected to Stripe which is the first step of 6 that require to actually make a purchase. Thanks for your help in advance.
Code for view controller before the one that lets user (through firebase authentication) make a purchase and stores data:
import UIKit
class PaymentInfoController: UIViewController {
#IBOutlet weak var paymentLabel: UILabel!
#IBOutlet weak var cardField: UITextField!
#IBOutlet weak var cvcField: UITextField!
#IBOutlet weak var expField: UITextField!
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let checkoutVC = segue.destination as? CheckoutController {
checkoutVC.cardNumber = self.cardField.text!
checkoutVC.cvc = self.cvcField.text!
if self.expField.text?.isEmpty == false {
expField.text?.contains("0123456789/")
(expField.text?.characters.count)! <= 7
let expDate = self.expField.text?.components(separatedBy: "/")
let expMonth = UInt((expDate?[0])!)
let expYear = UInt((expDate?[1])!)
checkoutVC.expMon = expMonth!
checkoutVC.expYear = expYear!
}
}
}
#IBAction func checkout(_ sender: Any) {
performSegue(withIdentifier: "checkout", sender: self)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
Code for view controller where the purchase is made:
import UIKit
import FirebaseAuth
import FirebaseDatabase
import Firebase
import Stripe
class CheckoutController: UIViewController {
let ref = Database.database().reference()
#IBOutlet weak var feeLabel: UILabel!
#IBOutlet weak var amountLabel: UILabel!
var cardNumber = String()
var cvc = String()
var expMon = UInt()
var expYear = UInt()
#IBAction func purchase(_ sender: Any) {
if Auth.auth().currentUser != nil {
// Initiate the card
let stripCard = STPCard()
STPAPIClient.shared().createToken(withCard: stripCard, completion: { (token, error) -> Void in
if error != nil {
print("There is an error")
}
else {
// Send the card info to Strip to get the token
stripCard.number = self.cardNumber
stripCard.cvc = self.cvc
stripCard.expMonth = UInt(self.expMon)
stripCard.expYear = UInt(self.expYear)
let alert = UIAlertController(title: "Your Stripe token is " + (token?.tokenId)!, message: "", preferredStyle: .alert)
let defaultAction = UIAlertAction(title: "OK", style: .default, handler: nil)
alert.addAction(defaultAction)
self.present(alert, animated: true, completion: nil)
print(token!)
}
})
}
}
override func viewDidLoad() {
super.viewDidLoad()
let user = Auth.auth().currentUser
let userID: String = (user?.uid)!
if user != nil {
let totalRef = ref.child("users/\(userID)").child("Total")
totalRef.observeSingleEvent(of: .value, with: { (snapshot) in
let cost = snapshot.value as! Int
self.totalLabel.text = "\(cost) bookies"
let total = (Double(cost) * 1.35) + 1.39
self.amountLabel.text = "$\(total)"
})
}
}
The problem is that you're submitting a blank STPCard. You are calling the createToken method without setting any of the fields on the card. For some reason, it looks like you're only setting the card fields in the Completion block after you've gotten a token. You need to move these lines:
stripCard.number = self.cardNumber
stripCard.cvc = self.cvc
stripCard.expMonth = UInt(self.expMon)
stripCard.expYear = UInt(self.expYear)
up before you make the call to createToken.

Swift - Login with FireBase - Can't retrieve user data after login

Firebase Authentication - Can't retrieve user data after login.
I'm trying to retrieve the users email after sign in (logincontroller) and display it in a UILABEL on another controller (maincontroller). When you create a user or use an existing one, it works fine the first time after launching the app, but when you sign out and try to use another email it does not work.
I have two view controllers:
import UIKit
import Firebase
import FirebaseAuth
import FirebaseDatabase
class LoginViewController: UIViewController {
var ref: FIRDatabaseReference!
#IBOutlet weak var emailTextField: UITextField!
#IBOutlet weak var passwordTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
ref = FIRDatabase.database().reference()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func signInButton(sender: AnyObject) {
FIRAuth.auth()?.signInWithEmail(emailTextField.text! , password: passwordTextField.text!, completion: { (user, error) in
if error != nil {
print(error!.localizedDescription)
}
})
self.performSegueWithIdentifier("MainViewSegue", sender: self)
}
#IBAction func createAccountButton(sender: AnyObject) {
FIRAuth.auth()?.createUserWithEmail(emailTextField.text!, password: passwordTextField.text!, completion: { (user, error) in
if error != nil {
print(error!.localizedDescription)
} else {
print("User Created.")
let userID: String = user!.uid
let userEmail:String = self.emailTextField.text!
self.ref.child("users").child(userID).setValue(["email": userEmail])
}
})
}
}
and:
import Foundation
import UIKit
import Firebase
import FirebaseDatabase
class MainViewController: UIViewController {
var ref: FIRDatabaseReference!
var refHandle: UInt!
#IBOutlet weak var userEmailLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
ref = FIRDatabase.database().reference()
refHandle = ref.observeEventType(FIRDataEventType.Value, withBlock: { (snapshot) in
let dataDict = snapshot.value as! [String: AnyObject]
print((dataDict))
})
let userID: String = (FIRAuth.auth()?.currentUser?.uid)!
ref.child("users").child(userID).observeSingleEventOfType(.Value, withBlock: { (snapshot) in
let userEmail = snapshot.value!["email"] as! String
self.userEmailLabel.text = userEmail
})
}
#IBAction func signOutButton(sender: AnyObject) {
try! FIRAuth.auth()!.signOut()
if let storyboard = self.storyboard {
let viewController = storyboard.instantiateViewControllerWithIdentifier("LoginViewController")
self.presentViewController(viewController, animated: false, completion: nil)
}
}
}
This is where I get the error EXC_BAD_INSTRUCTION:
let userID: String = (FIRAuth.auth()?.currentUser?.uid)!
CMD+CLICK on signInWithEmail in FIRAuth.auth()?.signInWithEmail(emailT... and you will be directed to its documentation, above that function in its documentation you will see :-
#param completion Optionally; a block which is invoked when the sign in flow finishes, or is
canceled. Invoked asynchronously on the main thread in the future.
which means your completionBlock is invoked when your user is either signed in or signInWithEmail function has given some error.But in your case self.performSegueWithIdentifier("MainViewSegue", sender: self) will get called even before your completionBlock: is called.
Try this:-
#IBAction func signInButton(sender: AnyObject) {
FIRAuth.auth()?.signInWithEmail(emailTextField.text! , password: passwordTextField.text!, completion: { (user, error) in
if error != nil {
print(error!.localizedDescription)
}else if error == nil{
self.performSegueWithIdentifier("MainViewSegue", sender: self)
}
})
}
Also replace :-
let userID: String = (FIRAuth.auth()?.currentUser?.uid)!
with
let userID: String = FIRAuth.auth()!.currentUser!.uid
No need to forcefully unwrap an optional value which you know does exist

swift prepareforsegue with mmdrawercontroller

I have searched every where to find a solution to this error i am getting, i have also tried to use, nsuserdefaults, struct and global var to pass on my vars to other viewcontrollers. I am use mmdrawer and i have set a navigationcontrol on my first viewcontroller that who i named userOview and identifier membersArea. Whenever i try to use prepareforsegue i am getting the following error
Could not cast value of type 'xxxxxxxx.DjInformation' (0x115da8) to 'UINavigationController' (0x3ad405e0).
My appdelegate looks like this
import UIKit
import CoreData
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var centerContainer: MMDrawerController?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
let prefs:NSUserDefaults = NSUserDefaults.standardUserDefaults()
let isLoggedIn:Int = prefs.integerForKey("ISLOGGEDIN") as Int
if (isLoggedIn == 1){
var rootViewController = self.window!.rootViewController
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
var centerViewController = mainStoryboard.instantiateViewControllerWithIdentifier("memberArea") as! userOverview
var leftViewController = mainStoryboard.instantiateViewControllerWithIdentifier("LeftSideViewController") as! LeftSideViewController
var rightViewController = mainStoryboard.instantiateViewControllerWithIdentifier("RightSideViewController")as! RightSideViewController
var leftSideNav = UINavigationController(rootViewController: leftViewController)
var centerNav = UINavigationController(rootViewController: centerViewController)
var rightNav = UINavigationController(rootViewController: rightViewController)
centerContainer = MMDrawerController(centerViewController: centerNav, leftDrawerViewController: leftSideNav,rightDrawerViewController:rightNav)
centerContainer!.openDrawerGestureModeMask = MMOpenDrawerGestureMode.PanningCenterView;
centerContainer!.closeDrawerGestureModeMask = MMCloseDrawerGestureMode.PanningCenterView;
window!.rootViewController = centerContainer
window!.makeKeyAndVisible()
UIApplication.sharedApplication().setStatusBarHidden(true, withAnimation: UIStatusBarAnimation.None)
}
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:.
// Saves changes in the application's managed object context before the application terminates.
self.saveContext()
}
// MARK: - Core Data stack
lazy var applicationDocumentsDirectory: NSURL = {
// The directory the application uses to store the Core Data store file. This code uses a directory named "bfd.Be_Fit_Donate" in the application's documents Application Support directory.
let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
return urls[urls.count-1] as! NSURL
}()
lazy var managedObjectModel: NSManagedObjectModel = {
// The managed object model for the application. This property is not optional. It is a fatal error for the application not to be able to find and load its model.
let modelURL = NSBundle.mainBundle().URLForResource("Be_Fit_Donate", withExtension: "momd")!
return NSManagedObjectModel(contentsOfURL: modelURL)!
}()
lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator? = {
// The persistent store coordinator for the application. This implementation creates and return a coordinator, having added the store for the application to it. This property is optional since there are legitimate error conditions that could cause the creation of the store to fail.
// Create the coordinator and store
var coordinator: NSPersistentStoreCoordinator? = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("Be_Fit_Donate.sqlite")
var error: NSError? = nil
var failureReason = "There was an error creating or loading the application's saved data."
if coordinator!.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: nil, error: &error) == nil {
coordinator = nil
// Report any error we got.
var dict = [String: AnyObject]()
dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data"
dict[NSLocalizedFailureReasonErrorKey] = failureReason
dict[NSUnderlyingErrorKey] = error
error = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict)
// Replace this with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog("Unresolved error \(error), \(error!.userInfo)")
abort()
}
return coordinator
}()
lazy var managedObjectContext: NSManagedObjectContext? = {
// Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.) This property is optional since there are legitimate error conditions that could cause the creation of the context to fail.
let coordinator = self.persistentStoreCoordinator
if coordinator == nil {
return nil
}
var managedObjectContext = NSManagedObjectContext()
managedObjectContext.persistentStoreCoordinator = coordinator
return managedObjectContext
}()
// MARK: - Core Data Saving support
func saveContext () {
if let moc = self.managedObjectContext {
var error: NSError? = nil
if moc.hasChanges && !moc.save(&error) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog("Unresolved error \(error), \(error!.userInfo)")
abort()
}
}
}
}
and the view controller i am trying to send the prepareforsegue from is as follows
import UIKit
import AVFoundation
import AVKit
import Foundation
import Social
public var AudioPlayer = AVPlayer()
public var SelectedSongNumber = Int()
public var TrackName = String()
public var TrackImage = String()
public var TrackDJ = String()
class MusicListTableViewController: UITableViewController, AVAudioPlayerDelegate{
#IBOutlet weak var playerView: UIView!
#IBOutlet weak var songName: UILabel!
#IBOutlet weak var playButton: UIButton!
#IBOutlet weak var trackDjName: UILabel!
#IBOutlet weak var imageArtwork: UIImageView!
var trackName = [String]()
var artistLabel = [String]()
var trackUrl = [String]()
var artWork = [String]()
var tags = [String]()
var artistId = [String]()
var djInfo = ""
#IBAction func facebookButton(sender: UIButton) {
if SLComposeViewController.isAvailableForServiceType(SLServiceTypeFacebook){
var facebookSheet:SLComposeViewController = SLComposeViewController(forServiceType: SLServiceTypeFacebook)
facebookSheet.setInitialText("Share on Facebook")
self.presentViewController(facebookSheet, animated: true, completion: nil)
} else {
var alert = UIAlertController(title: "Accounts", message: "Please login to a Facebook account to share.", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
}
#IBAction func tritterButton(sender: UIButton) {
if SLComposeViewController.isAvailableForServiceType(SLServiceTypeTwitter){
var twitterSheet:SLComposeViewController = SLComposeViewController(forServiceType: SLServiceTypeTwitter)
twitterSheet.setInitialText("Share on Twitter")
self.presentViewController(twitterSheet, animated: true, completion: nil)
} else {
var alert = UIAlertController(title: "Accounts", message: "Please login to a Twitter account to share.", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
}
#IBAction func myPlayList(sender: UIButton) {
var centerViewController = self.storyboard?.instantiateViewControllerWithIdentifier("myMusicList") as! myMusicList
var centerNavController = UINavigationController(rootViewController: centerViewController)
var appDelegate:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.centerContainer!.centerViewController = centerNavController
appDelegate.centerContainer!.toggleDrawerSide(MMDrawerSide.Right, animated: true, completion: nil)
}
#IBAction func favoriteButton(sender: UIButton) {
var favSong = trackName[SelectedSongNumber]
var alertView:UIAlertView = UIAlertView()
alertView.title = "Nummer Toegevoegd"
alertView.message = "Het nummer \(favSong) is nu toegevoegd aan uw favorieten muziek lijst"
alertView.delegate = self
alertView.addButtonWithTitle("OK")
alertView.show()
}
#IBAction func djInformation(sender: UIButton) {
var centerViewController = self.storyboard?.instantiateViewControllerWithIdentifier("djInformation") as! DjInformation
var centerNavController = UINavigationController(rootViewController: centerViewController)
var appDelegate:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.centerContainer!.centerViewController = centerNavController
appDelegate.centerContainer!.toggleDrawerSide(MMDrawerSide.Right, animated: true, completion: nil)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var artist_label = artistLabel[SelectedSongNumber]
if (segue.identifier == "djInformation" ){
var detailVC = segue.destinationViewController as! UINavigationController
let targetController = detailVC.topViewController as! DjInformation
targetController.djInfo = "hello"
}
}
func getMusicListJSON(){
let urlString = "http://xxxxxxxxxx"
let urlEncodedString = urlString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)
let url = NSURL( string: urlEncodedString!)
var task = NSURLSession.sharedSession().dataTaskWithURL(url!) {(data, response, innerError) in
let json = JSON(data: data)
let musicArray = json.arrayValue
//NSLog("\(json)")
dispatch_async(dispatch_get_main_queue(), {
for musiclist in musicArray
{
let track_name = musiclist["track_name"].stringValue
let artist = musiclist["artist"].stringValue
let track_url = musiclist["track_url"].stringValue
let art_work = musiclist["artwork"].stringValue
let track_tags = musiclist["tags"].stringValue
let artist_id = musiclist["artist_id"].stringValue
//println( "track_name: \(track_name) artist: \(artist) track_url: \(track_url) artwork: \(art_work) track_tags: \(track_tags) artist_id: \(artist_id)" )
self.trackName.append(track_name)
self.artistLabel.append(artist)
self.trackUrl.append(track_url)
self.artWork.append(art_work)
self.tags.append(track_tags)
self.artistId.append(artist_id)
}
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
return
})
})
}
task.resume()
}
override func viewDidLoad() {
super.viewDidLoad()
getMusicListJSON()
playButton.addTarget(self, action: "playButtonTapped:", forControlEvents: .TouchUpInside)
playerView.frame = CGRectMake(0, 0, self.view.frame.width, self.view.frame.height * 0.7)
playButton.hidden = true
var error: NSError?
var success = AVAudioSession.sharedInstance().setCategory(
AVAudioSessionCategoryPlayAndRecord,withOptions: .DefaultToSpeaker, error: &error)
if !success{
NSLog("Failed to set audio session category, Error: \(error)")
}
}
func playButtonTapped(sender: AnyObject){
// set play image to pause wehen video is paused and also back
if AudioPlayer.rate == 0
{
AudioPlayer.play()
playButton.setImage(UIImage(named:"pause"), forState: UIControlState.Normal)
} else {
AudioPlayer.pause()
playButton.setImage(UIImage(named: "play"), forState: UIControlState.Normal)
}
}
#IBAction func slideOutMenu(sender: AnyObject) {
var appDelegate:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.centerContainer!.toggleDrawerSide(MMDrawerSide.Left, animated: true, completion: nil)
}
#IBAction func musicMenu(sender: AnyObject) {
var appDelegate:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.centerContainer!.toggleDrawerSide(MMDrawerSide.Right, animated: true, completion: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.navigationBarHidden = true
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return trackName.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("playerCell", forIndexPath: indexPath) as! playerTableViewCell
// Configure the cell...
cell.artistName.text = trackName[indexPath.row]
cell.trackName.text = artistLabel[indexPath.row]
return cell
}
// set the song to play according to the table row selected. Also set the name and artist.
func playSong() {
var playnumber = trackUrl[SelectedSongNumber]
var TrackName = trackName[SelectedSongNumber]
var TrackImage = artWork[SelectedSongNumber]
var TrackDJ = artistLabel[SelectedSongNumber]
var DjId = artistId[SelectedSongNumber]
AudioPlayer = AVPlayer(URL: NSURL(string: playnumber))
AudioPlayer.play()
songName.text = TrackName
trackDjName.text = TrackDJ
load_artwork(TrackImage)
playButton.hidden = false
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath){
SelectedSongNumber = indexPath.row
playSong()
}
// set the song artwork to display in the player for the current playing song.
func load_artwork(urlString: String){
var imgURL: NSURL = NSURL(string: urlString)!
let request: NSURLRequest = NSURLRequest(URL: imgURL)
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue(), completionHandler: {(response: NSURLResponse!, data: NSData!,error: NSError!) -> Void in
if error == nil {
self.imageArtwork.image = UIImage(data: data)
}
})
}
}
These are the two codes that i think should be the cause of the problem. But so far i can't find any way to pas any variables and i will be needing that to complete my app. What am i doing wrong here?
I can really use some help here to get this to work. Maybe my navigation controller is correctly setup. As the first viewcontroller should be embedded in it. But i am not sure i have done that properly.
Thanks for the help.
I have found the solution which was very simple if you know what you should do. When you are using mmdrawer and would like to send over data you could just create a prepareforsegue, but you also need to create a segue with overrides the mmdrawer. I find that you use you use the following just like you would do with prepareforsgue
#IBAction func djInformation(sender: UIButton) {
var artist_label = artistLabel[SelectedSongNumber]
var centerViewController = self.storyboard?.instantiateViewControllerWithIdentifier("djInformation") as! DjInformation
centerViewController.djINfo = "\(artist_label)"
var centerNavController = UINavigationController(rootViewController: centerViewController)
var appDelegate:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.centerContainer!.centerViewController = centerNavController
appDelegate.centerContainer!.toggleDrawerSide(MMDrawerSide.Right, animated: true, completion: nil)
}
so basically what you do in prepareforsegue you can also do in button that is initiating the mmdrawercontroller.
Hope this makes sense and helps some one else. I am in no way a pro and i am not sure if this is proper or more something like a hack. but it fast and easy.

Resources