Today Extension App Group not work swift ios 11 - ios

i've a problem with sharing data from my app to Today extension.
I've enable the app group in capabilities.
In my app controller I've this
var defaults = UserDefaults(suiteName: "group.com.mygroup")!
defaults.setValue("today extension text", forKey: "customKey")
defaults.synchronize()
And this is my Today Extension code:
class TodayViewController: UIViewController, NCWidgetProviding {
#IBOutlet var TimerText: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
func widgetPerformUpdate(completionHandler: (#escaping (NCUpdateResult) -> Void)) {
completionHandler(NCUpdateResult.newData)
let defaults = UserDefaults(suiteName: "group.com.mygroup")!
let myString = defaults.value(forKey: "customKey")
if(String(describing: myString) == "" || myString == nil) {
TimerText.text = "no data"
} else {
TimerText.text = String(describing: myString!)
}
}
}
The widget after lunch application show always no data although in the m ain app the variable is correctly set.
Thanks!

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...

preference not working ios watch

I have developing an ios watch app. I want to pass a value to ios watch. i have try several methods. When storing value in a element of ios app. I am using xcode 8 and the code is written in swift.
ViewController.swift
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var webview: UIWebView!
override func viewDidLoad() {
super.viewDidLoad()
print("webview")
let url = URL (string: "http://www.google.com/")
let request = URLRequest(url: url!)
webview.loadRequest(request)
let preferences = UserDefaults.standard
let currentLevel = "cl"
let currentLevelKey = "currentLevel"
preferences.set(currentLevel, forKey: currentLevelKey)
preferences.synchronize()
print("pref")
}
}
Then i have call the preference in ios watch app the code is
InterfaceController.swift
class InterfaceController: WKInterfaceController {
#IBAction func clickingTheButton() {
print("clicked")
let preferences = UserDefaults.standard
let currentLevelKey = "currentLevel"
if preferences.object(forKey: currentLevelKey) == nil {
// Doesn't exist
print("Doesn't exist")
} else {
let currentLevel = preferences.integer(forKey: currentLevelKey)
print("preference : ",currentLevel)
}
}
}
How to pass value from ios to watch app. Please help me.

How to create a share with CloudKit's CKShare?

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)

Changing ViewController if user is Facebook Logged in

I'm having quite some troubles implementing Facebook login in my iOS , everything works fine if the user is not already logged in, the application fetches correctly the data from Facebook and pass them to the next ViewController , instead if is already logged in it should automatically segue to a recap page that shows user's info but i can't make it happen, currently I'm using this method :
override func viewDidLoad() {
super.viewDidLoad()
LoginButton.delegate = self
if (FBSDKAccessToken.currentAccessToken() != nil) {
self.performSegueWithIdentifier("Login", sender: self)
}
}
but in the console i get :
Facebook_Login.LoginViewController: 0x7fc04a519ca0 on Facebook_Login.ViewController: 0x7fc04a41c1e0 whose view is not in the window hierarchy!
i've also tried using the viewdidAppear method, but it segues to the recap page without updating the variables so i get an empty page
here' the complete code:
View Controller 1
import UIKit
import FBSDKLoginKit
class ViewController: UIViewController,FBSDKLoginButtonDelegate {
var nome1:String = ""
var cognome1:String = ""
var email1:String = ""
var compleanno:String = ""
var città:String = ""
var genere:String = ""
var immagine_url:String = ""
#IBOutlet weak var LoginButton: FBSDKLoginButton!
#IBAction func LoginAction(sender: AnyObject) {
LoginButton.delegate = self
LoginButton.readPermissions = ["email"]
}
func FetchInfo(){
print("scarico le informazioni...")
let parametri = ["fields":"email, first_name, last_name, birthday, hometown, gender, picture.type(large)"]
FBSDKGraphRequest(graphPath: "me", parameters: parametri).startWithCompletionHandler{(connection,result,error) -> Void in
if (error != nil){
print ("errore")
return
}
else {
if let email = result["email"] as? String {
print(email)
self.email1 = email
}
if let nome = result["first_name"] as? String {
print(nome)
self.nome1 = nome
}
if let cognome = result["last_name"] as? String {
print(cognome)
self.cognome1 = cognome
}
if let compleanno = result["birthday"] as? String{
print(compleanno)
}
if let città = result["hometown"] as? String{
print(città)
}
if var genere = result["gender"] as? String{
if (genere == "male"){
genere = "maschio"
}
else {
genere = "femmina"
}
print(genere)
}
}
if let picture = result["picture"] as? NSDictionary, data = picture["data"] as? NSDictionary, url = data["url"] as? String{
self.immagine_url = url
print(self.immagine_url)
}
}
return
}
func loginButton(loginButton: FBSDKLoginButton!, didCompleteWithResult result: FBSDKLoginManagerLoginResult!, error: NSError!){
if (result.isCancelled == true){
print("cancellato")
}
else {
print("login effettuato")
FetchInfo()
self.performSegueWithIdentifier("Login", sender: self)
}
}
func loginButtonDidLogOut(loginButton: FBSDKLoginButton!){
}
func loginButtonWillLogin(loginButton: FBSDKLoginButton!) -> Bool{
return true
}
#IBAction func returned(segue:UIStoryboardSegue){
}
override func viewDidLoad() {
super.viewDidLoad()
LoginButton.delegate = self
}
override func viewDidAppear(animated: Bool) {
if (FBSDKAccessToken.currentAccessToken() != nil) {
FetchInfo()
if(nome1 == ""){
}
else {
self.performSegueWithIdentifier("Login", sender: self)
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let destinazione:LoginViewController = segue.destinationViewController
as! LoginViewController
destinazione.temp_nome = nome1
destinazione.temp_cognome = cognome1
destinazione.temp_email = email1
destinazione.img_profile_url = immagine_url
}
}
ViewController 2:
import UIKit
import FBSDKLoginKit
class LoginViewController: UIViewController, FBSDKLoginButtonDelegate {
var temp_nome = ""
var temp_cognome = ""
var temp_email = ""
var img_profile_url:String = ""
#IBOutlet weak var Nome_Utente: UILabel!
#IBOutlet weak var email: UILabel!
#IBOutlet weak var Immagine_Utente: UIImageView!
#IBOutlet weak var Login_button: FBSDKLoginButton!
#IBAction func Login_button_Action(sender: AnyObject) {
Login_button.delegate = self
}
func loginButton(loginButton: FBSDKLoginButton!, didCompleteWithResult result: FBSDKLoginManagerLoginResult!, error: NSError!){
}
func loginButtonDidLogOut(loginButton: FBSDKLoginButton!){
self.performSegueWithIdentifier("Back", sender: self)
}
override func viewDidLoad() {
super.viewDidLoad()
Nome_Utente.text = "\(temp_nome)" + " " + "\(temp_cognome)"
email.text = "\(temp_email)"
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
First of all, avoid to reset your LoginButton.delegate in the LoginAction() function.
If the user is already logged in, to avoid an useless call to the Facebook Graph API (if you do not necessarily need to update his informations), you can store your user informations in CoreData or in a NSUserDefault.
If you never used it you can use the CDHelper lib (CoreDataHelper) which will allow you to use it without difficulties.
Hope I was helpful, if not, do not hesitate to give us a feedback.
Btw, in your viewDidAppear(animated: Bool) function, you have to call super.viewDidAppear(animated) !

Add initial note

I am looking at adding an inital note to the note page within my app. this is so that when people click to the notes part there will be some detail on how to use it rather than just a big empty screen. I have no idea where to implement this though. Could you please help, below is the page where it talks about the dictionaries.
import UIKit
import MessageUI
class DetailViewController: UIViewController, MFMailComposeViewControllerDelegate, UITextViewDelegate {
#IBOutlet weak var tView: UITextView!
#IBAction func BarButton(sender: UIBarButtonItem) {
let textToShare = ""
if let myWebsite = NSURL(string: "")
{
let objectsToShare = [textToShare, myWebsite]
let activityVC = UIActivityViewController(activityItems: objectsToShare, applicationActivities: nil)
self.presentViewController(activityVC, animated: true, completion: nil)
}
OpenMail()
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
tView.text = (allNotes[currentNoteIndex] as Note).note
tView.becomeFirstResponder()
// Set controller as swipe gesture recogniser, to allow keyboard dismissal for text box
var swipe: UISwipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: "dismissKeyboard")
swipe.direction = UISwipeGestureRecognizerDirection.Down
self.view.addGestureRecognizer(swipe)
self.tView.delegate = self
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
if tView.text == "" {
allNotes.removeAtIndex(currentNoteIndex)
}
else {
(allNotes[currentNoteIndex] as Note).note = tView.text
}
Note.saveNotes()
noteTable?.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func configuredMailComposeViewController() -> MFMailComposeViewController {
// Open mail controller on screen and prepare with preset values.
let mailComposerVC = MFMailComposeViewController()
var MessageText: String!
MessageText = tView.text
mailComposerVC.mailComposeDelegate = self
mailComposerVC.setToRecipients([""])
mailComposerVC.setSubject("")
mailComposerVC.setMessageBody(MessageText, isHTML: false)
return mailComposerVC
}
func showSendMailErrorAlert() {
// Alert user to email error
let sendMailErrorAlert = UIAlertView(title: "Could Not Send Email", message: "Your device could not send e-mail. Please check e-mail configuration and try again.", delegate: self, cancelButtonTitle: "OK")
sendMailErrorAlert.show()
}
// MARK: MFMailComposeViewControllerDelegate Method
func mailComposeController(controller: MFMailComposeViewController!, didFinishWithResult result: MFMailComposeResult, error: NSError!) {
controller.dismissViewControllerAnimated(true, completion: nil)
}
func OpenMail() {
//Function to open mail composer on screen
let mailComposeViewController = configuredMailComposeViewController()
if MFMailComposeViewController.canSendMail() {
self.presentViewController(mailComposeViewController, animated: true, completion: nil)
} else {
self.showSendMailErrorAlert()
}
}
func dismissKeyboard() {
// Dismiss keyboard for textfield
self.tView.resignFirstResponder()
}
}
note.swift
import UIKit
var allNotes:[Note] = []
var currentNoteIndex:NSInteger = -1
var noteTable:UITableView?
let KAllNotes:String = "notes"
class Note: NSObject {
var date:String
var note:String
override init() {
date = NSDate().description
note = ""
}
func dictionary() -> NSDictionary {
return ["note":note, "date":date]
}
class func saveNotes() {
var aDictionaries:[NSDictionary] = []
for (var i:NSInteger = 0; i < allNotes.count; i++) {
aDictionaries.append(allNotes[i].dictionary())
}
NSUserDefaults.standardUserDefaults().setObject(aDictionaries, forKey: KAllNotes)
// aDictionaries.writeToFile(filePath(), atomically: true)
}
class func loadnotes() {
allNotes.removeAll(keepCapacity: true)
var defaults:NSUserDefaults = NSUserDefaults.standardUserDefaults()
var savedData:[NSDictionary]? = defaults.objectForKey(KAllNotes) as? [NSDictionary]
// var savedData:NSArray? = NSArray(contentsOfFile: filePath())
if let data:[NSDictionary] = savedData {
for (var i:NSInteger = 0; i < data.count; i++) {
var n:Note = Note()
n.setValuesForKeysWithDictionary(data[i] as [NSObject : AnyObject])
allNotes.append(n)
}
}
}
class func filePath() -> String {
var d:[String]? = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.AllDomainsMask, true) as? [String]
if let directories:[String] = d {
var docsDirectory:String = directories[0]
var path:String = docsDirectory.stringByAppendingPathComponent("\(KAllNotes).notes")
return path;
}
return ""
}
}
Thanks in advance
Sam
Add an NSUserDefault boolean that stores whether or not the initial note should be shown, e.g. that the app has been launched for the first time. Then load an initial note accordingly. When a note is added or the initial note is deleted, then change the boolean accordingly so the initial note doesn't show up next time.
You could also initialize your database with an initial note. Not clear from your code how the notes are saved, but this approach would probably rely on the NSUserDefault approach above, except it could be done in the AppDelegate or something.
example:
let InitialSetupComplete = "InitialSetupComplete" // Note: I would define this at the top of a file
let defaults = NSUserDefaults.standardUserDefaults()
if defaults.boolForKey(InitialSetupComplete) {
// Show initial note
}
// Later on when the note is deleted, or modified (or immediately after initial note loaded into the database, see below)
defaults.setBool(true, forKey: InitialSetupComplete)
Would be easier/cleaner just to initialize your database with the initial note in the app delegate (e.g. call within applicationDidFinishLaunching), so your view controller doesn't have to figure this out. Similar code, except you would use setBool right away after the initial note has been saved to the database. I don't know anything about your database from the question, so can't really provide a more detailed example than this. Hope this helps.

Resources