Passing a array from notification object crashing the app in iOS 10 - ios

I am posting the below code from a view controller to another
self.genreString.append("Comedy")
self.sortByString.append("orderly")
let myDict:Dictionary<String, [String]> = ["sortoption": self.genreString, "contenttype":self.sortByString]
NSNotificationCenter.defaultCenter().postNotificationName("SecondViewControllerDismissed", object: nil, userInfo: myDict)
In receiving view controller I am accepting the data as this
if let info = sender.userInfo as? Dictionary<String,[String]> {
// Check if value present before using it
if let s = info["sortoption"] {
if info["sortoption"]?.count > 0
{
JLToast.makeText("2", duration: 2).show()
let s :[String] = info["sortoption"]!
self.genreFilter.removeAll()
self.genreFilter = []
self.genreFilter.appendContentsOf(s)
}
}
}
But the array is not initialised, its saying!!!

Following code runs fine in XCode playground
import Foundation
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
let myDict:Dictionary<String, [String]> = ["sortoption": ["Comdedy"], "contenttype": ["orderly"]]
NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: "SecondViewControllerDismissed"), object: nil, queue: OperationQueue.main) {
notification in
if let info = notification.userInfo as? Dictionary<String,[String]> {
// Check if value present before using it
if let s = info["sortoption"] {
print(s)
}
}
}
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "SecondViewControllerDismissed"), object: nil, userInfo: myDict)

Related

iOS: UIWindow.rootViewController must be used from main thread only

I have a problem by using
if let wd = UIApplication.shared.delegate?.window {
var vc = wd!.rootViewController
If I put this piece of code in a Dispatch, the warning message disappear, but the application doesn't display correctly.
If I remove the dispatch, I have warning message.
UIWindow.rootViewController must be used from main thread only
AND
UIApplication.delegate must be used from main thread only
That class is specially for downloading with a progressBar.
public func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
print("Download finished: \(location)")
...
do {
let result = try FileManager.default.replaceItemAt(URL(fileURLWithPath: Constants.Path.temp.stringByAppendingPathComponent(path: "temp.zip")), withItemAt: URL(fileURLWithPath: location.path))
let source = Constants.Path.tempZipFile
let destination = Constants.Path.temp.stringByAppendingPathComponent(path: "dezipped")
var myDict = [String:Any]()
myDict["source"] = source
myDict["destination"] = destination
DispatchQueue.main.async { //IF I REMOVE THIS => PB OR THREAD IN MAIN
if let wd = UIApplication.shared.delegate?.window {
var vc = wd!.rootViewController
if(vc is UINavigationController){
vc = (vc as! UINavigationController).visibleViewController
}
if(vc is WebViewController){
NotificationCenter.default.post(name: .DeflatSynchroFilesWebView, object: myDict, userInfo: nil)
}
else
{
NotificationCenter.default.post(name: .DeflatSynchroFiles, object: myDict, userInfo: nil)
}
}
}
} catch let writeError as NSError {
print("error writing file temp.zip to temp folder")
}
How to remove the warning without bugging my app?
Thanks in advance.
I am not sure if this can help, but to get the rootViewController I always use this:
if let window = UIApplication.shared.keyWindow?.rootViewController {
}
without the delegate

How to stop GCDAsyncUdpSocket send command?

I am using GCDAsyncUdpSocket for communication between my app and some smart-home hardware, and I have a problem with stopping a certain function. Logic goes something like this:
Send a command
If you didn't receive feedback from the hardware, it'll try to send it a few more times
When app receives feedback, notification DidReceiveDataForRepeatSendingHandler is posted (along with device information in userInfo)
For example, let's say I have a curtain that can react on 3 commands: Open, Close and Stop... and that curtain is currently closed.
I press Open (and don't receive feedback), and during the process I change my mind, so I press Stop. Now the app will send both commands simultaneously.
So without further ado, here's the code:
class RepeatSendingHandler: NSObject {
var byteArray: [UInt8]!
var gateway: Gateway!
var repeatCounter:Int = 1
var device:Device!
var appDel:AppDelegate!
var error:NSError? = nil
var sameDeviceKey: [NSManagedObjectID: NSNumber] = [:]
var didGetResponse:Bool = false
var didGetResponseTimer:Foundation.Timer!
//
// ================== Sending command for changing value of device ====================
//
init(byteArray:[UInt8], gateway: Gateway, device:Device, oldValue:Int) {
super.init()
appDel = UIApplication.shared.delegate as! AppDelegate
self.byteArray = byteArray
self.gateway = gateway
self.device = device
NotificationCenter.default.addObserver(self, selector: #selector(RepeatSendingHandler.didGetResponseNotification(_:)), name: NSNotification.Name(rawValue: NotificationKey.DidReceiveDataForRepeatSendingHandler), object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(sameDevice(_:)), name: NSNotification.Name(rawValue: NotificationKey.SameDeviceDifferentCommand), object: nil)
sendCommand()
}
func updateRunnableList(deviceID: NSManagedObjectID) {
RunnableList.sharedInstance.removeDeviceFromRunnableList(device: deviceID)
}
// Did get response from gateway
func didGetResponseNotification (_ notification:Notification) {
if let info = (notification as NSNotification).userInfo! as? [String:Device] {
if let deviceInfo = info["deviceDidReceiveSignalFromGateway"] {
if device.objectID == deviceInfo.objectID {
didGetResponse = true
didGetResponseTimer = nil
NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NotificationKey.DidReceiveDataForRepeatSendingHandler), object: nil)
}
}
}
}
func sameDevice(_ notification: Notification) {
print("NOTIFICATION RECEIVED for device with ID: ", self.device.objectID, "\n")
if let info = notification.userInfo as? [NSManagedObjectID: NSNumber] {
sameDeviceKey = info
}
}
func sendCommand () {
if sameDeviceKey != [device.objectID: device.currentValue] { print("keys have DIFFERENT values") } else { print("keys have SAME values") }
if sameDeviceKey != [device.objectID: device.currentValue] {
if !didGetResponse {
if repeatCounter < 4 {
print("Sending command. Repeat counter: ", repeatCounter)
SendingHandler.sendCommand(byteArray: byteArray, gateway: gateway)
didGetResponseTimer = Foundation.Timer.scheduledTimer(timeInterval: 2, target: self, selector: #selector(RepeatSendingHandler.sendCommand), userInfo: nil, repeats: false)
repeatCounter += 1
} else {
didGetResponseTimer = nil
updateRunnableList(deviceID: device.objectID)
CoreDataController.shahredInstance.saveChanges()
NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NotificationKey.DidReceiveDataForRepeatSendingHandler), object: nil)
NotificationCenter.default.post(name: Notification.Name(rawValue: NotificationKey.RefreshDevice), object: self)
}
}else{
didGetResponseTimer = nil
updateRunnableList(deviceID: device.objectID)
CoreDataController.shahredInstance.saveChanges()
NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NotificationKey.DidReceiveDataForRepeatSendingHandler), object: nil)
NotificationCenter.default.post(name: Notification.Name(rawValue: NotificationKey.RefreshDevice), object: self)
}
} else {
print("Command canceled")
didGetResponseTimer = nil
return
}
}
On the ViewController where I keep my devices, I call this like:
func openCurtain(_ gestureRecognizer:UITapGestureRecognizer){
let tag = gestureRecognizer.view!.tag
let address = [UInt8(Int(devices[tag].gateway.addressOne)),UInt8(Int(devices[tag].gateway.addressTwo)),UInt8(Int(devices[tag].address))]
if devices[tag].controlType == ControlType.Curtain {
let setDeviceValue:UInt8 = 0xFF
let deviceCurrentValue = Int(devices[tag].currentValue)
devices[tag].currentValue = 0xFF // We need to set this to 255 because we will always display Channel1 and 2 in devices. Not 3 or 4. And this channel needs to be ON for image to be displayed properly
let deviceGroupId = devices[tag].curtainGroupID.intValue
CoreDataController.shahredInstance.saveChanges()
DispatchQueue.main.async(execute: {
RunnableList.sharedInstance.checkForSameDevice(device: self.devices[tag].objectID, newCommand: NSNumber(value: setDeviceValue))
_ = RepeatSendingHandler(byteArray: OutgoingHandler.setCurtainStatus(address, value: setDeviceValue, groupId: UInt8(deviceGroupId)), gateway: self.devices[tag].gateway, device: self.devices[tag], oldValue: deviceCurrentValue)
})
}
}
What I did was I made a separate class where I have a dictionary that has Device's ManagedObjectID as a key, and the command we are sending is it's value. So whenever we are sending a command for a device that's already on the list, I post a notification SameDeviceDifferentCommand with userInfo containing device's ManagedObjectID and the old command. I use it on RepeatSendingHandler to populate sameDeviceKey dictionary. That's how I tried to distinguish which function should be stopped.
public class RunnableList {
open static let sharedInstance = RunnableList()
var runnableList: [NSManagedObjectID: NSNumber] = [:]
func checkForSameDevice(device: NSManagedObjectID, newCommand: NSNumber) {
if runnableList[device] != nil && runnableList[device] != newCommand {
let oldDataToSend = [device: runnableList[device]!]
NotificationCenter.default.post(name: Notification.Name(rawValue: NotificationKey.SameDeviceDifferentCommand), object: self, userInfo: oldDataToSend)
print("Notification sent for device with ID: ", device, "\n")
}
runnableList[device] = newCommand
print("Device with ID: ", device, "received a new command", newCommand, "\n")
}
func removeDeviceFromRunnableList(device: NSManagedObjectID) {
runnableList.removeValue(forKey: device)
print("Removed from list device with ID: ", device)
}
}
However, sometimes it does it's job as it should, and sometimes it doesn't. Using a bunch of prints I tried to see in which order everything happens, and it seems that sometimes even though sameDeviceKey gets it's value from the notification - it looks like it uses old (nil) value until repeatCounter maxes out. I do not understand why.
Could anyone explain what is happening, and/or advise a better solution than the one I provided?
(There is a bit of additional code which I removed as it's irrelevant to logic/question). Please bear in mind that I am a junior and that I'm relatively new to this.

NSNotificationCenter is not calling in swift2.2

let imageDataDict:[String: UIImage] = ["SelectedImages": image!]
NSNotificationCenter.defaultCenter().postNotificationName("PosterImage", object: self, userInfo: imageDataDict)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(frameViewController.takeImages(_:)), name:"PosterImage", object: nil)
func takeImages(notification: NSNotification) {
if let singleImage = notification.userInfo?["SelectedImages"] as? UIImage {
// do something with your image
self.f_singlImage = singleImage
}
}
I am working on NSNotificationCenter but it's not working properly in swift 2.2 . i tried alot no hope the resolve this .I am new to swift and any one explain this

How to make local notification actions directly delete To-Do Items in Swift

I have a Table View Controller project in Swift.
I would need 2 Local Notification actions: one to complete and delete a deadline to-do item, and the other one to open a View Controller of the app.
I have already added NSNotificationCenter.defaultCenter().addObserver for the actions but I'm still wondering how to delete the to-do items from the cellForRowAtIndexPath. using just the the notification actions.
I hope you could help me! Thank you in advance!
Here are some parts of my codes:
AppDelegate:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Actions
var firstAction:UIMutableUserNotificationAction = UIMutableUserNotificationAction()
firstAction.identifier = "FIRST_ACTION"
firstAction.title = "Complete" // "First Action"
firstAction.activationMode = UIUserNotificationActivationMode.Background
firstAction.destructive = true
firstAction.authenticationRequired = false
var secondAction:UIMutableUserNotificationAction = UIMutableUserNotificationAction()
secondAction.identifier = "SECOND_ACTION"
secondAction.title = "Edit" // "Second Action"
secondAction.activationMode = UIUserNotificationActivationMode.Foreground
secondAction.destructive = false
secondAction.authenticationRequired = false
var thirdAction:UIMutableUserNotificationAction = UIMutableUserNotificationAction()
thirdAction.identifier = "THIRD_ACTION"
thirdAction.title = "Third Action"
thirdAction.activationMode = UIUserNotificationActivationMode.Background
thirdAction.destructive = false
thirdAction.authenticationRequired = false
// category
var firstCategory:UIMutableUserNotificationCategory = UIMutableUserNotificationCategory()
firstCategory.identifier = "FIRST_CATEGORY"
let defaultActions:NSArray = [firstAction, secondAction, thirdAction]
let minimalActions:NSArray = [firstAction, secondAction]
firstCategory.setActions(defaultActions as! [UIUserNotificationAction], forContext: UIUserNotificationActionContext.Default)
firstCategory.setActions(minimalActions as! [UIUserNotificationAction], forContext: UIUserNotificationActionContext.Minimal)
// NSSet of all our categories
let categories:NSSet = NSSet(objects: firstCategory)
let types:UIUserNotificationType = UIUserNotificationType(arrayLiteral: .Alert, .Badge)
let mySettings:UIUserNotificationSettings = UIUserNotificationSettings(forTypes: types, categories: categories as! Set<UIUserNotificationCategory>)
UIApplication.sharedApplication().registerUserNotificationSettings(mySettings)
func application(application: UIApplication!,
handleActionWithIdentifier identifier:String!,
forLocalNotification notification:UILocalNotification!,
completionHandler: (() -> Void)!){
if (identifier == "First_Action"){
NSNotificationCenter.defaultCenter().postNotificationName("actionOnePressed", object: nil)
}else if (identifier == "Second_Action"){
NSNotificationCenter.defaultCenter().postNotificationName("actionTwoPressed", object: nil)
}
completionHandler()
ToDoTableViewController:
//
NSNotificationCenter.defaultCenter().addObserver(self, selector: "drawAShape", name: "actionOnePressed", object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "showAMessage", name: "actionTwoPressed", object: nil)
//
let newIndexPath = NSIndexPath(forRow: todoItems.count, inSection: 0)
todoItems.append(todoItem)
tableView.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: .Bottom)
//scheduleLocalNotification(todoItem)
let notification:UILocalNotification = UILocalNotification()
notification.category = "FIRST_CATEGORY"
notification.alertBody = "Notification \(todoItem.title)"
notification.fireDate = fixNotificationDate(todoItem.deadline)
notification.userInfo = ["note":todoItem.note, "title": todoItem.title]
UIApplication.sharedApplication().scheduleLocalNotification(notification)
At the moment you are not passing anything between application:handleActionWithIdentifier:forLocalNotification:completionHandler: and the functions to do something. Therefore those functions don't know what to do.
The identifier you pass needs to provide enough information to identify what to do. For example it might be "delete=123" to delete record 123. Then you would pass this in the postNotification:
NSNotificationCenter.defaultCenter().postNotificationName("actionTwoPressed",
object: nil, userInfo: identifier)
Now you want to pick it up in the handler for the actionTwoPressed notification, which for you is showAMessage, so it will need a parameter:
func showAMessage(notification: NSNotification) {
// notification.userInfo is the identifier
}
This also means you need to change the addObserver, including the ":" to show a parameter is passed:
NSNotificationCenter.defaultCenter().addObserver(self,
selector: "showAMessage", name: "actionTwoPressed:", object: nil)
Also, none of this code would be in cellForRowAtIndexPath.

upload files to Dropbox from iOS app with Swift

I have completed this tutorial(https://blogs.dropbox.com/developers/2014/09/swift-apps-with-dropbox/) and successfully linked my iOS app with Dropbox. However, I want to be upload a file from my app to Dropbox. All the tutorials out there only have code in Objective C, including the main one from Dropbox (https://www.dropbox.com/developers/core/start/ios). Does anyone know how to do it with Swift?
Thanks!
It works.
let textContent = "Hello Swift Upload"
let textData:NSData? = textContent.dataUsingEncoding(NSUTF8StringEncoding)
var client:DropboxClient? = Dropbox.authorizedClient
if let cli = client {
cli.files.upload(path: "/Swift-Upload.txt", mode: Files.WriteMode.Add, autorename: false, clientModified: nil, mute: false, body: textData!)
}
iOS 10.12.3 swift 3.0 SwiftyDropbox 4.1.1 A slightly more complete answer a year on.
func files_saver(sourcePath: String) {
let textContent = "Blah Blah Blah"
let textData:NSData? = textContent.data(using: String.Encoding.utf8) as NSData?
let client = DropboxClientsManager.authorizedClient!
client.files.upload(path: sourcePath, input: textData as! Data).response { response, error in
if let metadata = response {
print("Uploaded file name: \(metadata.name)")
print("Uploaded file revision: \(metadata.rev)")
// Get file (or folder) metadata
}
if let error = error {
switch error as! CallError<SwiftyDropbox.Files.UploadError> {
case .routeError(let boxed, let requestId):
switch boxed.unboxed {
case .path(let failedPath):
//print("Failed update 2 path: \(failedPath)")
NotificationCenter.default.post(name: Notification.Name("dbFileCreationError"), object: nil, userInfo: nil)
break
default:
//print("Unknown \(error)")
break
}
case .internalServerError(let code, let message, let requestId):
//print("InternalServerError[\(requestId)]: \(code): \(message)")
NotificationCenter.default.post(name: Notification.Name("dbInternalServerError"), object: nil, userInfo: nil)
break
case .badInputError(let message, let requestId):
//print("BadInputError[\(requestId)]: \(message)")
NotificationCenter.default.post(name: Notification.Name("dbBadInputError"), object: nil, userInfo: nil)
break
case .authError(let authError, let requestId):
//print("AuthError[\(requestId)]: \(authError)")
NotificationCenter.default.post(name: Notification.Name("dbAuthError"), object: nil, userInfo: nil)
break
case .rateLimitError(let rateLimitError, let requestId):
//print("RateLimitError[\(requestId)]: \(rateLimitError)")
NotificationCenter.default.post(name: Notification.Name("dbRateLimitError"), object: nil, userInfo: nil)
break
case .httpError(let code, let message, let requestId):
//print("HTTPError[\(requestId)]: \(code): \(message)")
NotificationCenter.default.post(name: Notification.Name("dbHTTPError"), object: nil, userInfo: nil)
break
default:
break
}
}
}
}
I was able to upload a large(r) file (> 800MB) with this (gist) code (Swift 2.2) -->
https://gist.github.com/cnharris10/3d744ca13abd13d4d5bd3a363be16dff
See example screen shot below with another 150mb file uploaded in chunks of 1mb

Resources