Crash on encode when adding DeveloperField to LapMesg or SessionMesg [Fit SDK c++ iOS]

Former Member
Former Member
The following page says that DeveloperField can be added to Lap and Session messages.


> Developer Data can be added to any existing FIT message. Developer Data is typically used in Record, Lap, and Session messages to include additional time series data in Activity files.

However, when I wrote the code to add DeveloperField to Lap and Session, it crashed.

Repro:
    fit::FieldDescriptionMesg fieldDescFrontGearShiftCount;
    fieldDescFrontGearShiftCount.SetDeveloperDataIndex(0);
    fieldDescFrontGearShiftCount.SetFieldDefinitionNumber(1);
    fieldDescFrontGearShiftCount.SetFitBaseTypeId(FIT_BASE_TYPE_UINT16);
    fieldDescFrontGearShiftCount.SetFieldName(0, L"front_gear_shift_count");
    fieldDescFrontGearShiftCount.SetUnits(0, L"count");

...

    [super.fe Open:file];
    [super.fe WriteMesg:fileId];
    [super.fe WriteMesg:devId];
    [super.fe WriteMesg:fieldDesc];
    [super.fe WriteMesg:fieldDescFrontGearShiftCount];

...

    // Write LapMessage
    fit::LapMesg lap;
    lap.SetTimestamp(1);
    lap.SetEvent(FIT_EVENT_LAP);
    lap.SetEventType(FIT_EVENT_TYPE_STOP);
    fit::DeveloperField lapFrontGearShiftCountField(fieldDescFrontGearShiftCount, devId);
    lapFrontGearShiftCountField.SetUINT16Value(1);
    lap.AddDeveloperField(lapFrontGearShiftCountField);
    [super.fe WriteMesg:lap];

    // Write SessionMessage
    fit::SessionMesg session;
    session.SetTimestamp(1);
    session.SetStartTime(1);
    session.SetTotalElapsedTime(1);
    session.SetSport(FIT_SPORT_CYCLING);
    fit::DeveloperField sessionFrontGearShiftCountField(fieldDescFrontGearShiftCount, devId);
    sessionFrontGearShiftCountField.SetUINT16Value(1);
    lap.AddDeveloperField(sessionFrontGearShiftCountField);
    [super.fe WriteMesg:session];

    // Write ActivityMessage
    fit::ActivityMesg activity;
    activity.SetTimestamp(1);
    activity.SetLocalTimestamp(1);
    activity.SetNumSessions(1);
    activity.SetType(FIT_ACTIVITY_MANUAL);
    activity.SetEvent(FIT_EVENT_ACTIVITY);
    activity.SetEventType(FIT_EVENT_TYPE_STOP);
    [super.fe WriteMesg:activity];

    if (![super.fe Close])
    {
        NSLog(@"Error closing file %@", super.fileName);
        return -1;
    }
    fclose(file);

Crash:




The following is a project that reproduces the problem.
If you run this project in the iOS simulator, it will run encode and crash.


# Environment

- Fit SDK: 21.40.00 (c++)
- Xcode12.1
- iOS Simulator: iPhone X(12.1)


Any help with a workaround would be appreciated!
  • There is an issue in the C++ FIT SDK (which the Objective-C FIT SDK wraps) where the DeveloperFieldDefinition class does not define a custom copy assignment operator which in turn causes an object to be free'd twice. That is what is causing the crash. There is another thread in the forums with the work around, which is to add the assignment operator to the DeveloperFieldDefinition class.

    https://forums.garmin.com/developer/fit-sdk/f/discussion/246172/developerfielddefinition-in-c-sdk-should-implement-operator-to-do-a-deep-copy 

    Looking at your code in GIT Hub, this line looks to be a copy-paste error and probably should be "session" not "lap"

    https://github.com/k-yamada/fit-ios-encode-bug/blob/master/exampleios/ActivityExample.mm#L186 

    You should not reuse FieldDescriptionMesg objects between different message types, you will want to create a distinct FieldDescriptionMesg for Lap and Session messages. 

    Search this page for "gear_change_data" to see how Garmin, and others, encode gear change data in FIT files. This method uses Event messages and stores the gear change data in the Event message's "data" field. The data field is a 32 bit uint and the four components (‘rear_gear_num’, ‘rear_gear’, ‘front_gear_num’ and ‘front_gear’) are encoded as 8bit values.
    developer.garmin.com/.../

  • Former Member
    0 Former Member over 4 years ago in reply to Ben FIT

    By using the workaround you answered, I was able to get it to work.
    Thank you very much.

    Fixed branch
    github.com/.../fixed

    # How to build C++ Static Lib in Xcode12

    ## Fix the source code of FitSDK

    Remove i386 architecture (because it causes build errors in Xcode12)

    $ cd cpp/MacStaticLib/
    $ find . \( -name "*.xcconfig" -o -name "*.pbxproj" \) | xargs sed -i '' -e 's/-arch i386//g' -e 's/i386//g'

    cpp/MacStaticLib/build.py

    # Compile the project
    # subprocess.call([XCODEBUILDER, XCODEPROJECT, project, XCODETARGET, p[TARGET]])
    subprocess.call([XCODEBUILDER, "-UseModernBuildSystem=NO", XCODEPROJECT, project, XCODETARGET, p[TARGET]])

    cpp/fit_developer_field_definition.hpp

    FIT_BOOL operator!=(const DeveloperFieldDefinition& field) const;
    // ↓ Added
    DeveloperFieldDefinition& operator=(const DeveloperFieldDefinition& other);

    cpp/fit_developer_field_definition.cpp

    FIT_BOOL DeveloperFieldDefinition::operator!=(const DeveloperFieldDefinition& field) const
    {
        return !(*this == field);
    }
    
    // ↓ Added
    DeveloperFieldDefinition& DeveloperFieldDefinition::operator=(const DeveloperFieldDefinition& other) 
    {
        if (this != &other){
            num = other.num;
            size = other.size;
            developerDataIndex = other.developerDataIndex;
            if (mesg != nullptr){
                delete mesg;
                mesg = nullptr;
            }
    
            if (other.mesg != nullptr) {
                mesg = new FieldDescriptionMesg(*other.mesg);
            }
            
            if (developer != nullptr){
                delete developer;
                developer = nullptr;
            }
    
            if (other.developer != nullptr){
                developer = new DeveloperDataIdMesg( *other.developer );
            }
        }
        return *this;
    }

    ## Build the static library

    $ cd cpp/MacStaticLib
    $ python build.py

  • The operator=() that was missing from the DeveloperFieldDefinition class was added to version 21.47 of the FIT C++ SDK. The C++ DeveloperFieldDefinition class is used by the FIT Objective-C++ SDK as well, so the issue should be resolved in that SDK too. The example C++ encode project was also updated to provide a better example of how to use Developer Fields in the C++ SDK. Let us know if you run into any issues with this change.