// 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