Fastest way to save structs iOS / Swift - ios

I have structs like
struct RGBA: Codable {
var r: UInt8
var g: UInt8
var b: UInt8
var a: UInt8
}
I want save large amount of this structs (>1_000_000)
Decode
guard let history = try? JSONDecoder().decode(HistoryRGBA.self, from: data) else { return }
Encode
guard let jsonData = try? encoder.encode(dataForSave) else { return false }
How can I improve encoding/decoding time and amount of RAM memory?

The performance of JSONEncoder/Decoder performance is...not great. ZippyJSON is a drop-in replacement that is supposedly about 4 times faster than Foundation's implmenetation, and if you're going for better performance and lower memory usage, you'll probably want to Google for some kind of streaming JSON decoder library.
However, you said in the comments that you don't need the JSON format. That's great, because we can store the data much more efficiently as just an array of raw bytes rather than a text-based format such as JSON:
extension RGBA {
static let size = 4 // the size of a (packed) RGBA structure
}
// encoding
var data = Data(count: history.rgba.count * RGBA.size)
for i in 0..<history.rgba.count {
let rgba = history.rgba[i]
data[i*RGBA.size] = rgba.r
data[i*RGBA.size+1] = rgba.g
data[i*RGBA.size+2] = rgba.b
data[i*RGBA.size+3] = rgba.a
}
// decoding
guard data.count % RGBA.size == 0 else {
// data is incomplete, handle error
return
}
let rgbaCount = data.count / RGBA.size
var result = [RGBA]()
result.reserveCapacity(rgbaCount)
for i in 0..<rgbaCount {
result.append(RGBA(r: data[i*RGBA.size],
g: data[i*RGBA.size+1],
b: data[i*RGBA.size+2],
a: data[i*RGBA.size+3]))
}
This is already about 50 times faster than using JSONEncoder on my machine (~100ms instead of ~5 seconds).
You can get even faster by bypassing some of Swift's safety checks and memory management and dropping down to raw pointers:
// encoding
let byteCount = history.rgba.count * RGBA.size
let rawBuf = malloc(byteCount)!
let buf = rawBuf.bindMemory(to: UInt8.self, capacity: byteCount)
for i in 0..<history.rgba.count {
let rgba = history.rgba[i]
buf[i*RGBA.size] = rgba.r
buf[i*RGBA.size+1] = rgba.g
buf[i*RGBA.size+2] = rgba.b
buf[i*RGBA.size+3] = rgba.a
}
let data = Data(bytesNoCopy: rawBuf, count: byteCount, deallocator: .free)
// decoding
guard data.count % RGBA.size == 0 else {
// data is incomplete, handle error
return
}
let result: [RGBA] = data.withUnsafeBytes { rawBuf in
let buf = rawBuf.bindMemory(to: UInt8.self)
let rgbaCount = buf.count / RGBA.size
return [RGBA](unsafeUninitializedCapacity: rgbaCount) { resultBuf, initializedCount in
for i in 0..<rgbaCount {
resultBuf[i] = RGBA(r: data[i*RGBA.size],
g: data[i*RGBA.size+1],
b: data[i*RGBA.size+2],
a: data[i*RGBA.size+3])
}
}
}
Benchmark results on my machine (I did not test ZippyJSON):
JSON:
Encode: 4967.0ms; 32280478 bytes
Decode: 5673.0ms
Data:
Encode: 96.0ms; 4000000 bytes
Decode: 19.0ms
Pointers:
Encode: 1.0ms; 4000000 bytes
Decode: 18.0ms
You could probably get even faster by just writing your array directly from memory to disk without serializing it at all, although I haven't tested that either. And of course, when you're testing performance, be sure you're testing in Release mode.

Considering that all your properties are UInt8 (bytes) you can make your struct conform to ContiguousBytes and save its raw bytes:
struct RGBA {
let r, g, b, a: UInt8
}
extension RGBA: ContiguousBytes {
func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R {
try Swift.withUnsafeBytes(of: self) { try body($0) }
}
}
extension ContiguousBytes {
init<T: ContiguousBytes>(_ bytes: T) {
self = bytes.withUnsafeBytes { $0.load(as: Self.self) }
}
}
extension RGBA: ExpressibleByArrayLiteral {
typealias ArrayLiteralElement = UInt8
init(arrayLiteral elements: UInt8...) {
self.init(elements)
}
}
extension Array {
var bytes: [UInt8] { withUnsafeBytes { .init($0) } }
var data: Data { withUnsafeBytes { .init($0) } }
}
extension ContiguousBytes {
var bytes: [UInt8] { withUnsafeBytes { .init($0) } }
var data: Data { withUnsafeBytes { .init($0) } }
}
extension ContiguousBytes {
func object<T>() -> T { withUnsafeBytes { $0.load(as: T.self) } }
func objects<T>() -> [T] { withUnsafeBytes { .init($0.bindMemory(to: T.self)) } }
}
extension ContiguousBytes {
var rgba: RGBA { object() }
var rgbaCollection: [RGBA] { objects() }
}
extension UIColor {
convenience init<T: Collection>(_ bytes: T) where T.Index == Int, T.Element == UInt8 {
self.init(red: CGFloat(bytes[0])/255,
green: CGFloat(bytes[1])/255,
blue: CGFloat(bytes[2])/255,
alpha: CGFloat(bytes[3])/255)
}
}
extension RGBA {
var color: UIColor { .init(bytes) }
}
let red: RGBA = [255, 0, 0, 255]
let green: RGBA = [0, 255, 0, 255]
let blue: RGBA = [0, 0, 255, 255]
let redBytes = red.bytes // [255, 0, 0, 255]
let redData = red.data // 4 bytes
let rgbaFromBytes = redBytes.rgba // RGBA
let rgbaFromData = redData.rgba // RGBA
let colorFromRGBA = red.color // r 1.0 g 0.0 b 0.0 a 1.0
let rgba: RGBA = [255,255,0,255] // RGBA yellow
let yellow = rgba.color // r 1.0 g 1.0 b 0.0 a 1.0
let colors = [red, green, blue] // [{r 255, g 0, b 0, a 255}, {r 0, g 255, b 0, a 255}, {r 0, g 0, b 255, a 255}]
let colorsData = colors.data // 12 bytes
let colorsFromData = colorsData.rgbaCollection // [{r 255, g 0, b 0, a 255}, {r 0, g 255, b 0, a 255}, {r 0, g 0, b 255, a 255}]
edit/update:
struct LayerRGBA {
var canvas: [[RGBA]]
}
extension LayerRGBA {
var data: Data { canvas.data }
init(_ data: Data) { canvas = data.objects() }
}
struct AnimationRGBA {
var layers: [LayerRGBA]
}
extension AnimationRGBA {
var data: Data { layers.data }
init(_ data: Data) {
layers = data.objects()
}
}
struct HistoryRGBA {
var layers: [LayerRGBA] = []
var animations: [AnimationRGBA] = []
}
extension HistoryRGBA {
var data: Data {
let layersData = layers.data
return layersData.count.data + layersData + animations.data
}
init(data: Data) {
let index = Int(Data(data.prefix(8))).advanced(by: 8)
self.init(layers: data.subdata(in: 8..<index).objects(),
animations: data.subdata(in: index..<data.endIndex).objects())
}
}
extension Numeric {
var data: Data {
var bytes = self
return .init(bytes: &bytes, count: MemoryLayout<Self>.size)
}
}
extension Numeric {
init<D: DataProtocol>(_ data: D) {
var value: Self = .zero
let _ = withUnsafeMutableBytes(of: &value, { data.copyBytes(to: $0)} )
self = value
}
}
Playground testing:
let layer1: LayerRGBA = .init(canvas: [colors,[red],[green, blue]])
let layer2: LayerRGBA = .init(canvas: [[red],[green, rgba]])
let loaded: LayerRGBA = .init(layer1.data)
loaded.canvas[0]
loaded.canvas[1]
loaded.canvas[2]
let animationRGBA: AnimationRGBA = .init(layers: [layer1,layer2])
let loadedAnimation: AnimationRGBA = .init(animationRGBA.data)
loadedAnimation.layers.count // 2
loadedAnimation.layers[0].canvas[0]
loadedAnimation.layers[0].canvas[1]
loadedAnimation.layers[0].canvas[2]
loadedAnimation.layers[1].canvas[0]
loadedAnimation.layers[1].canvas[1]
let hRGBA: HistoryRGBA = .init(layers: [loaded], animations: [animationRGBA])
let loadedHistory: HistoryRGBA = .init(data: hRGBA.data)
loadedHistory.layers[0].canvas[0]
loadedHistory.layers[0].canvas[1]
loadedHistory.layers[0].canvas[2]
loadedHistory.animations[0].layers[0].canvas[0]
loadedHistory.animations[0].layers[0].canvas[1]
loadedHistory.animations[0].layers[0].canvas[2]
loadedHistory.animations[0].layers[1].canvas[0]
loadedHistory.animations[0].layers[1].canvas[1]

If anyone else like me was wondering if using PropertyListEncoder/Decoder or writing custom encoding/decoding methods for Codable structs can make any difference in performance then I made some tests to check it and the answer is that they can improve it a little bit compared to standard JSONEncoder/Decoder but not much. I can't really recommended this as there are far faster ways of doing it in other answers but I think it might be useful in some cases so I'm putting the results here. Using unkeyedContainer for encoding/decoding Codable made encoding about 2x faster in my tests but it had minimal impact on decoding and using PropertyListEncoder/Decoder made only minimal difference as pasted below. Test code:
struct RGBA1: Codable {
var r: UInt8
var g: UInt8
var b: UInt8
var a: UInt8
}
struct RGBA2 {
var r: UInt8
var g: UInt8
var b: UInt8
var a: UInt8
}
extension RGBA2: Codable {
func encode(to encoder: Encoder) throws {
var container = encoder.unkeyedContainer()
try container.encode(r)
try container.encode(g)
try container.encode(b)
try container.encode(a)
}
init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
r = try container.decode(UInt8.self)
g = try container.decode(UInt8.self)
b = try container.decode(UInt8.self)
a = try container.decode(UInt8.self)
}
}
class PerformanceTests: XCTestCase {
var rgba1: [RGBA1] = {
var rgba1: [RGBA1] = []
for i in 0..<1_000_000 {
rgba1.append(RGBA1(r: UInt8(i % 256), g: UInt8(i % 256), b: UInt8(i % 256), a: UInt8(i % 256)))
}
return rgba1
}()
var rgba2: [RGBA2] = {
var rgba2: [RGBA2] = []
for i in 0..<1_000_000 {
rgba2.append(RGBA2(r: UInt8(i % 256), g: UInt8(i % 256), b: UInt8(i % 256), a: UInt8(i % 256)))
}
return rgba2
}()
func testRgba1JsonEncoding() throws {
var result: Data?
self.measure { result = try? JSONEncoder().encode(rgba1) }
print("rgba1 json size: \(result?.count ?? 0)")
}
func testRgba1JsonDecoding() throws {
let result = try? JSONEncoder().encode(rgba1)
self.measure { _ = try? JSONDecoder().decode([RGBA1].self, from: result!) }
}
func testRgba1PlistEncoding() throws {
var result: Data?
self.measure { result = try? PropertyListEncoder().encode(rgba1) }
print("rgba1 plist size: \(result?.count ?? 0)")
}
func testRgba1PlistDecoding() throws {
let result = try? PropertyListEncoder().encode(rgba1)
self.measure { _ = try? PropertyListDecoder().decode([RGBA1].self, from: result!) }
}
func testRgba2JsonEncoding() throws {
var result: Data?
self.measure { result = try? JSONEncoder().encode(rgba2) }
print("rgba2 json size: \(result?.count ?? 0)")
}
func testRgba2JsonDecoding() throws {
let result = try? JSONEncoder().encode(rgba2)
self.measure { _ = try? JSONDecoder().decode([RGBA2].self, from: result!) }
}
func testRgba2PlistEncoding() throws {
var result: Data?
self.measure { result = try? PropertyListEncoder().encode(rgba2) }
print("rgba2 plist size: \(result?.count ?? 0)")
}
func testRgba2PlistDecoding() throws {
let result = try? PropertyListEncoder().encode(rgba2)
self.measure { _ = try? PropertyListDecoder().decode([RGBA2].self, from: result!) }
}
}
Results on my device:
testRgba1JsonEncoding average 5.251 sec 32281065 bytes
testRgba1JsonDecoding average 7.749 sec
testRgba1PlistEncoding average 4.811 sec 41001610 bytes
testRgba1PlistDecoding average 7.529 sec
testRgba2JsonEncoding average 2.546 sec 16281065 bytes
testRgba2JsonDecoding average 7.906 sec
testRgba2PlistEncoding average 2.710 sec 25001586 bytes
testRgba2PlistDecoding average 6.432 sec

Related

Getting a STRANGE warning in Xcode during loading gif into UIImageView

I was working on a project which loads a gif to UIImageView from its url.
while running app in the Xcode gives a strange warning and the gif also looks laggy.
Warning is like:
Synchronous URL loading of
https://mir-s3-cdn-cf.behance.net/project_modules/max_1200/5eeea355389655.59822ff824b72.gif
should not occur on this application's main thread as it may lead to
UI unresponsiveness. Please switch to an asynchronous networking API
such as URLSession.
The code snippet I've used for loading gif with url.
//
// iOSDevCenters+GIF.swift
// Get Fit
//
// Created by Sandeep Sahani on 08/12/22.
//
import UIKit
import ImageIO
// FIXME: comparison operators with optionals were removed from the Swift Standard Libary.
// Consider refactoring the code to use the non-optional operators.
fileprivate func < <T : Comparable>(lhs: T?, rhs: T?) -> Bool {
switch (lhs, rhs) {
case let (l?, r?):
return l < r
case (nil, _?):
return true
default:
return false
}
}
extension UIImage {
public class func gifImageWithData(_ data: Data) -> UIImage? {
guard let source = CGImageSourceCreateWithData(data as CFData, nil) else {
print("image doesn't exist")
return nil
}
return UIImage.animatedImageWithSource(source)
}
public class func gifImageWithURL(_ gifUrl:String) -> UIImage? {
guard let bundleURL:URL? = URL(string: gifUrl)
else {
print("image named \"\(gifUrl)\" doesn't exist")
return nil
}
guard let imageData = try? Data(contentsOf: bundleURL!) else {
print("image named \"\(gifUrl)\" into NSData")
return nil
}
return gifImageWithData(imageData)
}
public class func gifImageWithName(_ name: String) -> UIImage? {
guard let bundleURL = Bundle.main
.url(forResource: name, withExtension: "gif") else {
print("SwiftGif: This image named \"\(name)\" does not exist")
return nil
}
guard let imageData = try? Data(contentsOf: bundleURL) else {
print("SwiftGif: Cannot turn image named \"\(name)\" into NSData")
return nil
}
return gifImageWithData(imageData)
}
class func delayForImageAtIndex(_ index: Int, source: CGImageSource!) -> Double {
var delay = 0.1
let cfProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil)
let gifProperties: CFDictionary = unsafeBitCast(
CFDictionaryGetValue(cfProperties,
Unmanaged.passUnretained(kCGImagePropertyGIFDictionary).toOpaque()),
to: CFDictionary.self)
var delayObject: AnyObject = unsafeBitCast(
CFDictionaryGetValue(gifProperties,
Unmanaged.passUnretained(kCGImagePropertyGIFUnclampedDelayTime).toOpaque()),
to: AnyObject.self)
if delayObject.doubleValue == 0 {
delayObject = unsafeBitCast(CFDictionaryGetValue(gifProperties,
Unmanaged.passUnretained(kCGImagePropertyGIFDelayTime).toOpaque()), to: AnyObject.self)
}
delay = delayObject as! Double
if delay < 0.1 {
delay = 0.1
}
return delay
}
class func gcdForPair(_ a: Int?, _ b: Int?) -> Int {
var a = a
var b = b
if b == nil || a == nil {
if b != nil {
return b!
} else if a != nil {
return a!
} else {
return 0
}
}
if a < b {
let c = a
a = b
b = c
}
var rest: Int
while true {
rest = a! % b!
if rest == 0 {
return b!
} else {
a = b
b = rest
}
}
}
class func gcdForArray(_ array: Array<Int>) -> Int {
if array.isEmpty {
return 1
}
var gcd = array[0]
for val in array {
gcd = UIImage.gcdForPair(val, gcd)
}
return gcd
}
class func animatedImageWithSource(_ source: CGImageSource) -> UIImage? {
let count = CGImageSourceGetCount(source)
var images = [CGImage]()
var delays = [Int]()
for i in 0..<count {
if let image = CGImageSourceCreateImageAtIndex(source, i, nil) {
images.append(image)
}
let delaySeconds = UIImage.delayForImageAtIndex(Int(i),
source: source)
delays.append(Int(delaySeconds * 1000.0)) // Seconds to ms
}
let duration: Int = {
var sum = 0
for val: Int in delays {
sum += val
}
return sum
}()
let gcd = gcdForArray(delays)
var frames = [UIImage]()
var frame: UIImage
var frameCount: Int
for i in 0..<count {
frame = UIImage(cgImage: images[Int(i)])
frameCount = Int(delays[Int(i)] / gcd)
for _ in 0..<frameCount {
frames.append(frame)
}
}
let animation = UIImage.animatedImage(with: frames,
duration: Double(duration) / 1000.0)
return animation
}
}
Place I have used the code.
//
// GifViewController.swift
// Get Fit
//
// Created by Sandeep Sahani on 08/12/22.
//
import UIKit
class GifViewController: UIViewController
{
#IBOutlet weak var gif: UIImageView!
override func viewDidLoad()
{
super.viewDidLoad()
let gifURL : String = "https://mir-s3-cdn-cf.behance.net/project_modules/max_1200/5eeea355389655.59822ff824b72.gif"
let imageURL = UIImage.gifImageWithURL(gifURL)
self.gif.image = imageURL
}
}
How can I remove that strange warning and make that gif smooth?
The API call you're using is executed on the main thread so the system warns you about that.
I'd recommend using modern projects' default choice library for loading remote images called Kingfisher: https://github.com/onevcat/Kingfisher
There is a wiki section on loading gif images with Kingfisher lib:
https://github.com/onevcat/Kingfisher/wiki/Cheat-Sheet#animated-gif
Loading a GIF
let imageView: UIImageView = ...
imageView.kf.setImage(with: URL(string: "your_animated_gif_image_url")!)
or
let imageView = AnimatedImageView()
imageView.kf.setImage(with: URL(string: "your_large_animated_gif_image_url")!)
You have to add Kingfisher to your project as a Swift Package Manager project and then import Kingfisher at the top of the file

How can I put multiple value inputs in an UITextField to get their outputs?

I'm building an iOS app that deals with binary and decimal outputs when a user types in a binary or decimal number. I have it to where a user can calculate one value, but how can I get it to where they can calculate multiple values in the same UITextField? Like they can type in 145, 10010110, 10011101, etc. and it can return the opposite binary or decimal.
My existing code is:
class BinaryDecimal {
var bits: [Int]?
var integer: Int?
init(_ bits: [Int]) {
self.bits = bits
}
init(_ decimal: Int) {
self.integer = decimal
}
func calculateBinaryValueForInt() -> String {
var newInt = integer!
let rows = [128, 64, 32, 16, 8, 4, 2, 1]
var binaryRows: [Int] = []
for row in rows {
let binaryDigit = oneOrZero(forValue: newInt, withBitValue: row)
binaryRows.append(binaryDigit)
if binaryDigit == 1 {
newInt = newInt - row
}
}
let stringFromIntArray = binaryRows.map { String($0) }
return stringFromIntArray.joined()
}
func calculateIntValueForBinary() -> String {
var value = 0
var multiplier = 1
guard let bits = bits else { return "Error" }
for bit in bits.reversed() {
let newValue = bit * multiplier
value = value + newValue
multiplier = multiplier * 2
}
return String(value)
}
func oneOrZero(forValue value: Int, withBitValue bitvalue: Int) -> Int {
if value - bitvalue >= 0 {
return 1
} else {
return 0
}
}
}

How to make a generic Vertex so that I can create Graphs (Collections) with mixed types?

I am having trouble with a design of a Vertex struct. I want to be able to create "Array" or "Set" so my "Vertex" doesnt all have to be the same type. Both situations are giving different errors. Check code below.
Thank you in advance
import Foundation
public struct Vertex<T: Equatable> where T: Equatable, T: Hashable {
public var data: T
public let index: Int?
init(data: T , index: Int ) {
self.data = data
self.index = index
}
}
extension Vertex: CustomStringConvertible {
public var description: String {
return "\(index): \(data)"
}
}
struct Coordinate {
var x : Int
var y : Int
init(x : Int, y: Int) {
self.x = x
self.y = y
}
}
extension Coordinate: Equatable {}
func ==(lhs: Coordinate, rhs: Coordinate) -> Bool {
guard lhs.x == rhs.x else {
return false
}
guard lhs.y == rhs.y else {
return false
}
return true
}
extension Coordinate: Hashable {
var hashValue: Int {
return "\(x)\(y)".hashValue
}
}
let coord1 = Coordinate(x : 5, y: 5)
let stringVertex1 = Vertex(data: "Hello World", index: 3)
let stringVertex2 = Vertex(data: "Foo ", index: 3)
let intVertex1 = Vertex(data: 2, index: 1)
let coordVertex1 = Vertex(data: coord1, index: 1)
//Error: Cannot convert value of type 'Vertex<String>' to expected element type 'Vertex'.
//Even if I make myArr1 equal to [stringVertex1, stringVertex1], I still get the same error.
let myArr1 : Array<Vertex> = [stringVertex1, intVertex1]
//This works since the data inside "Vertex" is the same type.
let myArr2 : Array<Vertex<String>> = [stringVertex1, stringVertex2]
//Error: Type "Vertex" does not conform to protocol "Hashable".
let mySet1 : Set<Vertex> = [stringVertex1, stringVertex2]
I got my answer. This is the final vertex code. THE POWER OF POP!
public enum TypeOfVertex : String {
case Int = "Mirror for Int"
case Float = "Mirror for Float"
case Double = "Mirror for Double"
case Coordinates = "Mirror for Coordinate"
case String = "Mirror for String"
}
protocol AnyVertexable {
var type: TypeOfVertex { get set }
var index: Int { get set }
}
struct Vertex<Element : Hashable> : AnyVertexable where Element: Equatable & Hashable {
var type : TypeOfVertex
var index: Int
var data : Element?
init(index: Int, data: Element) {
self.data = data
self.index = index
if let type = TypeOfVertex(rawValue: data.hashValue.customMirror.description) {
self.type = type
} else {
fatalError()
}
}
}
let sample1 = Vertex(index: 0, data: 1.0)
let sample2 = Vertex(index: 2, data: 1)
let sample3 = Vertex(index: 3, data: Coordinate(x: 5, y: 5))
let myArr : [AnyVertexable] = [sample1, sample2, sample3]

iOS Swift How To Use Gif File Inside My Project

In my app I need to use .gif and I did searched on it. Everyone asked to use UIImage.gifImageWithName("funny") or UIImage.gifWithName("jeremy") for adding .gif file. But I'm getting error on those Type 'UIImage' has no member 'gifWithName'. How to solve those Issues and How to use .gif in my app.
Download UIImage+Gif.swift file from https://github.com/bahlo/SwiftGif/tree/master/SwiftGifCommon and put into your project..
// An animated UIImage
let Gif = UIImage.gif(name: "jerry")
// A UIImageView with async loading
let imageView = UIImageView()
imageView.loadGif(name: "tom")]
iOS won't support .gif images directly. You have to use latest version of third party library like SDWebImage. The simplified solution is to use Webview.
Or else,
You can use UIImageView+Extension
this code seems to help you!
#IBOutlet weak var whiteView: UIImageView!
let imagePicker = UIImagePickerController()
override func viewDidLoad()
{
super.viewDidLoad()
GifView.loadGif(name: "funny")
imagePicker.delegate = self
}
Also, you have to add Controller for "loadGif" into your source and I think you can download on online.
//
// GifFunctions.swift
// GifFunctions
//
// Created by Ambu Sangoli on 11/12/16.
// Copyright © 2016 Ambu Sangoli. All rights reserved.
//
import UIKit
import ImageIO
fileprivate func < <T : Comparable>(lhs: T?, rhs: T?) -> Bool {
switch (lhs, rhs) {
case let (l?, r?):
return l < r
case (nil, _?):
return true
default:
return false
}
}
extension UIImage {
public class func gifImageWithData(_ data: Data) -> UIImage? {
guard let source = CGImageSourceCreateWithData(data as CFData, nil) else {
print("image doesn't exist")
return nil
}
return UIImage.animatedImageWithSource(source)
}
public class func gifImageWithURL(_ gifUrl:String) -> UIImage? {
guard let bundleURL:URL = URL(string: gifUrl)
else {
print("image named \"\(gifUrl)\" doesn't exist")
return nil
}
guard let imageData = try? Data(contentsOf: bundleURL) else {
print("image named \"\(gifUrl)\" into NSData")
return nil
}
return gifImageWithData(imageData)
}
public class func gifImageWithName(_ name: String) -> UIImage? {
guard let bundleURL = Bundle.main
.url(forResource: name, withExtension: "gif") else {
print("SwiftGif: This image named \"\(name)\" does not exist")
return nil
}
guard let imageData = try? Data(contentsOf: bundleURL) else {
print("SwiftGif: Cannot turn image named \"\(name)\" into NSData")
return nil
}
return gifImageWithData(imageData)
}
class func delayForImageAtIndex(_ index: Int, source: CGImageSource!) -> Double {
var delay = 0.1
let cfProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil)
let gifProperties: CFDictionary = unsafeBitCast(
CFDictionaryGetValue(cfProperties,
Unmanaged.passUnretained(kCGImagePropertyGIFDictionary).toOpaque()),
to: CFDictionary.self)
var delayObject: AnyObject = unsafeBitCast(
CFDictionaryGetValue(gifProperties,
Unmanaged.passUnretained(kCGImagePropertyGIFUnclampedDelayTime).toOpaque()),
to: AnyObject.self)
if delayObject.doubleValue == 0 {
delayObject = unsafeBitCast(CFDictionaryGetValue(gifProperties,
Unmanaged.passUnretained(kCGImagePropertyGIFDelayTime).toOpaque()), to: AnyObject.self)
}
delay = delayObject as! Double
if delay < 0.1 {
delay = 0.1
}
return delay
}
class func gcdForPair(_ a: Int?, _ b: Int?) -> Int {
var a = a
var b = b
if b == nil || a == nil {
if b != nil {
return b!
} else if a != nil {
return a!
} else {
return 0
}
}
if a < b {
let c = a
a = b
b = c
}
var rest: Int
while true {
rest = a! % b!
if rest == 0 {
return b!
} else {
a = b
b = rest
}
}
}
class func gcdForArray(_ array: Array<Int>) -> Int {
if array.isEmpty {
return 1
}
var gcd = array[0]
for val in array {
gcd = UIImage.gcdForPair(val, gcd)
}
return gcd
}
class func animatedImageWithSource(_ source: CGImageSource) -> UIImage? {
let count = CGImageSourceGetCount(source)
var images = [CGImage]()
var delays = [Int]()
for i in 0..<count {
if let image = CGImageSourceCreateImageAtIndex(source, i, nil) {
images.append(image)
}
let delaySeconds = UIImage.delayForImageAtIndex(Int(i),
source: source)
delays.append(Int(delaySeconds * 1000.0)) // Seconds to ms
}
let duration: Int = {
var sum = 0
for val: Int in delays {
sum += val
}
return sum
}()
let gcd = gcdForArray(delays)
var frames = [UIImage]()
var frame: UIImage
var frameCount: Int
for i in 0..<count {
frame = UIImage(cgImage: images[Int(i)])
frameCount = Int(delays[Int(i)] / gcd)
for _ in 0..<frameCount {
frames.append(frame)
}
}
let animation = UIImage.animatedImage(with: frames,
duration: Double(duration) / 1000.0)
return animation
}
}
Save above code with any file name example GifFunctions.Swift add it to your project and use it like below
let gifToplay = UIImage.gifImageWithName("YourGifName")
YourImageView.image = (image: gifToplay)
Also you can use other methods.
Enjoy (y)

How to convert a double into a byte array in swift?

I know how to do it in java (see here), but I couldn't find a swift equivalent for java's ByteBuffer, and consequently its .putDouble(double value) method.
Basically, I'm looking for a function like this:
func doubleToByteArray(value: Double) -> [UInt8]? {
. . .
}
doubleToByteArray(1729.1729) // should return [64, 155, 4, 177, 12, 178, 149, 234]
typealias Byte = UInt8
func toByteArray<T>(var value: T) -> [Byte] {
return withUnsafePointer(&value) {
Array(UnsafeBufferPointer(start: UnsafePointer<Byte>($0), count: sizeof(T)))
}
}
toByteArray(1729.1729)
toByteArray(1729.1729 as Float)
toByteArray(1729)
toByteArray(-1729)
But the results are reversed from your expectations (because of endianness):
[234, 149, 178, 12, 177, 4, 155, 64]
[136, 37, 216, 68]
[193, 6, 0, 0, 0, 0, 0, 0]
[63, 249, 255, 255, 255, 255, 255, 255]
Added:
func fromByteArray<T>(value: [Byte], _: T.Type) -> T {
return value.withUnsafeBufferPointer {
return UnsafePointer<T>($0.baseAddress).memory
}
}
let a: Double = 1729.1729
let b = toByteArray(a) // -> [234, 149, 178, 12, 177, 4, 155, 64]
let c = fromByteArray(b, Double.self) // -> 1729.1729
For Xcode8/Swift3.0:
func toByteArray<T>(_ value: T) -> [UInt8] {
var value = value
return withUnsafePointer(to: &value) {
$0.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout<T>.size) {
Array(UnsafeBufferPointer(start: $0, count: MemoryLayout<T>.size))
}
}
}
func fromByteArray<T>(_ value: [UInt8], _: T.Type) -> T {
return value.withUnsafeBufferPointer {
$0.baseAddress!.withMemoryRebound(to: T.self, capacity: 1) {
$0.pointee
}
}
}
For Xcode8.1/Swift3.0.1
func toByteArray<T>(_ value: T) -> [UInt8] {
var value = value
return withUnsafeBytes(of: &value) { Array($0) }
}
func fromByteArray<T>(_ value: [UInt8], _: T.Type) -> T {
return value.withUnsafeBytes {
$0.baseAddress!.load(as: T.self)
}
}
Well, it wasn't easy, but here it is:
func doubleToByteArray(value: Double) -> [UInt8] {
let count = sizeof(Double)
var doubles: [Double] = [value]
let data = NSData(bytes: doubles, length: count)
var result = [UInt8](count: count, repeatedValue: 0)
data.getBytes(&result, length: count)
return result
}
Use with caution.
Here's my updated version to the original solution.
/// input: array of bytes
/// -> get pointer to byte array (UnsafeBufferPointer<[Byte]>)
/// -> access its base address
/// -> rebind memory to target type T (UnsafeMutablePointer<T>)
/// -> extract and return the value of target type
func binarytotype <T> (_ value: [Byte], _: T.Type) -> T
{
return value.withUnsafeBufferPointer {
$0.baseAddress!
.withMemoryRebound(to: T.self, capacity: 1) {
$0.pointee
}
}
}
/// input type: value of type T
/// -> get pointer to value of T
/// -> rebind memory to the target type, which is a byte array
/// -> create array with a buffer pointer initialized with the source pointer
/// -> return the resulted array
func typetobinary <T> (_ value: T) -> [Byte]
{
var mv : T = value
let s : Int = MemoryLayout<T>.size
return withUnsafePointer(to: &mv) {
$0.withMemoryRebound(to: Byte.self, capacity: s) {
Array(UnsafeBufferPointer(start: $0, count: s))
}
}
}
PS: Don't forget to replace Byte with UInt8.
Solution in swift 3:
public func toByteArray<T>(_ value: T) -> [Byte] {
let totalBytes = MemoryLayout<T>.size
var value = value
return withUnsafePointer(to: &value) { valuePtr in
return valuePtr.withMemoryRebound(to: Byte.self, capacity: totalBytes) { reboundPtr in
return Array(UnsafeBufferPointer(start: reboundPtr, count: totalBytes))
}
}
}
The accepted answers are dangerous because of the fact that the MemoryLayout provides you the size of the static type T!
To workaround the problem you should create a custom protocol and ask for Self in it:
protocol ByteConvertible {}
extension ByteConvertible {
func toBytes() -> [UInt8] {
let capacity = MemoryLayout<Self>.size
var mutableValue = self
return withUnsafePointer(to: &mutableValue) {
return $0.withMemoryRebound(to: UInt8.self, capacity: capacity) {
return Array(UnsafeBufferPointer(start: $0, count: capacity))
}
}
}
}
I mentioned before that the accepted answers are dangerous and here is an example why:
let num = UInt8(42)
MemoryLayout.size(ofValue: num) //=> 1 byte as expected
let any: Any = num
MemoryLayout.size(ofValue: any) //=> 32 bytes which is what will happen in the generic functions from the all the answers
Swift 3.0
The method above works, using Swift 2 but, I discovered a much more simpler and faster method to do this conversion and vice versa:
func binarytotype <T> (value: [UInt8], _: T.Type) -> T
{
return value.withUnsafeBufferPointer
{
return UnsafePointer<T>($0.baseAddress).memory
}
}
func typetobinary <T> (var value: T) -> [UInt8]
{
return withUnsafePointer(&value)
{
Array(UnsafeBufferPointer(start: UnsafePointer<UInt8>($0), count: sizeof(T)))
}
}
let a: Double = 0.25
let b: [UInt8] = typetobinary(a) // -> [0, 0, 0, 0, 0, 0, 208, 63]
let c = binarytotype(b, Double.self) // -> 0.25
I have tested it with Xcode 7.2 in the playground.
func byteArray<T>(_ value: T) -> [UInt8] {
var value = value
var initialArray = withUnsafeBytes(of: &value) { Array($0) }
initialArray.reverse()
var count = initialArray.count
while initialArray.first == 0 && count > 1 {
initialArray[0...count - 2] = initialArray[1...count - 1]
count -= 1
}
if initialArray[0] >= 128 {
var newArray = [UInt8](repeating: 0, count: count + 1)
newArray[0] = UInt8(0)
newArray[1...count] = initialArray[0...count - 1]
return newArray
} else {
return Array(initialArray[0...count - 1])
}
}

Resources