Help
RSS
API
Feed
Maltego
Contact
Domain > blog.expressionsoftware.com
×
More information on this domain is in
AlienVault OTX
Is this malicious?
Yes
No
DNS Resolutions
Date
IP Address
2014-09-08
64.233.183.121
(
ClassC
)
2026-01-17
142.250.69.179
(
ClassC
)
Port 80
HTTP/1.1 200 OKContent-Type: text/html; charsetUTF-8Expires: Sat, 17 Jan 2026 15:03:52 GMTDate: Sat, 17 Jan 2026 15:03:52 GMTCache-Control: private, max-age0Last-Modified: Thu, 28 Nov 2024 12:28:26 GMTX-Content-Type-Options: nosniffX-XSS-Protection: 1; modeblockServer: GSEAccept-Ranges: noneVary: Accept-EncodingTransfer-Encoding: chunked !DOCTYPE html>html dirltr xmlns//www.w3.org/1999/xhtml xmlns:b//www.google.com/2005/gml/b xmlns:data//www.google.com/2005/gml/data xmlns:expr//www.google.com/2005/gml/expr>head>link hrefhttps://www.blogger.com/static/v1/widgets/2944754296-widget_css_bundle.css relstylesheet typetext/css/>meta contenttext/html; charsetUTF-8 http-equivContent-Type/>meta contentblogger namegenerator/>link hrefhttp://blog.expressionsoftware.com/favicon.ico relicon typeimage/x-icon/>link hrefhttp://blog.expressionsoftware.com/ relcanonical/>link relalternate typeapplication/atom+xml titleExpression Software Blog - Atom hrefhttp://blog.expressionsoftware.com/feeds/posts/default />link relalternate typeapplication/rss+xml titleExpression Software Blog - RSS hrefhttp://blog.expressionsoftware.com/feeds/posts/default?altrss />link relservice.post typeapplication/atom+xml titleExpression Software Blog - Atom hrefhttps://www.blogger.com/feeds/2362511696290915939/posts/default />!--Cant find substitution for tag blog.ieCssRetrofitLinks-->meta contenthttp://blog.expressionsoftware.com/ propertyog:url/>meta contentExpression Software Blog propertyog:title/>meta content<a href"https://www.expressionsoftware.com">home</a> propertyog:description/>title>Expression Software Blog/title>link href//fonts.googleapis.com/css?familyIM+Fell+English relstylesheet typetext/css/>link href//fonts.googleapis.com/css?familyOrbitron:900 relstylesheet typetext/css/>link href//fonts.googleapis.com/css?familyLekton:400,italic,700 relstylesheet typetext/css/>link href//fonts.googleapis.com/css?familyPermanent+Marker relstylesheet typetext/css/>link href//fonts.googleapis.com/css?familyCousine:regular,italic,bold,bolditalic relstylesheet typetext/css/>link href//fonts.googleapis.com/css?familyAllerta+Stencil relstylesheet typetext/css/>style idpage-skin-1 typetext/css>!--/* Blogger Template Style, Simple II, by Jason Sutter *//* Variable definitionsVariable namebgcolor descriptionPage Background Colortypecolor default#fff>Variable nametextcolor descriptionText Colortypecolor default#000>Variable namepagetitlecolor descriptionBlog Title Colortypecolor default#006>Variable nametitlecolor descriptionPost Title Colortypecolor default#000>Variable namefootercolor descriptionDate and Footer Colortypecolor default#555>Variable namelinkcolor descriptionLink Colortypecolor default#58a>Variable namevisitedlinkcolor descriptionVisited Link Colortypecolor default#999> Used to be #969Variable namebordercolor descriptionBorder Colortypecolor default#999>Variable namebodyfont descriptionText Fonttypefont defaultnormal normal 100% Georgia, Serif;>Variable namepagetitlefont descriptionBlog Title Fonttypefont defaultVerdana, Georgia, Serif>Variable namestartSide descriptionStart side in blog languagetypeautomatic defaultleft>Variable nameendSide descriptionEnd side in blog languagetypeautomatic defaultright>*/body {margin: 0;font: normal normal 100% Verdana, sans-serif;background: #fff;color: #000;}a:link {color: #000066;text-decoration: none;}a:visited {color: #000066;text-decoration: none;}a:hover {color: #000066;text-decoration: underline;}a img {border-width: 0;}#outer-wrapper {margin-top: 0px;margin-right: 3em;margin-bottom: 0;margin-left: 3em;}h1 {border-bottom:dotted 1px #99;margin-bottom:0px;/* color: #006; *//* color: red; */color: #000066;}h2 {margin: 0px;padding: 0px;}#main .widget {padding-bottom: 0px;margin-bottom: 0px;border-bottom: dotted 1px #999;clear: both;}#main .Header {border-bottom-width: 0px;/* background-color: red; */}h2.date-header {padding-top: 15px;color: #000;padding-bottom: 0px;margin-bottom: 0px;font-size: 90%;}h3.post-title {font-size: 150%;color: #000;}.post {padding-left: 5%;padding-right: 10%;}.post-footer {color: #555;}#comments {padding-top: 30px;color: #000;padding-bottom: 0px;margin-bottom: 0px;font-weight: bold;}#comments .comment-footer {font-size: 1em;font-weight: normal;color: #555;margin-right: 10px;display: inline;}.comment-author {margin-top: 3%;}.comment-body {font-size: 1em;font-weight: normal;}.deleted-comment {font-style: italic;color: gray;}.comment-link {margin-left: .6em;}.feed-links {clear: both;line-height: 2.5em;}#blog-pager-newer-link {float: left;}#blog-pager-older-link {float: right;}#blog-pager {text-align: center;}.clear {clear: both;}.profile-img {float: left;margin-top: 0;margin-right: 5px;margin-bottom: 5px;margin-left: 0;}body#layout #outer-wrapper {margin-top: 0px;margin-right: 50px;margin-bottom: 0;margin-left: 50px;}@media(prefers-color-scheme: dark) {body {background: #000;color: #fff;}a:link {color: #006fc8;text-decoration: none;}a:visited {color: #006fc8;text-decoration: none;}a:hover {color: #006fc8;text-decoration: underline;}h1 {color: #c0c0c0;}h1 a, h1 a:link, h1 a:visited {color: #c0c0c0;}h2.date-header {color: #c0c0c0;}h3.post-title {color: #c0c0c0;}}-->/style>!-- 12-28-10 google site verification meta tag -->meta contentrlzTHKr8Bjz-kvv7FrCmqDYGIxHEmk8PQg2a74kmJZ4 namegoogle-site-verification/>link hrefhttps://www.blogger.com/dyn-css/authorization.css?targetBlogID2362511696290915939&zx6d7641d4-c7a8-43a3-bf9a-a0e32c434c78 medianone onloadif(media!'all')media'all' relstylesheet/>noscript>link hrefhttps://www.blogger.com/dyn-css/authorization.css?targetBlogID2362511696290915939&zx6d7641d4-c7a8-43a3-bf9a-a0e32c434c78 relstylesheet/>/noscript>meta namegoogle-adsense-platform-account contentca-host-pub-1556223355139109/>meta namegoogle-adsense-platform-domain contentblogspot.com/>!-- data-ad-clientca-pub-2622810652364465 -->/head>body>div classnavbar section idnavbar>div classwidget Navbar data-version1 idNavbar1>script typetext/javascript> function setAttributeOnload(object, attribute, val) { if(window.addEventListener) { window.addEventListener(load, function(){ objectattribute val; }, false); } else { window.attachEvent(onload, function(){ objectattribute val; }); } } /script>div idnavbar-iframe-container>/div>script typetext/javascript srchttps://apis.google.com/js/platform.js>/script>script typetext/javascript> gapi.load(gapi.iframes:gapi.iframes.style.bubble, function() { if (gapi.iframes && gapi.iframes.getContext) { gapi.iframes.getContext().openChild({ url: https://www.blogger.com/navbar/2362511696290915939?origin\x3dhttp://blog.expressionsoftware.com, where: document.getElementById(navbar-iframe-container), id: navbar-iframe }); } }); /script>script typetext/javascript>(function() {var script document.createElement(script);script.type text/javascript;script.src //pagead2.googlesyndication.com/pagead/js/google_top_exp.js;var head document.getElementsByTagName(head)0;if (head) {head.appendChild(script);}})();/script>/div>/div>div idouter-wrapper>div classmain section idmain>div classwidget Header data-version1 idHeader1>div idheader-inner>div classtitlewrapper>h1 classtitle>Expression Software Blog/h1>/div>div classdescriptionwrapper>p classdescription>span>a hrefhttps://www.expressionsoftware.com>home/a>/span>/p>/div>/div>/div>div classwidget Blog data-version1 idBlog1>div classblog-posts hfeed> div classdate-outer> h2 classdate-header>span>12/31/16/span>/h2> div classdate-posts> div classpost-outer>div classpost hentry itemscopeitemscope itemtype//schema.org/BlogPosting>a name8403342461171573833>/a>h3 classpost-title entry-title itempropname>a hrefhttp://blog.expressionsoftware.com/2016/12/arduino-blink-sketch.html>Arduino Blink Sketch/a>/h3>div classpost-header>div classpost-header-line-1>/div>/div>div classpost-body entry-content idpost-body-8403342461171573833 itemproparticleBody>pre stylefont-family:courier new> /* * Upload Error: Error opening serial port '/dev/cu.usbmodem1421' * Fix: Close Serial Monitor window, reopen after upload is done. * * Misc * - On Micro, TX LED lights up when Serial print(), println() is called * - Serial Monitor can be started after upload or reset * - setup() runs once when you press reset or power the board * - loop() is a run-loop * * Keyboard Shortcuts * CS M Serial Monitor */// const int kLedPin LED_BUILTIN; const int kLedPin 10; const long kBlinkInterval 400; const int kMaxLogCount 10; unsigned long previousMillis 0; boolean onceToken false; int ledState LOW; int logCount 0; void setup() { const String kBoard "Arduino Micro, 5V"; Serial.begin(9600); delay(6000); // delay for serial monitor intialization Serial.println(kBoard); pinMode(kLedPin, OUTPUT); // LED output if (kLedPin LED_BUILTIN) { Serial.print("Built-In "); } Serial.print("LED Pin: "); Serial.println(kLedPin); Serial.print("LED Blink Interval: "); Serial.println(kBlinkInterval); Serial.print("Max Log Count: "); Serial.println(kMaxLogCount); } void loop() { if (onceToken false) { Serial.println(""); Serial.println("Loop started"); onceToken true; } unsigned long currentMillis millis(); if (currentMillis - previousMillis > kBlinkInterval) { previousMillis currentMillis; if (ledState LOW) { ledState HIGH; log("On"); } else { ledState LOW; log("Off"); } digitalWrite(kLedPin, ledState); } } void log(String message) { if (logCount < kMaxLogCount) { Serial.println(message); logCount++; } } // Output Arduino Micro, 5V LED Pin: 10 LED Blink Interval: 400 Max Log Count: 10 Loop started On Off On Off On Off On Off On Off/pre>a href//expressionsoftware.com/docs/arduino-blink-breadboard.pdf imageanchor1>img border0 height350 src//expressionsoftware.com/docs/arduino-blink-breadboard.svg stylemargin-left: -24px width500 />/a>a href//expressionsoftware.com/docs/arduino-blink-schematic.pdf imageanchor1>img border0 src//expressionsoftware.com/docs/arduino-blink-schematic.svg stylemargin-left: 20px width500/>/a>br />br />a hrefhttps://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiT5WDxfJrgKWKsYtlYNw2ME5qoLFKjLvVqbpXxNWV6WiE4er_FnYWurZWzAppL48vow82u1kFsPpOgbv2xsMzs6ztiU_H4QWeG59Ym6Ef4SEiBA6qXhlDxtxXAsxxduCboS8LgT6Q81EOc/s1600/arduino-blink-sketch-breadboard-1.jpg imageanchor1>img border0 srchttps://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiT5WDxfJrgKWKsYtlYNw2ME5qoLFKjLvVqbpXxNWV6WiE4er_FnYWurZWzAppL48vow82u1kFsPpOgbv2xsMzs6ztiU_H4QWeG59Ym6Ef4SEiBA6qXhlDxtxXAsxxduCboS8LgT6Q81EOc/s800/arduino-blink-sketch-breadboard-1.jpg styleheight: 300px />/a>a hrefhttps://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhAwAmRYYNAZps7Se6_gOtRIJWFdfUTOD-VRRNnM7fp903qTW-QpxMU1njOsKoAKVyCZ6K9_PEjJAJ0jOcc9dBkl5QpsIrl1m3Jp3FIQeHgdQceRqMXeS-myBVNy-7hheNLIG8bJgoV5gDB/s1600/arduino-blink-sketch-breadboard-2.jpg imageanchor1>img border0 srchttps://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhAwAmRYYNAZps7Se6_gOtRIJWFdfUTOD-VRRNnM7fp903qTW-QpxMU1njOsKoAKVyCZ6K9_PEjJAJ0jOcc9dBkl5QpsIrl1m3Jp3FIQeHgdQceRqMXeS-myBVNy-7hheNLIG8bJgoV5gDB/s800/arduino-blink-sketch-breadboard-2.jpg stylemargin-left: 10px; height: 300px; />/a>a hrefhttps://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigxa4RcKeCexRkHXGv0eut1SJTWTV-wANpeU6cqONhlQIRqCF_nMgiMQzmD_j_Bm7jNT_LYcfcG1o7IreabMudkNz3btTGQEbWWBqlNJnavzArpvSd3GnrrmNFob0CBqlW_n05uW_ucBd9/s1600/arduino-blink-sketch-breadboard-3.jpg imageanchor1>img border0 srchttps://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigxa4RcKeCexRkHXGv0eut1SJTWTV-wANpeU6cqONhlQIRqCF_nMgiMQzmD_j_Bm7jNT_LYcfcG1o7IreabMudkNz3btTGQEbWWBqlNJnavzArpvSd3GnrrmNFob0CBqlW_n05uW_ucBd9/s800/arduino-blink-sketch-breadboard-3.jpg stylemargin-left: 10px; height: 300px; />/a>br />br />br />div styleclear: both;>/div>/div>div classpost-footer>div classpost-footer-line post-footer-line-1>span classpost-comment-link>a classcomment-link hrefhttp://blog.expressionsoftware.com/2016/12/arduino-blink-sketch.html#comment-form onclick>No comments: /a>/span>span classpost-icons>span classitem-control blog-admin pid-825337817>a hrefhttps://www.blogger.com/post-edit.g?blogID2362511696290915939&postID8403342461171573833&frompencil titleEdit Post>img alt classicon-action height18 src//resources.blogblog.com/img/icon18_edit_allbkg.gif width18/>/a>/span>/span>/div>div classpost-footer-line post-footer-line-2>span classpost-labels>/span>/div>div classpost-footer-line post-footer-line-3>/div>/div>/div>/div> /div>/div> div classdate-outer> h2 classdate-header>span>12/11/16/span>/h2> div classdate-posts> div classpost-outer>div classpost hentry itemscopeitemscope itemtype//schema.org/BlogPosting>a name630173669210213205>/a>h3 classpost-title entry-title itempropname>a hrefhttp://blog.expressionsoftware.com/2016/12/arduino.html>Arduino/a>/h3>div classpost-header>div classpost-header-line-1>/div>/div>div classpost-body entry-content idpost-body-630173669210213205 itemproparticleBody>b>span stylefont-family: Verdana, sans-serif; font-size: 19px>Uno/span>/b> span stylefont-family: Verdana, sans-serif; font-size: 14px;>5V/span>br />b>span stylefont-family: Verdana, sans-serif; font-size: 19px>101/span>/b> span stylefont-family: Verdana, sans-serif; font-size: 14px;>3.3V/span>br />b>span stylefont-family: Verdana, sans-serif; font-size: 19px>MKR1000/span>/b> span stylefont-family: Verdana, sans-serif; font-size: 14px;>3.3V/span>br />b>span stylefont-family: Verdana, sans-serif; font-size: 19px>Micro/span>/b> span stylefont-family: Verdana, sans-serif; font-size: 14px;>5V/span>br />br />iframe height451 srchttp://expressionsoftware.com/docs/arduino-boards.pdf width90% frameborder0 allowtransparencytrue stylebackground: #000000 scrollingno marginheight0 marginwidth0 allowfullscreen yes>/iframe>br />br />a hrefhttps://www.arduino.cc/en/Reference/HomePage>Arduino Language Reference/a>br/>a hrefhttp://expressionsoftware.com/docs/arduino-boards.pdf>Arduino Boards PDF/a>br />br />br />div styleclear: both;>/div>/div>div classpost-footer>div classpost-footer-line post-footer-line-1>span classpost-comment-link>a classcomment-link hrefhttp://blog.expressionsoftware.com/2016/12/arduino.html#comment-form onclick>No comments: /a>/span>span classpost-icons>span classitem-control blog-admin pid-825337817>a hrefhttps://www.blogger.com/post-edit.g?blogID2362511696290915939&postID630173669210213205&frompencil titleEdit Post>img alt classicon-action height18 src//resources.blogblog.com/img/icon18_edit_allbkg.gif width18/>/a>/span>/span>/div>div classpost-footer-line post-footer-line-2>span classpost-labels>/span>/div>div classpost-footer-line post-footer-line-3>/div>/div>/div>/div> /div>/div> div classdate-outer> h2 classdate-header>span>8/10/16/span>/h2> div classdate-posts> div classpost-outer>div classpost hentry itemscopeitemscope itemtype//schema.org/BlogPosting>a name2324325584917950238>/a>h3 classpost-title entry-title itempropname>a hrefhttp://blog.expressionsoftware.com/2013/10/c2.html>iOS macOS tvOS watchOS/a>/h3>div classpost-header>div classpost-header-line-1>/div>/div>div classpost-body entry-content idpost-body-2324325584917950238 itemproparticleBody>pre stylefont-family:courier new>// 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? s0 // subscript(idx: Int) -> Character? { guard idx > 0 && idx < count else { return nil } return selfindex(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? s0 // se let cs: Array<Character?> c, s1, s100 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(ss.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 ss.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 infoid: \(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.arguments0 // app path let arg1: String CommandLine.arguments1 let arg2: String CommandLine.arguments2 } //-------------------------------- 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) \(defaultskey!)") } // 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 attrsFileAttributeKey.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 { $0query.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(sr) // 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 { "\(bytesi) " |> 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(mirrori.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 representations0; 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 m222; float m4; } union GLKMatrix3 { struct { float m00, m01, m02; m10, m11, m12; m20, m21, m22; } float m9; } union GLKMatrix4 { struct { float m00, m01, m02, m03; m10, m11, m12, m13; m20, m21, m22, m23; m30, m31, m32, m33; } float m16; } union GLKVector2 { struct { float x, y; } float v2; } union GLKVector3 { struct { float x, y, z; } float v3; } union GLKVector4 { struct { float x, y, z, w; } float v4; } union GLKQuaternion { struct { GLKVector3 v; float s; // scalar component } struct { float x, y, z, w; } float q4; }- 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.userInfokey); } } -(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_t16; uid getUUIDBytes:bytes; for (int i 0; i< sizeof(bytes)/sizeof(unsigned char); i++) { printf("%d ", bytesi); // 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, @@30, @@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(datai, datai + 1); v2 GLKVector2Make(datai + 2, datai + 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 { .columns0 {1, 0, 0, 0}, .columns1 {0, 1, 0, 0}, .columns2 {0, 0, 1, 0}, .columns3 {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 m4, 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 mm4, # 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?id5570A4B1-7E8B-434B-8BDB-F02538FD0B76&extJPG"; */ 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?id5570A4B1-7E8B-434B-8BDB-F02538FD0B76&extJPG"; self loadPhotoAssetFromURL:alPhotoAssetURL; -(void)loadPhotoAssetFromLocalID:(NSString *)aPhotoAssetLocalID { NSArray * photoAssetLocalID @aPhotoAssetLocalID; PHFetchResult * fetchResult PHAsset fetchAssetsWithLocalIdentifiers:photoAssetLocalID options:nil; if (fetchResult.count > 0) { PHAsset * photoAsset fetchResult0; self loadPhotoAsset:photoAsset; } } -(void)loadPhotoAssetFromURL:(NSURL *)aALPhotoAssetURL { NSArray * alPhotoAssetURL @aALPhotoAssetURL; PHFetchResult * fetchResult PHAsset fetchAssetsWithALAssetURLs:alPhotoAssetURL options:nil; if (fetchResult.count > 0) { PHAsset * photoAsset fetchResult0; 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 *)constraintsi; 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++) { bindingsbuttonBindingsi buttonsi; } } -(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)", buttonBindingsi; // 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.dataindexPath.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, changeNSKeyValueChangeOldKey, changeNSKeyValueChangeNewKey); } - (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 buff20; 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 buff20; 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/pre>div styleclear: both;>/div>/div>div classpost-footer>div classpost-footer-line post-footer-line-1>span classpost-comment-link>a classcomment-link hrefhttp://blog.expressionsoftware.com/2013/10/c2.html#comment-form onclick>No comments: /a>/span>span classpost-icons>span classitem-control blog-admin pid-825337817>a hrefhttps://www.blogger.com/post-edit.g?blogID2362511696290915939&postID2324325584917950238&frompencil titleEdit Post>img alt classicon-action height18 src//resources.blogblog.com/img/icon18_edit_allbkg.gif width18/>/a>/span>/span>/div>div classpost-footer-line post-footer-line-2>span classpost-labels>/span>/div>div classpost-footer-line post-footer-line-3>/div>/div>/div>/div> /div>/div> /div>div classblog-pager idblog-pager>span idblog-pager-older-link>a classblog-pager-older-link hrefhttp://blog.expressionsoftware.com/search?updated-max2016-08-10T00:00:00-07:00&max-results7 idBlog1_blog-pager-older-link titleOlder Posts>Older Posts/a>/span>a classhome-link hrefhttp://blog.expressionsoftware.com/>Home/a>/div>div classclear>/div>div classblog-feeds>div classfeed-links>Subscribe to:a classfeed-link hrefhttp://blog.expressionsoftware.com/feeds/posts/default target_blank typeapplication/atom+xml>Comments (Atom)/a>/div>/div>/div>div classwidget BlogArchive data-version1 idBlogArchive2>h2>Blog Archive/h2>div classwidget-content>div idArchiveList>div idBlogArchive2_ArchiveList>ul classhierarchy>li classarchivedate expanded>a classtoggle hrefjavascript:void(0)>span classzippy toggle-open> ▼ /span>/a>a classpost-count-link hrefhttp://blog.expressionsoftware.com/2016/>2016/a>span classpost-count dirltr>(5)/span>ul classhierarchy>li classarchivedate expanded>a classtoggle hrefjavascript:void(0)>span classzippy toggle-open> ▼ /span>/a>a classpost-count-link hrefhttp://blog.expressionsoftware.com/2016/12/>December/a>span classpost-count dirltr>(2)/span>ul classposts>li>a hrefhttp://blog.expressionsoftware.com/2016/12/arduino-blink-sketch.html>Arduino Blink Sketch/a>/li>li>a hrefhttp://blog.expressionsoftware.com/2016/12/arduino.html>Arduino/a>/li>/ul>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttp://blog.expressionsoftware.com/2016/08/>August/a>span classpost-count dirltr>(3)/span>/li>/ul>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttp://blog.expressionsoftware.com/2015/>2015/a>span classpost-count dirltr>(7)/span>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttp://blog.expressionsoftware.com/2015/03/>March/a>span classpost-count dirltr>(3)/span>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttp://blog.expressionsoftware.com/2015/02/>February/a>span classpost-count dirltr>(3)/span>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttp://blog.expressionsoftware.com/2015/01/>January/a>span classpost-count dirltr>(1)/span>/li>/ul>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttp://blog.expressionsoftware.com/2014/>2014/a>span classpost-count dirltr>(5)/span>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttp://blog.expressionsoftware.com/2014/12/>December/a>span classpost-count dirltr>(5)/span>/li>/ul>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttp://blog.expressionsoftware.com/2013/>2013/a>span classpost-count dirltr>(4)/span>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttp://blog.expressionsoftware.com/2013/12/>December/a>span classpost-count dirltr>(1)/span>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttp://blog.expressionsoftware.com/2013/11/>November/a>span classpost-count dirltr>(1)/span>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttp://blog.expressionsoftware.com/2013/04/>April/a>span classpost-count dirltr>(1)/span>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttp://blog.expressionsoftware.com/2013/01/>January/a>span classpost-count dirltr>(1)/span>/li>/ul>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttp://blog.expressionsoftware.com/2012/>2012/a>span classpost-count dirltr>(1)/span>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttp://blog.expressionsoftware.com/2012/05/>May/a>span classpost-count dirltr>(1)/span>/li>/ul>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttp://blog.expressionsoftware.com/2011/>2011/a>span classpost-count dirltr>(14)/span>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttp://blog.expressionsoftware.com/2011/09/>September/a>span classpost-count dirltr>(1)/span>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttp://blog.expressionsoftware.com/2011/08/>August/a>span classpost-count dirltr>(2)/span>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttp://blog.expressionsoftware.com/2011/03/>March/a>span classpost-count dirltr>(2)/span>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttp://blog.expressionsoftware.com/2011/02/>February/a>span classpost-count dirltr>(5)/span>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttp://blog.expressionsoftware.com/2011/01/>January/a>span classpost-count dirltr>(4)/span>/li>/ul>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttp://blog.expressionsoftware.com/2010/>2010/a>span classpost-count dirltr>(25)/span>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttp://blog.expressionsoftware.com/2010/12/>December/a>span classpost-count dirltr>(2)/span>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttp://blog.expressionsoftware.com/2010/11/>November/a>span classpost-count dirltr>(3)/span>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttp://blog.expressionsoftware.com/2010/08/>August/a>span classpost-count dirltr>(1)/span>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttp://blog.expressionsoftware.com/2010/05/>May/a>span classpost-count dirltr>(6)/span>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttp://blog.expressionsoftware.com/2010/04/>April/a>span classpost-count dirltr>(4)/span>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttp://blog.expressionsoftware.com/2010/03/>March/a>span classpost-count dirltr>(6)/span>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttp://blog.expressionsoftware.com/2010/02/>February/a>span classpost-count dirltr>(2)/span>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttp://blog.expressionsoftware.com/2010/01/>January/a>span classpost-count dirltr>(1)/span>/li>/ul>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttp://blog.expressionsoftware.com/2009/>2009/a>span classpost-count dirltr>(8)/span>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttp://blog.expressionsoftware.com/2009/11/>November/a>span classpost-count dirltr>(4)/span>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttp://blog.expressionsoftware.com/2009/08/>August/a>span classpost-count dirltr>(3)/span>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttp://blog.expressionsoftware.com/2009/05/>May/a>span classpost-count dirltr>(1)/span>/li>/ul>/li>/ul>/div>/div>div classclear>/div>/div>/div>/div>/div>script typetext/javascript srchttps://www.blogger.com/static/v1/widgets/3845888474-widgets.js>/script>script typetext/javascript>window__wavt AOuZoY7vt3XepQi9H6YNEWWxXK3c6TtKAw:1768585768829;_WidgetManager._Init(//www.blogger.com/rearrange?blogID\x3d2362511696290915939,//blog.expressionsoftware.com/,2362511696290915939);_WidgetManager._SetDataContext({name: blog, data: {blogId: 2362511696290915939, title: Expression Software Blog, url: http://blog.expressionsoftware.com/, canonicalUrl: http://blog.expressionsoftware.com/, homepageUrl: http://blog.expressionsoftware.com/, searchUrl: http://blog.expressionsoftware.com/search, canonicalHomepageUrl: http://blog.expressionsoftware.com/, blogspotFaviconUrl: http://blog.expressionsoftware.com/favicon.ico, bloggerUrl: https://www.blogger.com, hasCustomDomain: true, httpsEnabled: true, enabledCommentProfileImages: false, gPlusViewType: FILTERED_POSTMOD, adultContent: false, analyticsAccountNumber: , encoding: UTF-8, locale: en, localeUnderscoreDelimited: en, languageDirection: ltr, isPrivate: false, isMobile: false, isMobileRequest: false, mobileClass: , isPrivateBlog: false, isDynamicViewsAvailable: true, feedLinks: \x3clink rel\x3d\x22alternate\x22 type\x3d\x22application/atom+xml\x22 title\x3d\x22Expression Software Blog - Atom\x22 href\x3d\x22http://blog.expressionsoftware.com/feeds/posts/default\x22 /\x3e\n\x3clink rel\x3d\x22alternate\x22 type\x3d\x22application/rss+xml\x22 title\x3d\x22Expression Software Blog - RSS\x22 href\x3d\x22http://blog.expressionsoftware.com/feeds/posts/default?alt\x3drss\x22 /\x3e\n\x3clink rel\x3d\x22service.post\x22 type\x3d\x22application/atom+xml\x22 title\x3d\x22Expression Software Blog - Atom\x22 href\x3d\x22https://www.blogger.com/feeds/2362511696290915939/posts/default\x22 /\x3e\n, meTag: , adsenseClientId: ca-pub-2622810652364465, adsenseHostId: ca-host-pub-1556223355139109, adsenseHasAds: false, adsenseAutoAds: false, boqCommentIframeForm: true, loginRedirectParam: , isGoogleEverywhereLinkTooltipEnabled: true, view: , dynamicViewsCommentsSrc: //www.blogblog.com/dynamicviews/4224c15c4e7c9321/js/comments.js, dynamicViewsScriptSrc: //www.blogblog.com/dynamicviews/2dfa401275732ff9, plusOneApiSrc: https://apis.google.com/js/platform.js, disableGComments: true, interstitialAccepted: false, sharing: {platforms: {name: Get link, key: link, shareMessage: Get link, target: }, {name: Facebook, key: facebook, shareMessage: Share to Facebook, target: facebook}, {name: BlogThis!, key: blogThis, shareMessage: BlogThis!, target: blog}, {name: X, key: twitter, shareMessage: Share to X, target: twitter}, {name: Pinterest, key: pinterest, shareMessage: Share to Pinterest, target: pinterest}, {name: Email, key: email, shareMessage: Email, target: email}, disableGooglePlus: true, googlePlusShareButtonWidth: 0, googlePlusBootstrap: \x3cscript type\x3d\x22text/javascript\x22\x3ewindow.___gcfg \x3d {\x27lang\x27: \x27en\x27};\x3c/script\x3e}, hasCustomJumpLinkMessage: false, jumpLinkMessage: Read more, pageType: index, pageName: , pageTitle: Expression Software Blog}}, {name: features, data: {}}, {name: messages, data: {edit: Edit, linkCopiedToClipboard: Link copied to clipboard!, ok: Ok, postLink: Post Link}}, {name: template, data: {name: custom, localizedName: Custom, isResponsive: false, isAlternateRendering: false, isCustom: true}}, {name: view, data: {classic: {name: classic, url: ?view\x3dclassic}, flipcard: {name: flipcard, url: ?view\x3dflipcard}, magazine: {name: magazine, url: ?view\x3dmagazine}, mosaic: {name: mosaic, url: ?view\x3dmosaic}, sidebar: {name: sidebar, url: ?view\x3dsidebar}, snapshot: {name: snapshot, url: ?view\x3dsnapshot}, timeslide: {name: timeslide, url: ?view\x3dtimeslide}, isMobile: false, title: Expression Software Blog, description: \x3ca href\x3d\x22https://www.expressionsoftware.com\x22\x3ehome\x3c/a\x3e, url: http://blog.expressionsoftware.com/, type: feed, isSingleItem: false, isMultipleItems: true, isError: false, isPage: false, isPost: false, isHomepage: true, isArchive: false, isLabelSearch: false}});_WidgetManager._RegisterWidget(_NavbarView, new _WidgetInfo(Navbar1, navbar, document.getElementById(Navbar1), {}, displayModeFull));_WidgetManager._RegisterWidget(_HeaderView, new _WidgetInfo(Header1, main, document.getElementById(Header1), {}, displayModeFull));_WidgetManager._RegisterWidget(_BlogView, new _WidgetInfo(Blog1, main, document.getElementById(Blog1), {cmtInteractionsEnabled: false, lightboxEnabled: true, lightboxModuleUrl: https://www.blogger.com/static/v1/jsbin/4049919853-lbx.js, lightboxCssUrl: https://www.blogger.com/static/v1/v-css/828616780-lightbox_bundle.css}, displayModeFull));_WidgetManager._RegisterWidget(_BlogArchiveView, new _WidgetInfo(BlogArchive2, main, document.getElementById(BlogArchive2), {languageDirection: ltr, loadingMessage: Loading\x26hellip;}, displayModeFull));/script>/body>/html>
Port 443
HTTP/1.1 200 OKContent-Type: text/html; charsetUTF-8Expires: Sat, 17 Jan 2026 15:03:52 GMTDate: Sat, 17 Jan 2026 15:03:52 GMTCache-Control: private, max-age0Last-Modified: Thu, 28 Nov 2024 12:28:26 GMTX-Content-Type-Options: nosniffX-XSS-Protection: 1; modeblockServer: GSEAccept-Ranges: noneVary: Accept-EncodingTransfer-Encoding: chunked !DOCTYPE html>html dirltr xmlns//www.w3.org/1999/xhtml xmlns:b//www.google.com/2005/gml/b xmlns:data//www.google.com/2005/gml/data xmlns:expr//www.google.com/2005/gml/expr>head>link hrefhttps://www.blogger.com/static/v1/widgets/2944754296-widget_css_bundle.css relstylesheet typetext/css/>meta contenttext/html; charsetUTF-8 http-equivContent-Type/>meta contentblogger namegenerator/>link hrefhttps://blog.expressionsoftware.com/favicon.ico relicon typeimage/x-icon/>link hrefhttp://blog.expressionsoftware.com/ relcanonical/>link relalternate typeapplication/atom+xml titleExpression Software Blog - Atom hrefhttps://blog.expressionsoftware.com/feeds/posts/default />link relalternate typeapplication/rss+xml titleExpression Software Blog - RSS hrefhttps://blog.expressionsoftware.com/feeds/posts/default?altrss />link relservice.post typeapplication/atom+xml titleExpression Software Blog - Atom hrefhttps://www.blogger.com/feeds/2362511696290915939/posts/default />!--Cant find substitution for tag blog.ieCssRetrofitLinks-->meta contenthttp://blog.expressionsoftware.com/ propertyog:url/>meta contentExpression Software Blog propertyog:title/>meta content<a href"https://www.expressionsoftware.com">home</a> propertyog:description/>title>Expression Software Blog/title>link href//fonts.googleapis.com/css?familyIM+Fell+English relstylesheet typetext/css/>link href//fonts.googleapis.com/css?familyOrbitron:900 relstylesheet typetext/css/>link href//fonts.googleapis.com/css?familyLekton:400,italic,700 relstylesheet typetext/css/>link href//fonts.googleapis.com/css?familyPermanent+Marker relstylesheet typetext/css/>link href//fonts.googleapis.com/css?familyCousine:regular,italic,bold,bolditalic relstylesheet typetext/css/>link href//fonts.googleapis.com/css?familyAllerta+Stencil relstylesheet typetext/css/>style idpage-skin-1 typetext/css>!--/* Blogger Template Style, Simple II, by Jason Sutter *//* Variable definitionsVariable namebgcolor descriptionPage Background Colortypecolor default#fff>Variable nametextcolor descriptionText Colortypecolor default#000>Variable namepagetitlecolor descriptionBlog Title Colortypecolor default#006>Variable nametitlecolor descriptionPost Title Colortypecolor default#000>Variable namefootercolor descriptionDate and Footer Colortypecolor default#555>Variable namelinkcolor descriptionLink Colortypecolor default#58a>Variable namevisitedlinkcolor descriptionVisited Link Colortypecolor default#999> Used to be #969Variable namebordercolor descriptionBorder Colortypecolor default#999>Variable namebodyfont descriptionText Fonttypefont defaultnormal normal 100% Georgia, Serif;>Variable namepagetitlefont descriptionBlog Title Fonttypefont defaultVerdana, Georgia, Serif>Variable namestartSide descriptionStart side in blog languagetypeautomatic defaultleft>Variable nameendSide descriptionEnd side in blog languagetypeautomatic defaultright>*/body {margin: 0;font: normal normal 100% Verdana, sans-serif;background: #fff;color: #000;}a:link {color: #000066;text-decoration: none;}a:visited {color: #000066;text-decoration: none;}a:hover {color: #000066;text-decoration: underline;}a img {border-width: 0;}#outer-wrapper {margin-top: 0px;margin-right: 3em;margin-bottom: 0;margin-left: 3em;}h1 {border-bottom:dotted 1px #99;margin-bottom:0px;/* color: #006; *//* color: red; */color: #000066;}h2 {margin: 0px;padding: 0px;}#main .widget {padding-bottom: 0px;margin-bottom: 0px;border-bottom: dotted 1px #999;clear: both;}#main .Header {border-bottom-width: 0px;/* background-color: red; */}h2.date-header {padding-top: 15px;color: #000;padding-bottom: 0px;margin-bottom: 0px;font-size: 90%;}h3.post-title {font-size: 150%;color: #000;}.post {padding-left: 5%;padding-right: 10%;}.post-footer {color: #555;}#comments {padding-top: 30px;color: #000;padding-bottom: 0px;margin-bottom: 0px;font-weight: bold;}#comments .comment-footer {font-size: 1em;font-weight: normal;color: #555;margin-right: 10px;display: inline;}.comment-author {margin-top: 3%;}.comment-body {font-size: 1em;font-weight: normal;}.deleted-comment {font-style: italic;color: gray;}.comment-link {margin-left: .6em;}.feed-links {clear: both;line-height: 2.5em;}#blog-pager-newer-link {float: left;}#blog-pager-older-link {float: right;}#blog-pager {text-align: center;}.clear {clear: both;}.profile-img {float: left;margin-top: 0;margin-right: 5px;margin-bottom: 5px;margin-left: 0;}body#layout #outer-wrapper {margin-top: 0px;margin-right: 50px;margin-bottom: 0;margin-left: 50px;}@media(prefers-color-scheme: dark) {body {background: #000;color: #fff;}a:link {color: #006fc8;text-decoration: none;}a:visited {color: #006fc8;text-decoration: none;}a:hover {color: #006fc8;text-decoration: underline;}h1 {color: #c0c0c0;}h1 a, h1 a:link, h1 a:visited {color: #c0c0c0;}h2.date-header {color: #c0c0c0;}h3.post-title {color: #c0c0c0;}}-->/style>!-- 12-28-10 google site verification meta tag -->meta contentrlzTHKr8Bjz-kvv7FrCmqDYGIxHEmk8PQg2a74kmJZ4 namegoogle-site-verification/>link hrefhttps://www.blogger.com/dyn-css/authorization.css?targetBlogID2362511696290915939&zx6d7641d4-c7a8-43a3-bf9a-a0e32c434c78 medianone onloadif(media!'all')media'all' relstylesheet/>noscript>link hrefhttps://www.blogger.com/dyn-css/authorization.css?targetBlogID2362511696290915939&zx6d7641d4-c7a8-43a3-bf9a-a0e32c434c78 relstylesheet/>/noscript>meta namegoogle-adsense-platform-account contentca-host-pub-1556223355139109/>meta namegoogle-adsense-platform-domain contentblogspot.com/>!-- data-ad-clientca-pub-2622810652364465 -->/head>body>div classnavbar section idnavbar>div classwidget Navbar data-version1 idNavbar1>script typetext/javascript> function setAttributeOnload(object, attribute, val) { if(window.addEventListener) { window.addEventListener(load, function(){ objectattribute val; }, false); } else { window.attachEvent(onload, function(){ objectattribute val; }); } } /script>div idnavbar-iframe-container>/div>script typetext/javascript srchttps://apis.google.com/js/platform.js>/script>script typetext/javascript> gapi.load(gapi.iframes:gapi.iframes.style.bubble, function() { if (gapi.iframes && gapi.iframes.getContext) { gapi.iframes.getContext().openChild({ url: https://www.blogger.com/navbar/2362511696290915939?origin\x3dhttps://blog.expressionsoftware.com, where: document.getElementById(navbar-iframe-container), id: navbar-iframe }); } }); /script>script typetext/javascript>(function() {var script document.createElement(script);script.type text/javascript;script.src //pagead2.googlesyndication.com/pagead/js/google_top_exp.js;var head document.getElementsByTagName(head)0;if (head) {head.appendChild(script);}})();/script>/div>/div>div idouter-wrapper>div classmain section idmain>div classwidget Header data-version1 idHeader1>div idheader-inner>div classtitlewrapper>h1 classtitle>Expression Software Blog/h1>/div>div classdescriptionwrapper>p classdescription>span>a hrefhttps://www.expressionsoftware.com>home/a>/span>/p>/div>/div>/div>div classwidget Blog data-version1 idBlog1>div classblog-posts hfeed> div classdate-outer> h2 classdate-header>span>12/31/16/span>/h2> div classdate-posts> div classpost-outer>div classpost hentry itemscopeitemscope itemtype//schema.org/BlogPosting>a name8403342461171573833>/a>h3 classpost-title entry-title itempropname>a hrefhttps://blog.expressionsoftware.com/2016/12/arduino-blink-sketch.html>Arduino Blink Sketch/a>/h3>div classpost-header>div classpost-header-line-1>/div>/div>div classpost-body entry-content idpost-body-8403342461171573833 itemproparticleBody>pre stylefont-family:courier new> /* * Upload Error: Error opening serial port '/dev/cu.usbmodem1421' * Fix: Close Serial Monitor window, reopen after upload is done. * * Misc * - On Micro, TX LED lights up when Serial print(), println() is called * - Serial Monitor can be started after upload or reset * - setup() runs once when you press reset or power the board * - loop() is a run-loop * * Keyboard Shortcuts * CS M Serial Monitor */// const int kLedPin LED_BUILTIN; const int kLedPin 10; const long kBlinkInterval 400; const int kMaxLogCount 10; unsigned long previousMillis 0; boolean onceToken false; int ledState LOW; int logCount 0; void setup() { const String kBoard "Arduino Micro, 5V"; Serial.begin(9600); delay(6000); // delay for serial monitor intialization Serial.println(kBoard); pinMode(kLedPin, OUTPUT); // LED output if (kLedPin LED_BUILTIN) { Serial.print("Built-In "); } Serial.print("LED Pin: "); Serial.println(kLedPin); Serial.print("LED Blink Interval: "); Serial.println(kBlinkInterval); Serial.print("Max Log Count: "); Serial.println(kMaxLogCount); } void loop() { if (onceToken false) { Serial.println(""); Serial.println("Loop started"); onceToken true; } unsigned long currentMillis millis(); if (currentMillis - previousMillis > kBlinkInterval) { previousMillis currentMillis; if (ledState LOW) { ledState HIGH; log("On"); } else { ledState LOW; log("Off"); } digitalWrite(kLedPin, ledState); } } void log(String message) { if (logCount < kMaxLogCount) { Serial.println(message); logCount++; } } // Output Arduino Micro, 5V LED Pin: 10 LED Blink Interval: 400 Max Log Count: 10 Loop started On Off On Off On Off On Off On Off/pre>a href//expressionsoftware.com/docs/arduino-blink-breadboard.pdf imageanchor1>img border0 height350 src//expressionsoftware.com/docs/arduino-blink-breadboard.svg stylemargin-left: -24px width500 />/a>a href//expressionsoftware.com/docs/arduino-blink-schematic.pdf imageanchor1>img border0 src//expressionsoftware.com/docs/arduino-blink-schematic.svg stylemargin-left: 20px width500/>/a>br />br />a hrefhttps://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiT5WDxfJrgKWKsYtlYNw2ME5qoLFKjLvVqbpXxNWV6WiE4er_FnYWurZWzAppL48vow82u1kFsPpOgbv2xsMzs6ztiU_H4QWeG59Ym6Ef4SEiBA6qXhlDxtxXAsxxduCboS8LgT6Q81EOc/s1600/arduino-blink-sketch-breadboard-1.jpg imageanchor1>img border0 srchttps://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiT5WDxfJrgKWKsYtlYNw2ME5qoLFKjLvVqbpXxNWV6WiE4er_FnYWurZWzAppL48vow82u1kFsPpOgbv2xsMzs6ztiU_H4QWeG59Ym6Ef4SEiBA6qXhlDxtxXAsxxduCboS8LgT6Q81EOc/s800/arduino-blink-sketch-breadboard-1.jpg styleheight: 300px />/a>a hrefhttps://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhAwAmRYYNAZps7Se6_gOtRIJWFdfUTOD-VRRNnM7fp903qTW-QpxMU1njOsKoAKVyCZ6K9_PEjJAJ0jOcc9dBkl5QpsIrl1m3Jp3FIQeHgdQceRqMXeS-myBVNy-7hheNLIG8bJgoV5gDB/s1600/arduino-blink-sketch-breadboard-2.jpg imageanchor1>img border0 srchttps://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhAwAmRYYNAZps7Se6_gOtRIJWFdfUTOD-VRRNnM7fp903qTW-QpxMU1njOsKoAKVyCZ6K9_PEjJAJ0jOcc9dBkl5QpsIrl1m3Jp3FIQeHgdQceRqMXeS-myBVNy-7hheNLIG8bJgoV5gDB/s800/arduino-blink-sketch-breadboard-2.jpg stylemargin-left: 10px; height: 300px; />/a>a hrefhttps://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigxa4RcKeCexRkHXGv0eut1SJTWTV-wANpeU6cqONhlQIRqCF_nMgiMQzmD_j_Bm7jNT_LYcfcG1o7IreabMudkNz3btTGQEbWWBqlNJnavzArpvSd3GnrrmNFob0CBqlW_n05uW_ucBd9/s1600/arduino-blink-sketch-breadboard-3.jpg imageanchor1>img border0 srchttps://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigxa4RcKeCexRkHXGv0eut1SJTWTV-wANpeU6cqONhlQIRqCF_nMgiMQzmD_j_Bm7jNT_LYcfcG1o7IreabMudkNz3btTGQEbWWBqlNJnavzArpvSd3GnrrmNFob0CBqlW_n05uW_ucBd9/s800/arduino-blink-sketch-breadboard-3.jpg stylemargin-left: 10px; height: 300px; />/a>br />br />br />div styleclear: both;>/div>/div>div classpost-footer>div classpost-footer-line post-footer-line-1>span classpost-comment-link>a classcomment-link hrefhttps://blog.expressionsoftware.com/2016/12/arduino-blink-sketch.html#comment-form onclick>No comments: /a>/span>span classpost-icons>span classitem-control blog-admin pid-825337817>a hrefhttps://www.blogger.com/post-edit.g?blogID2362511696290915939&postID8403342461171573833&frompencil titleEdit Post>img alt classicon-action height18 src//resources.blogblog.com/img/icon18_edit_allbkg.gif width18/>/a>/span>/span>/div>div classpost-footer-line post-footer-line-2>span classpost-labels>/span>/div>div classpost-footer-line post-footer-line-3>/div>/div>/div>/div> /div>/div> div classdate-outer> h2 classdate-header>span>12/11/16/span>/h2> div classdate-posts> div classpost-outer>div classpost hentry itemscopeitemscope itemtype//schema.org/BlogPosting>a name630173669210213205>/a>h3 classpost-title entry-title itempropname>a hrefhttps://blog.expressionsoftware.com/2016/12/arduino.html>Arduino/a>/h3>div classpost-header>div classpost-header-line-1>/div>/div>div classpost-body entry-content idpost-body-630173669210213205 itemproparticleBody>b>span stylefont-family: Verdana, sans-serif; font-size: 19px>Uno/span>/b> span stylefont-family: Verdana, sans-serif; font-size: 14px;>5V/span>br />b>span stylefont-family: Verdana, sans-serif; font-size: 19px>101/span>/b> span stylefont-family: Verdana, sans-serif; font-size: 14px;>3.3V/span>br />b>span stylefont-family: Verdana, sans-serif; font-size: 19px>MKR1000/span>/b> span stylefont-family: Verdana, sans-serif; font-size: 14px;>3.3V/span>br />b>span stylefont-family: Verdana, sans-serif; font-size: 19px>Micro/span>/b> span stylefont-family: Verdana, sans-serif; font-size: 14px;>5V/span>br />br />iframe height451 srchttp://expressionsoftware.com/docs/arduino-boards.pdf width90% frameborder0 allowtransparencytrue stylebackground: #000000 scrollingno marginheight0 marginwidth0 allowfullscreen yes>/iframe>br />br />a hrefhttps://www.arduino.cc/en/Reference/HomePage>Arduino Language Reference/a>br/>a hrefhttp://expressionsoftware.com/docs/arduino-boards.pdf>Arduino Boards PDF/a>br />br />br />div styleclear: both;>/div>/div>div classpost-footer>div classpost-footer-line post-footer-line-1>span classpost-comment-link>a classcomment-link hrefhttps://blog.expressionsoftware.com/2016/12/arduino.html#comment-form onclick>No comments: /a>/span>span classpost-icons>span classitem-control blog-admin pid-825337817>a hrefhttps://www.blogger.com/post-edit.g?blogID2362511696290915939&postID630173669210213205&frompencil titleEdit Post>img alt classicon-action height18 src//resources.blogblog.com/img/icon18_edit_allbkg.gif width18/>/a>/span>/span>/div>div classpost-footer-line post-footer-line-2>span classpost-labels>/span>/div>div classpost-footer-line post-footer-line-3>/div>/div>/div>/div> /div>/div> div classdate-outer> h2 classdate-header>span>8/10/16/span>/h2> div classdate-posts> div classpost-outer>div classpost hentry itemscopeitemscope itemtype//schema.org/BlogPosting>a name2324325584917950238>/a>h3 classpost-title entry-title itempropname>a hrefhttps://blog.expressionsoftware.com/2013/10/c2.html>iOS macOS tvOS watchOS/a>/h3>div classpost-header>div classpost-header-line-1>/div>/div>div classpost-body entry-content idpost-body-2324325584917950238 itemproparticleBody>pre stylefont-family:courier new>// 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? s0 // subscript(idx: Int) -> Character? { guard idx > 0 && idx < count else { return nil } return selfindex(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? s0 // se let cs: Array<Character?> c, s1, s100 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(ss.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 ss.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 infoid: \(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.arguments0 // app path let arg1: String CommandLine.arguments1 let arg2: String CommandLine.arguments2 } //-------------------------------- 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) \(defaultskey!)") } // 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 attrsFileAttributeKey.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 { $0query.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(sr) // 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 { "\(bytesi) " |> 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(mirrori.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 representations0; 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 m222; float m4; } union GLKMatrix3 { struct { float m00, m01, m02; m10, m11, m12; m20, m21, m22; } float m9; } union GLKMatrix4 { struct { float m00, m01, m02, m03; m10, m11, m12, m13; m20, m21, m22, m23; m30, m31, m32, m33; } float m16; } union GLKVector2 { struct { float x, y; } float v2; } union GLKVector3 { struct { float x, y, z; } float v3; } union GLKVector4 { struct { float x, y, z, w; } float v4; } union GLKQuaternion { struct { GLKVector3 v; float s; // scalar component } struct { float x, y, z, w; } float q4; }- 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.userInfokey); } } -(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_t16; uid getUUIDBytes:bytes; for (int i 0; i< sizeof(bytes)/sizeof(unsigned char); i++) { printf("%d ", bytesi); // 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, @@30, @@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(datai, datai + 1); v2 GLKVector2Make(datai + 2, datai + 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 { .columns0 {1, 0, 0, 0}, .columns1 {0, 1, 0, 0}, .columns2 {0, 0, 1, 0}, .columns3 {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 m4, 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 mm4, # 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?id5570A4B1-7E8B-434B-8BDB-F02538FD0B76&extJPG"; */ 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?id5570A4B1-7E8B-434B-8BDB-F02538FD0B76&extJPG"; self loadPhotoAssetFromURL:alPhotoAssetURL; -(void)loadPhotoAssetFromLocalID:(NSString *)aPhotoAssetLocalID { NSArray * photoAssetLocalID @aPhotoAssetLocalID; PHFetchResult * fetchResult PHAsset fetchAssetsWithLocalIdentifiers:photoAssetLocalID options:nil; if (fetchResult.count > 0) { PHAsset * photoAsset fetchResult0; self loadPhotoAsset:photoAsset; } } -(void)loadPhotoAssetFromURL:(NSURL *)aALPhotoAssetURL { NSArray * alPhotoAssetURL @aALPhotoAssetURL; PHFetchResult * fetchResult PHAsset fetchAssetsWithALAssetURLs:alPhotoAssetURL options:nil; if (fetchResult.count > 0) { PHAsset * photoAsset fetchResult0; 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 *)constraintsi; 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++) { bindingsbuttonBindingsi buttonsi; } } -(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)", buttonBindingsi; // 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.dataindexPath.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, changeNSKeyValueChangeOldKey, changeNSKeyValueChangeNewKey); } - (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 buff20; 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 buff20; 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/pre>div styleclear: both;>/div>/div>div classpost-footer>div classpost-footer-line post-footer-line-1>span classpost-comment-link>a classcomment-link hrefhttps://blog.expressionsoftware.com/2013/10/c2.html#comment-form onclick>No comments: /a>/span>span classpost-icons>span classitem-control blog-admin pid-825337817>a hrefhttps://www.blogger.com/post-edit.g?blogID2362511696290915939&postID2324325584917950238&frompencil titleEdit Post>img alt classicon-action height18 src//resources.blogblog.com/img/icon18_edit_allbkg.gif width18/>/a>/span>/span>/div>div classpost-footer-line post-footer-line-2>span classpost-labels>/span>/div>div classpost-footer-line post-footer-line-3>/div>/div>/div>/div> /div>/div> /div>div classblog-pager idblog-pager>span idblog-pager-older-link>a classblog-pager-older-link hrefhttps://blog.expressionsoftware.com/search?updated-max2016-08-10T00:00:00-07:00&max-results7 idBlog1_blog-pager-older-link titleOlder Posts>Older Posts/a>/span>a classhome-link hrefhttps://blog.expressionsoftware.com/>Home/a>/div>div classclear>/div>div classblog-feeds>div classfeed-links>Subscribe to:a classfeed-link hrefhttps://blog.expressionsoftware.com/feeds/posts/default target_blank typeapplication/atom+xml>Comments (Atom)/a>/div>/div>/div>div classwidget BlogArchive data-version1 idBlogArchive2>h2>Blog Archive/h2>div classwidget-content>div idArchiveList>div idBlogArchive2_ArchiveList>ul classhierarchy>li classarchivedate expanded>a classtoggle hrefjavascript:void(0)>span classzippy toggle-open> ▼ /span>/a>a classpost-count-link hrefhttps://blog.expressionsoftware.com/2016/>2016/a>span classpost-count dirltr>(5)/span>ul classhierarchy>li classarchivedate expanded>a classtoggle hrefjavascript:void(0)>span classzippy toggle-open> ▼ /span>/a>a classpost-count-link hrefhttps://blog.expressionsoftware.com/2016/12/>December/a>span classpost-count dirltr>(2)/span>ul classposts>li>a hrefhttps://blog.expressionsoftware.com/2016/12/arduino-blink-sketch.html>Arduino Blink Sketch/a>/li>li>a hrefhttps://blog.expressionsoftware.com/2016/12/arduino.html>Arduino/a>/li>/ul>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttps://blog.expressionsoftware.com/2016/08/>August/a>span classpost-count dirltr>(3)/span>/li>/ul>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttps://blog.expressionsoftware.com/2015/>2015/a>span classpost-count dirltr>(7)/span>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttps://blog.expressionsoftware.com/2015/03/>March/a>span classpost-count dirltr>(3)/span>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttps://blog.expressionsoftware.com/2015/02/>February/a>span classpost-count dirltr>(3)/span>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttps://blog.expressionsoftware.com/2015/01/>January/a>span classpost-count dirltr>(1)/span>/li>/ul>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttps://blog.expressionsoftware.com/2014/>2014/a>span classpost-count dirltr>(5)/span>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttps://blog.expressionsoftware.com/2014/12/>December/a>span classpost-count dirltr>(5)/span>/li>/ul>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttps://blog.expressionsoftware.com/2013/>2013/a>span classpost-count dirltr>(4)/span>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttps://blog.expressionsoftware.com/2013/12/>December/a>span classpost-count dirltr>(1)/span>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttps://blog.expressionsoftware.com/2013/11/>November/a>span classpost-count dirltr>(1)/span>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttps://blog.expressionsoftware.com/2013/04/>April/a>span classpost-count dirltr>(1)/span>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttps://blog.expressionsoftware.com/2013/01/>January/a>span classpost-count dirltr>(1)/span>/li>/ul>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttps://blog.expressionsoftware.com/2012/>2012/a>span classpost-count dirltr>(1)/span>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttps://blog.expressionsoftware.com/2012/05/>May/a>span classpost-count dirltr>(1)/span>/li>/ul>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttps://blog.expressionsoftware.com/2011/>2011/a>span classpost-count dirltr>(14)/span>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttps://blog.expressionsoftware.com/2011/09/>September/a>span classpost-count dirltr>(1)/span>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttps://blog.expressionsoftware.com/2011/08/>August/a>span classpost-count dirltr>(2)/span>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttps://blog.expressionsoftware.com/2011/03/>March/a>span classpost-count dirltr>(2)/span>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttps://blog.expressionsoftware.com/2011/02/>February/a>span classpost-count dirltr>(5)/span>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttps://blog.expressionsoftware.com/2011/01/>January/a>span classpost-count dirltr>(4)/span>/li>/ul>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttps://blog.expressionsoftware.com/2010/>2010/a>span classpost-count dirltr>(25)/span>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttps://blog.expressionsoftware.com/2010/12/>December/a>span classpost-count dirltr>(2)/span>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttps://blog.expressionsoftware.com/2010/11/>November/a>span classpost-count dirltr>(3)/span>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttps://blog.expressionsoftware.com/2010/08/>August/a>span classpost-count dirltr>(1)/span>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttps://blog.expressionsoftware.com/2010/05/>May/a>span classpost-count dirltr>(6)/span>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttps://blog.expressionsoftware.com/2010/04/>April/a>span classpost-count dirltr>(4)/span>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttps://blog.expressionsoftware.com/2010/03/>March/a>span classpost-count dirltr>(6)/span>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttps://blog.expressionsoftware.com/2010/02/>February/a>span classpost-count dirltr>(2)/span>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttps://blog.expressionsoftware.com/2010/01/>January/a>span classpost-count dirltr>(1)/span>/li>/ul>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttps://blog.expressionsoftware.com/2009/>2009/a>span classpost-count dirltr>(8)/span>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttps://blog.expressionsoftware.com/2009/11/>November/a>span classpost-count dirltr>(4)/span>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttps://blog.expressionsoftware.com/2009/08/>August/a>span classpost-count dirltr>(3)/span>/li>/ul>ul classhierarchy>li classarchivedate collapsed>a classtoggle hrefjavascript:void(0)>span classzippy> ► /span>/a>a classpost-count-link hrefhttps://blog.expressionsoftware.com/2009/05/>May/a>span classpost-count dirltr>(1)/span>/li>/ul>/li>/ul>/div>/div>div classclear>/div>/div>/div>/div>/div>script typetext/javascript srchttps://www.blogger.com/static/v1/widgets/3845888474-widgets.js>/script>script typetext/javascript>window__wavt AOuZoY4jdlvkyAdZLqevyV-Oi-aclLRh-w:1768662232439;_WidgetManager._Init(//www.blogger.com/rearrange?blogID\x3d2362511696290915939,//blog.expressionsoftware.com/,2362511696290915939);_WidgetManager._SetDataContext({name: blog, data: {blogId: 2362511696290915939, title: Expression Software Blog, url: https://blog.expressionsoftware.com/, canonicalUrl: http://blog.expressionsoftware.com/, homepageUrl: https://blog.expressionsoftware.com/, searchUrl: https://blog.expressionsoftware.com/search, canonicalHomepageUrl: http://blog.expressionsoftware.com/, blogspotFaviconUrl: https://blog.expressionsoftware.com/favicon.ico, bloggerUrl: https://www.blogger.com, hasCustomDomain: true, httpsEnabled: true, enabledCommentProfileImages: false, gPlusViewType: FILTERED_POSTMOD, adultContent: false, analyticsAccountNumber: , encoding: UTF-8, locale: en, localeUnderscoreDelimited: en, languageDirection: ltr, isPrivate: false, isMobile: false, isMobileRequest: false, mobileClass: , isPrivateBlog: false, isDynamicViewsAvailable: true, feedLinks: \x3clink rel\x3d\x22alternate\x22 type\x3d\x22application/atom+xml\x22 title\x3d\x22Expression Software Blog - Atom\x22 href\x3d\x22https://blog.expressionsoftware.com/feeds/posts/default\x22 /\x3e\n\x3clink rel\x3d\x22alternate\x22 type\x3d\x22application/rss+xml\x22 title\x3d\x22Expression Software Blog - RSS\x22 href\x3d\x22https://blog.expressionsoftware.com/feeds/posts/default?alt\x3drss\x22 /\x3e\n\x3clink rel\x3d\x22service.post\x22 type\x3d\x22application/atom+xml\x22 title\x3d\x22Expression Software Blog - Atom\x22 href\x3d\x22https://www.blogger.com/feeds/2362511696290915939/posts/default\x22 /\x3e\n, meTag: , adsenseClientId: ca-pub-2622810652364465, adsenseHostId: ca-host-pub-1556223355139109, adsenseHasAds: false, adsenseAutoAds: false, boqCommentIframeForm: true, loginRedirectParam: , isGoogleEverywhereLinkTooltipEnabled: true, view: , dynamicViewsCommentsSrc: //www.blogblog.com/dynamicviews/4224c15c4e7c9321/js/comments.js, dynamicViewsScriptSrc: //www.blogblog.com/dynamicviews/2dfa401275732ff9, plusOneApiSrc: https://apis.google.com/js/platform.js, disableGComments: true, interstitialAccepted: false, sharing: {platforms: {name: Get link, key: link, shareMessage: Get link, target: }, {name: Facebook, key: facebook, shareMessage: Share to Facebook, target: facebook}, {name: BlogThis!, key: blogThis, shareMessage: BlogThis!, target: blog}, {name: X, key: twitter, shareMessage: Share to X, target: twitter}, {name: Pinterest, key: pinterest, shareMessage: Share to Pinterest, target: pinterest}, {name: Email, key: email, shareMessage: Email, target: email}, disableGooglePlus: true, googlePlusShareButtonWidth: 0, googlePlusBootstrap: \x3cscript type\x3d\x22text/javascript\x22\x3ewindow.___gcfg \x3d {\x27lang\x27: \x27en\x27};\x3c/script\x3e}, hasCustomJumpLinkMessage: false, jumpLinkMessage: Read more, pageType: index, pageName: , pageTitle: Expression Software Blog}}, {name: features, data: {}}, {name: messages, data: {edit: Edit, linkCopiedToClipboard: Link copied to clipboard!, ok: Ok, postLink: Post Link}}, {name: template, data: {name: custom, localizedName: Custom, isResponsive: false, isAlternateRendering: false, isCustom: true}}, {name: view, data: {classic: {name: classic, url: ?view\x3dclassic}, flipcard: {name: flipcard, url: ?view\x3dflipcard}, magazine: {name: magazine, url: ?view\x3dmagazine}, mosaic: {name: mosaic, url: ?view\x3dmosaic}, sidebar: {name: sidebar, url: ?view\x3dsidebar}, snapshot: {name: snapshot, url: ?view\x3dsnapshot}, timeslide: {name: timeslide, url: ?view\x3dtimeslide}, isMobile: false, title: Expression Software Blog, description: \x3ca href\x3d\x22https://www.expressionsoftware.com\x22\x3ehome\x3c/a\x3e, url: https://blog.expressionsoftware.com/, type: feed, isSingleItem: false, isMultipleItems: true, isError: false, isPage: false, isPost: false, isHomepage: true, isArchive: false, isLabelSearch: false}});_WidgetManager._RegisterWidget(_NavbarView, new _WidgetInfo(Navbar1, navbar, document.getElementById(Navbar1), {}, displayModeFull));_WidgetManager._RegisterWidget(_HeaderView, new _WidgetInfo(Header1, main, document.getElementById(Header1), {}, displayModeFull));_WidgetManager._RegisterWidget(_BlogView, new _WidgetInfo(Blog1, main, document.getElementById(Blog1), {cmtInteractionsEnabled: false, lightboxEnabled: true, lightboxModuleUrl: https://www.blogger.com/static/v1/jsbin/4049919853-lbx.js, lightboxCssUrl: https://www.blogger.com/static/v1/v-css/828616780-lightbox_bundle.css}, displayModeFull));_WidgetManager._RegisterWidget(_BlogArchiveView, new _WidgetInfo(BlogArchive2, main, document.getElementById(BlogArchive2), {languageDirection: ltr, loadingMessage: Loading\x26hellip;}, displayModeFull));/script>/body>/html>
View on OTX
|
View on ThreatMiner
Please enable JavaScript to view the
comments powered by Disqus.
Data with thanks to
AlienVault OTX
,
VirusTotal
,
Malwr
and
others
. [
Sitemap
]