Make a Data Class with Swift in Watchkit Target - ios

As simply using the sharedApplication() method is unavailable in WatchKit, utilizing the AppDelegate as a way to store data for all my interfaces/views is no longer an option in WatchKit. Is there an alternative method to do this in swift for WatchKit apps? I have tried making a singleton class like so:
import Foundation
import UIKit
class data: NSObject {
class var sharedInstance : data {
struct Example {
static let instance = data()
}
return Example.instance
}
var value:Int = 0
var increment:Int = 1
}
(This code is from another StackOverflow post, I just forgot the link.) I then try and access the Integer "value" by calling data.value in an interface controller but I get the error 'data.Type' does not have a member named 'value'. How can I fix this error? Or is there a better way to do what I am trying to achieve?
Thanks

The code data.value attempts to access a class variable or method on the class data called value. Since no such variable or method exists you get the error message.
In your definition value is defined as a property on the class data, so to use it you need a valid instance of data. Your sharedInstance method provides the instance.
Also, the convention in Swift is to capitalize class names, so I recommend using Data.

Related

Swift init from unknown class which conforms to protocol

I'm currently working on updating a large project from Objective-C to Swift and I'm stumped on how to mimic some logic. Basically we have a class with a protocol which defines a couple functions to turn any class into a JSON representation of itself.
That protocol looks like this:
#define kJsonSupport #"_jsonCompatible"
#define kJsonClass #"_jsonClass"
#protocol JsonProtocol <NSObject>
- (NSDictionary*)convertToJSON;
- (id)initWithJSON:(NSDictionary* json);
#end
I've adapted that to Swift like this
let JSON_SUPPORT = "_jsonCompatible"
let JSON_CLASS = "_jsonClass"
protocol JsonProtocol
{
func convertToJSON() -> NSDictionary
init(json: NSDictionary)
}
One of the functions in the ObjC class runs the convertToJSON function for each object in an NSDictionary which conforms to the protocol, and another does the reverse, creating an instance of the object with the init function. The output dictionary also contains two keys, one denoting that the dictionary in question supports this protocol (kJsonSupport: BOOL), and another containing the NSString representation of the class the object was converted from (kJsonClass: NSString). The reverse function then uses both of these to determine what class the object was converted from to init a new instance from the given dictionary.
All of the classes are anonymous to the function itself. All we know is each class conforms to the protocol, so we can call our custom init function on it.
Here's what it looks like in ObjC:
Class rootClass = NSClassFromString(obj[kJsonClass]);
if([rootClass conformsToProtocol:#protocol(JsonProtocol)])
{
Class<JsonProtocol> jsonableClass = (Class<JsonProtocol>)rootClass;
[arr addObject:[[((Class)jsonableClass) alloc] initWithJSON:obj]];
}
However, I'm not sure how to make this behavior in Swift.
Here's my best attempt. I used Swiftify to try and help me get there, but the compiler isn't happy with it either:
let rootClass : AnyClass? = NSClassFromString(obj[JSON_CLASS] as! String)
if let _rootJsonClass = rootClass as? JsonProtocol
{
weak var jsonClass = _rootJsonClass as? AnyClass & JsonProtocol
arr.add(jsonClass.init(json: obj))
}
I get several errors on both the weak var line and the arr.add line, such as:
Non-protocol, non-class type 'AnyClass' (aka 'AnyObject.Type') cannot be used within a protocol-constrained type
'init' is a member of the type; use 'type(of: ...)' to initialize a new object of the same dynamic type
Argument type 'NSDictionary' does not conform to expected type 'JsonProtocol'
Extraneous argument label 'json:' in call
Is there any way for me to instantiate from an unknown class which conforms to a protocol using a custom protocol init function?
You will likely want to rethink this code in the future, to follow more Swift-like patterns, but it's not that complicated to convert, and I'm sure you have a lot of existing code that relies on behaving the same way.
The most important thing is that all the objects must be #objc classes. They can't be structs, and they must subclass from NSObject. This is the major reason you'd want to change this to a more Swifty solution based on Codable.
You also need to explicitly name you types. Swift adds the module name to its type names, which tends to break this kind of dynamic system. If you had a type Person, you would want to declare it:
#objc(Person) // <=== This is the important part
class Person: NSObject {
required init(json: NSDictionary) { ... }
}
extension Person: JsonProtocol {
func convertToJSON() -> NSDictionary { ... }
}
This makes sure the name of the class is Person (like it would be in ObjC) and not MyGreatApp.Person (which is what it normally would be in Swift).
With that, in Swift, this code would be written this way:
if let className = obj[JSON_CLASS] as? String,
let jsonClass = NSClassFromString(className) as? JsonProtocol.Type {
arr.add(jsonClass.init(json: obj))
}
The key piece you were missing is as? JsonProtocol.Type. That's serving a similar function to +conformsToProtocol: plus the cast. The .Type indicates that this is a metatype check on Person.self rather than a normal type check on Person. For more on that see Metatype Type in the Swift Language Reference.
Note that the original ObjC code is a bit dangerous. The -initWithJSON must return an object. It cannot return nil, or this code will crash at the addObject call. That means that implementing JsonProtocol requires that the object construct something even if the JSON it is passed is invalid. Swift will enforce this, but ObjC does not, so you should think carefully about what should happen if the input is corrupted. I would be very tempted to change the init to an failable or throwing initializer if you can make that work with your current code.
I also suggest replacing NSDictionary and NSArray with Dictionary and Array. That should be fairly straightforward without redesigning your code.

Ios swift class without instantiate

How do you (if possible) create a class that anything that references it gets the same object?
So, if I had a LoginClass
and I dont want to create a new instance in each file but rather be able to just do
LoginClass.userID
in any file without first creating an instance of it?
It possible. Use singleton:
Stackoverflow question
Tutorial by Ray Wenderlich
You are looking for a Singleton
This is the code
class Login {
static let sharedInstance = Login()
var userID: String?
private init() {}
}
This is how you retrieve the same instance
Login.sharedInstance
And this is how you use it
Login.sharedInstance.userID = "123"
In a different point of your code...
print(Login.sharedInstance.userID) // 123
Creating one instance per application life cycle means you want to implement Singleton pattern. You can implement singleton like this
class LoginManager {
static let sharedInstance = LoginManager()
var userId:String?
var name:String?
}
And now you can use this like
LoginManager.sharedInstance.userId

Save EVObjects with CoreData

I need to save some data with CoreData. Generally thats not a problem at all. The problem is, that the data is created with EVReflection an therefore inherits the class EVObject. To save the gathered data to CoreData they have to also inherit NSManagedObject. The problem is that swift does not allow you to inherit multiple classes. Would appreciate any tips.
class Device : EVObject
{
var channel : [Channel] = [Channel]()
var name : String = ""
var ise_id : Int = 0
var unreach : Bool = false
var sticky_unreach : Bool = false
var config_pending : Bool = false
override internal func propertyMapping() -> [(String?, String?)] {
return [("name", "_name"), ("ise_id", "_ise_id"), ("unreach", "_unreach"), ("sticky_unreach", "_sticky_unreach"), ("config_pending", "_config_pending")]
}
}
You don't have to inherit. You can extend them. Example:
class User : NSManagedObject{
#NSManaged .....
}
//Extension
import EVReflection
extension User : EVReflectable { }
https://github.com/evermeer/EVReflection#extending-existing-objects
Note I'm not aware of EVReflection but I think this answer can generally apply.
Don't use multiple inheritance. Have two separate classes and a mechanism for creating/loading/updating one object from the other. Protocols may allow it to be done in a way that minimises translation boilerplate (possibly using valueForKey(_:) and setValue(_:forKey) if you can know the key names in a safe manner.
It may not even be even be necessary to have an NSManagedObject subclass but just have an instance of NSManagedObject in all your classes that is loaded/created/saved as necessary.
It depends on what functionality you want to use from EVReflection. Since NSManagedObject also has NSObject as it's base class you could use most functions by just setting NSManagedObject as your base class instead of EVObject.
The only thing you have to do is instead of calling EVObject functions directly, you have to implement the code snippets that are in that EVObject method. Almost any function there is just a convenience method that will call the corresponding EVReflection function.
If you have any questions in the future, then please also report this as an issue on GitHub.

Access array from all Swift Files

I have an array stored in a class that downloads its objects from the internet. My class is set up like so:
class StockManager {
var managerStock: [Dictionary<String, String>] {
return downloadStockFromDatabase()
}
...
}
I access the managerStock from other Swift files in my project like so, but it always resorts to re-downloading the stock again no matter if I have used the variable before (ie. recalls the function downloadStockFromDatabase):
let stockManager = StockManager()
print(stockManager.managerStock)
How would I make sure the managerStock only downloads once and I could use it in any of my files?
This is a question of correct software pattern usage. I would suggest:
make StockManager a singleton, so you will always access the same instance of it
initialize it e.g. in the AppDelegate, i.e. make sure it stays alive for the whole runtime
tip: call managerStock lazily, i.e. only when you really need it and not as part of initialization
As ff10 and holex suggested, make your class a singleton. It will look like this:
class StockManager {
static let sharedInstance = StockManager ()
var managerStock: [Dictionary<String, String>] {
return downloadStockFromDatabase()
}
...
}
Then access it using the static sharedInstance property:
print(StockManager.sharedInstance.managerStock)

How to create Singleton in swift with arguments

I learn the Swift Language and i need to create a manager like a Parse sdk.
For exemple when you initialize your Parse in app you write
Parse.setApplication("...", applicationId:"...")
And later you can write code like this
Parse.doSomething()
The method doSomething() use initial context.
Can you show me in my class should look like? I try some singleton exemple, but a have MyClass.sharedAttribute.doSomething() in case
What you have shown is no indication of singletons whatsoever, it sounds and looks more like a static class with static members and properties:
class MyStatic {
static var appIdA : String?
class func setApplicationId(a : String) {
appIdA = a
}
class func doSomething() {
print(appIdA)
}
}
MyStatic.setApplicationId("blabla")
MyStatic.doSomething() // prints Optional("blabla")
Of course there is the possibility that internally the class is a singleton, but Parse does not seem to be one, just looking at the functions it exposes.
The code comments even state
/*!
The `Parse` class contains static functions that handle global configuration
for the Parse framework.
*/

Resources