Settings Bundle

From Qt Wiki
Revision as of 20:04, 16 February 2017 by Jake Petroules (talk | contribs) (Add info about tvOS and watchOS)
Jump to navigation Jump to search

General

What is the Settings Bundle? In few words it's a special bundle which allows to you set settings of you application for system settings on your device on iOS, tvOS, and watchOS platforms. If application has this bundle all uses how installed this application can change settings from Settings -> <your application name>. For more information see this page.

What we need to use it in Qt application? All you need it's create the file <some_name>.bundle (in my case it will be Settings.bundle). I recommend use Xcode for create this file. Xcode has a special template and you don't need more time to generate folder structure and file. Also Xcode can edit plist (XML file format) file.

Step-by-step instruction

Ok let's see how to use Settings Bundle in Qt Application.

  1. Open Xcode and select File -> New File -> iOS/tvOS -> Resources -> Settings.bundle
  2. In dialog window set the name of settings file (in my case it's "Settings") and select a folder where this file will be saved on your disc.
  3. In result you will see the editor screen where you will see the Root.plist. This is a root plist file for configure root settings. In this file you can add all settings what you need. Also you can create another Plist files for another pages with other settings.
  4. When you finish edit file with settings save it Cmd + S or File -> Save.
  5. With bundle we were finished work let's add supports to Qt Application. Open your project in Qt Creator or any other editor which you are using. Select <your_project>.pro file and add code below:
app_launch_files.files = $$PWD/LaunchScreen.xib $$files($$PWD/*.png) $$files($$PWD/Settings.bundle)
QMAKE_BUNDLE_DATA += app_launch_files
  1. Save file and build your project.
  2. When your application will load on your device or simulator click to the Home button (device) or Cmd+Shift+H (for simulator) and open Settings.app. Scroll down and you will see the line with name of your application. Click to it and you will see the settings structure which you was created.
  3. That's it!

How to use settings in application

All settings which you save in Settings bundle will be allow from NSUserDefaults class. And all you need to use it that get access to NSUserDefaults class. Let's see how we can do it.

  1. Create a new C++ class in your project. Rename file with *.cpp extension to *.mm.
  2. In *.mm add new methods like this:
/*Return value by key.*/
QVariant YourClassName::valueForKey(QString key) {
    if(!key.isEmpty()) {
        id obj = [[NSUserDefaults standardUserDefaults] objectForKey: key.toNSString()];
        if(obj != nil) {
            QVariant res = //...convert objc type to QVariant
            return res;
        }
    }
    return QVariant();
}

/* Return full structure from NSUserDefaults.*/
QVariant YourClassName::dictionaryRepresentation() {
    NSDictionary<NSString*, id> *dic = [[NSUserDefaults standardUserDefaults] dictionaryRepresentation];
    if(dic != nil) {
        QVariantMap res = //...convert NSDictionary type to QVariantMap
        return QVariant(res);
    }
    return QVariant();
}

/* Prin the NSUserDefaults structure to console.*/
void YourClassName::printUserDefaults() {
    NSLog(@"NSUserDefaults: %@", [[NSUserDefaults standardUserDefaults] dictionaryRepresentation]);
}

/* Create or change value for key.*/
void YourClassName::setValueForKey(QString key, QVariant value) {
    if(!key.isEmpty() && !value.isNull()) {
        id obj = //Convert QVariant to objc
        if(obj != nil) {
            //Save object to NSUserDefaults.
            [[NSUserDefaults standardUserDefaults] setObject: obj forKey: key.toNSString()];
            [[NSUserDefaults standardUserDefaults] synchronize];
        }
    }
}

/* Remove value from NSUserDefaults.*/
void ShavUserDefaults::removeValueForKey(QString key) {
    if(!key.isEmpty()) {
        [[NSUserDefaults standardUserDefaults] removeObjectForKey: key.toNSString()];
        [[NSUserDefaults standardUserDefaults] synchronize];
    }
}


Conver some Objective-C type to QVariant

Class for convert types looks like this:

  1. Class interface
#ifndef SHAVHELPERMANAGER_H
#define SHAVHELPERMANAGER_H
#include <QtCore>
#ifdef __cplusplus
extern "C" {
#endif

#import <Foundation/Foundation.h>
#import <CoreGraphics/CoreGraphics.h>

@interface ShavHelperManager : NSObject
+ (NSDictionary*)variantToNSDictionary:(QVariantMap)settings;
+ (NSArray*)varianListToNSArray:(QVariantList)list;
+ (id)variantToNSObject:(QVariant)item;
+ (QVariant)NSObjectToVariant:(id)item;
+ (QVariantMap)NSDictionaryToVariantMap:(NSDictionary*)obj;
+ (QVariantList)NSArrayToVariantList:(NSArray*)array;
+ (QString)applicationBundle;
+ (QVariantMap)NSErrorToVariantMap:(NSError*)error;
+ (QSize)CGSizeToQSize:(CGSize)size;
+ (CGSize)QSizeToCGSize:(QSize)size;
+ (QPoint)CGPointToQPoint:(CGPoint)point;
+ (CGPoint)QPointToCGPoint:(QPoint)point;
+ (QRect)CGRectToQRect:(CGRect)frame;
+ (CGRect)QRectToCGRect:(QRect)frame;
@end
#ifdef __cplusplus
}
#endif

#endif // SHAVHELPERMANAGER_H
  1. Class implementation
#include "shavhelpermanager.h"

@implementation ShavHelperManager

+ (NSDictionary*)variantToNSDictionary:(QVariantMap)settings {
    NSMutableDictionary* userInfoDic = nil;
    if(!settings.isEmpty()) {
        userInfoDic = [NSMutableDictionary dictionary];
        foreach(QString key, settings.keys()){
            QVariant item = settings[key];
            if(!item.isNull()) {
                if(item.type() == QVariant::String) {
                    NSString* value = item.toString().toNSString();
                    if(value) {
                        [userInfoDic setObject: value forKey: key.toNSString()];
                    }
                } else if(item.type() == QVariant::Bool || item.type() == QVariant::Int || item.type() == QVariant::Double ||
                        item.type() == QVariant::LongLong || item.type() == QVariant::UInt || item.type() == QVariant::ULongLong) {
                    if(item.type() == QVariant::Int) {
                        NSNumber* value = [NSNumber numberWithInt:item.toInt()];
                        if(value) {
                            [userInfoDic setObject: value forKey: key.toNSString()];
                        }
                    } else if(item.type() == QVariant::UInt) {
                        NSNumber* value = [NSNumber numberWithUnsignedInt:item.toUInt()];
                        if(value) {
                            [userInfoDic setObject: value forKey: key.toNSString()];
                        }
                    } else if(item.type() == QVariant::LongLong) {
                        NSNumber* value = [NSNumber numberWithLongLong:item.toLongLong()];
                        if(value) {
                            [userInfoDic setObject: value forKey: key.toNSString()];
                        }
                    } else if(item.type() == QVariant::ULongLong) {
                        NSNumber* value = [NSNumber numberWithUnsignedLongLong:item.toULongLong()];
                        if(value) {
                            [userInfoDic setObject: value forKey: key.toNSString()];
                        }
                    } else if(item.type() == QVariant::Double) {
                        NSNumber* value = [NSNumber numberWithDouble:item.toDouble()];
                        if(value) {
                            [userInfoDic setObject: value forKey: key.toNSString()];
                        }
                    } else if(item.type() == QVariant::Bool) {
                        BOOL flag = ((item.toBool() == true) ? YES : NO);
                        NSNumber* value = [NSNumber numberWithBool:flag];
                        if(value) {
                            [userInfoDic setObject: value forKey: key.toNSString()];
                        }
                    }
                } else if(item.type() == QVariant::Url) {
                    QUrl url = item.toUrl();
                    NSURL* urlPath = [NSURL URLWithString: (url.toString().toNSString())];
                    [userInfoDic setObject: urlPath forKey: key.toNSString()];
                } else if(item.canConvert<QVariantMap>()) {
                    NSDictionary* value = [ShavHelperManager variantToNSDictionary: (QVariantMap)item.toMap()];
                    if(value) {
                        [userInfoDic setObject: value forKey: key.toNSString()];
                    }
                } else if(item.canConvert<QVariantList>()) {
                    NSArray* value = [ShavHelperManager varianListToNSArray: (QVariantList)item.toList()];
                    if(value) {
                        [userInfoDic setObject: value forKey: key.toNSString()];
                    }
                }
            }
        }
    }
    return userInfoDic;
}

+ (NSArray*)varianListToNSArray:(QVariantList)list {
    NSMutableArray* res = nil;
    if(!list.isEmpty()) {
        res = [NSMutableArray array];
        foreach(QVariant item, list) {
            if(item.type() == QVariant::String) {
                NSString* value = item.toString().toNSString();
                if(value) {
                    [res addObject: value];
                }
            } else if(item.type() == QVariant::Bool || item.type() == QVariant::Int || item.type() == QVariant::Double ||
                    item.type() == QVariant::LongLong || item.type() == QVariant::UInt || item.type() == QVariant::ULongLong) {
                if(item.type() == QVariant::Int) {
                    NSNumber* value = [NSNumber numberWithInt:item.toInt()];
                    if(value) {
                        [res addObject: value];
                    }
                } else if(item.type() == QVariant::UInt) {
                    NSNumber* value = [NSNumber numberWithUnsignedInt:item.toUInt()];
                    if(value) {
                        [res addObject: value];
                    }
                } else if(item.type() == QVariant::LongLong) {
                    NSNumber* value = [NSNumber numberWithLongLong:item.toLongLong()];
                    if(value) {
                        [res addObject: value];
                    }
                } else if(item.type() == QVariant::ULongLong) {
                    NSNumber* value = [NSNumber numberWithUnsignedLongLong:item.toULongLong()];
                    if(value) {
                        [res addObject: value];
                    }
                } else if(item.type() == QVariant::Double) {
                    NSNumber* value = [NSNumber numberWithDouble:item.toDouble()];
                    if(value) {
                        [res addObject: value];
                    }
                } else if(item.type() == QVariant::Bool) {
                    BOOL flag = ((item.toBool() == true) ? YES : NO);
                    NSNumber* value = [NSNumber numberWithBool:flag];
                    if(value) {
                        [res addObject: value];
                    }
                }
            } else if(item.type() == QVariant::Url) {
                QUrl url = item.toUrl();
                NSURL* urlPath = [NSURL URLWithString: (url.toString().toNSString())];
                [res addObject: urlPath];
            } else if(item.canConvert<QVariantMap>()) {
                [res addObject: [ShavHelperManager variantToNSDictionary: (QVariantMap)item.toMap()]];
            } else if(item.canConvert<QVariantList>()) {
                [res addObject: [ShavHelperManager varianListToNSArray: (QVariantList)item.toList()]];
            }
        }
    }
    return res;
}

+ (id)variantToNSObject:(QVariant)item {
    id res = nil;
    if(!item.isNull()) {
        if(item.type() == QVariant::String) {
            res = item.toString().toNSString();
        } else if(item.type() == QVariant::Bool || item.type() == QVariant::Int || item.type() == QVariant::Double ||
                item.type() == QVariant::LongLong || item.type() == QVariant::UInt || item.type() == QVariant::ULongLong) {
            if(item.type() == QVariant::Int) {
                res = [NSNumber numberWithInt:item.toInt()];
            } else if(item.type() == QVariant::UInt) {
                res = [NSNumber numberWithUnsignedInt:item.toUInt()];
            } else if(item.type() == QVariant::LongLong) {
                res = [NSNumber numberWithLongLong:item.toLongLong()];
            } else if(item.type() == QVariant::ULongLong) {
                res = [NSNumber numberWithUnsignedLongLong:item.toULongLong()];
            } else if(item.type() == QVariant::Double) {
                res = [NSNumber numberWithDouble:item.toDouble()];
            } else if(item.type() == QVariant::Bool) {
                BOOL flag = ((item.toBool() == true) ? YES : NO);
                res = [NSNumber numberWithBool:flag];
            }
        } else if(item.type() == QVariant::Url) {
            QUrl url = item.toUrl();
            res = [NSURL URLWithString: (url.toString().toNSString())];
        } else if(item.canConvert<QVariantMap>()) {
            res = [ShavHelperManager variantToNSDictionary: (QVariantMap)item.toMap()];
        } else if(item.canConvert<QVariantList>()) {
            res = [ShavHelperManager varianListToNSArray: (QVariantList)item.toList()];
        }
    }
    return res;
}

+ (QVariant)NSObjectToVariant:(id)item {
    QVariant res;
    res.clear();
    if(item) {
        if([item isKindOfClass: [NSDictionary class]]) {
            res = QVariant([ShavHelperManager NSDictionaryToVariantMap: (NSDictionary*)item]);
        } else if([item isKindOfClass: [NSArray class]]) {
            res = QVariant([ShavHelperManager NSArrayToVariantList: (NSArray*)item]);
        } else if([item isKindOfClass: [NSNumber class]]) {
            if((NSNumber*)item == (void*)kCFBooleanFalse || (NSNumber*)item == (void*)kCFBooleanTrue) {
                BOOL value = [(NSNumber*)item boolValue];
                res = QVariant(((value == YES) ? true : false));
            } else if(strcmp([item objCType], @encode(int)) == 0) {
                int value = [(NSNumber*)item intValue];
                res = QVariant(value);
            } else if(strcmp([item objCType], @encode(double)) == 0) {
                double value = [(NSNumber*)item doubleValue];
                res = QVariant(value);
            } else if(strcmp([item objCType], @encode(float)) == 0) {
                float value = [(NSNumber*)item floatValue];
                res = QVariant(value);
            } else if(strcmp([item objCType], @encode(uint)) == 0) {
                uint value = [(NSNumber*)item unsignedIntValue];
                res = QVariant(value);
            } else if(strcmp([item objCType], @encode(long long)) == 0) {
                long long value = [(NSNumber*)item longLongValue];
                res = QVariant(value);
            }
        } else if([item isKindOfClass: [NSString class]]) {
            res = QVariant(QString::fromNSString((NSString*)item));
        } else if([item isKindOfClass:[NSURL class]]) {
            NSURL* url = (NSURL*)item;
            res = QVariant(QUrl(QString::fromNSString(url.absoluteString)));
        }
    }
    return res;
}

+ (QVariantMap)NSDictionaryToVariantMap:(NSDictionary*)obj {
    QVariantMap res;
    res.clear();
    if(obj && [obj isKindOfClass: [NSDictionary class]]) {
        for(NSEnumerator* key in [obj keyEnumerator]) {
            id item = [obj objectForKey: key];
            QString keyString = QString::fromNSString((NSString*)key);
            if([item isKindOfClass: [NSDictionary class]]) {
                res[keyString] = [ShavHelperManager NSDictionaryToVariantMap: (NSDictionary*)item];
            } else if([item isKindOfClass: [NSArray class]]) {
                res[keyString] = [ShavHelperManager NSArrayToVariantList: (NSArray*)item];
            } else if([item isKindOfClass: [NSNumber class]]) {
                if((NSNumber*)item == (void*)kCFBooleanFalse || (NSNumber*)item == (void*)kCFBooleanTrue) {
                    BOOL value = [(NSNumber*)item boolValue];
                    res[keyString] = QVariant(((value == YES) ? true : false));
                } else if(strcmp([item objCType], @encode(int)) == 0) {
                    int value = [(NSNumber*)item intValue];
                    res[keyString] = QVariant(value);
                } else if(strcmp([item objCType], @encode(double)) == 0) {
                    double value = [(NSNumber*)item doubleValue];
                    res[keyString] = QVariant(value);
                } else if(strcmp([item objCType], @encode(float)) == 0) {
                    float value = [(NSNumber*)item floatValue];
                    res[keyString] = QVariant(value);
                } else if(strcmp([item objCType], @encode(uint)) == 0) {
                    uint value = [(NSNumber*)item unsignedIntValue];
                    res[keyString] = QVariant(value);
                } else if(strcmp([item objCType], @encode(long long)) == 0) {
                    long long value = [(NSNumber*)item longLongValue];
                    res[keyString] = QVariant(value);
                }
            } else if([item isKindOfClass: [NSString class]]) {
                res[keyString] = QString::fromNSString((NSString*)item);
            } else if([item isKindOfClass:[NSURL class]]) {
                NSURL* url = (NSURL*)item;
                res[keyString] = QVariant(QUrl(QString::fromNSString(url.absoluteString)));
            }
        }
    }
    return res;
}

+ (QVariantList)NSArrayToVariantList:(NSArray*)array {
    QVariantList list;
    list.clear();
    if(array) {
        for(id item in array) {
            if([item isKindOfClass: [NSDictionary class]]) {
                list.append([ShavHelperManager NSDictionaryToVariantMap: (NSDictionary*)item]);
            } else if([item isKindOfClass: [NSArray class]]) {
                list.append([ShavHelperManager NSArrayToVariantList: (NSArray*)item]);
            } else if([item isKindOfClass: [NSNumber class]]) {
                if((NSNumber*)item == (void*)kCFBooleanFalse || (NSNumber*)item == (void*)kCFBooleanTrue) {
                    BOOL value = [(NSNumber*)item boolValue];
                    list.append(QVariant(((value == YES) ? true : false)));
                } else if(strcmp([item objCType], @encode(int)) == 0) {
                    int value = [(NSNumber*)item intValue];
                    list.append(QVariant(value));
                } else if(strcmp([item objCType], @encode(double)) == 0) {
                    double value = [(NSNumber*)item doubleValue];
                    list.append(QVariant(value));
                } else if(strcmp([item objCType], @encode(float)) == 0) {
                    float value = [(NSNumber*)item floatValue];
                    list.append(QVariant(value));
                } else if(strcmp([item objCType], @encode(uint)) == 0) {
                    uint value = [(NSNumber*)item unsignedIntValue];
                    list.append(QVariant(value));
                } else if(strcmp([item objCType], @encode(long long)) == 0) {
                    long long value = [(NSNumber*)item longLongValue];
                    list.append(QVariant(value));
                }
            } else if([item isKindOfClass: [NSString class]]) {
                list.append(QString::fromNSString((NSString*)item));
            } else if([item isKindOfClass:[NSURL class]]) {
                NSURL* url = (NSURL*)item;
                list.append(QVariant(QUrl(QString::fromNSString(url.absoluteString))));
            }
        }
    }
    return list;
}

+ (QString)applicationBundle {
    return QString::fromNSString([[[NSBundle mainBundle] infoDictionary] objectForKey: @"CFBundleIdentifier"]);
}

+ (QVariantMap)NSErrorToVariantMap:(NSError*)error {
    QVariantMap map;
    map.clear();
    if(error) {
        map["code"] = (int)(error.code);
        map["domain"] = QString::fromNSString(error.domain);
        map["localizedDescription"] = QString::fromNSString(error.localizedDescription);
        map["localizedFailureReason"] = QString::fromNSString(error.localizedFailureReason);
        map["localizedRecoverySuggestion"] = QString::fromNSString(error.localizedRecoverySuggestion);
        if(error.userInfo != nil) {
            map["userInfo"] = [ShavHelperManager NSDictionaryToVariantMap: error.userInfo];
        }
    }

    return map;
}

+ (QSize)CGSizeToQSize:(CGSize)size {
    QSize res;
    res.setWidth((int)size.width);
    res.setHeight((int)size.height);
    return res;
}

+ (CGSize)QSizeToCGSize:(QSize)size {
    CGSize res;
    res.width = (float)(size.width());
    res.height = (float)(size.height());
    return res;
}

+ (QPoint)CGPointToQPoint:(CGPoint)point {
    QPoint res;
    res.setX((int)point.x);
    res.setY((int)point.y);
    return res;
}

+ (CGPoint)QPointToCGPoint:(QPoint)point {
    CGPoint res;
    res.x = (float)(point.x());
    res.y = (float)(point.y());
    return res;
}

+ (QRect)CGRectToQRect:(CGRect)frame {
    QRect rec;
    rec.setX((int)frame.origin.x);
    rec.setY((int)frame.origin.y);
    rec.setWidth((int)frame.size.width);
    rec.setHeight((int)frame.size.height);
    return rec;
}

+ (CGRect)QRectToCGRect:(QRect)frame {
    CGRect res;
    res.origin.x = frame.x();
    res.origin.y = frame.y();
    res.size.width = frame.width();
    res.size.height = frame.height();
    return res;
}
@end