i0sdev
i0sdev
iOS dev
450 posts
Don't wanna be here? Send us removal request.
i0sdev · 9 years ago
Link
2 notes · View notes
i0sdev · 10 years ago
Link
1 note · View note
i0sdev · 10 years ago
Link
Draws simple SVG flow chart diagrams from textual representation of the diagram
1 note · View note
i0sdev · 10 years ago
Link
1 note · View note
i0sdev · 10 years ago
Text
Native UIKit apps on Apple Watch
Security This is not a jailbreak. This will not make a jailbreak possible. This is no less secure than regular, sandboxed iOS apps. It’s not a way to bypass the security systems in place on your device. It does, however, let you build/prototype the kinds of apps you can do on iOS, but can’t on watchOS right now. Thought it important to say this up front 😜.
Preamble Now that watchOS 2 and iOS 9 have GMed I figured it was a good time to write up one of the methods we came to for running ‘real’ native apps, i.e. ones that can use UIKit, on watchOS.
All of watchOS is based on the same frameworks we use on iOS - system apps use a framework that subclasses a bunch of UIKit apps, called PepperUICore. However, as developers, we’re not given the same kind of access to the system. We can’t use UIKit directly, or any high level graphics frameworks like OpenGLES, SpriteKit, SceneKit, CoreAnimation, etc. WatchKit apps are handcuffed, as it were, only able to display UI through the elements WatchKit provides. They cannot get the position of touches onscreen, or use swipe gestures, or multitouch.
I did not have an Apple Watch at launch, but I was convinced UIKit apps were possible with the existing SDKs. However, WWDC came and went, and still nobody had revealed a method to use UIKit on watchOS. I roped @b3ll into a torturous day & night of testing things, he in a cafe across from WWDC (with @saurik for much-needed moral support!), me across the Atlantic.
At the time, watchOS did not provide a system console log of any kind. It would provide crash reports, when it felt like it, and usually an hour after they happened. I had no hardware to test on here, so you can imagine how much pain & frustration this was. We were relying on the visual cues of different kind of app crashes to debug - endless spinner & black screen meant our code didn’t load, instant crash back to Carousel meant any one of three possible linker errors.
Eventually, a crashlog confirmed what I was hoping: watchOS was actually trying to load our modified binary, and our native code!
So… spent a night hacking with @b3ll (& @saurik!) - we got UIKit (& SceneKit) apps running on Apple Watch 🎉 (video) pic.twitter.com/khcpHgVsZo
— Steve T-S (@stroughtonsmith)
June 12, 2015
My [Nano]FileBrowser app running on Apple Watch natively 😁 (as modeled by @b3ll) pic.twitter.com/KRhvkJBMox
— Steve T-S (@stroughtonsmith)
June 13, 2015
After getting something reproducible, and porting over some UIKit apps, I celebrated by ordering an Apple Watch of my own - I did want to see if the same method worked on watchOS 1, after all. With a few tweaks, it did!
Method The method I stumbled upon is far from elegant, but it works across watchOS 1.x and watchOS 2.x. There are a half-dozen similar ways to do it on watchOS 2, but I haven’t seen others for watchOS 1 thus far. Objective-C used here to sidestep the complexity of embedded Swift libraries.
What follows is a brain dump of things necessary to get this working. I don’t have any easy ‘just download this sample project’ method right now. If you don’t understand Xcode targets, mach-o linking, and codesigning - turn back now! Xcode 7 (with WatchOS 2 SDK) required.
Key Elements
Modified WatchKit Stub
WatchOS Framework target
Build script to copy your framework binary to a dylib inside your WatchKit app
(Ideally) you’ll want to copy the missing iOS headers and frameworks to the WatchOS SDK
Desired Result You want to end up with an iOS app, with a WatchKit 1 Extension, with an embedded WatchKit 1 App, with “MyApp.dylib” inside it. The WatchKit 1 App binary needs to have a linker reference to MyApp.dylib instead of SockPuppetGizmo (check this with otool -l), and the same goes for _WatchKitStub/WK inside the WatchKit 1 App bundle. You want to make sure each element is signed properly, too.
The steps are pretty similar for watchOS 2-only apps, though the layout on disk changes (you have to place your dylib in the WatchKit App’s embedded frameworks directory).
Missing Headers & Frameworks watchOS is iOS; most of the frameworks you expect to be there are actually on-disk. However, the SDK will not include them, or have complete headers for them. Fortunately, Xcode 7 makes it ridiculously easy to link to frameworks through new text-based .tbd files that list all the symbols, and the supported architectures, for a framework. For the most part, you just need to copy the tbd files from the iPhoneOS SDK into the appropriate places in the WatchOS SDK, as well as their headers, then edit the tbd files to include ‘armv7k’. I’m sure somebody will automate this repetitive and thankless job ;-p
Modified WatchKit Stub The trick is to use install_name_tool to change the WatchKit’s stub’s dependency on SockPuppetGizmo (the framework that boots up WatchKit) to your framework. As this is a non-vital framework for a UIKit app, you don’t have to worry about it. Now, when the WatchKit stub app is loaded, instead of loading up WatchKit, it will load your framework/dylib.
N.B. apps that include a modified stub will not be accepted by iTunes Connect, so make sure to make a backup before changing it, and switch back to the original when you’re done with UIKit apps and need to actually ship things.
PLATFORMSDIR=`xcode-select -p`/Platforms install_name_tool -change /System/Library/PrivateFrameworks/SockPuppetGizmo.framework/SockPuppetGizmo @rpath/MyApp.dylib $PLATFORMSDIR/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/Library/Application\ Support/WatchKit/WK
New in watchOS 2 - you cannot modify the stub’s rpath, but ./Frameworks works as an existing library location, though the validator will now trip if you have an actual .framework bundle in there. On watchOS 1, you can modify the rpaths to include custom locations inside the WatchKit app bundle.
WatchOS Framework Target Effectively, all your app’s code will be in a WatchOS framework target, instead of an app. Your resources can be added to the WatchKit app’s assets bundle, and accessed through regular APIs (imageNamed, etc).
Then, all you need to do is call UIApplicationMain in your constructor, like regular UIKit apps. You can use __attribute__((constructor)) or linker flags to achieve this.
void __attribute__((constructor)) injected_main() {     @autoreleasepool {         UIApplicationMain(0, nil, @“UIApplication”, @“NativeAppDelegate”);     } }
You’ll want to take the compiled binary from your framework as a dynamic library (as per 'MyApp.dylib’).
Build Script You might notice at this point that WatchKit app targets can’t have build scripts, according to Xcode. Here’s where a little knowledge of the xcodeproj format might help - you can add a build script to the iOS target, then hand-edit the pbxproj file to add the reference for that shell script to the WatchKit App target. Or you could manually piece together your app with dylib and sign it all manually. There are many less clunky ways to do this than mine, so this part I’ll leave up to you. It took hours of trial and error to get something that ‘worked’, and I’m still not happy with it.
Next Steps If you want to start building native apps that look like system apps, you’ll want to class-dump PepperUICore and investigate some of the apps in the Simulator. PepperUICore classes are prefixed PUIC and are mostly subclasses of UIKit classes. Your root UIApplication subclass should be PUICApplication. To implement a Force Touch menu on a view controller, you override -canProvideActionController and -actionController. There’s also an ORBTapGestureRecognizer if you want direct usage of Force Touch. Writing native PepperUICore apps deserves its own post, sometime…
Misc Notes watchOS can be so incredibly fickle when installing binaries. I’ve seen this with non-hacked regular WatchKit apps too, but you can get into a state where the OS refuses to install a binary and gives you random error messages. Sometimes rebooting fixes this, sometimes removing the app from your phone helps. Sometimes the exact same binary won’t install one minute, and works the next.
watchOS 1’s ABI is incompatible with Xcode 7’s compiler, for various reasons that I’m sure Apple want to keep to themselves. While you can build working apps with the toolchain, all kinds of things crash inexplicably. Ideally, unless you have specific reason to try this on watchOS 1, stick to watchOS 2.
Most importantly - SockPuppet apps have strict entitlements that disallow all kinds of things, like networking. URL loading will only work with local files. You can’t touch networking frameworks, or AirDrop, or seemingly anything of use. While that is a pain, all kinds of other frameworks work fine like OpenGLES, SpriteKit, SceneKit, UIKit, etc, so it lets you prototype things impossible with WatchKit apps. With luck, WatchKit apps will grow those features over time, and make hacks like this irrelevant.
Addendum Adam has an addendum on his side of the process, and where he took it from here. Check it out! 🍕
youtube
15 notes · View notes
i0sdev · 10 years ago
Link
1 note · View note
i0sdev · 10 years ago
Photo
Tumblr media
MyFitnessPal – Reminder notifications are automatically disabled if you ignore them after a few days.
/via Andrew
277 notes · View notes
i0sdev · 11 years ago
Link
QA1727: describes how the TLS session cache affects HTTPS connections that need different TLS parameters.
0 notes
i0sdev · 11 years ago
Link
A short cheat-sheet with Xcode 6 Playground
1 note · View note
i0sdev · 11 years ago
Link
ios-good-practices - Good ideas for iOS development, by Futurice developers
4 notes · View notes
i0sdev · 11 years ago
Link
SIOSocket - Realtime iOS application framework (client) http://socket.io
2 notes · View notes
i0sdev · 11 years ago
Link
ios-snapshot-test-case - Snapshot view unit tests for iOS
A "snapshot test case" takes a configured UIView or CALayer and uses the renderInContext: method to get an image snapshot of its contents. It compares this snapshot to a "reference image" stored in your source code repository and fails the test if the two images don't match.
0 notes
i0sdev · 11 years ago
Link
BEMSimpleLineGraph - iOS library to create simple line graphs/charts (charting library).
1 note · View note
i0sdev · 11 years ago
Link
AMWaveTransition - Custom transition between viewcontrollers holding tableviews
0 notes
i0sdev · 11 years ago
Link
ASValueTrackingSlider - A UISlider subclass that displays the slider value in a popup view
Tumblr media
0 notes
i0sdev · 11 years ago
Link
TWMessageBarManager - An iOS manager for presenting system-wide notifications via a dropdown message bar.
Tumblr media
0 notes
i0sdev · 11 years ago
Link
xcpretty - Flexible and fast xcodebuild formatter
0 notes