I'm trying to convert this Objective-C code to swift, but can't seem to figure it out:
#implementation MultiplayerNetworking {
uint32_t _ourRandomNumber;
GameState _gameState;
BOOL _isPlayer1, _receivedAllRandomNumbers;
NSMutableArray *_orderOfPlayers;
#define playerIdKey #"PlayerId"
#define randomNumberKey #"randomNumber"
- (id)init
{
if (self = [super init]) {
_ourRandomNumber = arc4random();
_gameState = kGameStateWaitingForMatch;
_orderOfPlayers = [NSMutableArray array];
[_orderOfPlayers addObject:#{playerIdKey : [GKLocalPlayer localPlayer].playerID, randomNumberKey : #(_ourRandomNumber)}];
}
return self;
}
};
This is what I thought I would be, but I'm no sure at all, so I would appreciate som help here.
class MultiplayerNetworking {
var _ourRandomNumber = uint32_t()
var _gameState = GameState()
var isPlayer1 = false
var receivedAllRandomNumbers = false
var orderOfPlayers = [AnyObject]()
let playerIdKey = "PlayerId"
let randomNumberKey = "randomNumber"
override init() {
super.init()
self.ourRandomNumber = arc4random()
self.gameState = kGameStateWaitingForMatch
self.orderOfPlayers = [AnyObject]()
orderOfPlayers.append([playerIdKey: GKLocalPlayer.localPlayer().playerID, randomNumberKey: ourRandomNumber])
}
}
But it gives me some errors, a lot:
I used this converter: objectivec2swift.com since I have no experience with Objective-C
enum GameState {
case waitingForMatch
}
class MultiplayerNetworking {
var isPlayer1 = false
var receivedAllRandomNumbers = false
var orderOfPlayers = [AnyObject]()
private let playerIdKey = "PlayerId"
private let randomNumberKey = "randomNumber"
private var ourRandomNumber = arc4random()
private var gameState = GameState.waitingForMatch
init() {
orderOfPlayers.append([playerIdKey: GKLocalPlayer.localPlayer.playerID, randomNumberKey: ourRandomNumber] as AnyObject)
}
}
I am assuming that GameState is an enum, and have made the enum with just the one case I can see from your code. I am also guessing that variables starting with an underscore are supposed to be private instance variables of that class, so I have made them private here. Instead of initializing everything in init, I have made use of Swift to give all the properties initial values.
class MultiplayerNetworking {
var _ourRandomNumber = [__uint32_t]()
var _gameState = GameState()
var isPlayer1 = false
var receivedAllRandomNumbers = false
var orderOfPlayers = [AnyObject]()
let playerIdKey = "PlayerId"
let randomNumberKey = "randomNumber"
init() {
self.ourRandomNumber = arc4random()
self.gameState = kGameStateWaitingForMatch
self.orderOfPlayers = [AnyObject]()
orderOfPlayers.append([playerIdKey: GKLocalPlayer.localPlayer().playerID, randomNumberKey: ourRandomNumber])
}
}
Try using this code
class Player{
var playerIdKey : String?
var randomNumberKey : UInt32?
}
class MultiplayerNetworking {
var ourRandomNumber = UInt32()
var gameState = GameState()
var isPlayer1 = false
var receivedAllRandomNumbers = false
var orderOfPlayers = [Player]()
let playerIdKey = "PlayerId"
let randomNumberKey = "randomNumber"
override init() {
self.ourRandomNumber = arc4random()
self.gameState = kGameStateWaitingForMatch
let player = Player()
player.playerIdKey = GKLocalPlayer.localPlayer().playerID
player.randomNumberKey = ourRandomNumber
orderOfPlayers.append(player)
}
}
I would recommend that you create a separate class for player in the previous code the variable was an array type of AnyObject and a tipple was being appended to it so the compiler was complaining. So create a separate player class and set its properties and then append it to your array and your code should run fine.I also removed override keyword from init as there is no super class for this class.
Related
Getting error message
Cannot convert value of type 'UnsafeMutablePointer<objc_property_t>?' (aka 'Optional<UnsafeMutablePointer>') to specified type 'UnsafeMutablePointer<objc_property_t?>' (aka 'UnsafeMutablePointer<Optional<OpaquePointer>>')
On this line
let properties : UnsafeMutablePointer <objc_property_t?> = class_copyPropertyList(self.classForCoder, &count)
Full code here
var count = UInt32()
let properties : UnsafeMutablePointer <objc_property_t?> = class_copyPropertyList(self.classForCoder, &count)
var propertyNames = [String]()
let intCount = Int(count)
for i in 0..<intCount {
let property : objc_property_t = properties[i]!
guard let propertyName = NSString(utf8String: property_getName(property)) as? String else {
debugPrint("Couldn't unwrap property name for \(property)")
break
}
propertyNames.append(propertyName)
}
You are getting the error because the return type of class_copyPropertyList is not UnsafeMutablePointer<objc_property_t?>.
Your line should read
let properties : UnsafeMutablePointer <objc_property_t> = class_copyPropertyList(self.classForCoder, &count)
class_copyPropertyList() returns a UnsafeMutablePointer<objc_property_t>? and not a UnsafeMutablePointer<objc_property_t?>. It is usually better to avoid explicit type annotations and just write
let properties = class_copyPropertyList(self.classForCoder, &count)
and let the compiler infer the type. The optional must then be unwrapped, for example with guard:
guard let properties = class_copyPropertyList(self.classForCoder, &count) else {
return // Handle error ...
}
The Swift String creation can also be simplified, leading to
var count = UInt32()
guard let properties = class_copyPropertyList(self.classForCoder, &count) else {
return
}
var propertyNames = [String]()
for i in 0..<Int(count) {
let propertyName = String(cString: property_getName(properties[i]))
propertyNames.append(propertyName)
}
You can remove type annotation, like so:
var count = UInt32()
let properties = class_copyPropertyList(self.classForCoder, &count)
Now the properties can also be mapped:
if let properties = class_copyPropertyList(self.classForCoder, &count) {
let range = 0..<Int(count)
let names = range.map {
String(cString: property_getName(properties[$0]))
}
}
Basically, what is happening is when trying to change a variable of a Managed class (UWP), it crashes. Additionally, it seems to only crash if I try modifying a variable which is created with the app namespace. In other words, if I create a new namspace and managed class, I can modify variables just fine.
void ASynTaskCategories(void)
{
concurrency::task_completion_event<Platform::String^> taskDone;
MainPage^ rootPage = this;
UberSnip::HELPER::ASYNC_RESPONSE^ responseItem = ref new UberSnip::HELPER::ASYNC_RESPONSE();
create_task([taskDone, responseItem, rootPage]() -> Platform::String^
{
//UBERSNIP API 0.4
UberSnip::UBERSNIP_CLIENT* UberSnipAPI = new UberSnip::UBERSNIP_CLIENT();
UberSnipAPI->Http->RequestURL = "http://api.ubersnip.com/categories.php";
UberSnipAPI->Http->request();
taskDone.set(UberSnipAPI->Client->BodyResponse);
responseItem->Body = UberSnipAPI->Client->BodyResponse;
return "(done)";
}).then([taskDone, responseItem, rootPage](Platform::String^ BodyResponse) {
Platform::String^ BR = responseItem->Body;
cJSON* cats = cJSON_Parse(_string(BR));
cats = cats->child;
int *cat_count = new int(cJSON_GetArraySize(cats));
rootPage->Categories->Clear();
GENERIC_ITEM^ all_item = ref new GENERIC_ITEM();
all_item->Title = "All";
rootPage->Categories->Append(all_item);
for (int i = 0; i < *cat_count; i++) {
cJSON* cat = new cJSON();
cat = cJSON_GetArrayItem(cats, i);
string *track_title = new string(cJSON_GetObjectItem(cat, "name")->valuestring);
GENERIC_ITEM gitem;
gitem.Title = "Hi";
GENERIC_ITEM^ ubersnipCategory = ref new GENERIC_ITEM();
ubersnipCategory->Title = _String(*track_title);
rootPage->Categories->Append(ubersnipCategory);
}
});
This does not crash
UberSnipAPI->Http->RequestURL = "http://api.ubersnip.com/categories.php";
but this one does
all_item->Title = "All";
I am almost positive it has something to do with it being in the apps default namespace and it being accessed outside of the main thread ... At least that's what it seems like as that's really the only difference besides the actual class.
This is what GENERIC_ITEM looks like.
[Windows::UI::Xaml::Data::Bindable]
public ref class GENERIC_ITEM sealed {
private:
Platform::String^ _title = "";
Platform::String^ _description;
Windows::UI::Xaml::Media::ImageSource^ _Image;
event PropertyChangedEventHandler^ _PropertyChanged;
void OnPropertyChanged(Platform::String^ propertyName)
{
PropertyChangedEventArgs^ pcea = ref new PropertyChangedEventArgs(propertyName);
_PropertyChanged(this, pcea);
}
public:
GENERIC_ITEM() {
};
property Platform::String^ Title {
Platform::String^ get() {
return this->_title;
}
void set(Platform::String^ val) {
this->_title = val;
OnPropertyChanged("Title");
}
}
property Platform::String^ Description {
Platform::String^ get() {
return this->_description;
}
void set(Platform::String^ val) {
this->_description = val;
OnPropertyChanged("Description");
}
}
void SetImage(Platform::String^ path)
{
Windows::Foundation::Uri^ uri = ref new Windows::Foundation::Uri(path);
_Image = ref new Windows::UI::Xaml::Media::Imaging::BitmapImage(uri);
}
};
I know there is no issue with the class because it works perfectly fine if I run this exact same code on the initial thread.
Any suggestions? Thanks! :D
Figured it out after several hours! In case someone else is having this issue, all you must do is use the Dispatcher to run code on the UI that is not available outside of the UI thread.
void loadCats(UberSnip::HELPER::ASYNC_RESPONSE^ responseItem, MainPage^ rootPage) {
Platform::String^ BR = responseItem->Body;
cJSON* cats = cJSON_Parse(_string(BR));
cats = cats->child;
int *cat_count = new int(cJSON_GetArraySize(cats));
rootPage->Categories->Clear();
GENERIC_ITEM^ all_item = ref new GENERIC_ITEM();
all_item->Title = "All";
rootPage->Categories->Append(all_item);
for (int i = 0; i < *cat_count; i++) {
cJSON* cat = new cJSON();
cat = cJSON_GetArrayItem(cats, i);
string *track_title = new string(cJSON_GetObjectItem(cat, "name")->valuestring);
GENERIC_ITEM gitem;
gitem.Title = "Hi";
GENERIC_ITEM^ ubersnipCategory = ref new GENERIC_ITEM();
ubersnipCategory->Title = _String(*track_title);
rootPage->Categories->Append(ubersnipCategory);
}
}
void ASyncTaskCategories(void)
{
concurrency::task_completion_event<Platform::String^> taskDone;
MainPage^ rootPage = this;
UberSnip::HELPER::ASYNC_RESPONSE^ responseItem = ref new UberSnip::HELPER::ASYNC_RESPONSE();
auto dispatch = CoreWindow::GetForCurrentThread()->Dispatcher;
auto op2 = create_async([taskDone, responseItem, rootPage, dispatch] {
return create_task([taskDone, responseItem, rootPage, dispatch]() -> Platform::String^
{
//UBERSNIP API 0.4
UberSnip::UBERSNIP_CLIENT* UberSnipAPI = new UberSnip::UBERSNIP_CLIENT();
UberSnipAPI->Http->RequestURL = "http://api.ubersnip.com/categories.php";
try{
UberSnipAPI->Http->request();
}
catch (...) {
printf("Error");
}
int err = UberSnip::UTILS::STRING::StringToAscIIChars(UberSnipAPI->Client->BodyResponse).find("__api_err");
if (err < 0) {
if (UberSnip::UTILS::STRING::StringToAscIIChars(UberSnipAPI->Client->BodyResponse).length() < 3) {
return;
}
}
taskDone.set(UberSnipAPI->Client->BodyResponse);
responseItem->Body = UberSnipAPI->Client->BodyResponse;
dispatch->RunAsync(Windows::UI::Core::CoreDispatcherPriority::High, ref new Windows::UI::Core::DispatchedHandler([=]()
{
rootPage->loadCats(responseItem, rootPage);
}));
for (int i = 0; i < 100;) {
}
return "(done)";
}).then([taskDone, responseItem, rootPage](Platform::String^ BodyResponse) {
});
});
//rootPage->loadCats(responseItem, rootPage);
}
Guys i am new to iOS development hardly 2 weeks, i want to convert custom object to NSDictionary or NSData. I want to pass that complex object to web API to get and save.
class customClass{
var firstName:String?
var lastName:String?
var services:Services
}
class Services{
var list:[String] = [String]()
}
can somebody help me to resolve this please.
A bit late... try this in playground. Please note that it will not handle NSData correctly. Also, for the code to work, you need to make sure that your custom class inherits from NSObject and adheres to protocol Parsable. That is because you need to make use of the Objective C runtime for this solution to work with classes. Furthermore all your objects need to be subclasses of NSObject (e.g. NSArray instead of Swift type Array).
import UIKit
protocol Parsable {
}
// Define classes as NSObjects
class customClass: NSObject, Parsable {
var firstName:String?
var lastName:String?
var services:Services?
}
class Services: NSObject, Parsable {
var list:NSMutableArray = NSMutableArray()
}
// Create objects
var obj = customClass()
obj.firstName = "My"
obj.lastName = "Name"
var serv = Services()
serv.list = NSMutableArray()
serv.list.addObject("AAA")
serv.list.addObject("BBB")
obj.services = serv
// The magic
func parse(o: NSObject) -> AnyObject {
let aClass : AnyClass? = o.dynamicType
var propertiesCount : CUnsignedInt = 0
let propertiesInAClass : UnsafeMutablePointer<objc_property_t> = class_copyPropertyList(aClass, &propertiesCount)
let propertiesDictionary : NSMutableDictionary = NSMutableDictionary()
if propertiesCount == 0 {
if let a = o as? [NSObject] {
return a.map( {parse($0)} )
} else {
// Take into account NSData here!!!
return o
}
} else {
for var i = 0; i < Int(propertiesCount); i++ {
let property = propertiesInAClass[i]
let propertyName = NSString(CString: property_getName(property), encoding: NSUTF8StringEncoding) as! String
let propertyValue : AnyObject = o.valueForKey(propertyName)!;
if propertyValue is NSObject && propertyValue is protocol<Parsable> {
propertiesDictionary.setValue(parse(propertyValue as! NSObject), forKey: propertyName)
} else {
propertiesDictionary.setValue(propertyValue, forKey: propertyName)
}
}
return propertiesDictionary
}
}
print(parse(obj))
//print(parse(obj.services!))
//print(parse(obj.services!.list))
I am trying to get the property list of a swift class. There is a similar question here and here. I have everything working except for certain types do not get returned from class_copyPropertyList. The ones I have tested it with so far are Int? and enums. I have an example class below of what I am trying.
enum PKApiErrorCode: Int {
case None = 0
case InvalidSignature = 1
case MissingRequired = 2
case NotLoggedIn = 3
case InvalidApiKey = 4
case InvalidLogin = 5
case RegisterFailed = 6
}
class ApiError: Serializable {
let code: PKApiErrorCode?
let message: String?
let userMessage: String?
init(error: JSONDictionary) {
code = error["errorCode"] >>> { (object: JSON) -> PKApiErrorCode? in
if let c = object >>> JSONInt {
return PKApiErrorCode.fromRaw(c)
}
return nil
}
message = error["message"] >>> JSONString
userMessage = error["userMessage"] >>> JSONString
}
}
And the Serializable class (with some help from https://gist.github.com/turowicz/e7746a9c035356f9483d is
public class Serializable: NSObject, Printable {
override public var description: String {
return "\(self.toDictionary())"
}
}
extension Serializable {
public func toDictionary() -> NSDictionary {
var aClass : AnyClass? = self.dynamicType
var propertiesCount : CUnsignedInt = 0
let propertiesInAClass : UnsafeMutablePointer<objc_property_t> = class_copyPropertyList(aClass, &propertiesCount)
var propertiesDictionary : NSMutableDictionary = NSMutableDictionary()
for var i = 0; i < Int(propertiesCount); i++ {
var property = propertiesInAClass[i]
var propName = NSString(CString: property_getName(property), encoding: NSUTF8StringEncoding)
var propType = property_getAttributes(property)
var propValue : AnyObject! = self.valueForKey(propName);
if propValue is Serializable {
propertiesDictionary.setValue((propValue as Serializable).toDictionary(), forKey: propName)
} else if propValue is Array<Serializable> {
var subArray = Array<NSDictionary>()
for item in (propValue as Array<Serializable>) {
subArray.append(item.toDictionary())
}
propertiesDictionary.setValue(subArray, forKey: propName)
} else if propValue is NSData {
propertiesDictionary.setValue((propValue as NSData).base64EncodedStringWithOptions(nil), forKey: propName)
} else if propValue is Bool {
propertiesDictionary.setValue((propValue as Bool).boolValue, forKey: propName)
} else if propValue is NSDate {
var date = propValue as NSDate
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "Z"
var dateString = NSString(format: "/Date(%.0f000%#)/", date.timeIntervalSince1970, dateFormatter.stringFromDate(date))
propertiesDictionary.setValue(dateString, forKey: propName)
} else {
propertiesDictionary.setValue(propValue, forKey: propName)
}
}
return propertiesDictionary
}
public func toJSON() -> NSData! {
var dictionary = self.toDictionary()
var err: NSError?
return NSJSONSerialization.dataWithJSONObject(dictionary, options:NSJSONWritingOptions(0), error: &err)
}
public func toJSONString() -> NSString! {
return NSString(data: self.toJSON(), encoding: NSUTF8StringEncoding)
}
}
The String seem to only appear if the Optionals are valid values and the code never appears on the object if it is an enum or Int unless the Int has a default value.
Thanks for any advice I can get for getting all properties of a class no matter what they are.
I got a response on the apple developer forums regarding this issue:
"class_copyPropertyList only shows properties that are exposed to the Objective-C runtime. Objective-C can't represent Swift enums or optionals of non-reference types, so those properties are not exposed to the Objective-C runtime."
Source
So, in summary, serialization to JSON using this approach is not currently possible. You might have to look into using other patterns to accomplish this, such as giving the task of serialization to each object, or potentially by using the reflect() method to serialize an object to JSON.
So I have a soundHandler class that's supposed to play sounds and then point back to a function on the timeline when the sound has completed playing. But somehow, only one of the sounds plays when I try it out. EDIT: After that sound plays, nothing happens, even though I have EventHandlers set up that are supposed to do something.
Here's the code:
import mx.events.EventDispatcher;
class soundHandler {
private var dispatchEvent:Function;
public var addEventListener:Function;
public var removeEventListener:Function;
var soundToPlay;
var soundpath:String;
var soundtype:String;
var prefix:String;
var mcname:String;
public function soundHandler(soundpath:String, prefix:String, soundtype:String, mcname:String) {
EventDispatcher.initialize(this);
_root.createEmptyMovieClip(mcname, 1);
this.soundpath = soundpath;
this.soundtype = soundtype;
this.prefix = prefix;
this.mcname = mcname;
}
function playSound(file, callbackfunc) {
_root.soundToPlay = new Sound(_root.mcname);
_global.soundCallbackfunc = callbackfunc;
_root.soundToPlay.onLoad = function(success:Boolean) {
if (success) {
_root.soundToPlay.start();
}
};
_root.soundToPlay.onSoundComplete = function():Void {
trace("Sound Complete: "+this.soundtype+this.prefix+this.file+".mp3");
trace(arguments.caller);
dispatchEvent({type:_global.soundCallbackfunc});
trace(this.toString());
trace(this.callbackfunction);
};
_root.soundToPlay.loadSound("../sound/"+soundpath+"/"+soundtype+prefix+file+".mp3", true);
_root.soundToPlay.stop();
}
}
Here's the code from the .fla file:
var playSounds:soundHandler = new soundHandler("signup", "su", "s", "mcs1");
var file = "000";
playSounds.addEventListener("sixtyseconds", this);
playSounds.addEventListener("transition", this);
function sixtyseconds() {
trace("I am being called! Sixtyseconds");
var phase = 1;
var file = random(6);
if (file == 0) {
file = 1;
}
if (file<10) {
file = "0"+file;
}
file = phase+file;
playSounds.playSound(file, "transition");
}
function transition() {
trace("this works");
}
playSounds.playSound(file, "sixtyseconds");
I'm at a total loss for this one. Have been wasting hours to figure it out already.
Any help will be deeply appreciated.
Well, after using the Delegate class, I got it to work with this code:
import mx.events.EventDispatcher;
import mx.utils.Delegate;
class soundHandler{
public var addEventListener:Function;
public var removeEventListener:Function;
private var dispatchEvent:Function;
private var soundToPlay;
private var soundpath:String;
private var soundtype:String;
private var prefix:String;
private var mcname:String;
public function soundHandler(soundpath:String, prefix:String, soundtype:String, mcname:String) {
EventDispatcher.initialize(this);
_root.createEmptyMovieClip(mcname, 1);
this.soundpath = soundpath;
this.soundtype = soundtype;
this.prefix = prefix;
this.mcname = mcname;
}
private function playSoundCallback(file, callbackfunc) {
var soundname = "s"+file;
_root[soundname] = new Sound(_parent.mcname);
_root[soundname].onLoad = function(success:Boolean) {
if (success) {
_root[soundname].start();
}
};
trace("Play Sound: "+file+".mp3; Function To Call: "+callbackfunc);
_root[soundname].onSoundComplete = Delegate.create(this,function():Void {
trace("Sound Complete: "+this.soundtype+this.prefix+this.file+".mp3");
dispatchEvent({type:callbackfunc});
});
_root[soundname].loadSound("../sound/"+soundpath+"/"+soundtype+prefix+file+".mp3", true);
_root[soundname].stop();
}
private function playRandomSoundCallback(phase, scope, callbackfunc) {
var file = random(scope);
if (file == 0) {
file = 1;
}
if (file<10) {
file = "0"+file;
}
file = phase+file;
playSoundCallback(file, callbackfunc);
}
private function playSound(file) {
var soundname = "s"+file;
_root[soundname] = new Sound(_root.mcname);
_root[soundname].onLoad = function(success:Boolean) {
if (success) {
_root[soundname].start();
}
};
trace("Play Sound: "+file+".mp3");
_root[soundname].loadSound("../sound/"+soundpath+"/"+soundtype+prefix+file+".mp3", true);
_root[soundname].stop();
}
private function playSoundRandom(phase, scope) {
var file = random(scope);
if (file == 0) {
file = 1;
}
if (file<10) {
file = "0"+file;
}
file = phase+file;
playSound(file);
}
private function playSoundAS(identifier) {
var soundname = identifier;
_root[soundname] = new Sound(_root.mcname);;
trace("Play Sound AS: "+identifier+".mp3");
_root[soundname].attachSound("identifier");
_root[soundname].start();
}
}