今日已更新 166 条资讯 | 累计 40611 条内容
关于我们

标签:#ios

找到 134 篇相关文章

AI 资讯

GitHub Actions' free macOS minutes, explained

GitHub Actions is GitHub's built-in CI/CD system — it spins up a fresh virtual machine, runs whatever commands you tell it to, and tears the machine down when it's done. It supports Linux, Windows, and macOS runners. The macOS runners are the interesting part here, because they're actual macOS machines with Xcode's command-line build tools available, which means they can build and sign iOS apps — not just run tests. The headline rule: public repos are free On a public repository, standard GitHub-hosted runner minutes — including macOS — don't cost anything, on any plan, including the free plan. It's a genuine free tier, not a trial or a limited allowance that runs out. One nuance: this covers standard runners. GitHub also offers "larger runners" (more CPU/RAM) — those are billed regardless of repo visibility. A default macOS build for signing and archiving a typical app doesn't need one, so this rarely matters in practice. What changes on a private repo If your repository is private, you get a monthly allowance of free minutes instead of unlimited free usage: Plan Included minutes / month Free 2,000 Pro 3,000 Team 3,000 The number that actually matters for iOS builds is how fast macOS runners burn through that allowance. macOS minutes cost roughly 10x GitHub applies a multiplier against your included minutes: Linux runners run at the baseline rate, macOS runners run at roughly 10x that rate. A 6-minute macOS build eats through the same allowance as roughly 60 minutes of Linux CI. Applied to the table above, a Free-plan private repo effectively gets around 200 macOS-runner-minutes worth of free build time per month before you're billed per minute past it (Pro/Team works out to roughly 300). The practical upshot: if you're fine building in the open, a public repo gets you unlimited macOS build minutes at zero cost, indefinitely. If you'd rather keep the code private, everything about the pipeline still works the same way — you're just drawing from a metered allowance

2026-08-16 原文 →
AI 资讯

I set the font to the largest size and found the same bug eleven times

I was cleaning up the UI on a side-project iOS app and did one thing: set Dynamic Type to XXXL and screenshot every screen. Reading the code had turned up nothing. The screenshots showed problems immediately. Eleven of them, in the end. All the same cause. Here's the conclusion first. "A parent that pins things side by side" × "text that grows" is not a bug, it's a pattern. And in Japanese it breaks reliably worse than in English. English truncates. Japanese stacks vertically. Same layout, different failure depending on language. English wraps at word boundaries and, failing that, ends in … . You can't read it, but you can tell something was cut. Japanese doesn't do that. It can break between almost any two characters, so once a column is squeezed to one character wide, you get one character per line, stacked downward. Actual output: Rendered Intended Paire / d / Macs Paired Macs ( broken mid-word ) Claud / e / Code Claude Code One character per line "実行中のセッション" (Running sessions) Three step labels stacked vertically A three-step horizontal stepper De / mo , turning the capsule into a circle A "Demo" badge Paire / d / Macs is English breaking mid-word. Once the column has only a few characters left, even English gets there. Japanese gets there much earlier. The cause had the same shape every time Nearly all eleven were this: HStack { Image ( systemName : icon ) . frame ( width : 44 ) // fixed Text ( label ) Spacer () Text ( value ) // pinned right } The 44pt icon and the trailing value claim their width first, leaving the label column a few characters. The fix: rows that carry a value drop the value to the line below — but affordances like chevrons and toggles stay on the right. That row component was shared across the whole settings tree, so fixing one place fixed the entire settings screen. Which also means one decision inside a shared component was breaking eleven screens. ViewThatFits is not a general answer I used ViewThatFits to switch to a stacked layout. It

2026-08-16 原文 →
AI 资讯

Presentation: Migrating Uber Eats Feeds to Webview

Nick DiStefano shares how Uber Eats migrated from traditional native app screens to a native-driven, single-page WebView architecture. He explains key strategies for engineering leaders and software architects looking to bypass native release cycles, manage cross-platform state, build generic native-web message bridges, and execute large-scale UI migrations without degrading metrics. By Nick DiStefano

2026-08-13 原文 →
AI 资讯

The iOS Safari keyboard scroll bug, fixed with one line of CSS

If you build a full-screen mobile editor as a position: fixed overlay with a fixed toolbar on top and a nav bar on the bottom , iOS Safari will happily scroll your entire chrome off-screen the moment the soft keyboard opens — but only when the content is short . The fix isn't a JavaScript viewport dance. It's one line: .editor .ProseMirror { padding-bottom : 60vh ; } Give the inner scroll container something to scroll , and iOS keeps the scroll inside it instead of falling back to scrolling the document (which drags your "fixed" elements along). No html / body locking required. I hit this while building the mobile editor for PenPage , a local-first WYSIWYG markdown notes app (React + TipTap/ProseMirror). Everything below is verified on a real iOS device. The setup Picture a mobile note editor that takes over the whole screen: ┌──────────────────────────┐ │ Toolbar (absolute,top) │ ← stays put ├──────────────────────────┤ │ │ │ Editable content │ ← scrolls │ (overflow-y: auto) │ │ │ ├──────────────────────────┤ │ Nav bar (absolute,bottom)│ ← stays put └──────────────────────────┘ The outer container is position: fixed; inset: 0 . The toolbar and nav bar are position: absolute inside it. The middle is the only thing that scrolls. Standard app-shell layout. Works great on desktop and Android. The symptom Tap into the editor, the iOS keyboard slides up, and: Long document (taller than the viewport): perfect. The content scrolls under the keyboard, the toolbar and nav bar stay nailed in place. Short document (shorter than the viewport): broken. Trying to scroll drags the whole screen — toolbar and nav bar included — as if the entire fixed overlay were a normal scrolling page. That "only when short" detail is the whole story. The root cause This is the long tail of WebKit bug #191204 : when the soft keyboard appears, iOS Safari's layout viewport gets shorter than the visual viewport , and the document itself becomes scrollable by the keyboard's height. Worse, in that stat

2026-08-12 原文 →
产品设计

Code Signing for Android/iOS

Every dev course teaches you to build the app. Almost none teach you how to actually ship it. I found that out the hard way on my first job — no signing, no publish, no exceptions. So I wrote the full setup, both platforms, both ways: → Android: keystore via CLI and Android Studio's GUI → iOS: manual signing (certs, App IDs, provisioning profiles) AND automatic signing → The exact steps before every archive/build Full walkthrough → https://medium.com/@smitp7502/from-keystore-to-app-store-understanding-code-signing-for-android-ios-30671b5fd2a2 flutter #android #ios #mobiledev

2026-08-12 原文 →
AI 资讯

Ad-Hoc distribution vs TestFlight in React Native — a practical comparison

If you're testing an iOS build with real devices, you've got two main paths: Apple's TestFlight, or Expo's EAS Preview using Ad-Hoc provisioning. They solve the same problem — getting a build onto a real iPhone without the App Store — but the workflows are genuinely different, not just cosmetically. How each one works TestFlight uses Apple's official infrastructure. You upload your build to App Store Connect (often via npx testflight to speed this up), Apple processes/reviews it, and testers install the TestFlight app and accept an email or public link invite. No UDID collection needed — Apple handles device registration behind the scenes. Expo EAS Preview (Ad-Hoc) uses Ad-Hoc provisioning. You register each tester's device UDID against your Apple Developer account before building — either manually (eas device:create, eas device:list) or by having the tester scan a QR code that installs a temporary profile. Once devices are tied to your provisioning profile, you build with: bash eas build --platform ios --profile preview This generates a direct install link/QR code — no App Store account or TestFlight app required. Comparison table Feature Expo Preview / Ad-Hoc Apple TestFlight Device limit ~100 devices/device class/year (Apple Developer account tier) Up to 10,000 external testers Processing time Immediate after cloud build finishes Apple review/processing (mins to hours) UDID management Manual or profile-based registration required Not required, handled by Apple Best for Fast internal testing, client demos, strict ad-hoc distribution Larger-scale beta testing, staging before production Which one should you use? Fast internal iteration, client demos, small teams → Ad-Hoc. No waiting on Apple, instant install links. Wider beta testing before a production release → TestFlight. Built-in scale, no manual device management. Most teams I've worked with end up using both at different stages: Ad-Hoc during active development for quick feedback loops, TestFlight once the bui

2026-08-12 原文 →
AI 资讯

Adults don't want parental control apps. They want a wall they choose themselves

I keep seeing the same mismatch in the screen-time category: a lot of apps are technically blockers, but they feel like parental control software. That is fine if a parent is the customer. It is a bad fit if the user is an adult trying to manage their own habits. The difference is not cosmetic. When a blocker feels like surveillance, adults bounce. They do not want an account, a dashboard, or the feeling that their phone behavior is being watched somewhere else. That is the gap I built SproutGuard for: built for adults blocking themselves , not kids runs on-device through Apple's Screen Time APIs no account no server no usage data leaving the phone App Store: https://apps.apple.com/us/app/sproutguard-screen-time-detox/id6768664921?ct=devto-adults I also put the positioning plainly on the product page: self-control, not parental control The hard lesson from launching it is that being right about the problem is not the same thing as being shareable . Privacy architecture matters, but users rarely tell friends about architecture. What they do share is something emotional or visible: a streak, a mascot, a challenge, a before/after feeling. So the current working question for me is not "how do I explain on-device privacy better?" It is: How do you make a self-control product feel human enough that people talk about it? Website: https://shantj.github.io/sproutguard/ If you've worked on consumer productivity or habit products, I'm interested in what actually made users talk about them.

2026-08-11 原文 →
AI 资讯

Rendering a live 3D earthquake globe on iOS from the USGS feed

I wanted to see earthquakes the way they actually happen: as points lighting up on a spinning planet, in near real time. That became Earthquake: Live Seismic Monitor , an iOS app that renders a 3D globe of recent quakes straight from the USGS feed. No backend of my own - just the public data and the device. Here's how it comes together. The data source The USGS publishes earthquake data as GeoJSON feeds, updated continuously, at several time/magnitude cutoffs (past hour, past day, 2.5+, 4.5+, etc). Each feature has coordinates, magnitude, depth and time. That's everything you need to place a quake on a globe - no custom API required. The app polls the appropriate feed, diffs against what it already has, and updates the scene. Because USGS does the heavy lifting, the whole thing is effectively serverless from my side. Putting quakes on a globe The core mapping problem is turning (latitude, longitude) into a point on a sphere. Once you have that, each earthquake becomes a marker whose size and color encode magnitude and depth, so a glance tells you "big and shallow" vs "small and deep". Design decisions that mattered: Encode magnitude visually. Radius and color do more than any label. A magnitude 6 should look like a magnitude 6. Cluster sensibly. Active regions produce swarms; markers need to stay readable when dozens land in one area. Keep the globe interactive. Rotate, zoom, tap a quake for details. It should feel like an object, not a chart. Real-time without a server Every network-dependent app has to answer: what happens offline, and how fresh is "live"? My rules: Cache the last good feed so the globe still renders with no connection. Refresh on foreground and on an interval, and show the data's own timestamp so "live" is honest. Never block the UI on the network - render what you have, then update. Why no backend It's tempting to proxy the feed through your own server "for control". But USGS is reliable, public, and built for exactly this. Skipping a backend me

2026-08-10 原文 →
AI 资讯

Swift Protocols — Opaque Return Types and the Mystery of `some` 🔮

You've seen some View in every SwiftUI file you've ever opened. Now let's find out what it actually means, why it exists, and why returning a plain protocol doesn't work the same way. Fair warning: this topic is genuinely one of the more brain-bendy things in Swift. I'm going to tell you upfront that you don't need to fully understand the internals to keep going — but you do need to know it exists and roughly what it's doing, because you've already been using it every single time you've written a SwiftUI view. That some View in every SwiftUI file? That's an opaque return type. And now we're going to actually understand what that means. 🍥 Let's Start With Something That Works Two simple functions: func getRandomJutsu () -> Int { Int . random ( in : 1 ... 100 ) } func getRandomSuccess () -> Bool { Bool . random () } Both Int and Bool conform to a protocol called Equatable — which means they can be compared using == . So you can do this: print ( getRandomJutsu () == getRandomJutsu ()) That works fine, comparing two random integers. Now, since both return types conform to Equatable , you might think: what if we simplify both functions to return Equatable instead of their specific types? The Thing That Doesn't Work func getRandomJutsu () -> Equatable { // ❌ Int . random ( in : 1 ... 100 ) } func getRandomSuccess () -> Equatable { // ❌ Bool . random () } Swift refuses this with an error message so confusing it might as well be written in ancient runes: "protocol 'Equatable' can only be used as a generic constraint because it has Self or associated type requirements." Here's the actual problem in plain English: if both functions return Equatable , Swift loses track of what specific type is coming back. And if it doesn't know the specific type, it can't know whether two Equatable things can actually be compared to each other. Think about it: an Int and a Bool both conform to Equatable , but you can't compare them with == . That doesn't make sense. Swift isn't going to let y

2026-08-08 原文 →
AI 资讯

Understanding MVVM by Building a Simple Weather App with SwiftUI

MVVM with swiftUI When learning SwiftUI, one of the first architectural patterns you'll encounter is MVVM (Model-View-ViewModel). In this tutorial, we'll build a simple weather application that consumes the OpenWeather API while applying MVVM, dependency injection, and protocol-oriented programming. By the end, you'll understand not only how to structure the project, but also why each layer exists. This is the link for the OpenWeather API https://openweathermap.org/api . What you'll learn By the end of this tutorial you'll know how to: Structure a SwiftUI project using MVVM. Consume a REST API using async/await. Apply dependency injection using protocols. Display loading and error states. Keep Views focused only on UI. This is how the data flow looks. User taps "Search" │ ▼ ┌──────────────┐ │ ContentView │ └──────┬───────┘ │ await fetchWeather() │ ▼ ┌────────────────────┐ │ WeatherViewModel │ └─────────┬──────────┘ │ ▼ WeatherServiceProtocol │ ▼ ┌─────────────────┐ │ WeatherService │ └──────┬──────────┘ │ ▼ OpenWeather API Project Structure WeatherApp ├── Configuration │ └──AppConfig.swift ├── Models │ ├── Main.swift │ ├── Weather.swift │ └── WeatherResponse.swift ├── Services │ ├── WeatherService.swift │ └── WeatherServiceProtocol.swift ├── ViewModels │ └── WeatherViewModel.swift └── Views └── ContentView.swift Configuration contains application-wide constants such as the API key and base URLs. Models contains the data structures used to decode the API response. Services is responsible for networking and fetching data. ViewModels contains the presentation logic and exposes data to the UI. Views contains the SwiftUI interface. On the AppConfig file, we are going to keep static info, just like the base URL, API key, etc struct AppConfig { static let apiKey = "YOUR API KEY" static let baseGeoCodingAPIURL = "https://api.openweathermap.org/geo/1.0/direct?q=" static let baseURL = "https://api.openweathermap.org/data/2.5/weather?&units=metric&lat=" } Designing the UI Befo

2026-08-06 原文 →
AI 资讯

A Lightweight Rich Text Component Without a Web View

PR #5421 adds RichTextComponent , a read-only component for formatted application text. It supports headings, inline styles, lists, links, and images without embedding a web view. What is Codename One? Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at codenameone.com . A SpanLabel applies one style to wrapped text. A BrowserComponent renders a complete web page. RichTextComponent covers formatted document content between those two cases and participates in ordinary Codename One layout. Rich text inside a scrollable container A common screen mixes formatted text with buttons, images, forms, and other Codename One components inside one scrollable container. A BrowserComponent is a poor fit for that layout because it owns a rectangular native surface and its own page viewport. The browser's document height does not naturally become the height of a child inside the parent Codename One layout. RichTextComponent measures wrapped runs for the width it receives and reports the corresponding height. In the default SizeMode.SHRINK , it behaves like a SpanLabel : the parent container scrolls the rich text together with the surrounding components. SizeMode.SCROLL is available when the rich text should keep an assigned height and scroll its own content. The read-only view and editor agree on paragraph attributes, inline styles, links, image runs, and wrapping because they do not maintain competing renderers. Supply the format you already have HTML is not the only input: RichTextComponent view = new RichTextComponent (); view . setMarkdown ( "# Trip summary\n\n" + "Departs **09:40**, arrives *11:15*. " + "See the [itinerary](app://itinerary).\n\n" + "- Window seat\n" + "- Carry-on only" ); form . add ( view ); setContent(...) accepts RichTextFormat.HTML , MARKDOWN , ASCIIDOC , or RTF . The model covers headings, emphasis, inline code, links, images, lists, quotes, literal blocks, p

2026-08-04 原文 →
AI 资讯

Pure Codename One Text Editing Without Native Overlays

PR #5386 adds a pure Codename One text-editing path. EditField , RichTextArea , and CodeEditor can now keep their document, selection, and painting inside the lightweight UI while each port supplies keyboard and input-method events. What is Codename One? Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at codenameone.com . Text input must handle virtual keyboards, hardware keys, autocorrect, dictation, marked text from an input method editor, bidirectional text, selection, clipboard formats, and accessibility geometry. Codename One traditionally delegates that work to a native platform field placed over the lightweight component during editing. The overlay remains the default for TextField and TextArea . It can create a small visual jump, and it cannot participate in lightweight painting for syntax highlights, rich runs, masks, inline images, or a custom selection model. The port sends text operations instead of key codes A soft keyboard does not type keys. It commits words, replaces a marked composition range, deletes text around the caret, and changes selection. Dictation may insert a sentence without producing one key event. The new TextInputClient contract models those operations: commitText(...) inserts final text. setComposingText(...) replaces the active marked-text range. finishComposing() accepts that range. deleteSurroundingText(...) implements virtual-keyboard deletion. onKeyCommand(...) carries navigation, selection, clipboard, undo, and redo. Geometry queries locate the caret and selection for candidate windows and accessibility. All offsets use UTF-16 indices. That matches Java String , Android Editable , and Apple string APIs. The document normalizes line endings before it updates selection, undo history, formatting runs, or the state returned to the platform. The port still owns the keyboard session. Codename One owns the document and what appears on scr

2026-08-03 原文 →
AI 资讯

I automated my weight logging into Notion, and gave myself a new daily chore

What I wanted I'm building a system where all my daily records live in Notion, so I can point an AI at it and get feedback. Goals, tasks, daily logs, finances — those are all manual entry, and that's fine. But one day it hit me that weight would be nice to sync automatically. The requirements were simple: Every morning, my weight and body fat percentage get appended to a Notion database as one row No manual typing That's it. My scale is a Withings Body Smart. The design I picked first This one: Scale → vendor app → Apple Health → iOS Shortcut → Notion API I chose Apple Health as the hub for these reasons: It doesn't depend on the scale model. As long as the data lands in Health, the same implementation works for any vendor. No server required. A time-based Shortcuts automation handles it end to end — no always-on machine, no cron. Free. No extra subscription. Extensible later. Anything that's already in Health — steps, sleep, heart rate — could be added the same way (if I ever wanted to). Generic, zero cost, extensible. The design looked sound to me. Implementation Here's what the Shortcut looks like: 1. Find Health Samples [Weight] latest, limit 1 2. Get Details of Health Sample [Value] → variable Kg 3. Get Details of Health Sample [Start Date] → variable SampleDate 4. Format Date yyyy-MM-dd → variable Ymd 5. If Ymd == today 6. Text ← build the JSON 7. Get Contents of URL ← POST to the Notion API Step 5 matters. Without it, on a day you don't step on the scale, yesterday's weight gets appended under today's date . Here's the JSON built in step 6: { "parent" : { "database_id" : "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" }, "properties" : { "Date" : { "title" : [ { "text" : { "content" : "@@YMD@@" } } ] }, "Measured" : { "date" : { "start" : "@@YMD@@" } }, "Weight kg" : { "number" : @@KG@@ }, "Body fat %" : { "number" : @@FAT@@ } } } (My real database uses Japanese property names. What matters is that they match your database exactly.) I write this as a plain string in a

2026-08-01 原文 →
AI 资讯

Coordinate-based UI tests break. So we read the accessibility tree instead — from inside the simulator.

Every recorded mobile test I have ever inherited died the same way: someone moved a button. The recording said "tap at (340, 712)". The redesign moved that button up by one row, and the test kept tapping — now on empty space, or whatever happened to land there instead. It didn't fail right away. Three sprints later, it started failing in confusing ways, and by then nobody trusted the suite anymore. The fix isn't a better recorder. It's recording a different thing: not where you tapped, but what you tapped. That needs an element tree, and for a while we didn't have one. tapflow is an open-source, self-hosted tool that streams iOS simulators and Android emulators into a browser, so a whole team can test builds without installing anything. Until now, everything it moved was pixels in one direction and taps in the other. This post is about getting an element tree out of a simulator with no window, on both platforms. What we do with that tree — replaying flows that survive a redesign — is the next post in this series. The automation axis this feeds — the flow runner and the MCP server — is experimental . The manual browser QA path is the mature one. The constraint: no WebDriverAgent, and no simulator window tapflow already injects touches into the iOS simulator without WebDriverAgent — it loads CoreSimulator.framework and pushes HID events through SimDeviceLegacyHIDClient (that story is ep.1 ). Streaming reads the framebuffer IOSurface directly. Neither path needs Simulator.app on screen, and that's deliberate: an agent Mac in a closet running four simulators shouldn't be babysitting four windows. So whatever we used for the tree had to follow the same rule. No WDA to install and keep in sync with Xcode. No simulator window on screen. Our first attempt ran into exactly that limitation. macOS exposes an accessibility API ( AXUIElement ), and Simulator.app publishes its content through it. We wrote a helper around it, and it worked perfectly on a developer's laptop. On the

2026-07-30 原文 →
AI 资讯

Port Support You Can Trace Back to a Green Test

“Supported on iOS, Android, desktop, and web” sounds useful until you need one method on one target. Does WebSocket work on watchOS? Which Linux architectures do we build? Was the JavaScript media test green this week, or did somebody update a table six months ago and forget it? What is Codename One? Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at codenameone.com . PR #5389 turns those questions into the Codename One Port Status page . It maps 49 user-facing feature groups across 10 portability targets to current conformance results, environment data, skip reasons, and the date of the run. The table is an output, not an opinion The HelloCodenameOne suite already exercises APIs and screenshot goldens on Android, iOS, tvOS, watchOS, JavaScript, native Linux, native Windows, and Mac Catalyst. The missing part was a contract that translated thousands of test cases into a stable public vocabulary. The new conformance mapping connects registered tests and screenshots to rows such as networking, media, databases, maps, notifications, input, accessibility, and 3D. CI normalizes each port's result into the same report format. A publishing workflow writes the latest reports to a data-only branch. The website consumes those reports and renders the matrix. The page currently renders 490 feature cells. Ten targets appear because architectures and renderer variants matter. iOS Metal and legacy OpenGL are separate evidence paths. Windows x64 and ARM64 are separate. Linux x64 and ARM64 are separate. JavaSE is deliberately excluded from the public portability matrix. It is the simulator and development runtime, not one of the deployed native targets the table is meant to prove. A green cell has a chain of evidence Each status report records the commit, environment, registered tests, outcome, duration, and skipped cases. The website data also records the runtime used for browser and

2026-07-29 原文 →