How to init a struct var with Data in swift3 - swift3

I have a struct like this
struct Str{
let item1: UINT16
let item2: UINT16
let item3: UINT32
}
I got a struct var mystr
var mystr = Str(item1: 0x0101, item2: 0xffff, item3: 4)
And I've store the struct var into a Data var
var myData = Data(bytes: &mystr, count:MemoryLayout<Str>.size)
My question is how to use the Data var to initialize a new Str var(store the value of the Data into the struct)
Thanks

I'm not enough of an expert to tell how dangerous just dumping some memory to a Data object and then using that memory to init a struct is, but here is what you could do:
let strFromData = myData.withUnsafeBytes { (p: UnsafePointer<Str>) -> Str in
return p.pointee
}
Or even shorter:
let strFromData2 = myData.withUnsafeBytes { $0.pointee as Str }
What I have done in former projects is to process a struct's members one by one:
extension Data
{
mutating func append<T>(value: T)
{
var v = value
self.append(UnsafeBufferPointer(start: &v, count: 1))
}
}
var data = Data()
data.append(value: mystr.item1)
data.append(value: mystr.item2)
data.append(value: mystr.item3)
Building a Str from myData using Str's initializer:
let strFromData3 = myData.withUnsafeBytes { (p: UnsafePointer<UInt16>) -> Str in
let item1 = p[0]
let item2 = p[1]
let p2 = UnsafeRawPointer(p).bindMemory(to: UInt32.self, capacity: 2)
let item3 = p2[1]
return Str(item1: item1, item2: item2, item3: item3)
}

Related

Heterogeneous collection

In the new versions of C++, you can check if an item is in a unordered_set (a HashSet), even if that item is not the same type as the unordered_set, whilst maintaining O(1) time complexity.
I'm trying to find out how to do this in Swift.
Here is the C++ example:
struct First {
int data;
std::string otherData;
First(int data, std::string otherData) : data(data), otherData(otherData) { }
};
struct Second {
int data;
int otherData;
Second(int data, int otherData) : data(data), otherData(otherData) { }
};
Suppose I want to create an unordered_set of First, but I want to check if a Second object is in the Set, comparing by its data field. You could do this:
struct Equal {
using is_transparent = void;
template<class F, class S>
bool operator()(const F& lhs, const S& rhs) const {
return lhs.data == rhs.data;
}
};
struct Hash {
using is_transparent = void;
template<class T>
size_t operator()(const T& t) const {
return std::hash<int>{}(t.data);
}
};
int main()
{
std::unordered_set<First, Hash, Equal> set;
set.insert(First(100, "test"));
std::cout << set.contains(First(100, "bla")) << "\n"; // true
std::cout << set.contains(Second(100, 1000)) << "\n"; // true
}
And this works great. However, I'm not sure how you would achieve this in Swift. In Swift, a Set is the same thing as unordered_set, but its contains method only accepts that specific element (no overloads).
You could iterate through all the elements, but you lose the O(1) HashSet time complexity.
I was wondering, is this possible in Swift?
To meet the basic requirement (partial matching), you can use contains(where:) with a predicate to compare the hash values of elements to the hash of the target.
class First:Hashable {
var data:Int;
var otherData:String;
static func == (lhs:First, rhs:First) -> Bool {
return lhs.data == rhs.data;
}
init(data:Int, otherData:String) {
self.data = data;
self.otherData = otherData;
}
func hash(into hasher: inout Hasher) {
hasher.combine(data)
}
};
class Second:Hashable {
var data:Int;
var otherData:Int;
static func == (lhs:Second, rhs:Second) -> Bool {
return lhs.data == rhs.data;
}
init(data:Int, otherData:Int) {
self.data = data;
self.otherData = otherData;
}
func hash(into hasher: inout Hasher) {
hasher.combine(data)
}
};
var set: Set = [First(data: 100, otherData: "test")];
print(set.contains(First(data: 100, otherData: "bla")));
var hasher = Hasher();
Second(data: 100, otherData: 1000).hash(into:&hasher);
var target = hasher.finalize();
print(set.contains(where: {(candidate:First) -> Bool in
var hasher = Hasher();
candidate.hash(into:&hasher);
return hasher.finalize() == target;
}));
To meet the performance requirement, there are (at least) two options: refactor the hashable data to a common base class, or write an extension method that creates a temporary element of the appropriate type with the hashable data.
Moving the hashable data to a base class is the most straight-forward, though the resultant Set will only be homogenous in the base class. Also, this approach can't be implemented if you don't have control over the source of the element classes.
Once the classes are defined, Set.contains(_:) will work as desired.
class Zeroth:Hashable {
var data:Int;
static func == (lhs:Zeroth, rhs:Zeroth) -> Bool {
return lhs.data == rhs.data;
}
init(_ data:Int) {
self.data = data;
}
func hash(into hasher: inout Hasher) {
hasher.combine(data)
}
};
class First:Zeroth {
var otherData:String;
init(data:Int, otherData:String) {
self.otherData = otherData;
super.init(data)
}
};
class Second:Zeroth {
var otherData:Int;
init(data:Int, otherData:Int) {
self.otherData = otherData;
super.init(data)
}
};
var test = First(data: 100, otherData: "test");
var bla = First(data: 100, otherData: "bla");
var set: Set<Zeroth> = [test];
print(set.contains(bla));
var member = Second(data: 100, otherData: 1000);
print(set.contains(member));
An extension method gets the closest to the C++ interface. Use a protocol so the extension method can be constrained to classes that only hash some of their data. The protocol used below also adds a method, partialCopy(from:), that handles converting between classes.
protocol DataElement {
var data:Int {get}
init(_ data:Int)
static func partialCopy<Other:DataElement>(from other:Other) -> Self;
}
extension DataElement {
static func partialCopy<Other:DataElement>(from other:Other) -> Self {
return Self(other.data);
}
}
class First:Hashable, DataElement {
var data:Int;
var otherData:String = "";
static func == (lhs:First, rhs:First) -> Bool {
return lhs.data == rhs.data;
}
required init(_ data:Int) {
self.data = data;
}
init(data:Int, otherData:String) {
self.data = data;
self.otherData = otherData;
}
func hash(into hasher: inout Hasher) {
hasher.combine(data)
}
};
class Second:Hashable, DataElement {
var data:Int;
var otherData:Int = 0;
static func == (lhs:Second, rhs:Second) -> Bool {
return lhs.data == rhs.data;
}
required init(_ data:Int) {
self.data = data;
}
init(data:Int, otherData:Int) {
self.data = data;
self.otherData = otherData;
}
func hash(into hasher: inout Hasher) {
hasher.combine(data)
}
};
var test = First(data: 100, otherData: "test");
var bla = First(data: 100, otherData: "bla");
var set: Set<First> = [test];
print(set.contains(bla));
extension Set where Element:DataElement {
func contains<Other:DataElement>(matching member:Other) -> Bool {
let matching : Element = Element.partialCopy(from:member); //Element(member.data);
return self.contains(matching);
}
}
var other = Second(data: 100, otherData: 1000);
print(set.contains(matching:other));
Method #1
You can use an enum to store First and Second in the same set. You will have a case for First and a case for Second.
In the Hashable conformance for the enum, you should hash the data which is the same between both structs. The Equatable conformance just makes sure that if the hashes are equal, they are equivalent, even if the enum case is different.
Example:
enum Both: Hashable {
case first(First)
case second(Second)
func hash(into hasher: inout Hasher) {
switch self {
case .first(let first):
hasher.combine(first.data)
case .second(let second):
hasher.combine(second.data)
}
}
static func == (lhs: Both, rhs: Both) -> Bool {
lhs.hashValue == rhs.hashValue
}
}
struct First {
let data: Int
let otherData: String
}
struct Second {
let data: Int
let otherData: Int
}
let set: Set<Both> = [.first(First(data: 100, otherData: "test"))]
let first = First(data: 100, otherData: "bla")
print(set.contains(.first(first))) // true
let second = Second(data: 100, otherData: 1000)
print(set.contains(.second(second))) // true
Method #2
This may not be possible, if First and Second must be a struct. However, if they don't, you can have a superclass that does the Hashable conformance.
Example:
class Superclass: Hashable {
let data: Int
init(data: Int) {
self.data = data
}
func hash(into hasher: inout Hasher) {
hasher.combine(data)
}
static func == (lhs: Superclass, rhs: Superclass) -> Bool {
lhs.data == rhs.data
}
}
class First: Superclass {
let otherData: String
init(data: Int, otherData: String) {
self.otherData = otherData
super.init(data: data)
}
}
class Second: Superclass {
let otherData: Int
init(data: Int, otherData: Int) {
self.otherData = otherData
super.init(data: data)
}
}
let set: Set<Superclass> = [First(data: 100, otherData: "test")]
let first = First(data: 100, otherData: "bla")
print(set.contains(first)) // true
let second = Second(data: 100, otherData: 1000)
print(set.contains(second)) // true

Send Value of a variable from view to class - SwiftUI

What I am trying to do is send the value of a variable from a view to a class, but I keep getting an error. Not sure what to do.
Here is the class:
class perfCalcDep: ObservableObject {
var tom:Double
var arm:Double
#Published var tempDep:String = ""
#Published var elevDep:String = ""
#Published var qnhDep:String = ""
#Published var windDep:String = ""
#Published var slopeDep:String = ""
#Published var rwyCondDep = 0
var altDep: Double {
let pressCalc = (1013 - (Double(qnhDep) ?? 1013)) * 30
return (Double(elevDep) ?? 0) + pressCalc
}
var altVar : Double { 0.21 * altDep }
var tempVar : Double { 24 * (Double(tempDep) ?? 0) }
var windVar : Double { 20.67 * (Double(windDep) ?? 0) }
var tomVar : Double { 2.22 * Double(2550-Double(tom)) }
var slpVar : Double { (Double(slopeDep) ?? 0) / 2 }
var tod : Double { (1700 + altVar + tempVar - tomVar - windVar) }
var todr : Double {
if rwyCondDep == 1 {
return (tod + ((0.1 * tod) * slpVar)) * 1.2
} else if rwyCondDep == 2 {
return (tod + ((0.1 * tod) * slpVar)) * 1.3
} else {
return (tod + ((0.1 * tod) * slpVar))
}
}
init(tom:Double, arm:Double) {
self.tom = tom
self.arm = arm
}
}
And here is part of the view:
struct TakeOffPerf: View {
// The variables I want to send to the class - their values are received from the previous view.
var tMss:Double
var tArm:Double
#ObservedObject var performance = perfCalcDep(tom: tMss, arm: tArm) // Error: Cannot use instance member 'tArm' within property initializer; property initializers run before 'self' is available
#ObservedObject var settings = Settings()
var body: some View {,,,} // just a list that shows the values from the class
Any help would be greatly appreciated.
Properties are initialised before self, so you cannot make initializing dependency between properties, but you can do this in init, eg.
struct TakeOffPerf: View {
var tMss:Double
var tArm:Double
#ObservedObject var performance: perfCalcDep // << only declare !!
#ObservedObject var settings = Settings()
init(tMss:Double, tArm:Double) {
self.tArm = tArm
self.tMss = tMss
self.performance = perfCalcDep(tom: tMss, arm: tArm)
}
// ... other code
}
Note: preserved original style, but it is good practice to name types capitalised, like PerfCalcDep

How to publish changes to a single object in a object array

I have the following classes
class ListItem: Identifiable {
var id: UUID
var name: String
var description: String
var isFavorite: Bool
var debugDescription: String {
return "Name: \(self.name) | Favorite?: \(self.isFavorite)"
}
public init(name: String) {
self.name = name
id = UUID()
self.description = "Some text describing why \(self.name.lowercased()) is awesome"
self.isFavorite = false
}
}
class ListItems: ObservableObject {
#Published var items: [ListItem]
let defaultAnimals = ["Ant", "Bear", "Cat", "Dog", "Elephant",
"Fish", "Giraffe", "Hyena", "Iguana", "Jackal", "Kingfisher", "Leopard", "Monkey"]
public init(animals: [String] = []) {
let animalList: [String] = animals.count > 0 ? animals : defaultAnimals
self.items = animalList.sorted {
$0.lowercased() < $1.lowercased()
}.map {
ListItem(name: $0.firstUppercased)
}
}
}
and the following image view in ContentView
struct ContentView: View {
#ObservedObject var list: ListItems = ListItems()
var body: some View {
List(list.items) {
animal in HStack {
// ...
Image(systemName: animal.isFavorite ? "heart.fill" : "heart").foregroundColor(.pink).onTapGesture {
let index = self.list.items.firstIndex { $0.id == animal.id } ?? -1
if (index >= 0) {
self.list.items[index].isFavorite = !animal.isFavorite
self.list.items = Array(self.list.items[0...self.list.items.count-1]) // <--
}
}
// ...
}
}
}
}
Everytime, the image view is tapped, I am basically reassigning the entire array like this so that the changes can be reflected in the UI
self.list.items = Array(self.list.items[0...self.list.items.count-1])
My question: How can I refactor my code to prevent reassigning the entire object array every time some object property changes?
I am fairly new to Swift & iOS development, not sure if I am missing something basic.
Declare ListItem as an struct instead of a class, this way the view will be notified when isFavorite changes. And just a little suggestion; you can use toggle to change the value of a boolean: self.list.items[index].isFavorite.toggle()

How to append Int to the new Data struct (Swift 3)

With NSMutableData I could create an array of Int's or Float's and store those to disk.
protocol BinaryConvertible
{
init()
}
extension Int : BinaryConvertible {}
struct Storage<T: BinaryConvertible>
{
let data = NSMutableData()
func append(value: T)
{
var input = value
data.append(&input, length: sizeof(T))
}
func extract(index: Int) -> T
{
var output = T()
let range = NSRange(location: index * sizeof(T), length: sizeof(T))
data.getBytes(&output, range: range)
return output
}
}
Swift 3 has a new Data type which uses NSData under the hood. Like String and NSString. I can't figure out how to add e.g. a Double using the new methods.
The append function now expects a UnsafePointer<UInt8>, but how do you create this from a Double or any random struct for that matter?
Working with pointers is one of my least favorite thing to do in Swift, but it also offer a good learning experience. This works for me:
struct Storage<T: BinaryConvertible>
{
var data = Data()
mutating func append(value: T)
{
var input = value
let buffer = UnsafeBufferPointer(start: &input, count: 1)
self.data.append(buffer)
}
func extract(index: Int) -> T
{
let startIndex = index * sizeof(T)
let endIndex = startIndex + sizeof(T)
var output = T()
let buffer = UnsafeMutableBufferPointer(start: &output, count: 1)
let _ = self.data.copyBytes(to: buffer, from: startIndex..<endIndex)
return output
}
}
var s = Storage<Double>()
s.append(value: M_PI)
s.append(value: 42)
s.append(value: 100)
print(s.extract(index: 0))
print(s.extract(index: 1))
print(s.extract(index: 2))
I like to use + or +=
public protocol DataConvertible {
static func + (lhs: Data, rhs: Self) -> Data
static func += (lhs: inout Data, rhs: Self)
}
extension DataConvertible {
public static func + (lhs: Data, rhs: Self) -> Data {
var value = rhs
let data = Data(buffer: UnsafeBufferPointer(start: &value, count: 1))
return lhs + data
}
public static func += (lhs: inout Data, rhs: Self) {
lhs = lhs + rhs
}
}
extension UInt8 : DataConvertible { }
extension UInt16 : DataConvertible { }
extension UInt32 : DataConvertible { }
extension Int : DataConvertible { }
extension Float : DataConvertible { }
extension Double : DataConvertible { }
extension String : DataConvertible {
public static func + (lhs: Data, rhs: String) -> Data {
guard let data = rhs.data(using: .utf8) else { return lhs}
return lhs + data
}
}
extension Data : DataConvertible {
public static func + (lhs: Data, rhs: Data) -> Data {
var data = Data()
data.append(lhs)
data.append(rhs)
return data
}
}
sample
var data = Data()
data += 1
data += 1.0
data += UInt8(1)
data += "1"

How to initialize a 3D array which is of type myStruct in Swift?

After some heavy searching, I finally found a very intuitive way of initializing a 3D array in Swift:
var firstArray = [Int](count:4, repeatedValue: 0)
var secondArray = [[Int]](count:4, repeatedValue: firstArray)
var thirdArray = [[[Int]]](count:4, repeatedValue: secondArray)
It works great. I can access any value of the thirdArray:
thirdArray[a][b][c]
, just like in C++.
But what if I have a struct like:
struct myStruct
{
var color: UIColor = UIColor.redColor()
var number: Int = 0
var used: Bool = true
}
How do I use now repeatedValue?
var firstArray = [myStruct](count:4, repeatedValue: ???)
Simply use:
var newArray = [myStruct](count:4, repeatedValue: myStruct())
The syntax for creating instances of structs and classes is the same.