// Swift # swift repl :version // 5.7.2 :quit swift package --version # Apple Swift Package Manager - Swift 5.7.1 swift package --help swift package init swift package generate-xcodeproj swift build swift test swift test --filter "FooTests" swift test --filter "BarTests" swift test --filter "testFoo" import Darwin M_PI // Double = 3.1415926535897931 log2(100 as Float) // 6.64385605 log2(100 as Double) // 6.6438561897747244 "Foo".lowercased() "foo".count // 3 "foo".index(of: "f") // String.Index? let s = "foo bar" if let idx: String.Index = s.firstIndex(of: "f") { s.distance(from: s.startIndex, to: idx) |> p // Int = 0 } extension String { // se // let c: Character? = s[0] // subscript(idx: Int) -> Character? { guard idx >= 0 && idx < count else { return nil } return self[index(startIndex, offsetBy: idx)] } // let idx: Int? = s.index("f") // func index(_ c: Character) -> Int? { guard let idx: String.Index = firstIndex(of: c) else { return nil } let id: Int = distance(from: startIndex, to: idx) print("\"\(self)\" idx: \(c) = \(id)") return id } } let c: Character? = s[0] // se let cs: Array<Character?> = [c, s[1], s[100]] print(cs) // [Optional("f"), Optional("o"), nil] if let idx: Int = s.index("f") { // se print("DB idx = \(idx)") } // replace s.replacingOccurrences(of: "o", with: "*") // f** bar s.replacingOccurrences(of: ",", with: "").replacingOccurrences(of: " ", with: "_").lowercased() let range: Range<String.Index> = Range(uncheckedBounds: (lower: s.index(s.startIndex, offsetBy: 0), upper: s.index(s.endIndex, offsetBy: 0))) s.replacingOccurrences(of: "o", with: "*", options: NSString.CompareOptions.literal, range: range) s.substring(from: s.index(s.startIndex, offsetBy: 0)) // foo bar s.substring(from: s.index(s.startIndex, offsetBy: 1)) // oo bar let startIdx = s.index(s.startIndex, offsetBy: 4) s.substring(from: startIdx) // bar s.substring(to: s.index(s.startIndex, offsetBy: 0)) // s.substring(to: s.index(s.startIndex, offsetBy: 1)) // f, first char, string s.substring(to: s.index(s.startIndex, offsetBy: 2)) // fo let endIdx = s.index(s.startIndex, offsetBy: 3) s.substring(to: endIdx) // foo String(s[s.startIndex ..< endIdx]) // foo let range: Range<String.Index> = Range(uncheckedBounds: (lower: s.index(s.startIndex, offsetBy: 1), upper: s.index(s.endIndex, offsetBy: -1))) s.substring(with: range) // oo ba let c: Character = s[s.startIndex] // f, first char let p = CGPoint(x:1, y:2) // CGPointMake(1, 2) Locale.current.identifier // en_US Locale.current.languageCode // en Locale.current.regionCode // US ... closed range operator ..< half-open range for x in 0..<1 { // 0 for x in 0...1 { // 0 1 x |> p } for x in 1...8 { x % 4 |> p // 1 2 3 0 1 2 3 0 mod } for _ in 1...10 { "." |> p } String(repeating: ".", count: 10) // .......... for x in 1...3 { "\(x)." |> p } // 1. // 2. // 3. for x in stride(from: 10, through: 100, by: 10 ) { "\(x) " |> p } // 10 20 30 40 50 60 70 80 90 100 closed for x in stride(from: 10, to: 100, by: 10 ) { "\(x) " |> p } // 10 20 30 40 50 60 70 80 90 half-open let df = DateFormatter() df.dateFormat = "MM-dd-yyyy" let d1 = df.date(from: "01-08-1947")! let d2 = df.date(from: "01-10-2016")! // db let cal = Calendar.current cal.dateComponents([.day], from: d1, to: d2).day // 25204 cal.dateComponents([.year], from: d1, to: d2).year // 69 let nf = NumberFormatter() nf.numberStyle = .currency let d = Double(0.99 + 42.00) let n = NSNumber(value: d) nf.string(from: n) // $42.99 import GLKit for x in stride(from:0, through:360, by:90) { String(format:"%3d degrees = %.2f radians", x, GLKMathDegreesToRadians(Float(x))) |> p } 0 degrees = 0.00 radians 90 degrees = 1.57 radians 180 degrees = 3.14 radians 270 degrees = 4.71 radians 360 degrees = 6.28 radians //-------------------------------- swiftui import SwiftUI // ContentView.swift struct ContentView: View { var body: some View { let _ = "DEBUG body" |> p VStack { Text("Hello SwiftUI") Button("Test") { "DEBUG button tap" |> p } } .padding() .frame(maxWidth: CGFloat.infinity, maxHeight: .infinity) .onAppear() { "DEBUG onAppear()" |> p } } } // App.swift @main struct xApp: App { var body: some Scene { WindowGroup { let _ = logProcessInfo() ContentView() } } } func logProcessInfo() { let pi = ProcessInfo.processInfo """ process info id: \(pi.processIdentifier) name: \(pi.processName) args: \(pi.arguments) cmd: ps aux \(pi.processIdentifier) """ |> log } // output process info id: 4200 name: x args: ["/Users/a/Library/Developer/Xcode/DerivedData/*/Build/Products/Debug/x.app/Contents/MacOS/x", "-NSDocumentRevisionsDebugMode", "YES"] cmd: ps aux 4200 //-------------------------------- binary String(42, radix: 2) // 101010 print int to binary let b1: UInt = 0b1 // 1 0x1 let b63: UInt = 0b1000000000000000000000000000000000000000000000000000000000000000 // 9223372036854775808 0x8000000000000000 let ba: UInt = 0b1111111111111111111111111111111111111111111111111111111111111111 // 18446744073709551615 0xffffffffffffffff ba.nonzeroBitCount // 64 0b0.leadingZeroBitCount // 64 0b0.trailingZeroBitCount // 64 b1.leadingZeroBitCount // 63 b1.trailingZeroBitCount // 0 0b1111.leadingZeroBitCount // 60 0b1000.leadingZeroBitCount // 60 0b1000.trailingZeroBitCount // 3 0b1100.trailingZeroBitCount // 2 0b1110.trailingZeroBitCount // 1 0b11000.trailingZeroBitCount // 3 // c bit funcs // 1-based, max input: Int64 // flsll(0b1100) // 4 find last bit set most sig ffsll(0b1100) // 3 find first bit set least sig let a = 0b0100000 // 32 100000 0x20 let b = 0b1000000 // 64 1000000 0x40 let ab = a | b // 96 1100000 0x60 var b = 0b1 b |= (1 << 3) // 9 1001 0x9 let b: UInt = (1 << 63) // 9223372036854775808 //-------------------------------- hex 0xa // 10 0x2a // 42 String(42, radix: 16) // print int to hex let h: UInt = 0xffffffffffffffff // 18446744073709551615 dec hex 0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 A 11 B 12 C 13 D 14 E 15 F //-------------------------------- int range min max Int8 -128 127 Int16 -32768 32767 Int32 -2147483648 2147483647 Int64 -9223372036854775808 9223372036854775807 // Int UInt8 0 255 UInt16 0 65535 UInt32 0 4294967295 UInt64 0 18446744073709551615 // UInt //-------------------------------- scope do { var a = 0 } ({ var a = 0 })() //-------------------------------- closures let c = { } let c = { [weak self] in self?.foo() } let c: () -> Void = { } // () -> () let c: (() -> Void) = { } let c: (String) -> Void = { s in } //-------------------------------- pipe operator infix operator |> : LogicalConjunctionPrecedence public func |> <T, U>(lhs: T, rhs: (T) -> U) -> U { rhs(lhs) } // generic pipe print wrapper // "foo" |> p // #function |> p // [0, 7] |> p // [0, 7] // Array<Int>(0...3) |> p // [0, 1, 2, 3] // func p<T>(_ data: T) { Swift.print(data) } // generic pipe log // func log<T>(_ data: T) { "\(Date().formatDateTimeMilliseconds()): \(data)" |> p } //-------------------------------- xcode: show errors and warnings on build only disable "show live issues" > prefs > gen > show live issues dismiss errors and warnings > prod > clear all issues // alt no unused warn func // param must be initd // // a |> nuwarn // (a, b) |> nuwarn // closure |> nuwarn // func nuwarn<T>(_ x: T) { // } //-------------------------------- enums enum WildThing { case dragon, rous, troll } let wildThing = WildThing.rous wildThing |> p // rous app.WildThing.rous //-------------------------------- console input args // app "foo, bar" 42 if CommandLine.argc == 3 { // arg count // CommandLine.arguments[0] // app path let arg1: String = CommandLine.arguments[1] let arg2: String = CommandLine.arguments[2] } //-------------------------------- app delegate // AppKit // NSApp = NSApplication.shared() let appDelegate = NSApp.delegate let wc = NSApp.mainWindow?.windowController // UIKit let appDelegate = UIApplication.shared.delegate AppDelegate * appDelegate = (AppDelegate *)UIApplication.sharedApplication.delegate; // OC // WatchKit let appDelegate = WKExtension.shared().delegate //-------------------------------- user defaults, Swift 3 let keyPeerID = "peerID" saveData(peerIDFromUser(), key:keyPeerID) func peerIDFromUser() -> MCPeerID { let peerID = "foo" // UIDevice.current.name return MCPeerID(displayName: peerID) } func saveData(_ data: Any, key: String) { let archive: Data = NSKeyedArchiver.archivedData(withRootObject: data) UserDefaults.standard.set(archive, forKey: key) } func getPeerID() -> MCPeerID? { var peerID: MCPeerID? let data: Data? = UserDefaults.standard.data(forKey: keyPeerID) if data != nil { peerID = NSKeyedUnarchiver.unarchiveObject(with: data!) as? MCPeerID } return peerID } let defaults = UserDefaults.standard.dictionaryRepresentation() for key in defaults.keys.sorted(by: { $0.caseInsensitiveCompare($1) == .orderedAscending }) { print("\(key) = \(defaults[key]!)") } // OC NSDictionary * defaults = [NSUserDefaults.standardUserDefaults dictionaryRepresentation]; for (NSString * key in [defaults.allKeys sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)]) { NSLog(@"%@ = %@", key, [defaults valueForKey:key]); } //-------------------------------- file and directory comparisons, Swift 3 let file1 = "~/temp.txt" let file2 = "/volumes/vol1/temp.txt" let result = fileContentsEqual(file1, file2) if result == .equal { print("equal\n") } else if result == .notEqual { print("NOT equal\n") } // alt file path build let file1Base = "/volumes/vol1/" let file2Base = "/volumes/vol2/" let root = "temp/" let file = "foo" let file1 = "\(file1Base)\(root)\(file)" let file2 = "\(file2Base)\(root)\(file)" enum FileContentsEqualResult { case equal, notEqual, error } func filesExist(_ filePath1: String, _ filePath2: String) -> Bool { let file1Exists = FileManager().fileExists(atPath: filePath1) let file2Exists = FileManager().fileExists(atPath: filePath2) if !file1Exists { print("error, file 1 does not exist: \(filePath1)") } if !file2Exists { print("error, file 2 does not exist: \(filePath2)") } return file1Exists && file2Exists } func fileContentsEqual(_ filePath1: String, _ filePath2: String) -> FileContentsEqualResult { print("comparing: \(filePath1)\n \(filePath2)\n") guard filesExist(filePath1, filePath2) else { return .error } guard filePath1.lowercased() != filePath2.lowercased() else { print("error, file paths are the same"); return .error } return FileManager().contentsEqual(atPath: filePath1, andPath: filePath2) ? .equal : .notEqual } //-------------------------------- enumerate directories import Foundation let dir: URL = URL(fileURLWithPath: "/users/sn/documents", isDirectory: true) let dirEnumerator: FileManager.DirectoryEnumerator = FileManager().enumerator(at: dir, includingPropertiesForKeys: [], options: [], errorHandler: { (file: URL, error: Error) -> Bool in print("error: \(error) at file: \(file.path)") return true })! let df = DateFormatter() df.dateFormat = "yyyy-MM-dd HH:mm:ss" func date(_ date: Date) -> String { return df.string(from: date) } func fileSize(_ file: URL) -> String { let attrs: [FileAttributeKey : Any] = try! FileManager().attributesOfItem(atPath: file.path) let sizeAttr: Any = attrs[FileAttributeKey.size] let size = (sizeAttr as! NSNumber) return ByteCountFormatter.string(fromByteCount: size.int64Value, countStyle: ByteCountFormatter.CountStyle.file) } let fileResourceKeys: Set<URLResourceKey> = [.contentModificationDateKey, .isDirectoryKey, .isPackageKey, .isRegularFileKey] for (index, object) in dirEnumerator.enumerated() { let file = object as! URL do { let resource: URLResourceValues = try file.resourceValues(forKeys: fileResourceKeys) if resource.isRegularFile! { print("\(index). \(file.path), size: \(fileSize(file)), mod: \(date(resource.contentModificationDate!))") } else if resource.isDirectory! { print("\(index). dir: \(file)") } else if resource.isPackage! { print("\(index). package: \(file)") } else { print("\(index). unknown file type: \(file)") } } catch { print(error) } } //-------------------------------- tax rate solver, Swift 3 - solve tax rate from subtotal and tax solve total from subtotal and tax rate solve subtotal from total and tax rate import Foundation protocol Singleton: class { static var sharedInstance: Self { get } } final class Formatter: Singleton { static let sharedInstance = Formatter() // calls init private let nfDollar = NumberFormatter() private let nfDollarApproximate = NumberFormatter() private let nfDecimal = NumberFormatter() private init() { // runs once nfDollar.numberStyle = .currency nfDollar.minimumFractionDigits = 2 nfDollar.maximumFractionDigits = 2 nfDollarApproximate.numberStyle = .currency nfDollarApproximate.maximumFractionDigits = 4 nfDecimal.numberStyle = .decimal nfDecimal.maximumFractionDigits = 8 } // format dollar static func dollar(_ val: Decimal) -> String { let sval = String(describing: val) // convert decimal to double via string let dval = Double(sval)! let nval = NSNumber(value: dval) return sharedInstance.nfDollar.string(from: nval)! } // one-liner version of dollar() static func dollarApproximate(_ val: Decimal) -> String { return sharedInstance.nfDollarApproximate.string(from: NSNumber(value: Double(String(describing: val))!))! } static func decimal(_ val: Decimal) -> String { return sharedInstance.nfDecimal.string(from: NSNumber(value: Double(String(describing: val))!))! } } extension String { func leftPadding(toLength: Int, withPad character: Character) -> String { let newLength = self.characters.count if newLength < toLength { return String(repeatElement(character, count: toLength - newLength)) + self } else { return self.substring(from: index(self.startIndex, offsetBy: newLength - toLength)) } } } enum TaxCalculation { case none, solveTaxRateFromSubTotalAndTax, solveTotalFromSubTotalAndTaxRate, solveSubTotalFromTotalAndTaxRate } struct TaxRateSolver { let subTotalDollar: Decimal let taxRatePercent: Decimal let taxDollar: Decimal let total: Decimal let calc: TaxCalculation init() { self.calc = .none self.subTotalDollar = 0 self.taxDollar = 0 self.taxRatePercent = 0 self.total = 0 } init(subTotalDollar: Decimal, taxDollar: Decimal) { calc = .solveTaxRateFromSubTotalAndTax self.subTotalDollar = subTotalDollar self.taxDollar = taxDollar self.taxRatePercent = self.taxDollar / self.subTotalDollar // solve, approximate self.total = self.subTotalDollar + self.taxDollar } init(subTotalDollar: Decimal, taxRatePercent: Decimal) { calc = .solveTotalFromSubTotalAndTaxRate self.subTotalDollar = subTotalDollar self.taxRatePercent = taxRatePercent self.taxDollar = self.subTotalDollar * self.taxRatePercent // solve total via tax, approximate self.total = self.subTotalDollar + self.taxDollar // solve, approximate } init(totalDollar: Decimal, taxRatePercent: Decimal) { calc = .solveSubTotalFromTotalAndTaxRate self.total = totalDollar self.taxRatePercent = taxRatePercent self.subTotalDollar = self.total / (1.0 + self.taxRatePercent) // solve, approximate self.taxDollar = self.total - self.subTotalDollar // solve, approximate } } extension TaxRateSolver: CustomStringConvertible, CustomDebugStringConvertible { public var description: String { // calc right-align formatting using total-dollar length func formattedLength() -> Int { let nstr = Formatter.dollar(total) return nstr.characters.count + 1 } func formatDollar(_ val: Decimal) -> String { return Formatter.dollar(val).leftPadding(toLength: formattedLength(), withPad: " ") } let solveSubTotal: String = calc == .solveSubTotalFromTotalAndTaxRate ? "= \(Formatter.dollarApproximate(subTotalDollar))" : "" let solveTaxRate: String = "\(calc == .solveTaxRateFromSubTotalAndTax ? "= " : " ")\(Formatter.decimal(taxRatePercent))%" let solveTotal: String = calc == .solveTotalFromSubTotalAndTaxRate ? "= \(Formatter.dollarApproximate(total))" : "" return "sub: \(formatDollar(subTotalDollar)) " + "\(solveSubTotal)\n" + "tax: \(formatDollar(taxDollar)) " + "\(solveTaxRate)\n" + "total: \(formatDollar(total)) " + "\(solveTotal)\n" } // print(String(reflecting: taxInfo)) public var debugDescription: String { get { return "calc: \(calc)\n" + "sub: \(subTotalDollar)\n" + "tax: \(taxDollar) \(taxRatePercent)\n" + "total: \(total)" } } } // repl wrapper funcs func taxrate( _ subTotal: Decimal, _ tax: Decimal) { print(TaxRateSolver(subTotalDollar: subTotal, taxDollar: tax)) } func total( _ subTotal: Decimal, _ taxRate: Decimal) { print(TaxRateSolver(subTotalDollar: subTotal, taxRatePercent: taxRate)) } func subtotal(_ total: Decimal, _ taxRate: Decimal) { print(TaxRateSolver(totalDollar: total, taxRatePercent: taxRate)) } // example let t = TaxRateSolver(subTotalDollar: 15.99, taxDollar: 1.40) print(t.taxRatePercent) // 0.0875547217010631644777986241400875547 print(t) // sub: $15.99 // tax: $1.40 = 0.08755472% // total: $17.39 taxrate(15.99, 1.40) // sub: $15.99 // tax: $1.40 = 0.08755472% // total: $17.39 let taxRate: Decimal = 0.0875 total(9.99, taxRate) // sub: $9.99 // tax: $0.87 0.0875% // total: $10.86 = $10.8641 subtotal(17.39, taxRate) // sub: $15.99 = $15.9908 // tax: $1.40 0.0875% // total: $17.39 // unit test import XCTest let TR: Decimal = 0.0875 // tax rate let P: Decimal = 0.01 // 1 penny let HP: Decimal = 0.005 // 1/2 penny let QP: Decimal = 0.0025 // 1/4 penny let TP: Decimal = 0.001 // 1/10 penny let CP: Decimal = 0.0001 // 1/100 penny class TaxRateSolver_1: XCTestCase { let ST: Decimal = 1 // subtotal let TX: Decimal = 0.09 // tax dollar let T: Decimal = 1.09 // total var t = TaxRateSolver() func testInput() { XCTAssertEqual(ST + TX, T) } /* sub: $1.00 tax: $0.09 = 0.09% total: $1.09 sub: 1 tax: 0.09 0.09 total: 1.09 */ func testSolveTaxRate() { t = TaxRateSolver(subTotalDollar: ST, taxDollar: TX) XCTAssertEqual(t.calc, .solveTaxRateFromSubTotalAndTax) XCTAssertEqual(t.subTotalDollar, ST) XCTAssertEqual(t.taxDollar, TX) XCTAssertEqual(t.taxRatePercent, TX) XCTAssertEqual(abs(t.taxRatePercent - TR), QP) XCTAssertEqual(t.total, T) } /* sub: $1.00 tax: $0.09 0.0875% total: $1.09 = $1.0875 sub: 1 tax: 0.0875 0.0875 total: 1.0875 */ func testSolveTotal() { t = TaxRateSolver(subTotalDollar: ST, taxRatePercent: TR) XCTAssertEqual(t.calc, .solveTotalFromSubTotalAndTaxRate) XCTAssertEqual(t.subTotalDollar, ST) XCTAssertEqual(t.taxDollar, TR) XCTAssertEqual(t.taxRatePercent, TR) XCTAssertEqual(abs(t.total - T), QP) } /* sub: $1.00 = $1.0023 tax: $0.09 0.0875% total: $1.09 sub: 1.002298850574712643678160919540229885 tax: 0.087701149425287356321839080459770115 0.0875 total: 1.09 */ func testSolveSubTotal() { t = TaxRateSolver(totalDollar: T, taxRatePercent: TR) XCTAssertEqual(t.calc, .solveSubTotalFromTotalAndTaxRate) XCTAssertLessThan(abs(t.subTotalDollar - ST), HP) XCTAssertLessThan(abs(t.taxDollar - TR), TP) XCTAssertEqual(t.taxRatePercent, TR) XCTAssertEqual(t.total, T) } override func setUp() { super.setUp() // Put setup code here. This method is called before the invocation of each test method in the class. } override func tearDown() { // Put teardown code here. This method is called after the invocation of each test method in the class. print(t) print(String(reflecting: t)) super.tearDown() } } class TaxRateSolver_70: XCTestCase { let ST: Decimal = 70 // subtotal let TX: Decimal = 6.12 // tax dollar let T: Decimal = 76.12 // total var t = TaxRateSolver() func testInput() { XCTAssertEqual(ST + TX, T) } /* sub: $70.00 tax: $6.12 = 0.08742857% total: $76.12 sub: 70 tax: 6.12 0.087428571428571428571428571428571428571 total: 76.12 */ func testSolveTaxRate() { t = TaxRateSolver(subTotalDollar: ST, taxDollar: TX) XCTAssertEqual(t.calc, .solveTaxRateFromSubTotalAndTax) XCTAssertEqual(t.subTotalDollar, ST) XCTAssertEqual(t.taxDollar, TX) XCTAssertLessThan(abs(t.taxRatePercent - TR), CP) XCTAssertEqual(t.total, T) } /* sub: $70.00 tax: $6.12 0.0875% total: $76.12 = $76.125 sub: 70 tax: 6.125 0.0875 total: 76.125 */ func testSolveTotal() { t = TaxRateSolver(subTotalDollar: ST, taxRatePercent: TR) XCTAssertEqual(t.calc, .solveTotalFromSubTotalAndTaxRate) XCTAssertEqual(t.subTotalDollar, ST) XCTAssertEqual(abs(t.taxDollar - TX), HP) XCTAssertEqual(t.taxRatePercent, TR) XCTAssertEqual(abs(t.total - T), HP) } /* sub: $70.00 = $69.9954 tax: $6.12 0.0875% total: $76.12 sub: 69.995402298850574712643678160919540229 tax: 6.124597701149425287356321839080459771 0.0875 total: 76.12 */ func testSolveSubTotal() { t = TaxRateSolver(totalDollar: T, taxRatePercent: TR) XCTAssertEqual(t.calc, .solveSubTotalFromTotalAndTaxRate) XCTAssertLessThan(abs(t.subTotalDollar - ST), HP) XCTAssertLessThan(abs(t.taxDollar - TX), HP) XCTAssertEqual(t.taxRatePercent, TR) XCTAssertEqual(t.total, T) } override func setUp() { super.setUp() // Put setup code here. This method is called before the invocation of each test method in the class. } override func tearDown() { // Put teardown code here. This method is called after the invocation of each test method in the class. print(t) print(String(reflecting: t)) super.tearDown() } } //-------------------------------- group collection values by properties using this Group By collection extension from Alejandro Martinez' blog post "GroupBy in Swift 2" http://alejandromp.com/blog/2015/6/28/group-by-swift extension CollectionType { public func groupBy(grouper: (Self.Generator.Element, Self.Generator.Element) -> Bool) -> [[Self.Generator.Element]] { var result : Array<Array<Self.Generator.Element>> = [] var previousItem : Self.Generator.Element? var group : [Self.Generator.Element] = [] for item in self { defer { previousItem = item } guard let previous = previousItem else { group.append(item) continue } if grouper(previous, item) { // item in the same group group.append(item) } else { // new group result.append(group) group = [] group.append(item) } } result.append(group) return result } } import Foundation struct Match : Equatable, Hashable, CustomStringConvertible { let id: NSUUID let row: Int let col: Int var hashValue: Int { return self.id.hashValue } init(row:Int, col: Int) { id = NSUUID() self.row = row self.col = col } var description: String { return "id: \(id.UUIDString), r: \(row), c: \(col)" } } func ==(lhs: Match, rhs: Match) -> Bool { return lhs.id == rhs.id } let m1 = Match(row: 10, col: 20) let m2 = Match(row: 100, col: 200) var data: Set<Match> = Set([m1, m2]) data.insert(Match(row: 500, col: 1)) data.insert(Match(row: 100, col: 201)) data.insert(Match(row: 1, col: 2)) data.insert(Match(row: 1, col: 42)) data.insert(Match(row: 1, col: 66)) data.insert(Match(row: 10, col: 20)) data.insert(Match(row: 42, col: 1)) data.insert(Match(row: 2, col: 42)) data.insert(Match(row: 1, col: 42)) let sortedData: [Match] = Array(data).sort { // sort by Column and Row if $0.0.col != $0.1.col { return $0.0.col < $0.1.col } else { return $0.0.row < $0.1.row } } let groupedData: [[Match]] = sortedData.groupBy { $0.col == $1.col } // group by Column print(groupedData) // 7 elements // output [ [id: 0, r: 42, c: 1, id: 1, r: 500, c: 1], [id: 2, r: 1, c: 2], [id: 3, r: 10, c: 20, id: 4, r: 10, c: 20], [id: 5, r: 1, c: 42, id: 6, r: 1, c: 42, id: 7, r: 2, c: 42], [id: 8, r: 1, c: 66], [id: 9, r: 100, c: 200], [id: A, r: 100, c: 201] ] //-------------------------------- search using filter and map functions - find records by key find records where column count >= search index get record column by index, return data rows func fooSearch() { func getData() -> [String] { // data has duplicate keys let data = "A:a1,a2,a3\n" // key : col1, col2, col3 + "B:b1,b2,b3\n" + "B:b10,b20\n" + "B:b100\n" + "C:c1\n" + "F:"; return split(data) { $0 == "\n" } } let data:[String] = getData() func search(query:(String, Int)) -> [String] { let recs:[String] = data.filter { $0.hasPrefix(query.0) } // filter - where data records match key // var cols:[[String]] = recs.map { split( split($0) { $0 == ":" }[1] ) { $0 == "," } } // [1] = "array index out of range" error if record is empty var cols:[[String]] = recs.map { split( split($0, { $0 == ":" }, maxSplit:Int.max, allowEmptySlices:true)[1] ) { $0 == "," } } cols = cols.filter { $0.count >= query.1 } // filter - where records column count >= search index var results:[String] = cols.map { $0[query.1-1] } // get column by index, return data rows return results } func searchQueries() -> [(String, Int)] // array of tuples { return [("A", 1), // key, column index ("A", 2), ("A", 3), ("A", 4), ("B", 2), ("C", 1), ("C", 2), ("F", 1)]; } for q:(String, Int) in searchQueries() { var results:[String] = search(q) for x in results { println(x) } } } // output a1 a2 a3 b2 b20 c1 //-------------------------------- get unique objects from array by name, then by max date // file: Foo.swift import Foundation class Foo { var name: String var id: Int var date: NSDate init(name:String, id:Int, date:NSDate) { self.name = name self.id = id self.date = date } } extension Foo: Comparable, Equatable { } func <(lhs:Foo, rhs:Foo) -> Bool { return lhs.date.compare(rhs.date) == NSComparisonResult.OrderedAscending } func ==(lhs:Foo, rhs:Foo) -> Bool { return lhs.name == rhs.name } // file: FooSet.swift // Set Class by Kametrixom http://stackoverflow.com/questions/24044190/how-to-create-array-of-unique-object-list-in-swift // class Set<T: Equatable> { var items:[T] = [] func add(item:T) { if !contains(items, { $0 == item }) { items.append(item) } } } // file: FooViewController.swift func debugFoo() { func getFooData() -> [Foo] { var i = 0 func getRandomFooName() -> String { return (arc4random_uniform(2) % 2) == 1 ? "Foo" : "Bar" // mod } func getRandomFooDate() -> NSDate { let secondsPerDay = 86400 let dayRange:UInt32 = 30 var randomTimeOffset = Double((Int(arc4random_uniform(dayRange)) * secondsPerDay) + (i * 1)) return NSDate().dateByAddingTimeInterval(-randomTimeOffset) } var fooData = [Foo]() for (i = 0; i < 10; i++) { let foo = Foo(name: getRandomFooName(), id: i, date: getRandomFooDate()) fooData.append(foo) } return fooData } func printFoos(a_foos: [Foo]) { func formatDate(a_date:NSDate) -> String { var df = NSDateFormatter() df.dateFormat = "MM-dd-yy HH:mm:ss" df.timeZone = NSTimeZone.localTimeZone() return df.stringFromDate(a_date) } println("\n\n----------------") for foo in a_foos { println("\(foo.name), id: \(foo.id), date: \(formatDate(foo.date))") } } func printMaxFoo(a_foos:[Foo]) { let maxFoo = maxElement(a_foos) printFoos([maxFoo]) // one element array } func printSortedFoos(a_foos:[Foo]) { let sortedFoos = sorted(a_foos) printFoos(sortedFoos) } func getSetFromArray(a_foos:[Foo]) -> Set<Foo> { var fooSet = Set<Foo>() for foo in a_foos { fooSet.add(foo) } return fooSet } let foos:[Foo] = getFooData() printFoos(foos) printSortedFoos(foos) printMaxFoo(foos) let fooSet:Set<Foo> = getSetFromArray(foos) printFoos(fooSet.items) // loop thru the foo set and filter the foos array using the set element to find the matching max foo var maxFoos = [Foo]() for foo in fooSet.items { maxFoos.append(maxElement((foos.filter({ $0 == foo })))) } printFoos(maxFoos) } // output // raw data ---------------- Bar, id: 0, date: 10-29-14 19:42:17 Foo, id: 1, date: 10-21-14 19:42:16 Foo, id: 2, date: 10-16-14 19:42:15 Foo, id: 3, date: 11-02-14 18:42:14 // standard time diff Bar, id: 4, date: 10-22-14 19:42:13 Foo, id: 5, date: 10-20-14 19:42:12 Bar, id: 6, date: 10-11-14 19:42:11 Bar, id: 7, date: 10-26-14 19:42:10 Foo, id: 8, date: 10-17-14 19:42:09 Foo, id: 9, date: 10-20-14 19:42:08 // sorted data ---------------- Bar, id: 6, date: 10-11-14 19:42:11 Foo, id: 2, date: 10-16-14 19:42:15 Foo, id: 8, date: 10-17-14 19:42:09 Foo, id: 9, date: 10-20-14 19:42:08 Foo, id: 5, date: 10-20-14 19:42:12 Foo, id: 1, date: 10-21-14 19:42:16 Bar, id: 4, date: 10-22-14 19:42:13 Bar, id: 7, date: 10-26-14 19:42:10 Bar, id: 0, date: 10-29-14 19:42:17 Foo, id: 3, date: 11-02-14 18:42:14 // max foo ---------------- Foo, id: 3, date: 11-02-14 18:42:14 // set data ---------------- Bar, id: 0, date: 10-29-14 19:42:17 Foo, id: 1, date: 10-21-14 19:42:16 // unique foo by name, then by max date ---------------- Bar, id: 0, date: 10-29-14 19:42:17 Foo, id: 3, date: 11-02-14 18:42:14 //-------------------------------- sort string array, Swift 3 let names = ["foo", "bar", "", " ", "abc"] let sortedNames = names.sorted() // names.sorted() { $0 < $1 } // names.sorted(by: <) // names.sorted(by: { $0 < $1 }) // names.sorted(by: { (s1: String, s2: String) -> Bool in return s1 < s2 }) for name in sortedNames { "'\(name)'" |> p // '' ' ' 'abc' 'bar' 'foo' } //-------------------------------- print unicode chars, Swift 4 let dog: Character = "\u{1F436}" "animal farm: \(dog) \u{1F431} \u{1F638} \u{1F414} \u{1F42E}" |> p // 🐶 🐱 😸 🐔 🐮 //-------------------------------- print number string, Swift 4 import Foundation var s = String() var ms = String() // mod string for x in 1...20 { s += "\(x)," // 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20, ms += "\(x % 10)" // 12345678901234567890 } let r: Range<String.Index> = s.startIndex..<s.index(before: s.endIndex) // range up to last char "," let ss1 = String(s[r]) // substring, subscript range to remove last char 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20 let ss2 = s[..<s.index(s.endIndex, offsetBy: -1)] // alt, substring, subscript range to remove last char w/offset 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20 //-------------------------------- uuids, Swift 3 import Foundation let uid = UUID() uid.uuidString |> p // 00000000-0000-0000-0000-000000000000 let buffSize = 16 var bytes: UnsafeMutablePointer<UInt8> = UnsafeMutablePointer.allocate(capacity: buffSize) (uid as NSUUID).getBytes(UnsafeMutablePointer(bytes)) for i in 0..<buffSize { "\(bytes[i]) " |> p // 4 98 113 71 54 71 70 187 161 54 82 147 74 33 226 33 } bytes.deallocate(capacity: buffSize) //-------------------------------- string to bytes, Swift 3 import Foundation // String -> bytes let s = "swift" let b: [(UInt8)] = Array(s.utf8) // [UInt8](s.utf8) b |> p // [115, 119, 105, 102, 116] // NSString -> NSData -> bytes let d: Data = ("swift" as NSString).data(using: String.Encoding.utf8.rawValue)! var b1: [(UInt8)] = Array(repeating: 0, count: d.count) // 7 values, [0..6] = 0 // [UInt8](repeating: 0, count: d.count) (d as NSData).getBytes(&b1, length: d.count) // copy data bytes into buffer b1 |> p // [115, 119, 105, 102, 116] //-------------------------------- decode url, Swift 3 func decodeURL(_ url: String) -> String { return (url as NSString).replacingOccurrences(of: "%20", with: " ") } decodeURL("foo%20bar") //-------------------------------- download url - to test non-secure http endpoints disable App Transport Security restrictions for all network connections info.plist: NSAppTransportSecurity > NSAllowsArbitraryLoads : YES func syncDownloadString(_ url: URL) { do { let html = try NSString(contentsOf: url, encoding: String.Encoding.utf8.rawValue) as String logHtml(html, url) } catch { error |> log } } func asyncDownloadString(_ url: URL) { DispatchQueue.global().async { do { let html = try NSString(contentsOf: url, encoding: String.Encoding.utf8.rawValue) as String DispatchQueue.main.async(execute: DispatchWorkItem { self.logHtml(html, url) }) } catch { error |> log } } } func sessionDataTask(_ url: URL) { let task = URLSession.shared.dataTask(with: url, completionHandler: { (data: Data?, urlResponse: URLResponse?, error: Error?) -> Void in if let error = error { error |> p } else { let statusCode: Int = (urlResponse as! HTTPURLResponse).statusCode let html = String(data: data!, encoding: String.Encoding.utf8)! as String DispatchQueue.main.async(execute: DispatchWorkItem { self.logHtml(html, url, statusCode) }) } }) task.resume() } // async await // Button("Download") { Task { let url = URL(string: "https://localhost:8443")! try await asyncAwaitDownload(url) } } func asyncAwaitDownload(_ url: URL) async throws { let result: (data: Data, response: URLResponse) = try await URLSession.shared.data(from: url) let statusCode: Int = (result.response as! HTTPURLResponse).statusCode let html = String(data: result.data, encoding: String.Encoding.utf8)! as String self.logHtml(html, url, statusCode) } func logHtml(_ html: String, _ url: URL, _ statusCode: Int? = nil) { """ url: \(url.absoluteString) sc: \(String(describing: statusCode)) \(html) """ |> log } //-------------------------------- average, Swift 3 let add: (Int, Int) -> Int = { x, y in x + y } func average(_ numbers: Int...) -> Float { let sum = numbers.reduce(0, add); return Float(sum) / Float(numbers.count) } func average(_ numbers: Int...) -> Float { let sum = numbers.reduce(0) { (x: Int, y: Int) -> Int in x + y }; return Float(sum) / Float(numbers.count) } func average(_ numbers: Int...) -> Float { let sum = numbers.reduce(0) { $0 + $1 }; return Float(sum) / Float(numbers.count) } func average(_ numbers: Int...) -> Float { let sum = numbers.reduce(0, +); return Float(sum) / Float(numbers.count) } average(1, 2, 3) // 2.0 let a: Array<Int> = [1, 2, 3] apply(average, a) // 2.0 // apply func from Matt Bradley's blog post "The Missing Apply Function in Swift" // https://www.drivenbycode.com/the-missing-apply-function-in-swift func apply<T, U>(_ fn: (T...) -> U, _ args: [T]) -> U { typealias FunctionType = ([T]) -> U return unsafeBitCast(fn, to: FunctionType.self)(args) } //-------------------------------- unique words, count Swift 1.2 has Set data struct let input = "FOO bar bar foo 123" let data = input.componentsSeparatedByCharactersInSet(NSCharacterSet.whitespaceCharacterSet()) let uniqueWords = Set(data) // string set elements are case-sensitive println("count: \(uniqueWords.count)") // 4 for word in uniqueWords { println("\(word)") } // output foo 123 bar FOO //-------------------------------- swift algorithms https://github.com/apple/swift-algorithms - Combinations // Collection extension // // func combinations(ofCount k: Int) -> CombinationsSequence<Self> // // Returns a sequence of all the different combinations of a collection’s elements // with each combination in the order of the original collection. import Algorithms let fenCastling = ["k", "q", "K", "Q"] let combos: Array<String> = combos(fenCastling) combos.flatMap { combo in combo } .sorted(by: { c1, c2 in if (c1.count == c2.count) { // 1. sort by string length return c1 < c2 // 2. sort by string, asc < } else { return c1.count > c2.count // sort by string length, desc > } }) .forEach { combo in combo |> p // } } func combos(_ data: Array<String>) -> Array<String> { let range: ClosedRange<Int> = (1...data.count) let sortAndJoin: (Array<String>) -> String = { cd in // map closure let combo: String = cd.sorted().joined() return combo } // algorithm // let combos: Array<String> = data.combinations(ofCount: range).map(sortAndJoin) return combos } // output // combos() sort and print k KQkq q KQk K KQq Q Kkq kq Qkq Kk KQ Qk Kk Kq Kq Qq Qk KQ Qq Kkq kq Qkq K KQk Q KQq k KQkq q //-------------------------------- crypto import CryptoKit let s = "foo" let data: Data = s.data(using: String.Encoding.utf8)! let hash: SHA512.Digest = CryptoKit.SHA512.hash(data: data) "input '\(s)'" |> p // 'foo' "output \(hash.description)" |> p // SHA512 digest: f7fbba6e0636f890e56fbbf3283e524c6fa3204ae298382d624741d0dc6638326e282c41be5e4254d8820772c5518a2c5a8c0c7f7eda19594a7eb539453e1ed7 SHA512.Digest.byteCount |> p // 64 let hashdata: Array<String> = hash.map { (byte: UInt8) in let hex = String(format: "%02x", byte) // byte to hex return hex } hashdata.joined() |> p hashdata.joined(separator: " ") |> p // f7fbba6e0636f890e56fbbf3283e524c6fa3204ae298382d624741d0dc6638326e282c41be5e4254d8820772c5518a2c5a8c0c7f7eda19594a7eb539453e1ed7 // f7 fb ba 6e 06 36 f8 90 e5 6f bb f3 28 3e 52 4c 6f a3 20 4a e2 98 38 2d 62 47 41 d0 dc 66 38 32 6e 28 2c 41 be 5e 42 54 d8 82 07 72 c5 51 8a 2c 5a 8c 0c 7f 7e da 19 59 4a 7e b5 39 45 3e 1e d7 //-------------------------------- c apis https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithCAPIs.html // file: foo.h #ifndef fooApp__foo__ #define fooApp__foo__ const char foo[] = "foo"; const char * bar = "bar"; char * getCheezburger(); #endif // file: foo.c #include "foo.h" char * getCheezburger() { return "cheezburger!"; } // file: fooApp-bridging-header.h // import target public headers to expose to Swift #import "foo.h" // file: main.swift import Foundation // foo char array is tuple (Int8, Int8, Int8, Int8) println(foo) // (102, 111, 111, 0) // iterate func by dankogai http://stackoverflow.com/users/1588574/dankogai // http://stackoverflow.com/questions/24299045/any-way-to-iterate-a-tuple-in-swift func iterate<C,R>(t:C, block:(Any)->R) { let mirror = reflect(t) for i in 0..<mirror.count { block(mirror[i].1.value) } } // iterate tuple (foo char array) iterate(foo) { print( String(UnicodeScalar((("\($0)") as String).toInt()!)) ) // foo } println() var _bar:UnsafePointer<Int8> = bar // bar char pointer println(_bar) // 0x00000000 println(String.fromCString(_bar)!) // bar var cb:UnsafeMutablePointer<CChar> = getCheezburger() // typealias CChar = Int8 println(String.fromCString(cb)!) // cheezburger! - OpenGL - OpenGL GPU Compatibility iOS OpenGL ES https://developer.apple.com/library/content/documentation/DeviceInformation/Reference/iOSDeviceCompatibility/HardwareGPUInformation/HardwareGPUInformation.html macOS OpenGL/CL https://support.apple.com/en-us/HT202823 - EAGL Version // Swift 4 var major: UInt32 = 0 var minor: UInt32 = 0 EAGLGetVersion(&major, &minor) print("EAGL version: \(major).\(minor)") // 1.0 // OC uint major, minor; EAGLGetVersion(&major, &minor); NSLog(@"EAGL version: %d.%d", major, minor); // 1.0 - Upgrade iOS Game to OpenGL ES 3 1. Update vertex shader program 2. Update fragment shader program 3. Init the EAGLContext with OpenGL ES version 3 // 1. file: shader.vsh #version 300 es in vec4 position; in vec3 normal; out lowp vec4 colorVarying; uniform mat4 modelViewProjectionMatrix; uniform mat3 normalMatrix; void main() { vec3 eyeNormal = normalize(normalMatrix * normal); vec3 lightPosition = vec3(0.0, 0.0, 1.0); vec4 diffuseColor = vec4(0.4, 0.4, 1.0, 1.0); float nDotVP = max(0.0, dot(eyeNormal, normalize(lightPosition))); colorVarying = diffuseColor * nDotVP; gl_Position = modelViewProjectionMatrix * position; } // 2. file: shader.fsh #version 300 es in lowp vec4 colorVarying; out lowp vec4 fragmentColor; void main() { fragmentColor = colorVarying; } // 3. file: gameViewController.swift/m self.context = EAGLContext(api: .openGLES3) // Swift 4 self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]; // OC - OpenGL Version Info Also available in debug capture GPU frame context info print("vendor: \(String(cString: unsafeBitCast(glGetString(GLenum(GL_VENDOR)), to: UnsafePointer<CChar>.self)))") // Swift 4 printf("vendor: %s", (char *)glGetString(GL_VENDOR)); // OC GL_VENDOR Apple Inc. GL_RENDERER Apple A8 GPU iphone 6/6+ Apple A8X GPU ipad air 2 Apple A9 GPU iphone 6s/6s+ Apple A9X GPU ipad pro Apple A10 GPU iphone 7/7+ Apple A10X GPU ipad pro 2017 Apple A11 GPU iphone 8/8+/X GL_VERSION OpenGL ES 3.0 Apple A8X GPU - 75.11.5 OpenGL ES 3.0 Apple A9 GPU - 75.11.5 OpenGL ES 3.0 Apple A9X GPU - 75.11.5 OpenGL ES 3.0 Metal - 33 A9X/A10 iOS 10 OpenGL ES 3.0 Metal - 52.1.2 A10X/A11 iOS 11 GL_SHADING_LANGUAGE_VERSION OpenGL ES GLSL ES 3.00 - OpenGL ES 3 Extensions Also available in debug capture GPU frame context info #import <OpenGLES/ES3/gl.h> int count = 0; glGetIntegerv(GL_NUM_EXTENSIONS, &count); NSLog(@"count: %d", count); // 19 NSMutableArray * extensions = [NSMutableArray array]; for (int i = 0; i < count; i++) { char * ext = (char *)glGetStringi(GL_EXTENSIONS, i); [extensions addObject: [NSString stringWithUTF8String:ext]]; } for (NSString * ext in [extensions sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)]) { NSLog(@"%@", ext); } // output GL_APPLE_clip_distance GL_APPLE_color_buffer_packed_float GL_APPLE_copy_texture_levels GL_APPLE_rgb_422 GL_APPLE_texture_format_BGRA8888 GL_EXT_color_buffer_half_float GL_EXT_debug_label GL_EXT_debug_marker GL_EXT_pvrtc_sRGB GL_EXT_read_format_bgra GL_EXT_separate_shader_objects GL_EXT_shader_framebuffer_fetch GL_EXT_shader_texture_lod GL_EXT_shadow_samplers GL_EXT_texture_filter_anisotropic GL_IMG_read_format GL_IMG_texture_compression_pvrtc GL_KHR_texture_compression_astc_ldr GL_OES_standard_derivatives - OpenGL Active Uniforms GLchar * name; GLint nameBufSize; GLsizei nameLength; GLint count, loc, size; GLenum type; glGetProgramiv(_program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &nameBufSize); name = (GLchar *)malloc(nameBufSize); glGetProgramiv(_program, GL_ACTIVE_UNIFORMS, &count); printf("uniform count: %d\n", count); for (int i = 0; i < count; i++) { glGetActiveUniform(_program, i, nameBufSize, &nameLength, &size, &type, name); loc = glGetUniformLocation(_program, name); printf("%d. loc: %d, name: %s, type: 0x%02x/%d, size: %d, len: %d\n", (i + 1), loc, name, type, type, size, nameLength); } free(name); // output uniform count: 2 1. loc: 0, name: modelViewProjectionMatrix, type: 0x8b5c/35676, size: 1, len: 25 // type GL_FLOAT_MAT4 (GLKMatrix4) 2. loc: 4, name: normalMatrix, type: 0x8b5b/35675, size: 1, len: 12 // GL_FLOAT_MAT3 (GLKMatrix3) - Sprite Kit - Scene Frame Animation Loop Methods https://developer.apple.com/library/content/documentation/GraphicsAnimation/Conceptual/SpriteKit_PG/Actions/Actions.html - SKScene - SKSceneDelegate protocol update: update:forScene: (NSTimeInterval)currentTime (SKScene *)scene didEvaluateActions didEvaluateActionsForScene: didSimulatePhysics didSimulatePhysicsForScene: didApplyConstraints didApplyConstraintsForScene: didFinishUpdate didFinishUpdateForScene: - scene frame processing update: // execute actions and animations didEvaluateActions // simulate physics didSimulatePhysics // apply constraints didApplyConstraints didFinishUpdate // render and display updated content - SKPhysicsContactDelegate Protocol Implement to receive messages when 2 physics bodies begin or end contact -(void)didBeginContact:(SKPhysicsContact *)contact { NSLog(@"a: %@", contact.bodyA); NSLog(@"b: %@", contact.bodyB); } - SKPhysicsBody -allContactedBodies Use to check if any nodes of interest are in contact -(void)didSimulatePhysics { NSArray * nodes = [currentNode.physicsBody allContactedBodies]; if (nodes.count > 1) // if nodes count > 1 then 3 or more nodes are in contact { for (SKNode * n in nodes) { // } } } - Debug Frame Updates Every n Seconds -(void)update:(NSTimeInterval)currentTime { void (^debugFrame)(float) = ^(float interval) { static NSTimeInterval lastUpdateTime = 0; if (lastUpdateTime == 0) { lastUpdateTime = currentTime; } NSTimeInterval delta = (currentTime - lastUpdateTime); if (delta == 0 || delta >= interval) { lastUpdateTime = currentTime; // ... } }; debugFrame(0.5f); } // NSDate * debugFrameTimer; // ivar -(void)didSimulatePhysics { void (^debugFrame)(float) = ^(float interval) { if ((debugFrameTimer == nil) || (debugFrameTimer.timeIntervalSinceNow <= -interval)) { debugFrameTimer = [NSDate date]; // ... } }; debugFrame(0.5f); } - Load SKTexture with Size NSString * filePath = [[NSBundle mainBundle] pathForResource:@"foo" ofType:@"png" NSImage * image = [[NSImage alloc] initWithContentsOfFile:filePath]; NSData * data = [image TIFFRepresentation]; NSImageRep * rep = [image representations][0]; CGSize size = CGSizeMake(rep.pixelsWide, rep.pixelsHigh); // actual image dimensions SKTexture * texture = [SKTexture textureWithData:data size:size]; SKSpriteNode * sprite = [SKSpriteNode spriteNodeWithTexture:texture]; - TV Noise Shader with SKShader (OpenGL ES 2) -(void)touchesBegan: (NSSet *)touches withEvent: (UIEvent *)event { CGPoint location = [[touches anyObject] locationInNode: self]; SKShader * shader = [SKShader shaderWithFileNamed: @"noise.fsh"]; SKUniform * u_red = [SKUniform uniformWithName: @"u_red" float: 0.20f]; [shader addUniform: u_red]; SKShapeNode * node = [SKShapeNode shapeNodeWithRectOfSize: CGSizeMake(400, 300)]; node.position = location; node.fillShader = shader; [self addChild:node]; } // fragment shader file: noise.fsh // void main() { highp float a = 12.9898; highp float b = 78.233; highp float c = 43758.5453; highp float dt = dot(v_tex_coord.xy, vec2(a, b)); highp float sn = (dt * u_time); highp float n = fract(sin(sn) * c); // gl_FragColor = vec4(n, n, n, 1.0); gl_FragColor = vec4(u_red, n, n, 1.0); } - Scene Kit - Node Transformation Properties constraints NSArray * non-animatable eulerAngles SCNVector3 orientation as roll, yaw, and pitch angles orientation SCNQuaternion pivot SCNMatrix4 pivot point for position, rotation, and scale position SCNVector3 rotation SCNVector4 orientation as a rotation angle about an axis scale SCNVector3 transform SCNMatrix4 worldTransform SCNMatrix4 read-only, non-animatable - Scene Frame Animation Loop Methods https://developer.apple.com/reference/scenekit/scnscenerendererdelegate - SCNSceneRendererDelegate protocol renderer:updateAtTime: (id<SCNSceneRenderer>)aRenderer (NSTimeInterval)time renderer:didApplyAnimationsAtTime: renderer:didSimulatePhysicsAtTime: renderer:willRenderScene:atTime: (SCNScene *)scene renderer:didRenderScene:atTime: - scene frame processing renderer:updateAtTime: // execute actions and animations renderer:didApplyAnimationsAtTime: // simulate physics renderer:didSimulatePhysicsAtTime: // apply constraints renderer:willRenderScene:atTime: // render scene renderer:didRenderScene:atTime: - Determining Viewable Nodes // SCNSceneRenderer protocol if ([aRenderer isNodeInsideFrustum:node withPointOfView:cameraNode]) { NSLog(@"node visible"); } else { NSLog(@"..."); } - Modify Node-Copy Geometry and Material Copied nodes share attributes, so copy geometry and material before mutating SCNNode * debugNode = [node copy]; debugNode.geometry = [node.geometry copy]; debugNode.geometry.firstMaterial = [node.geometry.firstMaterial copy]; debugNode.geometry.firstMaterial.diffuse.contents = [UIColor redColor]; ((SCNPlane *)debugNode.geometry).widthSegmentCount = 10; // add detail debugNode.position = SCNVector3Make(2.5, 0, 0); // offset [scene.rootNode addChildNode:debugNode]; - Bananas Scene Kit Demo: Use a SCNLookAtConstraint to have monkeys look at player // add head node property to SkinnedCharacter interface, monkey heads will look at player head @property (nonatomic) SCNNode * head; // set head nodes in char create, names are defined in dae file self.playerCharacter = [[PlayerCharacter alloc] initWithNode:characterRootNode]; self.playerCharacter.head = [self.playerCharacter childNodeWithName:@"Bip001_Head" recursively:YES]; MonkeyCharacter * monkey = [[MonkeyCharacter alloc] initWithNode:monkeyRootNode]; monkey.head = [monkey childNodeWithName:@"Bone_Head" recursively:YES]; // in monkey update frame, set look-at-constraint based on distance and interact range const CGFloat interactRange = 1050; CGFloat distanceToCharacter = GLKVector3Distance(SCNVector3ToGLKVector3(playerCharacter.position), position); self.head.constraints = (distanceToCharacter <= interactRange) ? @[ [SCNLookAtConstraint lookAtConstraintWithTarget:playerCharacter.head] ] : nil; - Bananas Scene Kit Demo: Clone explorer w/o animations PlayerCharacter * clone = [self.playerCharacter clone]; clone.position = SCNVector3Make(clone.position.x + 100, clone.position.y, clone.position.z); clone.geometry = [self.playerCharacter.geometry copy]; clone.mainSkeleton = [self.playerCharacter.mainSkeleton copy]; [clone enumerateChildNodesUsingBlock:^(SCNNode * child, BOOL * stop) { if (child.skinner) { [child.skinner.skeleton removeAllAnimations]; *stop = YES; } }]; /* alt SCNNode * n = [clone childNodeWithName:@"explorer" recursively:YES]; [n.skinner.skeleton removeAllAnimations]; */ [self.rootNode addChildNode:clone]; - Hit Test // SCNSceneRenderer protocol // (NSArray *)hitTest:(CGPoint)thePoint options:(NSDictionary *)options -(void)tap:(UIGestureRecognizer *)gestureRecognize { SCNView * view = (SCNView *)self.view; CGPoint tp = [gestureRecognize locationInView:view]; NSArray * hitTestResults = [view hitTest:tp options:nil]; [hitTestResults enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL * stop) { SCNHitTestResult * result = (SCNHitTestResult *)obj; SCNNode * node = result.node; printNode(node); }]; } - JavaScript Bridge, JavaScript Core Framework #import <JavaScriptCore/JavaScript.h> #import <JavaScriptCore/JSContext.h> #import <SceneKit/SCNJavascript.h> // setup light node SCNNode * light = [SCNNode node]; light.light = [SCNLight light]; light.light.type = SCNLightTypeOmni; light.light.color = [UIColor redColor]; light.position = SCNVector3Make(10, 10, 10); [scene.rootNode addChildNode:light]; // control light node via javascript JSContext * ctx = [[JSContext alloc] initWithVirtualMachine:[JSVirtualMachine new]]; SCNExportJavaScriptModule(ctx); ctx.globalObject[@"jsLight"] = light; // use evaluateScript to print, debug, and modify values, returns the last value generated by script // get current light type JSValue * jval = [ctx evaluateScript:@"jsLight.light.type"]; NSLog(@"%@", jval); // omni // set light type to ambient and color white NSString * js = @"jsLight.light.type = 'ambient'; \ jsLight.light.color = SCNColor.color(1, 1, 1, 1);"; jval = [ctx evaluateScript:js]; NSLog(@"%@", jval); // UIDeviceRGBColorSpace 1 1 1 1 - Double-Precision Literal Defines usr/include/math.h e M_E 2.71 828182845904523536028747135266250 log2(e) M_LOG2E 1.44 269504088896340735992468100189214 log10(e) M_LOG10E 0.43 4294481903251827651128918916605082 loge(2) M_LN2 0.69 3147180559945309417232121458176568 loge(10) M_LN10 2.30 258509299404568401799145468436421 pi M_PI 3.14 159265358979323846264338327950288 pi/2 M_PI_2 1.57 079632679489661923132169163975144 pi/4 M_PI_4 0.78 5398163397448309615660845819875721 1/pi M_1_PI 0.31 8309886183790671537767526745028724 2/pi M_2_PI 0.63 6619772367581343075535053490057448 2/sqrt(pi) M_2_SQRTPI 1.12 837916709551257389615890312154517 sqrt(2) M_SQRT2 1.41 421356237309504880168872420969808 1/sqrt(2) M_SQRT1_2 0.70 7106781186547524400844362104849039 - Core Foundation typedef const CF_BRIDGED_TYPE(id) void * CFTypeRef; typedef const struct CF_BRIDGED_TYPE(NSString) __CFString * CFStringRef; typedef struct CF_BRIDGED_MUTABLE_TYPE(NSMutableString) __CFString * CFMutableStringRef; toll-free bridging transfer of ownership __bridge CF <-> F transfer a pointer between F and CF none __bridge_transfer CF -> F moves a CF pointer to an F pointer transfers ownership to ARC __bridge_retained F -> CF casts an F pointer to a CF pointer transfers ownership to caller, must call CFRelease() __bridge_transfer CFBridgingRelease() CF -> F __bridge_retained CFBridgingRetain() F -> CF (__bridge T) // CF <-> F (__bridge void *) // CF <-> F id CFBridgingRelease(CFTypeRef X) return (__bridge_transfer id)X // CF -> F CFTypeRef CFBridgingRetain(id X) return (__bridge_retained CFTypeRef)X // F -> CF CFShow(CFSTR("foo")); CFUUIDRef uid = CFUUIDCreate(kCFAllocatorDefault); CFShow(uid); // <CFUUID 0x00000000> 7E67C7A8-7DB3-4B80-AD0B-6C7E6E15A858 CFRelease(uid); - CoreGraphics/CGBase.h typedef float CGFloat; // 32-bit typedef double CGFloat; // 64-bit - CoreGraphics/CGGeometry.h struct CGPoint { CGFloat x, y; } struct CGRect { CGPoint origin; CGSize size; } struct CGSize { CGFloat width, height; } struct CGVector { CGFloat dx, dy; } - CoreGraphics/CGAffineTransform.h struct CGAffineTransform { CGFloat a, b, c, d, tx, ty; } a b 0 c d 0 tx ty 1 CGAffineTransformIdentity - CoreMotion/CMAttitude.h struct CMRotationMatrix { double m11, m12, m13; m21, m22, m23; m31, m32, m33; } // q.x*i + q.y*j + q.z*k + q.w struct CMQuaternion { double x, y, z, w; } - CoreMotion/CMDeviceMotion.h CMDeviceMotion: CMLogItem - CoreMotion/CMMotionManager.h CMMotionManager - GameController/GCController.h GCController - GameController/GCMotion.h GCMotion // 3-axis rotation rate data struct GCRotationRate { double x, y, z; } // q.x*i + q.y*j + q.z*k + q.w struct GCQuaternion { double x, y, z, w; } - GLKit/GLKMathTypes.h union GLKMatrix2 { struct { float m00, m01; m10, m11; } float m2[2][2]; float m[4]; } union GLKMatrix3 { struct { float m00, m01, m02; m10, m11, m12; m20, m21, m22; } float m[9]; } union GLKMatrix4 { struct { float m00, m01, m02, m03; m10, m11, m12, m13; m20, m21, m22, m23; m30, m31, m32, m33; } float m[16]; } union GLKVector2 { struct { float x, y; } float v[2]; } union GLKVector3 { struct { float x, y, z; } float v[3]; } union GLKVector4 { struct { float x, y, z, w; } float v[4]; } union GLKQuaternion { struct { GLKVector3 v; float s; // scalar component } struct { float x, y, z, w; } float q[4]; } - GLKit/GLKMatrix3.h GLKMatrix3Identity - GLKit/GLKMatrix4.h GLKMatrix4Identity - GLKit/GLKMatrixStack.h struct _GLKMatrixStack * GLKMatrixStackRef // 4x4 matrices - QuartzCore/CATransform3D.h struct CATransform3D { CGFloat m11, m12, m13, m14; m21, m22, m23, m24; m31, m32, m33, m34; m41, m42, m43, m44; } CATransform3DIdentity - SceneKit/SceneKitTypes.h struct SCNMatrix4 { float m11, m12, m13, m14; m21, m22, m23, m24; m31, m32, m33, m34; m41, m42, m43, m44; } SCNMatrix4Identity struct SCNVector3 { float x, y, z; } struct SCNVector4 { float x, y, z, w; } // scene kit uses unit quaternions for node orientation, where quaternion components satisfy the equation: x*x + y*y + z*z + w*w == 1 SCNVector4 SCNQuaternion - SIMD/matrix_types.h matrix_float2x2 matrix_float3x3 matrix_float4x4 - SIMD/matrix.h matrix_identity_float3x3 matrix_identity_float4x4 {{ ([0] = 1, [1] = 0, [2] = 0, [3] = 0) ([0] = 0, [1] = 1, [2] = 0, [3] = 0) ([0] = 0, [1] = 0, [2] = 1, [3] = 0) ([0] = 0, [1] = 0, [2] = 0, [3] = 1) }} - SIMD/vector_types.h vector_float2 vector_float3 vector_float4 vector_int2 vector_int3 vector_int4 - GLKit, Scene Kit, SIMD Conversions - SceneKit/SceneKit_simd.h scene kit to simd SCNMatrix4ToMat4 SCNMatrix4FromMat4 SCNVector3ToFloat3 SCNVector4ToFloat4 SCNVector3FromFloat3 SCNVector4FromFloat4 - SceneKit/SceneKitTypes.h scene kit to glkit SCNMatrix4FromGLKMatrix4 SCNMatrix4ToGLKMatrix4 SCNVector3FromGLKVector3 SCNVector3ToGLKVector3 SCNVector4FromGLKVector4 SCNVector4ToGLKVector4 - GLKit/GLKMathUtils.h GLKMathDegreesToRadians GLKMathRadiansToDegrees NSStringFromGLKMatrix2 NSStringFromGLKMatrix3 NSStringFromGLKMatrix4 NSStringFromGLKVector2 NSStringFromGLKVector3 NSStringFromGLKVector4 NSStringFromGLKQuaternion - OC //-------------------------------- typedef NS_ENUM(NSInteger, FooType) { None, Foo, Bar }; //-------------------------------- debug // file: main.m NSLog(@"app start, in %s %d %s %s", __FILE__, __LINE__, __PRETTY_FUNCTION__, __FUNCTION__); // /users/db/app/app/main.m 15 int main(int, char **) main NSString * appDir = (NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSAllDomainsMask, YES)[0]) // /var/mobile/containers/data/application/<uid>/documents .stringByDeletingLastPathComponent; // /var/mobile/containers/data/application/<uid> // /users/db/library/developer/coresimulator/devices/<uid>/data/containers/data/application/<uid> NSLog(@"app dir: %@", appDir); NSLog(@"app bundle id: %@", [NSBundle mainBundle].bundleIdentifier); // com.expressionSoftware.app //-------------------------------- blocks http://goshdarnblocksyntax.com // void (^b)(void) = ^(void) { // void (^b)(void) = ^() { // void (^b)() = ^() { // void (^b)() = ^{ // typedef void (^dispatch_block_t)(void); dispatch_block_t b = ^{ NSLog(@"1"); }; b(); // void (^b)(NSString *) = ^(NSString * s) { typedef void (^b_t)(NSString *); b_t b = ^(NSString * s) { NSLog(@"%@", s); }; b(@"2"); // int (^b)() = ^{ typedef int (^b_t)(); b_t b = ^{ return 3; }; NSLog(@"%d", b()); // NSString * (^b)(NSString *) = ^(NSString * s) { typedef NSString * (^b_t)(NSString *); b_t b = ^(NSString * s) { return s; }; NSLog(@"%@", b(@"4")); //-------------------------------- download url info.plist NSAppTransportSecurity > NSAllowsArbitraryLoads : YES -(void)syncDownloadString:(NSURL *)url { NSError * error; NSString * html = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error]; if (error) { [self logError:error]; } else { [self showHtml:html url:url statusCode:0]; } } -(void)asyncDownloadString:(NSURL *)url { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){ NSError * error; NSString * html = [NSString stringWithContentsOfURL:url.absoluteURL encoding:NSUTF8StringEncoding error:&error]; if (error) { [self logError:error]; } else { dispatch_async(dispatch_get_main_queue(), ^(void){ [self showHtml:html url:url statusCode:0]; }); } }); } -(void)sessionDataTask:(NSURL *)url { NSURLSessionDataTask * task = [NSURLSession.sharedSession dataTaskWithURL:url completionHandler:^(NSData * data, NSURLResponse * response, NSError * error) { if (error) { [self logError: error]; } else { NSInteger statusCode = ((NSHTTPURLResponse *)response).statusCode; NSString * html = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; dispatch_async(dispatch_get_main_queue(), ^(void){ [self showHtml:html url:url statusCode:statusCode]; }); } }]; [task resume]; } -(void)logError:(NSError *)error { NSLog(@"error: %@", error); // sort dictionary keys w/NSString -(NSComparisonResult)caseInsensitiveCompare:(NSString *)aString for (NSString * key in [error.userInfo.allKeys sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)]) { NSLog(@"%@ = %@", key, error.userInfo[key]); } } -(void)showHtml:(NSString *)html url:(NSURL *)url statusCode:(NSInteger)statusCode { NSString * status = statusCode > 0 ? [NSString stringWithFormat:@"\nstatus: %ld", (long)statusCode] : @""; self.textView.string = [NSString stringWithFormat:@"url: %@%@\n\n%@", url, status, html]; } //-------------------------------- save to webservice // file: ESWebService.h @import Foundation; @interface ESWebService: NSObject<NSURLSessionDelegate> -(instancetype)initWithURL: (NSString *)aURL; -(void)save: (NSData *)aData; @end // file: ESWebService.m #import "ESWebService.h" @implementation ESWebService { NSURLSession * session; NSURL * url; } -(instancetype)initWithURL: (NSString *)aURL { if (self = [super init]) { session = [NSURLSession sessionWithConfiguration: NSURLSessionConfiguration.ephemeralSessionConfiguration delegate: self delegateQueue: NSOperationQueue.mainQueue]; url = [NSURL URLWithString: aURL]; } return self; } -(void)save: (NSData *)aData { NSMutableURLRequest * req = [NSMutableURLRequest requestWithURL: url]; [req setValue: @"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"]; [req setValue: @"ios app" forHTTPHeaderField:@"User-Agent"]; req.HTTPMethod = @"POST"; req.HTTPBody = aData; NSURLSessionUploadTask * task = [session uploadTaskWithRequest: req fromData: aData completionHandler: ^(NSData * data, NSURLResponse * response, NSError * error) { if (error) { NSLog(@"error: %@", error); } else { NSInteger statusCode = ((NSHTTPURLResponse *)response).statusCode; NSLog(@"status: %ld", (long)statusCode); } }]; [task resume]; } // NSURLSessionTaskDelegate protocol (for self signed ssl cert) // -(void)URLSession: (NSURLSession *)session task: (NSURLSessionTask *)task didReceiveChallenge: (NSURLAuthenticationChallenge *)challenge completionHandler: (void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * credential))completionHandler { completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust: challenge.protectionSpace.serverTrust]); } @end // file: ViewController.m webservice = [[ESWebService alloc] initWithURL: @"https://localhost:8443/foo/"]; -(void)saveData { NSString * data = [NSString stringWithFormat: @"userName=%@&password=%@", username, password]; data = [crypto encrypt: data]; // encrypt data w/public key, decrypt on web server backend process w/private key data = [data stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]; [webservice save: [data dataUsingEncoding: NSUTF8StringEncoding]]; } //-------------------------------- regex search and replace -(NSString *)regexSearchAndReplaceString:(NSString *)string pattern:(NSString *)pattern template:(NSString *)template { NSError * error; NSRegularExpression * regex = [NSRegularExpression regularExpressionWithPattern:pattern options:NSRegularExpressionCaseInsensitive error:&error]; return error ? [NSString stringWithFormat:@"regex error: %@", error] : [regex stringByReplacingMatchesInString:string options:0 range:NSMakeRange(0, string.length) withTemplate:template]; } // block NSString * (^regexSearchAndReplace)(NSString *, NSString *, NSString *) = ^(NSString * string, NSString * pattern, NSString * template) [self regexSearchAndReplaceString:content pattern:@"\r" template:@""] regexSearchAndReplace(content, @"\r", @"") //-------------------------------- sound completion @implementation ViewController { SystemSoundID boomSound; } // typedef void (*AudioServicesSystemSoundCompletionProc)(SystemSoundID ssID, void * clientData); void soundCompletion(SystemSoundID ssid, void * clientData) { AudioServicesRemoveSystemSoundCompletion(ssid); AudioServicesDisposeSystemSoundID(ssid); UIButton * b = (__bridge UIButton *)clientData; [b setImage:[UIImage imageNamed:@"default.png"] forState:UIControlStateNormal]; } -(void)playSound { NSString * soundFilePath = [NSBundle.mainBundle pathForResource:@"boom.caf" ofType:nil]; NSURL * url = [NSURL fileURLWithPath:soundFilePath]; AudioServicesCreateSystemSoundID((__bridge CFURLRef)url, &boomSound); // AudioServicesAddSystemSoundCompletion(boomSound, nil, nil, soundCompletion, nil); AudioServicesAddSystemSoundCompletion(boomSound, nil, nil, soundCompletion, (__bridge void *)self.button); AudioServicesPlaySystemSound(boomSound); [self.button setImage:[UIImage imageNamed:@"blowdUp.png"] forState:UIControlStateNormal]; } @end //-------------------------------- macOS toggle bold font w/menu add IBAction method, then control-drag Bold menu item to First Responder icon, connect -(IBAction)toggleBoldText:(id)sender { NSLog(@"sender: %@", sender); // <NSMenuItem: 0x00000000 Bold> NSTextView * tv = (NSTextView *)self.textView.documentView; NSMutableAttributedString * attrStr = tv.textStorage; NSRange rangePointer = NSMakeRange(0, 0); NSDictionary * indexAttributes = [attrStr attributesAtIndex:tv.selectedRange.location effectiveRange:&rangePointer]; NSLog(@"index attributes: %@", indexAttributes); NSLog(@"range pointer: %@", NSStringFromRange(rangePointer)); NSDictionary * selectedFontAttributes = [attrStr fontAttributesInRange:tv.selectedRange]; NSLog(@"selected font attributes: %@", selectedFontAttributes); NSFont * selectedFont = selectedFontAttributes[@"NSFont"]; NSLog(@"font: %@", selectedFont); NSFontTraitMask indexFontTraits = [[NSFontManager sharedFontManager] traitsOfFont:selectedFont]; if (indexFontTraits & NSBoldFontMask) { // [[NSFontManager sharedFontManager] removeFontTrait:sender]; [attrStr applyFontTraits:NSUnboldFontMask range:tv.selectedRange]; } else { // [[NSFontManager sharedFontManager] addFontTrait:sender]; [attrStr applyFontTraits:NSBoldFontMask range:tv.selectedRange]; } } //-------------------------------- macOS key view loop custom tab order -(void)viewDidLoad { [super viewDidLoad]; [self.tf3 setNextKeyView:self.tf2]; [self.tf2 setNextKeyView:self.tf1]; [self.tf1 setNextKeyView:self.tf3]; } -(void)viewWillAppear { [self.view.window setInitialFirstResponder:self.tf3]; [super viewWillAppear]; } //-------------------------------- byte array to base64 string const char * chars = @"foo bar".UTF8String; // char chars[] = { 102, 111, 111, 32, 98, 97, 114 }; int len = sizeof(chars)/sizeof(char); NSData * data = [NSData dataWithBytes:chars length:len]; NSString * base64String = [data base64EncodedStringWithOptions:0]; NSLog(@"%@", base64String); // Zm9vIGJhcgA= //-------------------------------- uuids NSUUID * uid = [NSUUID UUID]; printf("%s\n", uid.UUIDString.UTF8String); // 00000000-0000-0000-0000-000000000000 uuid_t bytes; // typedef unsigned char __darwin_uuid_t[16]; [uid getUUIDBytes:bytes]; for (int i = 0; i< sizeof(bytes)/sizeof(unsigned char); i++) { printf("%d ", bytes[i]); // 11 36 135 226 133 84 71 138 162 69 163 112 214 72 247 218 } //-------------------------------- null dictionary values NSString * k1 = @"k1"; NSString * k2 = @"k2"; NSString * k3 = @"k3"; NSString * k4 = @"k4"; NSString * s1 = @"foo"; NSString * s2 = nil ?: (NSString*)[NSNull null]; NSString * s3 = @""; NSString * s4 = @"bar"; NSDictionary * d = @{ k1 : s1, k2 : s2, k3 : s3, k4 : s4 }; for (NSString * k in d) { if (![[d objectForKey:k] isEqual:[NSNull null]]) { NSLog(@"%@ = %@", k, [d objectForKey:k]); } } // output k4 = bar k3 = k1 = foo //-------------------------------- collection operators, flatten nested arrays https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/KeyValueCoding/Articles/CollectionOperators.html [@[@[@1, @2, @3]] valueForKeyPath: @"@unionOfArrays.self"] // 1, 2, 3 [@[@[@1, @2], @[@3]] valueForKeyPath: @"@unionOfArrays.self"] [@[@[@1, @2, @3, @4]] valueForKeyPath: @"@unionOfArrays.self"] // 1, 2, 3, 4 [@[@[@1, @2, @[@3][0]], @[@4]] valueForKeyPath: @"@unionOfArrays.self"] [@[@[@1, @2], @[@3], @[@4]] valueForKeyPath: @"@unionOfArrays.self"] [@[@[@1, @2, @3], @[@4]] valueForKeyPath: @"@unionOfArrays.self"] //-------------------------------- OC++ GLKit #import <GLKit/GLKit.h> #include <iostream> #include <vector> using std::vector; using std::cout; using std::endl; vector<float> data = { 0, 0 , 0.0, 0.0 , 0, 0 , 1, 1 , -1, -1 , 1, 1 , 1.5, 10.5 , -2, -20 }; GLKVector2 v1; GLKVector2 v2; GLKVector2 v3; for (int i = 0, n = 1; i < data.size(); i++, n++) { v1 = GLKVector2Make(data[i], data[i + 1]); v2 = GLKVector2Make(data[i + 2], data[i + 3]); v3 = GLKVector2Add(v1, v2); // printf("%d. %s + %s = %s\n", n, CStringFromGLKVector2(v1), CStringFromGLKVector2(v2), CStringFromGLKVector2(v3)); cout << n << ". " << CStringFromGLKVector2(v1) << " + " << CStringFromGLKVector2(v2) << " = " << CStringFromGLKVector2(v3) << endl; i += 3; } // output 1. {0, 0} + {0, 0} = {0, 0} 2. {0, 0} + {1, 1} = {1, 1} 3. {-1, -1} + {1, 1} = {0, 0} 4. {1.5, 10.5} + {-2, -20} = {-0.5, -9.5} //-------------------------------- glkit utils const char * CStringFromGLKVector2(GLKVector2 aVector) { return NSStringFromGLKVector2(aVector).UTF8String; } const char * CStringFromGLKVector3(GLKVector3 aVector) { return NSStringFromGLKVector3(aVector).UTF8String; } // OC++ #ifdef __cplusplus NSString * NSStringFromGLKVector2(GLKVector2 aVector) { return [NSString stringWithFormat:@"{%g, %g}", aVector.x, aVector.y]; } NSString * NSStringFromGLKVector3(GLKVector3 aVector) { return [NSString stringWithFormat:@"{%g, %g, %g}", aVector.x, aVector.y, aVector.z]; } #endif //-------------------------------- glkit matrix rows columns /* GLKMatrix4Identity {{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}} */ float matrixData[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; GLKMatrix4 m = GLKMatrix4MakeWithArray(matrixData); /* {{0, 1, 2, 3}, {4, 5, 6, 7}, {8, 9, 10, 11}, {12, 13, 14, 15}} */ NSMutableString * mRows = [NSMutableString string]; NSMutableString * mCols = [NSMutableString string]; for (int i = 0; i <= 3; i++) { [mRows appendFormat:@"row %i: %@\n", i, NSStringFromGLKVector4(GLKMatrix4GetRow(m, i))]; [mCols appendFormat:@"col %i: %@\n", i, NSStringFromGLKVector4(GLKMatrix4GetColumn(m, i))]; } NSLog(@"%@\n%@", mRows, mCols); // output row 0: {0, 4, 8, 12} row 1: {1, 5, 9, 13} row 2: {2, 6, 10, 14} row 3: {3, 7, 11, 15} col 0: {0, 1, 2, 3} col 1: {4, 5, 6, 7} col 2: {8, 9, 10, 11} col 3: {12, 13, 14, 15} //-------------------------------- glkit multiplication GLKMatrix4 m = GLKMatrix4MakeTranslation(2, 4, 6); /* {{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {2, 4, 6, 1}} */ GLKVector4 v = GLKVector4Make(1, 2, 3, 1); // x, y, z, w // matrix * vector = vector GLKVector4 mv = GLKMatrix4MultiplyVector4(m, v); // {3, 6, 9, 1} // matrix * matrix = matrix GLKMatrix4 mm = GLKMatrix4Multiply(m, m); /* {{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {4, 8, 12, 1}} */ // matrix * vector formula by index m v + v 0 1 2 3 * 0 = m0v0 m4v1 m8v2 m12v3 = x 4 5 6 7 1 m1v0 m5v1 m9v2 m13v3 y 8 9 10 11 2 m2v0 m6v1 m10v2 m14v3 z 12 13 14 15 3 m3v0 m7v1 m11v2 m15v3 w // alpha m v + v abcd * x = ax ey iz mw = x efgh y bx fy jz nw y ijkl z cx gy kz ow z mnop w dx hy lz pw w // data m v + v 1000 * 1 = 1002 = 3 x 0100 2 0204 6 y 0010 3 0036 9 z 2461 1 0001 1 w //-------------------------------- simd version simd/matrix.h matrix_float4x4 m = { .columns[0] = {1, 0, 0, 0}, .columns[1] = {0, 1, 0, 0}, .columns[2] = {0, 0, 1, 0}, .columns[3] = {2, 4, 6, 1} }; /* {{ ([0] = 1, [1] = 0, [2] = 0, [3] = 0) ([0] = 0, [1] = 1, [2] = 0, [3] = 0) ([0] = 0, [1] = 0, [2] = 1, [3] = 0) ([0] = 2, [1] = 4, [2] = 6, [3] = 1) }} */ vector_float4 v = {1, 2, 3, 1}; // ([0] = 1, [1] = 2, [2] = 3, [3] = 1) vector_float4 mv = matrix_multiply(m, v); // ([0] = 3, [1] = 6, [2] = 9, [3] = 1) matrix_float4x4 mm = matrix_multiply(m, m); /* {{ ([0] = 1, [1] = 0, [2] = 0, [3] = 0) ([0] = 0, [1] = 1, [2] = 0, [3] = 0) ([0] = 0, [1] = 0, [2] = 1, [3] = 0) ([0] = 4, [1] = 8, [2] = 12, [3] = 1) }} */ //-------------------------------- blender python version import mathutils m = Matrix.Translation((2, 4, 6, 1)) # Matrix(((1.0, 0.0, 0.0, 2.0), # (0.0, 1.0, 0.0, 4.0), # (0.0, 0.0, 1.0, 6.0), # (0.0, 0.0, 0.0, 1.0))) v = Vector((1, 2, 3, 1)) # Vector((1.0, 2.0, 3.0, 1.0)) mv = m * v # Vector((3.0, 6.0, 9.0, 1.0)) mm = m * m # Matrix(((1.0, 0.0, 0.0, 4.0), # (0.0, 1.0, 0.0, 8.0), # (0.0, 0.0, 1.0, 12.0), # (0.0, 0.0, 0.0, 1.0))) //-------------------------------- r version m4Identity = diag(4) # [,1] [,2] [,3] [,4] # [1,] 1 0 0 0 # [2,] 0 1 0 0 # [3,] 0 0 1 0 # [4,] 0 0 0 1 m = m4Identity m[4,] = c(2, 4, 6, 1) # [,1] [,2] [,3] [,4] # [1,] 1 0 0 0 # [2,] 0 1 0 0 # [3,] 0 0 1 0 # [4,] 2 4 6 1 v = c(1, 2, 3, 1) # [1] 1 2 3 1 #vector * matrix = matrix mv = v %*% m # [,1] [,2] [,3] [,4] # [1,] 3 6 9 1 #matrix * matrix = matrix mm = m %*% m # [,1] [,2] [,3] [,4] # [1,] 1 0 0 0 # [2,] 0 1 0 0 # [3,] 0 0 1 0 # [4,] 4 8 12 1 mm[4,] # [1] 4 8 12 1 as.vector(mm) # [1] 1 0 0 4 0 1 0 8 0 0 1 12 0 0 0 1 as.vector(t(mm)) # [1] 1 0 0 0 0 1 0 0 0 0 1 0 4 8 12 1 //-------------------------------- simd/geometry.h geometry functions (float only) // simd vs glkit vector_float2 v = {skNode.physicsBody.velocity.dx, skNode.physicsBody.velocity.dy}; vector_length(v) // float GLKVector2 v = GLKVector2Make(skNode.physicsBody.velocity.dx, skNode.physicsBody.velocity.dy); GLKVector2Length(v) // float vector_length_squared((vector_float2){skNode.physicsBody.velocity.dx, skNode.physicsBody.velocity.dy})) //-------------------------------- image with color, alt to CIConstantColorGenerator core image filter CIImage * getImageWithColor(CIColor * aColor, CGSize aSize) { CIImage * image = [CIImage imageWithColor:aColor]; return [image imageByCroppingToRect:CGRectMake(0, 0, aSize.width, aSize.height)]; } //-------------------------------- assets library, image picker controller in iOS 8, the photos framework replaces the assets library, but does not provide an image picker control // conform to protocol @interface ViewController: UIViewController<UIImagePickerControllerDelegate> -(void)pickImage { UIImagePickerController * ipc = [UIImagePickerController new]; ipc.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; ipc.delegate = (id)self; [self presentViewController:ipc animated:YES completion:nil]; } // UIImagePickerControllerDelegate -(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { NSLog(@"image picked, info: %@", info); /* UIImagePickerControllerMediaType = "public.image"; UIImagePickerControllerOriginalImage = "<UIImage:0x00000000> size {2048, 1536} orientation 0 scale 1.000000"; UIImagePickerControllerReferenceURL = "assets-library://asset/asset.JPG?id=5570A4B1-7E8B-434B-8BDB-F02538FD0B76&ext=JPG"; */ [self dismissViewControllerAnimated:YES completion:nil]; // dismiss picker // NSURL * url = [info objectForKey:UIImagePickerControllerReferenceURL]; UIImage * image = [info objectForKey:UIImagePickerControllerOriginalImage]; [self imageView].image = image; } // UIImagePickerControllerDelegate -(void)imagePickerControllerDidCancel:(UIImagePickerController *)picker { NSLog(@"image picker cancelled"); [self dismissViewControllerAnimated:YES completion:nil]; // dismiss picker } //-------------------------------- load image using photos framework or assets library framework #import <AssetsLibrary/AssetsLibrary.h> // ALAssetsLibrary @import Photos; NSString * photoAssetLocalID = @"A3277D72-1FE7-4627-8534-D5B78C3A57D2/L0/001"; [self loadPhotoAssetFromLocalID:photoAssetLocalID]; NSURL * alPhotoAssetURL = [NSURL URLWithString:@"assets-library://asset/asset.JPG?id=5570A4B1-7E8B-434B-8BDB-F02538FD0B76&ext=JPG"]; [self loadPhotoAssetFromURL:alPhotoAssetURL]; -(void)loadPhotoAssetFromLocalID:(NSString *)aPhotoAssetLocalID { NSArray * photoAssetLocalID = @[aPhotoAssetLocalID]; PHFetchResult * fetchResult = [PHAsset fetchAssetsWithLocalIdentifiers:photoAssetLocalID options:nil]; if (fetchResult.count > 0) { PHAsset * photoAsset = fetchResult[0]; [self loadPhotoAsset:photoAsset]; } } -(void)loadPhotoAssetFromURL:(NSURL *)aALPhotoAssetURL { NSArray * alPhotoAssetURL = @[aALPhotoAssetURL]; PHFetchResult * fetchResult = [PHAsset fetchAssetsWithALAssetURLs:alPhotoAssetURL options:nil]; if (fetchResult.count > 0) { PHAsset * photoAsset = fetchResult[0]; [self loadPhotoAsset:photoAsset]; } } -(void)loadPhotoAsset:(PHAsset *)aPhotoAsset { CGSize imageSize = CGSizeMake(aPhotoAsset.pixelWidth, aPhotoAsset.pixelHeight); NSLog(@"image size w x h: %@\n", NSStringFromCGSize(imageSize)); // image size w x h: {1536, 2048} PHImageRequestOptions * options = [PHImageRequestOptions new]; options.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat; PHImageManager * im = [PHImageManager defaultManager]; [im requestImageForAsset:aPhotoAsset targetSize:imageSize contentMode:PHImageContentModeDefault options:options resultHandler:^(UIImage * result, NSDictionary * info) { // result handler executes on main thread NSLog(@"info: %@", info); /* PHImageFileOrientationKey = 0; PHImageFileSandboxExtensionTokenKey = "..."; PHImageFileURLKey = "file:///var/mobile/Media/PhotoStreamsData/1234567890/100APPLE/IMG_0999.JPG"; // photo stream // file:///var/mobile/Media/DCIM/100APPLE/IMG_0123.JPG // camera roll PHImageFileUTIKey = "public.jpeg"; PHImageResultDeliveredImageFormatKey = 9999; PHImageResultIsDegradedKey = 0; PHImageResultIsInCloudKey = 0; PHImageResultIsPlaceholderKey = 0; PHImageResultRequestIDKey = 1; PHImageResultWantedImageFormatKey = 4037; */ [self loadImage:result]; }]; } // on iOS 8, using assets library to load photo with url works only with camera roll, not photo stream -(void)loadPhotoFromCameraRollWithAssetsLibrary:(NSURL *)aALPhotoAssetURL { ALAssetsLibrary * assetLib = [ALAssetsLibrary new]; // assetForURL is async [assetLib assetForURL:aALPhotoAssetURL resultBlock:^(ALAsset * asset) { ALAssetRepresentation * assetRep = [asset defaultRepresentation]; CGImageRef cgImage = [assetRep fullResolutionImage]; if (cgImage != nil) // image exists { UIImage * image = [[UIImage alloc] initWithCGImage:cgImage]; [self loadImage:image]; } else // image does not exist, deleted, etc { NSLog(@"image not available"); } } failureBlock:^(NSError * error) { NSLog(@"image access denied"); }]; } -(void)loadImage:(UIImage *)aImage { [self imageView].image = aImage; } //-------------------------------- keychain security #import <Security/Security.h> NSString * service; // foo service NSString * key; // foo key -(void)setupKeychain { NSString * secret = @"foo"; NSData * secretData = [secret dataUsingEncoding:NSUTF8StringEncoding]; NSDictionary * keychainAttributes = @{ (__bridge id)kSecClass : (__bridge id)kSecClassGenericPassword, (__bridge id)kSecAttrService : service, (__bridge id)kSecAttrAccount : key, (__bridge id)kSecValueData : secretData }; [self addKeychainItem:keychainAttributes]; } -(void)getKeychainItem { NSDictionary * keychainSearchAttributes = @{(__bridge id)kSecClass : (__bridge id)kSecClassGenericPassword, (__bridge id)kSecMatchLimit : (__bridge id)kSecMatchLimitOne, (__bridge id)kSecAttrService : service, (__bridge id)kSecAttrAccount : key, (__bridge id)kSecReturnData : (__bridge id)kCFBooleanTrue }; NSString * result = [self searchKeychain:keychainSearchAttributes]; NSLog(@"result: %@", result); } -(BOOL)addKeychainItem:(NSDictionary *)aKeychainAttributes { BOOL result; CFTypeRef kcResultCode = NULL; OSStatus status = SecItemAdd((__bridge CFDictionaryRef)aKeychainAttributes, &kcResultCode); if (status == errSecSuccess) { NSLog(@"keychain item added"); result = YES; } else { NSLog(@"keychain error code: %ld", (long)status); } return result; } -(NSString *)searchKeychain:(NSDictionary *)aKeychainSearchAttributes { NSString * result; CFTypeRef kcResultCode = NULL; OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)aKeychainSearchAttributes, &kcResultCode); if (status == errSecSuccess) { NSLog(@"keychain item found"); NSData * resultData = (__bridge NSData *)kcResultCode; result = [[NSString alloc] initWithData:resultData encoding:NSUTF8StringEncoding]; } else { NSLog(@"keychain error code: %ld", (long)status); } return result; } //-------------------------------- qr code core image filter https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9328Iiz8qja5QkuI0H8pCuM2vablWRRQD1X3L47K9oR-BaObCFZTx5T5N0mTmKRgBushr9728yIJ7WdlU8JV7x8HO87v1ebx4_nP-iBZVUKTkbteFip1ldXhWEdtZMbKCAjQ47cjq67AY/s0/fooQRCode.png @property (nonatomic, weak) IBOutlet UIImageView * qrCode; NSString * message = @"foo"; NSData * messageData = [message dataUsingEncoding:NSISOLatin1StringEncoding]; self.qrCode.image = [self getQRCode:messageData size:self.qrCode.bounds.size]; -(UIImage *)getQRCode:(NSData *)aMessageData size:(CGSize)aSize { CIFilter * filter = [CIFilter filterWithName:@"CIQRCodeGenerator" withInputParameters:@{ @"inputMessage" : aMessageData, @"inputCorrectionLevel" : @"L" }]; CIImage * filterOutputImage = [filter outputImage]; UIImage * qrCode = [UIImage imageWithCIImage:filterOutputImage scale:1.0f orientation:UIImageOrientationUp]; UIGraphicsBeginImageContextWithOptions(aSize, YES, 1.0f); CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSetInterpolationQuality(context, kCGInterpolationNone); // 1 [qrCode drawInRect:CGRectMake(0.0f, 0.0f, aSize.width, aSize.height)]; // error: BSXPCMessage received error for message: Connection interrupted qrCode = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return qrCode; } //-------------------------------- calayer bezier path // file: ESLine.h @import UIKit; @interface ESLine: CALayer -(instancetype)initWithRect:(CGRect)aRect; @property (nonatomic) UIBezierPath * path; @property (nonatomic) UIColor * color; @end // file: ESLine.m @import UIKit; #import "ESLine.h" @implementation ESLine -(instancetype)init { if (self = [super init]) { self.contentsScale = UIScreen.mainScreen.scale; } return self; } -(instancetype)initWithRect:(CGRect)aRect { if (self = [self init]) { self.frame = aRect; } return self; } -(void)drawInContext:(CGContextRef)ctx { UIGraphicsPushContext(ctx); [_color setStroke]; [_path stroke]; UIGraphicsPopContext(); } @end // functions UIBezierPath * getLine(CGPoint, CGPoint); UIBezierPath * getHorizontalLine(CGSize); UIBezierPath * getVerticalLine(CGSize); UIBezierPath * getLine(CGPoint aStartPoint, CGPoint aEndPoint) { UIBezierPath * line = [UIBezierPath bezierPath]; [line moveToPoint:aStartPoint]; [line addLineToPoint:aEndPoint]; return line; } UIBezierPath * getHorizontalLine(CGSize aSize) { CGFloat y = aSize.height/2.0f; CGPoint startPoint = CGPointMake(0.0f, y); CGPoint endPoint = CGPointMake(aSize.width, y); UIBezierPath * line = getLine(startPoint, endPoint); return line; } UIBezierPath * getVerticalLine(CGSize aSize) { CGFloat x = aSize.width/2.0f; CGPoint startPoint = CGPointMake(x, 0.0f); CGPoint endPoint = CGPointMake(x, aSize.height); UIBezierPath * line = getLine(startPoint, endPoint); return line; } // example ESLine * line = [[ESLine alloc] initWithRect:self.view.frame]; line.color = [UIColor grayColor]; line.path = getHorizontalLine(line.frame.size); line.path.lineWidth = 2.0f; CGFloat dashes[] = {10, 8}; [line.path setLineDash:dashes count:2 phase:10]; [self.view.layer addSublayer:line]; [line setNeedsDisplay]; //-------------------------------- calayer border [self showLayerBorder:button.layer color:[UIColor grayColor]]; -(void)showLayerBorder:(CALayer *)aLayer color:(UIColor *)aColor { [aLayer setMasksToBounds:YES]; [aLayer setCornerRadius:0.0f]; [aLayer setBorderWidth:1.0f]; [aLayer setBorderColor:aColor.CGColor]; } //-------------------------------- debug autolayout constraints -(void)viewWillAppear:(BOOL)animated { [self setupConstraints]; [super viewWillAppear:animated]; } -(void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; debugAutolayoutConstraints(nodeView, @"node view"); debugAutolayoutConstraints(self.view, @"self.view"); } -(void)setupConstraints { self.view.translatesAutoresizingMaskIntoConstraints = NO; nodeView.translatesAutoresizingMaskIntoConstraints = NO; // ... } void debugAutolayoutConstraints(UIView * aView, NSString * aViewDescription) { printf("\n"); NSLog(@"Debug Autolayout Constraints"); printf("view: %s %p\n", aViewDescription.UTF8String, aView); printf("frame: %s\n", NSStringFromCGRect(aView.frame).UTF8String); printf("ambiguous layout: %d\n", aView.hasAmbiguousLayout); void (^printConstraints)(NSArray *, NSString *) = ^(NSArray * constraints, NSString * constraintsDescription) { if (constraints.count > 0) { printf("\n%s constraints (%lu)\n", constraintsDescription.UTF8String, (unsigned long)constraints.count); for (int i = 0; i < constraints.count; i++) { NSString * c = [NSString stringWithFormat:@"%@", (NSLayoutConstraint *)constraints[i]]; printf("%d. %s\n", (i + 1), c.UTF8String); } } }; printConstraints([aView constraintsAffectingLayoutForAxis:UILayoutConstraintAxisHorizontal], @"H"); printConstraints([aView constraintsAffectingLayoutForAxis:UILayoutConstraintAxisVertical], @"V"); printConstraints([aView constraints], @"All"); } //-------------------------------- autolayout with dynamic controls NSMutableArray * buttons; NSArray * buttonTitles = @[ @"Foo", @"Bar", @"Button", @"Button 4", @"Button 5", @"Button 6" ]; NSMutableDictionary * bindings; NSArray * buttonBindings = @[ @"b0", @"b1", @"b2", @"b3", @"b4", @"b5" ]; NSMutableArray * bHorizontalConstraints; NSMutableArray * bVerticalConstraints; UIView * topLayoutGuide = (UIView *)self.topLayoutGuide; NSLayoutConstraint * top = [NSLayoutConstraint constraintWithItem:headerLabel attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:topLayoutGuide attribute:NSLayoutAttributeBottom multiplier:1.0f constant:0.0f]; -(void)setupBindings { bindings = [NSMutableDictionary dictionary]; for (int i = 0; i < buttons.count; i++) { bindings[buttonBindings[i]] = buttons[i]; } } -(void)setupConstraints { self.view.translatesAutoresizingMaskIntoConstraints = NO; bHorizontalConstraints = [NSMutableArray array]; bVerticalConstraints = [NSMutableArray array]; [self setupHorizontalConstraints]; [self setupVerticalConstraints]; [self updateFont]; } -(void)setupHorizontalConstraints { BOOL port = UIApplication.sharedApplication.statusBarOrientation == UIDeviceOrientationPortrait; NSDictionary * metrics = @{ @"maxWidth" : @(self.view.frame.size.width * (port ? 0.6f : 0.25f)) }; for (int i = 0; i < buttons.count; i++) { NSString * vf = [NSString stringWithFormat:@"H:|-[%@(maxWidth)]", buttonBindings[i]]; // H:|-[b0(maxWidth)] NSArray * c = [NSLayoutConstraint constraintsWithVisualFormat:vf options:0 metrics:metrics views:bindings]; [self.view addConstraints:c]; [bHorizontalConstraints addObjectsFromArray:c]; } } -(void)setupVerticalConstraints { NSString * vf = @"V:|-yPos-[b0]-[b1(b0)]-[b2(b0)]-[b3(b0)]-[b4(b0)]-[b5(b0)]-(8@800)-|"; NSDictionary * metrics = @{ @"yPos" : @((self.view.frame.size.height * 0.33f)) }; NSArray * c = [NSLayoutConstraint constraintsWithVisualFormat:vf options:0 metrics:metrics views:bindings]; [self.view addConstraints:c]; [bVerticalConstraints addObjectsFromArray:c]; } -(void)updateViewConstraints { [self.view removeConstraints:bHorizontalConstraints]; [self.view removeConstraints:bVerticalConstraints]; [self setupHorizontalConstraints]; [self setupVerticalConstraints]; [self updateFont]; [super updateViewConstraints]; } //-------------------------------- autolayout pin views opposite horizontally -(void)setupConstraints { self.view.translatesAutoresizingMaskIntoConstraints = NO; a.translatesAutoresizingMaskIntoConstraints = NO; b.translatesAutoresizingMaskIntoConstraints = NO; NSDictionary * bindings = NSDictionaryOfVariableBindings(a, b); NSMutableArray * constraints = [NSMutableArray array]; [constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[a(40)]-(>=0)-[b(a)]-|" options:NSLayoutFormatAlignAllTop metrics:nil views:bindings]]; [constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-50-[a(20)]" options:0 metrics:nil views:bindings]]; // set B view vertical constraint == A view [constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[b(a)]" options:0 metrics:nil views:bindings]]; [self.view addConstraints:constraints]; } //-------------------------------- set mutable array property with mutable or immutable array if array param responds to addObject: then array param is mutable, no copy required [array isMemberOfClass:[NSMutableArray class]] always returns false because of param type -(void)setMutableArray: (NSArray *)array { self.mutableArray = [array respondsToSelector: @selector(addObject:)] ? array : [array mutableCopy]; } //-------------------------------- tap gesture recognizer -(void)viewDidLoad { [super viewDidLoad]; UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tap:)]; [self.view addGestureRecognizer:tap]; } -(void)tap:(UIGestureRecognizer *)sender { NSLog(@"gr state: %ld", (long)sender.state); NSLog(@"gr number of touches: %lu", (unsigned long)[sender numberOfTouches]); NSLog(@"gr location: %@", NSStringFromCGPoint([sender locationInView:sender.view])); } //-------------------------------- NSDate - (NSTimeInterval)timeIntervalSinceDate:(NSDate *)anotherDate instance param date result self anotherDate before after < 0 after before > 0 = = 0 //-------------------------------- table view data source // file: TableViewDataSource.h @import UIKit; @interface TableViewDataSource : NSObject <UITableViewDataSource> @property (nonatomic, strong) NSArray <NSString *> * data; @end // file: TableViewDataSource.m #import "TableViewDataSource.h" @implementation TableViewDataSource - (NSUInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.data.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString * cellID = @"cellID"; // matches the ID that is set in the storyboard scene table view cell UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:cellID]; cell.textLabel.text = self.data[indexPath.row]; return cell; } @end //-------------------------------- kvc kvo @interface LocationInfo : NSObject @property (nonatomic, assign) CGPoint point; @property (nonatomic, strong) NSDate * date; @property (nonatomic, strong, readonly) NSUUID * uid; @end @interface LocationViewController : NSViewController @property (nonatomic, strong) LocationInfo * locationInfo; @end @implementation ViewController - (void)mouseUp:(NSEvent *)event { NSStoryboard * storyBoard = [NSStoryboard storyboardWithName:@"Main" bundle:nil]; LocationViewController * locationVC = [storyBoard instantiateControllerWithIdentifier:@"locationVC_sbid"]; [self presentViewControllerAsModalWindow:locationVC]; } @end static void * const KVOContext = (void *)&KVOContext; @implementation LocationInfo - (instancetype)initWithPoint:(CGPoint)point { if (self == [super init]) { _point = point; _date = [NSDate date]; _uid = [NSUUID UUID]; } return self; } -(id)valueForUndefinedKey:(NSString *)key { return [NSString stringWithFormat:@"KVC getter error, undefined key: %@", key]; } - (NSString *)description { return [NSString stringWithFormat:@"x: %f, y: %f, date: %@, uid: %@", self.point.x, self.point.y, self.date, self.uid]; } @end @implementation LocationViewController - (void)viewDidLoad { [super viewDidLoad]; self.locationInfo = [[LocationInfo alloc] initWithPoint:CGPointZero]; [self addKeyValueObservers]; NSLog(@"locationInfo: %@", self.locationInfo); // kvc getter NSValue * val = [self.locationInfo valueForKey:@"point"]; CGPoint point = [val pointValue]; NSDate * date = [self.locationInfo valueForKey:@"date"]; NSUUID * uid = [self.locationInfo valueForKey:@"uid"]; NSLog(@"point: %@", NSStringFromPoint(point)); NSLog(@"date: %@", date); NSLog(@"id: %@", uid); // kvc setter point = CGPointMake(100, 200); val = [NSValue valueWithPoint:point]; [self.locationInfo setValue:val forKey:@"point"]; [self.locationInfo setValue:[NSDate date] forKey:@"date"]; [self.locationInfo setValue:[NSUUID UUID] forKey:@"uid"]; // set readonly property [self.locationInfo setValue:nil forKey:@"uid"]; NSLog(@"error val: %@", [self.locationInfo valueForKey:@"foo"]); } - (void)addKeyValueObservers { [self.locationInfo addObserver:self forKeyPath:@"point" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:KVOContext]; [self.locationInfo addObserver:self forKeyPath:@"date" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:KVOContext]; [self.locationInfo addObserver:self forKeyPath:@"uid" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:KVOContext]; } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context { NSLog(@"observeValueForKeyPath: %@, old: %@, new: %@", keyPath, change[NSKeyValueChangeOldKey], change[NSKeyValueChangeNewKey]); } - (void)mouseUp:(NSEvent *)event { self.locationInfo.point = [event locationInWindow]; self.locationInfo.date = [NSDate date]; [self.locationInfo setValue:[NSUUID UUID] forKey:@"uid"]; // set readonly property } - (void)dealloc { [self.locationInfo removeObserver:self forKeyPath:@"point" context:KVOContext]; [self.locationInfo removeObserver:self forKeyPath:@"date" context:KVOContext]; [self.locationInfo removeObserver:self forKeyPath:@"uid" context:KVOContext]; self.locationInfo = nil; } @end IB Storyboard - Bindings Inspector Value Bind To: Location // ViewController Model Key Path: self.locationInfo.point // file: main.storyboard <!--Location--> <scene sceneID="100-00-000"> <objects> <viewController id="200-00-000"> <view> <subviews> <textField> <connections> <binding destination="200-00-000" name="value" keyPath="self.locationInfo.point" id="300-00-000"/> </connections> </textField> </subviews> </view> </viewController> </objects> </scene> //-------------------------------- init -(instancetype)init { if (self = [super init]) { // ... } return self; } -(instancetype)initWithSize:(CGSize)size { if (self = [super init]) { // ... } return self; } //-------------------------------- unit test -(void)testRandomBounds { const int minLowValue = 3; const int randomUpperBound = 10; const int minIterations = 100; const int maxIterations = 100000; int min = randomUpperBound; int max = 0; int r = 0; int i = 0; for (; i < maxIterations; i++) { r = fmax(arc4random_uniform(randomUpperBound), minLowValue); min = r < min ? r : min; max = r > max ? r : max; //* if ((i >= minIterations) && (max >= (randomUpperBound - 1)) && (min <= minLowValue)) break; //*/ } // expected min 3, max 9 XCTAssert(min == minLowValue); XCTAssert(max == (randomUpperBound - 1)); printf("\nmin: %d, max: %d, iterations: %d\n\n", min, max, i); } //-------------------------------- unit test lower <= random < upper bound lower increases each iteration -(void)testRandomLowerToUpperBound { const int upperBound = 10; int i, r, lower; for (i = 0; i < upperBound; i++) { lower = i; r = arc4random_uniform(upperBound - lower) + lower; printf("%d. r: %d%s\n", i, r, (r == i) ? " ==" : ""); XCTAssert(r >= lower); if (i == (upperBound - 1)) { XCTAssert(r == i); // last r will always match index/lower } } } // output 0. r: 0 == 1. r: 5 2. r: 7 3. r: 3 == 4. r: 8 5. r: 6 6. r: 6 == 7. r: 8 8. r: 9 9. r: 9 == // last r will always match index/lower //-------------------------------- unit test typedef void (^printDate_t)(NSDate *); typedef void (^printDict_t)(id, id, BOOL *); typedef void (^printJsonData_t)(NSData *); -(void)testJsonSerialization { printDate_t printDate = ^(NSDate * aDate) { NSDateFormatter * df = [NSDateFormatter new]; df.dateFormat = @"MM-dd-yyyy HH:mm:ss.SSS"; NSLog(@"date: %@", [df stringFromDate:aDate]); }; printDict_t printDict = ^(id key, id obj, BOOL *stop) { printf("%s", [NSString stringWithFormat:@"%@ = %@\n", key, obj].UTF8String); }; printJsonData_t printJsonData = ^(NSData * aJsonData) { NSLog(@"json data:\n%@", [[NSString alloc] initWithData:aJsonData encoding:NSUTF8StringEncoding]); /* NSJSONSerialization dataWithJSONObject:options 0, 1 NSJSONWritingPrettyPrinted 0: {"panic":false,"foo":{"date":1428231364.498701,"code":"HG2G"},"answer":42,"trilogyParts":[1,2,3,4,5],"question":null} 1: { "panic" : false, "foo" : { "date" : 1428231364.498701, // 04-05-2015 04:56:04.499 "code" : "HG2G" }, "answer" : 42, "trilogyParts" : [ 1, 2, 3, 4, 5 ], "question" : null } */ }; NSDate * date = [NSDate date]; id dateVal = [NSNumber numberWithDouble:date.timeIntervalSince1970]; printDate(date); // json data source NSDictionary * inData = @{ @"answer" : @42, @"question" : [NSNull null], @"panic" : @NO, @"trilogyParts" : @[ @1, @2, @3, @4, @5 ], @"foo" : @{ @"code" : @"HG2G", @"date" : dateVal } }; NSLog(@"in data"); [inData enumerateKeysAndObjectsUsingBlock:printDict]; if ([NSJSONSerialization isValidJSONObject:inData]) { NSError * error; // serialize NSData * jsonData = [NSJSONSerialization dataWithJSONObject:inData options:0 error:&error]; if (error) { NSLog(@"json serialization error: %@", error); } else { printJsonData(jsonData); printJsonData([NSJSONSerialization dataWithJSONObject:inData options:NSJSONWritingPrettyPrinted error:NULL]); // deserialize NSDictionary * outData = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error]; if (error) { NSLog(@"json deserialization error: %@", error); } else { XCTAssert([outData isEqualToDictionary:inData]); NSLog(@"out data"); [outData enumerateKeysAndObjectsUsingBlock:printDict]; /* NSDictionary * foo = [outData objectForKey:@"foo"]; date = [NSDate dateWithTimeIntervalSince1970:[[foo objectForKey:@"date"] doubleValue]]; */ date = [NSDate dateWithTimeIntervalSince1970:[[outData valueForKeyPath:@"foo.date"] doubleValue]]; printDate(date); } } } } //-------------------------------- unit test -(void)testRegexMatchDates { NSArray * dates = @[ @"Jan 1 1201", @"Jan 01 1800", @"Jan 1 1899", @"Dec 31 1900", @"Jan 1 1901", @"Jan 24 2084", @"Jan 1 3000" ]; NSString * text = [dates componentsJoinedByString:@" ; "]; NSError * regexError; NSString * regexPattern = @"\\w{3}\\s+\\d{1,2}\\s+\\d{4}"; id regex = [NSRegularExpression regularExpressionWithPattern:regexPattern options:0 error:®exError]; if (regexError) { NSLog(@"regex error: %@", regexError); } else { NSDateFormatter * df = [NSDateFormatter new]; df.dateFormat = @"MM-dd-yyyy"; NSRange textRange = NSMakeRange(0, text.length); [regex enumerateMatchesInString:text options:0 range:textRange usingBlock:^(NSTextCheckingResult * result, NSMatchingFlags flags, BOOL * stop) { NSString * match = [text substringWithRange:result.range]; NSDate * date = [df dateFromString:match]; NSLog(@"%11s (%@)", match.UTF8String, [df stringFromDate:date]); }]; const NSUInteger matchCount = [regex numberOfMatchesInString:text options:0 range:textRange]; const NSUInteger expectedMatchCount = dates.count; XCTAssert(matchCount == expectedMatchCount, @"expected regex match count failed"); } } // output Jan 1 1201 (01-01-1201) Jan 01 1800 (01-01-1800) Jan 1 1899 (01-01-1899) Dec 31 1900 (12-31-1900) Jan 1 1901 (01-01-1901) Jan 24 2084 (01-24-2084) Jan 1 3000 (01-01-3000) - Action Methods #define IBAction void // UINibDeclarations.h // Swift 3 @IBAction func buttonTap(sender: UIButton) { print("button was tapped, sender: \(sender)") // button was tapped, sender: <UIButton: 0x00000000> } // OC - (IBAction)buttonTap:(id)sender { NSLog(@"button was tapped, sender: %@", sender); } - C Dialects //-------------------------------- Swift 3 import Foundation let now = Date() let df = DateFormatter() df.dateFormat = "MM-dd-yyyy HH:mm:ss" print("\(df.string(from:now))") // 09-01-2016 00:00:00 let a = "HAL" let x = 9000 print("Swift hello \(a) \(x).") // Swift hello HAL 9000. //-------------------------------- OC #import <Foundation/Foundation.h> int main() { @autoreleasepool { NSDate * now = [NSDate date]; NSDateFormatter * df = [NSDateFormatter new]; df.dateFormat = @"MM-dd-yyyy HH:mm:ss"; NSLog(@"%@", [df stringFromDate:now]); // 10-02-2013 00:00:00 NSString * a = @"HAL"; int x = 9000; NSLog(@"OC hello %@ %d%c", a, x, '.'); // OC hello HAL 9000. } return 0; } //-------------------------------- C++ #include <iostream> #include <ctime> int main() { time_t now = time(0); struct tm * t; t = localtime(&now); char buff[20]; strftime(buff, sizeof(buff), "%m-%d-%Y %H:%M:%S", t); // 10-02-2013 00:00:00 std::cout << buff << std::endl; const std::string a = {"HAL"}; int x = 9000; std::cout << "C++ hello " << a << " " << x << '.' << std::endl; // C++ hello HAL 9000. return 0; } //-------------------------------- C #include <stdio.h> #include <time.h> int main() { time_t now = time(0); struct tm * t; // t = gmtime(&now); // 10-02-2013 06:00:00 t = localtime(&now); // 10-02-2013 00:00:00 char buff[20]; strftime(buff, sizeof(buff), "%m-%d-%Y %H:%M:%S", t); printf("%s\n", buff); const char a[] = "HAL"; int x = 9000; printf("C hello %s %d%c\n", a, x, '.'); // C hello HAL 9000. return 0; } - C //-------------------------------- linked list // file: main.c #include "list.h" int getData(); void test(); int main() { test(); return 0; } void test() { const int nodeCount = 4; for (int i = 0; i < nodeCount; i++) { int data = getData(); struct node * n = createNodeWithData(data); appendNodeToList(n); } printList(); reverseList(); printList(); prependNodeToList(createNodeWithData(getData())); printList(); freeList(); } int getData() { static int dataValue; dataValue += 100; return dataValue; } // file: list.h struct node { struct node * next; int data; }; struct node * createNodeWithData(int); void appendNodeToList(struct node *); void prependNodeToList(struct node *); void reverseList(); void printList(); void freeList(); // file: list.c #include <stdio.h> #include <stdlib.h> #include "list.h" struct node * head; struct node * tail; struct node * createNodeWithData(int aData) { struct node * n = malloc(sizeof(struct node)); n->data = aData; n->next = NULL; return n; } void appendNodeToList(struct node * aNode) { if (head == NULL) { head = aNode; tail = head; } else { tail->next = aNode; tail = aNode; } } void prependNodeToList(struct node * aNode) { if (head == NULL) { head = aNode; tail = head; } else { aNode->next = head; head = aNode; } } void reverseList() { struct node * prev = NULL; struct node * current = head; struct node * next = NULL; while (current != NULL) { next = current->next; current->next = prev; prev = current; current = next; } tail = head; head = prev; } void printList() { const struct node * current = head; while (current != NULL) { printf("%p, %d, %p\n", current, current->data, current->next); current = current->next; } } void freeList() { struct node * current = head; struct node * next; while (current != NULL) { next = current->next; current->data = 0; current = NULL; free(current); current = next; } } - C++ //-------------------------------- C++ 11 range-based for-statements #include <iostream> #include <vector> using namespace std; int main() { int a[] = { 0, 1, 2 }; for (const auto &i: a) { // } vector<int> v; // = { 0, 1, 2 }; for (int i = 0; i < 3; ++i) { v.push_back(i); } for (const auto &i: v) { cout << i << " "; // 0 1 2 } cout << "\n"; for (auto &i: v) { i += 1; cout << i << " "; // 1 2 3 } return 0; } - String Conversions - C string char * to C++ const char c[] = "foo"; std::string cc(c); std::cout << cc << std::endl; - C string char * to OC const char c[]="foo"; NSString * oc = [NSString stringWithUTF8String:c]; NSLog(@"%@", oc); - C++ string to C char * std::string cc = {"foo"}; const char * c = cc.c_str(); printf("%s\n", c); - OC string to C char * NSString * oc = @"foo"; const char * c = oc.UTF8String; printf("%s\n", c); - Cocoa Framework Header Files iOS /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/Foundation.framework/Headers/NSObject.h /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/UIKit.framework/Headers/UIView.h macOS /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSObject.h /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/AppKit.framework/Versions/C/Headers/NSView.h watchOS /Applications/Xcode.app/Contents/Developer/Platforms/WatchOS.platform/Developer/SDKs/WatchOS.sdk/System/Library/Frameworks/Foundation.framework/Headers/NSObject.h /Applications/Xcode.app/Contents/Developer/Platforms/WatchOS.platform/Developer/SDKs/WatchOS.sdk/System/Library/Frameworks/WatchKit.framework/Headers/WKInterfaceObject.h - App Icon Adobe Photoshop CC v 20.0.5 Preferences - Plug-Ins - Generator - Enable File - Generate - Image Assets https://helpx.adobe.com/photoshop/using/generate-assets-layers.html https://github.com/adobe-photoshop/generator-core https://github.com/adobe-photoshop/generator-assets https://github.com/adobe-photoshop/generator-assets/wiki/Generate-Image-Assets-Functional-Spec Layer Name iPhone: 40x40 notification_20@2x.png, 60x60 notification_20@3x.png, 58x58 settings_29@2x.png, 87x87 settings_29@3x.png, 80x80 spotlight_40@2x.png, 120x120 spotlight_40@3x.png, 120x120 appicon_60@2x.png, 180x180 appicon_60@3x.png, 1024x1024 appicon_1024.png iPad: 40x40 notification_20@2x.png, 58x58 settings_29@2x.png, 80x80 spotlight_40@2x.png, 152x152 appicon_76@2x.png, 167x167 appicon_83.5@2x.png, 1024x1024 appicon_1024.png - iPhone 40x40 notification_20@2x.png 60x60 notification_20@3x.png 58x58 settings_29@2x.png 87x87 settings_29@3x.png 80x80 spotlight_40@2x.png 120x120 spotlight_40@3x.png 120x120 appicon_60@2x.png 180x180 appicon_60@3x.png 1024x1024 appicon_1024.png - iPad 40x40 notification_20@2x.png 58x58 settings_29@2x.png 80x80 spotlight_40@2x.png 152x152 appicon_76@2x.png 167x167 appicon_83.5@2x.png 1024x1024 appicon_1024.png - Xcode Regex Search and Replace indent line s:^(.) r: $1 replace first n chars s:^.{3} deleting first n chars, 2 part search & replace s:^.{3} r: x s:^ x remove trailing chars d:^(.{39}-) # debug show length s:^(.{39}-).* # keep first 40 chars, delete everything after char 40 r:$1 add trailing chars s:^(.{39}-) # keep first 40 chars r:$1---- s:spinner|((ui)?activityIndicator(View)?) - Xcode Scheme Scripts - Build Pre-Actions Script: Redirect Output to File vLine="-------------------------------" vDate=$(date '+%m-%d-%y.%H%M%S') echo "$vLine\n$vDate\nbuild pre-actions...\npwd: $(pwd)\n$vLine" >> ~/xcodeActions.txt # build post-actions script vDate=$(date '+%m-%d-%y.%H%M%S') echo "$vDate\nbuild post-actions..." >> ~/xcodeActions.txt # text file output ------------------------------- 11-21-14.120100 build pre-actions... pwd: /private/var/folders/p4/abc7123x999xyz_ab1qwe0xx9999xs/T ------------------------------- 11-21-14.120100 build post-actions... - Test Post-Actions Script: Apple Script to set Focus on Xcode after Running Unit Tests osascript -e 'tell application "Xcode" to activate' - Disable debugging Document Versions for Run and Profile actions Removes NSDocumentRevisionsDebugMode from app launch args Nav to scheme window and uncheck Document Versions debugging from Run and Profile options # script open project nav to scheme window (CS + <) // > prod > scheme > edit scheme, will gen xcscheme file esc to dismiss scheme window close project # run script from root // scheme name = project name # sed -i '' -e s/'debugDocumentVersioning = "YES"'/'debugDocumentVersioning = "NO"'/g x.xcodeproj/xcshareddata/xcschemes/x.xcscheme open project - Xcode Symbolic Breakpoints symbol: Swift.print NSLog -[Foo name] // read property -[Foo setName:] // write property -[Foo update] // method
8/10/16
iOS macOS tvOS watchOS
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment