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

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"

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

Swift 3 - iterate over generic collection

I have struct Queue<T> that is based on a LinkedList<T>() I want to be able to iterate over the element in the queue and do something with them.
After doing some digging, I believe I have to inherit from Sequence and do something like this:
extension Sequence {
public func makeIterator() -> CountableRange<T>.Iterator {
return (0..<self).makeIterator()
}
}
and after I can have a function in my Queue class something like:
func iter(q: T) -> T? {
for i in q {
}
}
except the extension throws Use of undeclared type 'T' and the for loop a Type 'T' does not conform to protocol 'Sequence'
I am fairly new to Swift and I understand what I have to do I just don't know how to do it and find most explanations quite confusing. Could someone point me in the right direction?
import Foundation
public struct Queue<T> : Sequence{
fileprivate var list = LinkedList<T>()
public var queueCount : Int {
return list.getCount()
}
public var isEmpty: Bool {
return list.isEmpty
}
public mutating func enqueue(_ element: T) {
list.append(value: element)
}
public mutating func dequeue() -> T? {
guard !list.isEmpty, let element = list.first else { return nil }
list.remove(node: element)
return element.value
}
public func peek() -> T? {
return list.first?.value
}
func iter(q: T) -> T? {
for i in q {
}
}
}
extension Queue: CustomStringConvertible {
// 2
public var description: String {
// 3
return list.description
}
}
extension Sequence {
public func makeIterator() -> CountableRange<T>.Iterator {
return (0..<self).makeIterator()
}
}

Swift | Set with NSObject

I'm trying to create a Set with custom objects.
This is working, If I use a Set of my custom objects there is no duplicates :
public class AttributesGroup: Hashable, Equatable, Comparable {
open var id: Int!
open var name: String!
open var position: Int!
public init (id: Int = 0, name: String = "", position: Int = 0) {
self.id = id
self.name = name
self.position = position
}
open var hashValue: Int {
get {
return id.hashValue
}
}
public static func ==(lhs: AttributesGroup, rhs: AttributesGroup) -> Bool {
return lhs.id == rhs.id
}
public static func < (lhs: AttributesGroup, rhs:AttributesGroup) -> Bool {
return lhs.position < rhs.position
}
}
I extend my class with NSObject, since NSObject already implements Hashable protocol (and also Equatable) I have to override hashValue, and this is not working anymore, If I use a Set of my custom objects there is duplicates, what do I do wrong here ? :
public class AttributesGroup: NSObject, Comparable {
open var id: Int!
open var name: String!
open var position: Int!
public init (id: Int = 0, name: String = "", position: Int = 0) {
self.id = id
self.name = name
self.position = position
}
open override var hashValue: Int {
get {
return id.hashValue
}
}
public static func ==(lhs: AttributesGroup, rhs: AttributesGroup) -> Bool {
return lhs.id == rhs.id
}
public static func < (lhs: AttributesGroup, rhs:AttributesGroup) -> Bool {
return lhs.position < rhs.position
}
}
Thanks for your help !
NSObject is a Cocoa type. The rules for NSObject are different from the rules for Swift. To make an NSObject work in a set, it must have an implementation of isEqual consonant with its implementation of hash.

Creating A Numbered List In UITextView Swift 3

I am trying to make a numbered list out of the information the user inputs into a UITextView. For example,
List item one
List item two
List item three
Here is the code that I have tried but does not give me the desired effect.
var currentLine: Int = 1
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
// Add "1" when the user starts typing into the text field
if (textView.text.isEmpty && !text.isEmpty) {
textView.text = "\(currentLine). "
currentLine += 1
}
else {
if text.isEmpty {
if textView.text.characters.count >= 4 {
let str = textView.text.substring(from:textView.text.index(textView.text.endIndex, offsetBy: -4))
if str.hasPrefix("\n") {
textView.text = String(textView.text.characters.dropLast(3))
currentLine -= 1
}
}
else if text.isEmpty && textView.text.characters.count == 3 {
textView.text = String(textView.text.characters.dropLast(3))
currentLine = 1
}
}
else {
let str = textView.text.substring(from:textView.text.index(textView.text.endIndex, offsetBy: -1))
if str == "\n" {
textView.text = "\(textView.text!)\(currentLine). "
currentLine += 1
}
}
}
return true
}
as suggested here: How to make auto numbering on UITextview when press return key in swift but had no success.
Any help is much appreciated.
You can subclass UITextView, override method willMove(toSuperview and add an observer for UITextViewTextDidChange with a selector that break up your text into lines enumerating and numbering it accordingly. Try like this:
class NumberedTextView: UITextView {
override func willMove(toSuperview newSuperview: UIView?) {
frame = newSuperview?.frame.insetBy(dx: 50, dy: 80) ?? frame
backgroundColor = .lightGray
NotificationCenter.default.addObserver(self, selector: #selector(textViewDidChange), name: .UITextViewTextDidChange, object: nil)
}
func textViewDidChange(notification: Notification) {
var lines: [String] = []
for (index, line) in text.components(separatedBy: .newlines).enumerated() {
if !line.hasPrefix("\(index.advanced(by: 1))") &&
!line.trimmingCharacters(in: .whitespaces).isEmpty {
lines.append("\(index.advanced(by: 1)). " + line)
} else {
lines.append(line)
}
}
text = lines.joined(separator: "\n")
// this prevents two empty lines at the bottom
if text.hasSuffix("\n\n") {
text = String(text.characters.dropLast())
}
}
}
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let textView = NumberedTextView()
view.addSubview(textView)
}
}

swift 3, PHFetchResult.enumerateObjects error

In swift 3,the method is show me "ambiguous use of 'enumerateObjects'",what happen.how can i do?
extension PHFetchResult {
public func assetCollection() -> [PHAssetCollection] {
var list :[PHAssetCollection] = []
self.enumerateObjects { (object, index, stop) in
if object is PHAssetCollection {
let collection = object as! PHAssetCollection
list.append(collection)
}
}
return list
}
}
Swift 3.0: Just add the Round Brackets before Curly Brackets starts after enumerateObjects.
extension PHFetchResult {
public func assetCollection() -> [PHAssetCollection] {
var list :[PHAssetCollection] = []
self.enumerateObjects ({ (object, index, stop) in
if object is PHAssetCollection {
let collection = object as! PHAssetCollection
list.append(collection)
}
})
return list
}
}
Do something like this noh. You can't directly add extension for PHFetchResult because it has other ObjectType as its generic parameter PHFetchResult<ObjectType> . So you must do something else.
class FetchPhoto {
class func assetCollection() -> [PHAssetCollection] {
var list :[PHAssetCollection] = []
PHAssetCollection.fetchMoments(with: nil).enumerateObjects(EnumerationOptions.concurrent) { (collection, _, _) in
list.append(collection)
}
return list
}
}
PHAssetCollection.fetchMoments returns PHFetchResult<PHAssetCollection> here PHAssetCollection is the ObjectType for the PHFetchResult. You got the ambiguous error because you have not specified the objectType.
A generic way to approach this.
class FetchPhoto {
class func assetCollection<T : PHObject>(result : PHFetchResult<T>) -> [T] {
var list : [T] = []
result.enumerateObjects(EnumerationOptions.concurrent) { (object, _, _) in
list.append(object)
}
return list
}
}
Swift 3
class PhotosHelper {
class func fetchAllLocalIdentifiersOfPhotos(completion : (_ localIdentifiers : [String]) -> ()) {
let photos : PHFetchResult<PHAsset> = PHAsset.fetchAssets(with: PHAssetMediaType.image, options: nil)
photos.enumerateObjects ({ _,_,_ in
// Do your operations, you can see that there is no warnings/errors in this one
})
}
}