NordicSemiconductor / IOS-DFU-Library

OTA DFU Library for Mac and iOS, compatible with nRF5x SoCs
http://www.nordicsemi.com
BSD 3-Clause "New" or "Revised" License
527 stars 215 forks source link

DFU Stuck on Connecting #50

Closed sanderdb01 closed 7 years ago

sanderdb01 commented 8 years ago

We have a device that has DFU enabled on it, and my iOS code was working, but recently stopped working (around the time of iOS 10 and Swift 3). The latest issue that I have is that I am getting stuck on the didChangeStateTo call back of "connecting". I set the flag high on the DFU service so that I disconnect and change to the bootloader so I am getting DFUTarg and the advertisement. I set the DFUFirmware with a .bin and .dat file from my server. I connect to the DFUTarg peripheral, and once the connection is complete, the following code executes:

let initiator = DFUServiceInitiator(centralManager: self.centralManager!, target: peripheral)
initiator.withFirmwareFile(firmware)
// Optional:
// initiator.forceDfu = true/false; // default false
// initiator.packetReceiptNotificationParameter = N; // default is 12
initiator.logger = self // - to get log info
initiator.delegate = self // - to be informed about current state and errors
initiator.progressDelegate = self // - to show progress bar
// initiator.peripheralSelector = ... // the default selector is used
initiator.start()

I then get the call back response of "Connecting" and it gets stuck there. I seem to be implementing the code from nRFToolbox the same way, and as I said before, it seemed to be working fine before.

This is a pretty hot issue for my company, so any help would be greatly appreciated. Thanks a bunch.

filippocamillo commented 8 years ago

Hi guys! Did you already fixed this issue? I'm stucked on it too.

Thanks!

philips77 commented 8 years ago

Did you try branch Version3? It will soon replace master.

sanderdb01 commented 8 years ago

Now I am getting a EXC_BAD_ACCESS(code=1,address=0x10) on initiator.with(firmware: firmware). I was not getting this issue before with the master branch. I am printing my details of the firmware beforehand, and that data is there. Also it is a firmware that is already in production and used on Android devices. I am using a url to an online server, with the .bin and .dat files. Could this be an issue? I tried with the Zip and got errors saying that the zip could not be done (Error while creating ZIP stream: The operation couldn’t be completed. (Zip.ZipError error 0.)). All of the correct files and manifest are there. Thanks again.

mostafaberg commented 8 years ago

We need to see some actual code or a symbolicated crash log to be able to help with those type of errors.

If you revert back to the latest version of the Pods release does the error go away ?

if not let us know, step through the code and see what exactly is giving you the EXC_BAD_ACCESS code so we can debug

sanderdb01 commented 8 years ago

The function gets called 3 times after the device is initially connected. The first time, the device enters bootloader mode. The second time, connects to the bootloader (dfuTarg). The third time, it starts the firmware update. self.selectedFirmware is the firmware taken from the website. Everything seems to execute fine except for the last step at initiator.withfirmware. Before, with the old revision, everything would work fine, but the update would be stuck on Connecting....

I hope this is everything you need, but if you need more or need me to explain more, please let me know. I am struggling with this and I am way past my deadline. Thanks so much for everything

func DFUUpdateStart(){
        print("Entered DFUUpdateStart function")
        // (Step 1) If connected to a new 110 PLC and no DFUPeripheral has been selected and saved
        if self.selectedPeripherals.count == 1 && self.DFUPeripheral == nil {
            self.dfuInitiatorRan = false
            self.dfuDelayRunAgain = true
            let url = URL(string: "http://reverie.com/RnD/110_plc.bin")
            let dtUrl = URL(string: "http://reverie.com/RnD/110_plc.dat")
            self.selectedFirmwareDFU = DFUFirmware(urlToBinOrHexFile: url!, urlToDatFile: dtUrl, type: DFUFirmwareType.application)
//            let url = URL(string: "http://reverie.com/RnD/110_plc_sd110_dfu_1_03_10-5.zip")
//            self.selectedFirmwareDFU = DFUFirmware(urlToZipFile: url!)
            if let characteristicArray = self.selectedCharacteristicsBTLE110[selectedPeripherals[0].identifier.uuidString] {
                for characteristic in characteristicArray where characteristic.uuid.uuidString == REVERIE_110_DFU_UPDATE_CHARACTERISTIC_UUID {
                    print("DFU Characteristic is notifying: \(characteristic.isNotifying)")
                    //                    let bytes:[UInt16] = [0x0401]
                    let bytes:[UInt8] = [0x01, 0x04]
                    let data = Data(bytes: UnsafePointer<UInt8>(bytes), count: bytes.count)
                    selectedPeripherals[0].writeValue(data, for: characteristic, type: .withResponse)
                    print("Writing to DFU to flag it manually for update.")
                    //TODO: Add check for implementation of delegate function
                    self.dfuStep = 1
                    print("Changed DFU Step to 1")
                    self.delegate?.dfuUpdate!(self.dfuStep, state: self.dfuState, part: self.dfuPart, totalParts: self.dfuTotalParts, progress: self.dfuProgress, currentSpeed: self.dfuCurrentSpeed, avgSpeed: self.dfuAvgSpeed, error: self.dfuError, errorMessage: self.dfuErrorMessage)
                }
            }
            return
        }
        // (Step 2) once disconencted from PLC, scan for DFU Peripherals and if found, connect to it
        if self.selectedPeripherals.count == 0 {
            self.centralManager?.scanForPeripherals(withServices: [CBUUID.init(string: REVERIE_110_DFU_SERVICE_UUID)], options: nil)
            print("Scanning for DFU peripheral")
            if let peripheral = self.DFUPeripheral {
                self.dfuDelayRunAgain = true
                self.centralManager?.connect(peripheral, options: nil)
                print("Saved DFU peripheral detected, connecting to it")
                self.dfuStep = 2
                print("Changed DFU Step to 2")
                self.delegate?.dfuUpdate!(self.dfuStep, state: self.dfuState, part: self.dfuPart, totalParts: self.dfuTotalParts, progress: self.dfuProgress, currentSpeed: self.dfuCurrentSpeed, avgSpeed: self.dfuAvgSpeed, error: self.dfuError, errorMessage: self.dfuErrorMessage)
            } else {
                if self.dfuDelayRunAgain {
                    Timer.scheduledTimer(timeInterval: 1.5, target: self, selector: #selector(BTLEInterface.DFUUpdateStart), userInfo: nil, repeats: false)
                    self.dfuDelayRunAgain = false
                    print("DFU Update delay started 1.")
                }
            }
            return
        }
        // (Step 3) if connected to a DFU Peripheral, update it.
        if let peripheral = self.DFUPeripheral {
            if let firmware = self.selectedFirmwareDFU {
                if self.dfuDelayRunAgain {
                    Timer.scheduledTimer(timeInterval: 1.5, target: self, selector: #selector(BTLEInterface.DFUUpdateStart), userInfo: nil, repeats: false)
                    self.dfuDelayRunAgain = false
                    print("DFU Update delay started 2.")
                } else {
                    print("Firmware Size: \(firmware.size)   Parts: \(firmware.parts)")
                    if self.centralManager?.retrieveConnectedPeripherals(withServices: [CBUUID(string: REVERIE_110_DFU_SERVICE_UUID)]).count != 1 {
                        Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(BTLEInterface.DFUUpdateStart), userInfo: nil, repeats: false)
                        print("Refiring DFUUpdateStart because device is not connected yet")
                    } else if !self.dfuInitiatorRan {
                        print("Starting DFU Update")
//                        let initiator = DFUServiceInitiator(centralManager: self.centralManager!, target: peripheral).withFirmwareFile(firmware)
                        let initiator = DFUServiceInitiator(centralManager: self.centralManager!, target: peripheral)
                        initiator.with(firmware: firmware)
                        // Optional:
                        // initiator.forceDfu = true/false; // default false
                        // initiator.packetReceiptNotificationParameter = N; // default is 12
                        initiator.logger = self // - to get log info
                        initiator.delegate = self // - to be informed about current state and errors
                        initiator.progressDelegate = self // - to show progress bar
                        // initiator.peripheralSelector = ... // the default selector is used

                        initiator.start()
                        //        let controller = initiator.start()
                        self.dfuStep = 3
                        print("Changed DFU Step to 3")
                        self.delegate?.dfuUpdate!(self.dfuStep, state: self.dfuState, part: self.dfuPart, totalParts: self.dfuTotalParts, progress: self.dfuProgress, currentSpeed: self.dfuCurrentSpeed, avgSpeed: self.dfuAvgSpeed, error: self.dfuError, errorMessage: self.dfuErrorMessage)
                        self.dfuInitiatorRan = true
                    }
                }
            } else {
                print("DFU Firmware is nil")
                self.dfuStep = 0
                print("Changed DFU Step to 0")
                self.dfuErrorMessage = "DFU Firmware is not loaded. Please try again."
                self.delegate?.dfuUpdate!(self.dfuStep, state: self.dfuState, part: self.dfuPart, totalParts: self.dfuTotalParts, progress: self.dfuProgress, currentSpeed: self.dfuCurrentSpeed, avgSpeed: self.dfuAvgSpeed, error: self.dfuError, errorMessage: self.dfuErrorMessage)
            }
            return
        }
    }
mostafaberg commented 8 years ago

Perfect ! Itll be the first thing i look at tomorrow morning during work and will update you right away!

mostafaberg commented 8 years ago

@sanderdb01 quick question, is that Secure DFU or Legacy DFU ?

sanderdb01 commented 8 years ago

I'm not sure. How can I tell if it is legacy or secure?

mostafaberg commented 8 years ago

it's secure if it requires signing, you can also check the UUID of the DFU service, if it's FE59 then it's the secure one, if it's not then it's legacy

sanderdb01 commented 8 years ago

Yeah, looking at the DFU service UUID (00001530-1212-EFDE-1523-785FEABCD123), and I am 99% sure we are not doing signing, I think it is legacy then.

mostafaberg commented 8 years ago

Perfect, I see you had the same Zip issue before here 11 , how did you fix that ?

sanderdb01 commented 8 years ago

I never did, I just used the .dat and .bin file instead. The issue was never resolved.

sanderdb01 commented 7 years ago

Hey guys, any update yet on the issue? I never heard anything back. Never made if further than telling you it was legacy.

sanderdb01 commented 7 years ago

I am attaching a screenshot of the error for your reference screen shot 2016-12-09 at 3 14 25 pm

mostafaberg commented 7 years ago

So sorry about the late response, i saw another email that an issue was closed and thought it was this one so i never looked back!

your issue seems to be due to the Zip library, and since you never actually fixed the issue i linked earlier I'm pretty certain that your issue is with the Zip framework.

The fastest way I can help you is that if you create a small example project that uses the Zip cocoapod (make sure it's the exact same version as the one you are having in our iOSDFULibrary by checking the library's PodSpec file, under the dependencies, you'll find the version of the Zip Pod.

Then try to unzip one of the files that are failing, if it fails, please send me a link to the project so i can apply the fix as soon as possible!, if it doesn't reproduce, then let me know too!

again, please accept my apology for the late response

sanderdb01 commented 7 years ago

Hi, I was wondering if it was ok if i just sent you a link to our project on bitbucket, and you could just check our code there? Would that be easier?

mostafaberg commented 7 years ago

Sure thing, if that's fine by you i'll gladly take a look on Bitbucket and send you a PR when i find the fix

sanderdb01 commented 7 years ago

ok great. I will push the latest version later on today and send you the link. Should I post it here or email it to you?

mostafaberg commented 7 years ago

perfect, keep me updated, better keep it private via email, i'll shoot you an email now

mostafaberg commented 7 years ago

Ok we have seem to found the issue, in your code you need to hold a reference to a DFUServiceController, you seem to have left that out on start.

so your line that says: initiator.start() needs to be: self.dfuController = initiator.start()

and dfuController can be defined as a property in your view var dfuController: DFUServiceController?

i gave it a try here and it worked, let me know how it works for you

My colleague @philips77 is also going to work on getting a fix so that you don't need to hold a reference that will come in version 3.0.2 soon.

@filippocamillo I see you had a similar issue, can you try this fix and see if it resolves it for you ?

thanks

philips77 commented 7 years ago

Hi, I've tested the library and, as I expected, you don't have to hold any reference to make it work. I did test with the Example app where initiator is local and I removed assigning the controller and still DFU for Legacy and Secure works fine. When DFU is started there are always 2 classes: executor and peripheral that hold a cyclic reference to one each other which is released on Complete, Abort or Error.

philips77 commented 7 years ago

@sanderdb01 Question: why do you trigger the jump to bootloader on your own? The library does this automatically. You can start initialiser just after you have the DFUFirmware object.

sanderdb01 commented 7 years ago

@philips77 We connect to our peripheral normally, then we have to set a characteristic value to 1 to make it jump to the bootloader. Once this happens, the app automatically disconnected from the peripheral (because it is "no longer there"), and the peripheral turns into DFUTarg. I then Have to connect to DFUTarg to start the DFU process. I think this answered your question. If there is a better way of doing it, I am all ears lol.

So @philips77 and @mostafaberg , assigning the return value from the initiator does not make it work, right? I will still try it on my end, JIC. Any other ideas? Thanks a bunch.

sanderdb01 commented 7 years ago

@mostafaberg Also, did the ZIP issue ever get figured out? I still cannot get my URL for the zip to work. Thanks.

sanderdb01 commented 7 years ago

@mostafaberg @philips77 I just check the code with that change, and it still does not work. I should also mention, the error starts at the .with(Firmware) line, not the .start() line. So it happens before I get to start the process.

philips77 commented 7 years ago

How about:

let url = URL(string: "http://reverie.com/RnD/110_plc.bin")
let dtUrl = URL(string: "http://reverie.com/RnD/110_plc.dat")
let firmware = DFUFirmware(urlToBinOrHexFile: url!, urlToDatFile: dtUrl, type: DFUFirmwareType.application)

print("Starting DFU Update")
let initiator = DFUServiceInitiator(centralManager: self.centralManager!, target: peripheral).with(firmware: firmware)
initiator.logger = self // - to get log info
initiator.delegate = self // - to be informed about current state and errors
initiator.progressDelegate = self // - to show progress bar
initiator.start()

Switching to DFU mode will be handled automatically by the library. No need to enable notifications and send 0x01-04 on your own. There is nothing custom there, as I see.

philips77 commented 7 years ago

The peripheral does not have to be in DFU mode. It also can be connected if you held the connection before but it will disconnect at the end of the process so you need to reconnect to it when done/error (if needed).

sanderdb01 commented 7 years ago

@philips77 I talked to our firmware designer, and there is a bug in our module that makes it so that we HAVE to set the characteristic and reconnect. That was our issue, not yours.

Still, I am getting the same BAD_EXC on the .with function call. I am also getting an error in the terminal about the Zip module (the same as in the screenshot above). I am wondering if it is related.

mostafaberg commented 7 years ago

I didnt check with zip i just used the code you had that uses the bin and dat files, i can debug the zip issue tomorrow and update you !

sanderdb01 commented 7 years ago

@mostafaberg @philips77 I had a thought. Is it possible that perhaps I have a build error? I know it was mentioned that I have (somehow) several copies of the framework. Maybe that is the issue? Is there a way that I can remove everything related to the iOSDFULibrary, then reinstall it? How would I go about doing that and not mess anything up? Thanks.

mostafaberg commented 7 years ago

Its quite simple, run pod deintegrate this will remove all cocoapods related files, then delete any extra files that you see related to the library from the filesystem, then delete any remaining files or framework fiels from xcode, after all is cleared update to the latest version of cocoapods sudo gem install cocoapods and finally reinstall the pods pod install and you should be set

ArduinoManager commented 7 years ago

I have exactly the same issue. My environment is Xcode 8.2.1 and my app is in Objective-c. I rewrote a little bit the way I start the upload but it stuck on connecting.

 DFUFirmware *newFirmware = [[DFUFirmware alloc] initWithUrlToZipFile:url];

 _initiator = [[[DFUServiceInitiator alloc] initWithCentralManager:_centralManager
                                                               target:_peripheral] withFirmware:newFirmware];
 _initiator.logger = self; // - to get log info
 _initiator.delegate = self; // - to be informed about current state and errors
 _initiator.progressDelegate = self;

 _controller = [_initiator start];

I just have a doubt, do I need to use the experimental_ble_app_buttonless_dfu (or similar architecture in my own app) to make the library working?

Last but not least, the peripheral object I am using was created before that I brought the device into the DFU mode. Do I have to rescan the device after it entered into DFU mode?

Thx

mostafaberg commented 7 years ago

@ArduinoManager Your code seems correct, but seems like you're connecting to a peripheral, jumping to bootloader your self, then using that same _peripheral in the DFU process, which will not work, after switching a peripheral to DFU/Bootloader mode, it starts broadcasting as a new peripheral with a new identifier, you'll need to rescan for it and add reassign it to _peripheral before starting DFU.

However, the library does the DFU/Bootloader jump for you, and yes, that is what experimental_ble_app_buttonless_dfu does

I would recommend simply doing the following and see if this helps :

  1. Scan for target
  2. Assign it to your _peripheral variable
  3. Create a DFUServiceInitiator with your _peripheral, and set the experimental_ble_app_buttonless_dfu flag.
  4. Start the initiator

If you have a reason to do the DFU jump yourself, you just scan and find the new peripheral after it jumps to DFU, usually named DFUTarg and broadcasts the DFU / Secure DFU service UUID.

philips77 commented 7 years ago

@ArduinoManager what Mustafa wrote applies to SDK 12 and Secure DFU only. If you are using Legacy DFU (SDK before 12) the buttonless jump feature is demonstrated for example in HRM_DFU sample in the SDK. There the device after switching to DFU advertises with the same address, so you don't have to scan on your own. Actually, the lib will also switch to BL mode automatically when you use the standard impl. If you use SDK 12 and Secure DFU and the experimental buttonless feature for Secure DFU you may set the flag to true in the DFUServiceInitiator and lib will also jump to BL for you. As Mustafa wrote, the only case when you have to trigger switch to BL and scan on your own is when you have a proprietary solution for this, for example you use your own service UUIDs, etc.

ArduinoManager commented 7 years ago

@mostafaberg Thank you for your valuable support. Because when the device is in DFU mode, the peripheral changes, scanning again and getting a new _peripheral object was the solution. Right now, I manually switch the device in DFU mode because I don't know yet if my final device will implement the DFU service. I'll try experimental_ble_app_buttonless_dfu out anyway. It would be great if the library returned an error because it is not able to get connected to the device instead of stuck without any error. That said, integrating the library was quite smooth.

philips77 Thank you for the clarification. I am using SDK12.

sanderdb01 commented 7 years ago

@philips77 @mostafaberg I am back from the holiday and I tried uninstalling the library and then reinstalling it to see if it worked, but it still does not. I am still getting a fail at initiator.with(firmware: firmware) with an error of Thread 1: EXC_BAD ACCESS (code=1, address=0x10) Has there been any update on the Zip not working? Thanks a bunch

sanderdb01 commented 7 years ago

HI, I also noticed that the Zip.framework text in the navigation tree is in red. I assume this is a problem having to do with the file locations. I however didnt change anything. COuld this be the problem? And if so, how do I fix it? Thanks. screen shot 2017-01-10 at 5 26 46 pm

mostafaberg commented 7 years ago

@sanderdb01 It's a bit unclear on why you're having this issue, but as I said earlier, Zip seems to be working fine for us, I can take a look if you email me one of the zip packages that's crashing for you and see if it works on our side maybe ? might rule out one issue at least that the Zip is in fact ok

sanderdb01 commented 7 years ago

@mostafaberg I believe I sent a zipped copy of our project before for you guys to test, but I am unclear on how the results of those tests went (if you got it to work or not). I can send our version of just the Zip package if you want. How would you want me to go about that (should I zip up the zip folders, and if so where in the project tree should I get it)?

I was wondering if perhaps we could skype or get on a phone call to resolve this just to make sure we are on the same page. This was a hot topic some time ago, and it is a big emergency now. Thanks a bunch for everything.

mostafaberg commented 7 years ago

@sanderdb01 I mean the Zip package and not the whole project, regarding the last time when i tested the project I have responded here as you see, it did work and i was able to run the DFU process and sent you the changes required to get it to work, However it didn't work out on your side. I didn't manage to test it with the distribution package Zip file. so If you can send me the Actual distribution package, i can test locally and see if it's actually the problem or not.

I would still recommend updating the cocoapods installation and update to the latest version of the library in any case

We can get in touch by email to discuss your issue further ! you still have my email ?

sanderdb01 commented 7 years ago

@mostafaberg I tried assigning the return value and it still didn't work (referring to your response from before. I thought I had mentioned it, but it turns out I did not, I am sorry about that. I also didnt realize that you were able to get my program's DFU functionality working, I must have missed that as well or misunderstood. Very sorry about all the confusion.

I am uncertain what you mean by the zip package. I am assuming you mean the zipped file of the new firmware. If that is the case, it is located at the url: http://reverie.com/RnD/110_plc_sd110_dfu_1_03_10-5.zip

The zip file seems to work on the nordic app as well as Android, so I am thinking that the zipped firmware is not the issue.

If you mean the Zip Library files that are part of the project, let me know how you would want me to send that to you.

I also want to reiterate that I am getting the failure at: initiator.with(firmware: firmware) with an error of Thread 1: EXC_BAD ACCESS (code=1, address=0x10). I am currently using the .dat/.bin DFUFirmware initializer (example: let url = URL(string: "http://reverie.com/RnD/110_plc.bin") let dtUrl = URL(string: "http://reverie.com/RnD/110_plc.dat") self.selectedFirmwareDFU = DFUFirmware(urlToBinOrHexFile: url!, urlToDatFile: dtUrl, type: DFUFirmwareType.application))

I also tried changing the DFUFirmwareType field to all available options, and they all had the same result.

If there is anything else that I can provide that will help, please let me know. Thanks a bunch.

I want to also add what my Podfile.lock file looks like. I just want to make sure that I have the correct version of the Zip library: PODS:

DEPENDENCIES:

SPEC CHECKSUMS: iOSDFULibrary: 201dec70ae2d7e1cfbdc3cd8005b163f009c9dea Zip: 805fc2fa9d2f05bbb7762d982d7a42ccdcc51f42

PODFILE CHECKSUM: 7b25d8e1e32a48733e6372f90cd842d1ec96c1eb

COCOAPODS: 1.1.1

sanderdb01 commented 7 years ago

@mostafaberg I wanted to add to my above post after I ran some tests. Another issue I am finding has to do with the execution of the with(Firmware) function. That is where I was getting the EXC_BAD error. In an attempt to hunt the issue down, I was trying to figure out why I was getting the error, and it was point to a null pointer. So, I then tried to add a new function to the DFUServiceInitalizer class called test(), and all it did was print "test". When I then tried to execute that function on the newly initialized DFUServiceInitializer object, I got the same EXC_BAD error. I then added a testString variable to the DFUServiceInitializer class (var testString:String = ""), and then after running the DFUServiceInitalizer init function (per usual), I then tried to set the testString variable just to see if I could access the initiator variable at all, and still got the EXC_BAD error. So, it seems that the program fails with the EXC_BAD error whenever I try and access the initiator variable (DFUServiceIntializer object). I was not getting this error before the conversion to Swift3, I wanted to make sure I was mentioning that. Thanks again.

mostafaberg commented 7 years ago

@sanderdb01 Thanks for all the input !, I have everything I need now so i'll be debugging a bit today and will let you know as soon as possible how it goes, if i get things working i'll just send you a working copy of the project later in the day!

mostafaberg commented 7 years ago

@sanderdb01 Ok I got things working, I pushed a new branch on your repository bugfix/dfu with a working version, make sure to check for the //HACK comments where i had to modify minor parts of your code to be able to connect to any peripheral in DFU directly, I'll shoot you an email now too with some info!

mostafaberg commented 7 years ago

@sanderdb01 This works now after the fix, right ? I'll close by the end of the day if i don't hear back, or feel free to close it if you read this earlier, or update me with the status so we can fix anything that's remaining!

kodartcha commented 7 years ago

Hello. Sorry to open again this issue but I am having the same and I am not able to fix it. Today I updated the iOS DFU Library and I am getting the EXC_BAD_ACCESS (code=1, address=0x10). Before the update everything was working properly.

I also tried removing and reinstalling all the pods from scratch.

Here is my code:

func setFilePath(){
        let bundleURL = Bundle.main.bundleURL
        let dataFolderURL = bundleURL.appendingPathComponent("firmware")
        let fileURL = dataFolderURL.appendingPathComponent("edd_new.hex")

        let fileManager = FileManager.default
        if fileManager.fileExists(atPath: fileURL.path) {
            print("FILE AVAILABLE")
        } else {
            print("FILE NOT AVAILABLE")
        }

        let selectedFirmware = DFUFirmware(urlToBinOrHexFile: fileURL, urlToDatFile: nil, type: DFUFirmwareType.application)

        startUpdate(selectedFirmware!)
    }

    func startUpdate(_ fw: DFUFirmware){

        if(self.dfuPeripheral == nil){
            print("DFU PERIPHERAL NIL")
        }

        let initiator = DFUServiceInitiator(centralManager: centralManager!, target: self.dfuPeripheral!)

        initiator.logger = self; // - to get log info
        initiator.delegate = self; // - to be informed about current state and errors
        initiator.progressDelegate = self; // - to show progress bar

        self.dfuController = initiator.with(firmware: fw).start()
    }

File is available, and peripheral is not nil. It also crashes on the .with(firmware: fw) part. Tried both to put in on the initiator declaration and before the start as the code shows. Same problem.

Could you please share with me the solution? I am a bit in a rush. Thank you!

usamaaftab80 commented 6 years ago

@mostafaberg

I am running into the same problem. For some reason, as soon as it executes the DFUFirmware(URL) constructor, it gives me the error: "Error while creating ZIP stream: The operation couldn't be completed."

Here is my code:

let documentsUrl:URL =  (FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first as URL?)!
        let destinationFileUrl = documentsUrl.appendingPathComponent(Constant.firmwareLocalPath)

        let selectedFirmware = DFUFirmware(urlToZipFile: destinationFileUrl)

        let initiator = DFUServiceInitiator(centralManager: self.central, target: self.peripheral).with(firmware: selectedFirmware!)
        initiator.logger = self; // - to get log info
        initiator.delegate = self; // - to be informed about current state and errors
        initiator.progressDelegate = self; // - to show progress bar
        initiator.enableUnsafeExperimentalButtonlessServiceInSecureDfu = true

        self.dfuServiceController = initiator.start()

and my firmware path looks like this: file:///var/mobile/Containers/Data/Application/64D07EC7-A64F-4D7B-9F89-7A5A700CAD5A/Documents/firmware.zip

A quick reply will be appreciated. I am not sure why it is behaving like this? To be clear, nothing is nil. Central, Peripheral and path, everything is valid.

ghost commented 6 years ago

I resolved my "Error while creating ZIP stream: The operation couldn't be completed."

It seems that the iOS ZipArchive.unzip(urlToZipFile) create by default two folders : including the below in DFUStreamZip.swift solved my problem.

init(urlToZipFile: URL, type: DFUFirmwareType) throws {
        // Try to unzip the file. This may throw an exception
        var contentUrls = try ZipArchive.unzip(urlToZipFile)

//----- INCLUDE ----
        let file_name = urlToZipFile.deletingPathExtension().lastPathComponent
        for url in contentUrls {
            if url.lastPathComponent == file_name {
                contentUrls = try FileManager.default.contentsOfDirectory(at: url, includingPropertiesForKeys: nil,options: [])
                break
            }
        }
        //TEST: List zip archive content
        //for url in contentUrls { print(url.absoluteString) }
//---- END INCLUDE ----

       // Look for MANIFEST_FILE
.
.
.