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
Related
I am an Android developer trying to port my app to iOS and this is my first time working with Swift and Xcode, or anything Mac in general. The app is to scan for a QR code, if the QR code is invalid then I'd like it to go back to the previous screen and display a toast. I've already made a function that displays the toast stating the QR code was invalid.
func found(code: String) {
print(code)
// convert JSON to object
do {
let qrMessage = try JSONDecoder().decode(QrMessage.self, from: code.data(using: .utf8)!)
print("QrMessage object - device name: " + qrMessage.pcName)
}
catch let error {
print(error)
_ = navigationController?.popViewController(animated: true)
// call ShowInvalidQrToast() from the new VC that is now on top of the ViewController stack
}
EDIT:
I managed to figure out a solution using the NotificationCenter from the first answer in this thread: Calling function from another ViewController in swift. More info in my answer below. If you think there is a better way of doing this, please post your own answer.
My solution using NotificationCenter:
In ConnectViewController
override func viewDidLoad() {
super.viewDidLoad()
...
NotificationCenter.default.addObserver(self, selector: #selector(showInvalidQrCodeToast(_:)), name: Notification.Name(rawValue: "showInvalidQrCodeToast"), object: nil)
}
#objc func showInvalidQrCodeToast(_ notification: Notification) {
self.view.makeToast("Invalid QR code")
}
In ScannerViewController
func found(code: String) {
print(code)
// convert JSON to object
do {
let qrMessage = try JSONDecoder().decode(QrMessage.self, from: code.data(using: .utf8)!)
print("QrMessage object - device name: " + qrMessage.pcName)
}
catch let error {
print(error)
_ = navigationController?.popViewController(animated: true)
NotificationCenter.default.post(name: Notification.Name(rawValue: "showInvalidQrCodeToast"), object: nil)
}
I have developed my application in swift 4.0 and laster I have updated the Xcode with 10.2 , now it was stating lot of error which were working perfectly with previous version. I don't know how to fix these, I have attached the issue lines and wither issues.
1.Value of type 'Notification' has no member 'userInfo' (Error Name)
if let image = notification.userInfo?["image"] as? Int {
let indexPath = IndexPath(item: image, section: 0)
objtableview.reloadRows(at: [indexPath], with: .left)
}
Type 'Notification' has no member 'Name' (ERROR Name)
let dataDict:[String: String] = ["token": fcmToken]
NotificationCenter.default.post(name: Notification.Name("FCMToken"), object: nil, userInfo: dataDict)
3.Value of type 'Notification' has no member 'object' (ERROR Name)
let reachability = notification.object as! Reachability // ERROR Line
switch reachability.connection {
case .none:
debugPrint("Network became unreachable")
case .wifi:
debugPrint("Network reachable through WiFi")
case .cellular:
debugPrint("Network reachable through Cellular Data")
}
4.Type 'Notification' has no member 'Name' (ERROR NAME)
NotificationCenter.default.addObserver(self,
selector: #selector(self.reachabilityChanged),
name: Notification.Name.reachabilityChanged,
object: reachability)
'Name' is not a member type of 'Notification' (ERROR NAME)
public extension Notification.Name { // ERROR LINe
static let reachabilityChanged = Notification.Name("reachabilityChanged") //ERROR Line
}
6.Ambiguous reference to member 'post(name:object:)'
func reachabilityChanged() {
let block = connection != .none ? whenReachable : whenUnreachable
DispatchQueue.main.async { [weak self] in
guard let strongSelf = self else { return }
block?(strongSelf)
strongSelf.notificationCenter.post(name: .reachabilityChanged, object: strongSelf) // ERROR LInE
}
}
Please check this and help me in clearing the issue
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.
I have been working to try getting Multipeer Connectivity working in our app in a relatively short period of time. Most things have gone fairly smooth but we have hit a really puzzling issue now.
We have all the data being transferred fine when following the happy path but then when trying to implement the error handling... Which is done through turning off the wifi mid transfer.. My code...:
Sharer:
func sendResource(data: Data?, name: String, fileName: String, peerId: MCPeerID){
if data != nil{
let url = createTransferFile(jsonData: data!, name: fileName)
if url != nil{
session.sendResource(at: url!, withName: name, toPeer: peerId, withCompletionHandler: { (error) -> Void in
if error != nil{
NSLog("Error in sending resource send resource: \(error!.localizedDescription)")
}
})
}
}
}
Receiver:
func session(_ session: MCSession, didFinishReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, at localURL: URL, withError error: Error?) {
NSLog("%#", "didFinishReceivingResourceWithName: \(resourceName)")
if error != nil{
NSLog("error in receiving")
}
if resourceName.contains("clinicDetails"){
if error == nil{
if let data = self.readClinicJsonFromFile(path: localURL){
NSLog("passing to broadcast delegate")
sendDelegate?.addClinicDetails(self, clinicDetailsJSON: data)
}else{
NSLog("there was an error in finding the retrieved file in clinic retrieve finished")
_ = Util.showAlert(retrievePatientDelegate as! UIViewController, code: 3021, actions: nil, isCustom: true) as! AlertController
}
}else{
_ = Util.showAlert(retrievePatientDelegate as! UIViewController, code: 3021, actions: nil, isCustom: true) as! AlertController
}
}else if resourceName.contains("patients"){
//NSLog("clinicId in retrievePatient: \(json["clinicId"])")
if error == nil{
if let data = self.readPatientJsonFromFile(path: localURL){
NSLog("passing to retrieve patients delegate")
retrievePatientDelegate?.addPatients(self, patientJSON: data , clinicId: resourceName.components(separatedBy: "/")[1])
}else{
NSLog("there was an error in finding the retrieved file in patient retrieve finished")
_ = Util.showAlert(retrievePatientDelegate as! UIViewController, code: 3021, actions: nil, isCustom: true) as! AlertController
}
}else{
_ = Util.showAlert(retrievePatientDelegate as! UIViewController, code: 3021, actions: nil, isCustom: true) as! AlertController
}
}else if resourceName == "clinicList"{
if error == nil{
if let data = self.readClinicListJsonFromFile(path: localURL){
NSLog("passing to retrieve retrieveDelegate")
retrieveDelegate?.addClinics(self, clinicsJSON: data["jsonData"] as! [[String:Any]], passcode: data["passcode"] as! String)
}else{
NSLog("there was an error in finding the retrieved file in patient retrieve finished")
_ = Util.showAlert(retrievePatientDelegate as! UIViewController, code: 3021, actions: nil, isCustom: true) as! AlertController
}
}else{
_ = Util.showAlert(retrievePatientDelegate as! UIViewController, code: 3021, actions: nil, isCustom: true) as! AlertController
}
}
}
The errors we receive:
2017-03-06 16:52:54.416352 DC[2445:1444277] [GCKSession] Failed to send a DTLS packet with 78 bytes; sendmsg error: Can't assign requested address (49).
2017-03-06 16:52:54.416560 DC[2445:1444277] [GCKSession] SSLWrite failed, packet was not sent for participant [05280B9E] channelID [4] DTLS context [0x103043ea0] pCList [0x10e94f960]; osStatus = -9803: errno = Can't assign requested address (49).
These lines print out more based on the amount of progress left.
Then we also get the following stack in xcode (I can't add images directly into my posts yet :< )
Stack Frame from the thread causing the error
It seems like this bug is linked to a bug in apples framework due to the localURL in the didFinishReceivingResourceWithName function not being an optional.The value on error or progress cancelled is nil. I have come to this conclusion after looking through each call in the crashing thread then finding a related SO Post
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)