Just like software, this document will rot unless we take care of it. We encourage everyone to help us on that – just open an issue or send a pull request!
Getting on board with iOS can be intimidating. Neither Swift nor Objective-C are widely used elsewhere, the platform has its own names for almost everything, and it's a bumpy road for your code to actually make it onto a physical device. This living document is here to help you, whether you're taking your first steps in Cocoaland or you're curious about doing things "the right way". Everything below is just suggestions, so if you have a good reason to do something differently, by all means go for it!
If you are looking for something specific, you can jump right into the relevant section from here.
If you're coming from another platform, do take some time to familiarize yourself with Apple's Human Interface Guidelines for the platform. There is a strong emphasis on good design in the iOS world, and your app should be no exception. The guidelines also provide a handy overview of native UI elements, technologies such as 3D Touch or Wallet, and icon dimensions for designers.
Xcode is the IDE of choice for most iOS developers, and the only one officially supported by Apple. There are some alternatives, of which AppCode is arguably the most famous, but unless you're already a seasoned iOS person, go with Xcode. Despite its shortcomings, it's actually quite usable nowadays!
To install, simply download Xcode on the Mac App Store. It comes with the newest SDK and simulators, and you can install more stuff under Preferences > Downloads.
A common question when beginning an iOS project is whether to write all views in code or use Interface Builder with Storyboards or XIB files. Both are known to occasionally result in working software. However, there are a few considerations:
To get the best of both worlds, you can also take a hybrid approach: Start off by sketching the initial design with Storyboards, which are great for tinkering and quick changes. You can even invite designers to participate in this process. As the UI matures and reliability becomes more important, you then transition into a code-based setup that's easier to maintain and collaborate on.
A good first step when putting a project under version control is to have a decent .gitignore
file. That way, unwanted files (user settings, temporary files, etc.) will never even make it into your repository. Luckily, GitHub has us covered for both Swift and Objective-C.
If you're planning on including external dependencies (e.g. third-party libraries) in your project, CocoaPods offers easy and fast integration. Install it like so:
sudo gem install cocoapods
To get started, move inside your iOS project folder and run
pod init
This creates a Podfile, which will hold all your dependencies in one place. After adding your dependencies to the Podfile, you run
pod install
to install the libraries and include them as part of a workspace which also holds your own project. For reasons stated here and here, we recommend committing the installed dependencies to your own repo, instead of relying on having each developer run pod install
after a fresh checkout.
Note that from now on, you'll need to open the .xcworkspace
file instead of .xcproject
, or your code will not compile. The command
pod update
will update all pods to the newest versions permitted by the Podfile. You can use a wealth of operators to specify your exact version requirements.
Carthage takes the "simple, not easy" approach by building your dependencies into binary frameworks, without magically integrating them with your project in any way. This also greatly reduces build times, because your dependencies have already been compiled by the time you start building.
There is no centralized repository of projects, which means any library that can be compiled into a framework supports Carthage out of the box.
To get started, follow the instructions in Carthage's documentation.
To keep all those hundreds of source files from ending up in the same directory, it's a good idea to set up some folder structure depending on your architecture. For instance, you can use the following:
├─ Models
├─ Views
├─ Controllers (or ViewModels, if your architecture is MVVM)
├─ Stores
├─ Helpers
First, create them as groups (little yellow "folders") within the group with your project's name in Xcode's Project Navigator. Then, for each of the groups, link them to an actual directory in your project path by opening their File Inspector on the right, hitting the little gray folder icon, and creating a new subfolder with the name of the group in your project directory.
Keep all user strings in localization files right from the beginning. This is good not only for translations, but also for finding user-facing text quickly. You can add a launch argument to your build scheme to launch the app in a certain language, e.g.
-AppleLanguages (Finnish)
For more complex translations such as plural forms that depending on a number of items (e.g. "1 person" vs. "3 people"), you should use the .stringsdict
format instead of a regular localizable.strings
file. As soon as you've wrapped your head around the crazy syntax, you have a powerful tool that knows how to make plurals for "one", some", "few" and "many" items, as needed e.g. in Russian or Arabic.
Find more information about localization in these presentation slides from the February 2012 HelsinkiOS meetup. Most of the talk is still relevant.
Keep your constants' scope as small as possible. For instance, when you only need it inside a class, it should live in that class. Those constants that need to be truly app-wide should be kept in one place. In Swift, you can use enums defined in a Constants.swift
file to group, store and access your app-wide constants in a clean way:
enum Config {
static let baseURL = NSURL(string: "http://www.example.org/")!
static let splineReticulatorName = "foobar"
}
enum Color {
static let primaryColor = UIColor(red: 0.22, green: 0.58, blue: 0.29, alpha: 1.0)
static let secondaryColor = UIColor.lightGray
// A visual way to define colours within code files is to use #colorLiteral
// This syntax will present you with colour picker component right on the code line
static let tertiaryColor = #colorLiteral(red: 0.22, green: 0.58, blue: 0.29, alpha: 1.0)
}
When using Objective-C, keep app-wide constants in a Constants.h
file that is included in the prefix header.
Instead of preprocessor macro definitions (via #define
), use actual constants:
static CGFloat const XYZBrandingFontSizeSmall = 12.0f;
static NSString * const XYZAwesomenessDeliveredNotificationName = @"foo";
Actual constants are type-safe, have more explicit scope (they’re not available in all imported/included files until undefined), cannot be redefined or undefined in later parts of the code, and are available in the debugger.
Especially when distributing an app to the public (e.g. through the App Store), it's a good idea to isolate releases to their own branch with proper tags. Also, feature work that involves a lot of commits should be done on its own branch. git-flow
is a tool that helps you follow these conventions. It is simply a convenience wrapper around Git's branching and tagging commands, but can help maintain a proper branching structure especially for teams. Do all development on feature branches (or on develop
for smaller work), tag releases with the app version, and commit to master only via
git flow release finish <version>
It’s useful to make an early decision on the minimum iOS version you want to support in your project: knowing which OS versions you need to develop and test against, and which system APIs you can rely on, helps you estimate your workload, and enables you to determine what’s possible and what’s not.
Use these resources to gather the data necessary for making this choice:
Generally speaking, make it a conscious decision to add an external dependency to your project. Sure, this one neat library solves your problem now, but maybe later gets stuck in maintenance limbo, with the next OS version that breaks everything being just around the corner. Another scenario is that a feature only achievable with external libraries suddenly becomes part of the official APIs. In a well-designed codebase, switching out the implementation is a small effort that pays off quickly. Always consider solving the problem using Apple's extensive (and mostly excellent) frameworks first!
Therefore this section has been deliberately kept rather short. The libraries featured here tend to reduce boilerplate code (e.g. Auto Layout) or solve complex problems that require extensive testing, such as date calculations. As you become more proficient with iOS, be sure to dive into the source here and there, and acquaint yourself with their underlying Apple frameworks. You'll find that those alone can do a lot of the heavy lifting.
The majority of iOS developers use one of these network libraries. While NSURLSession
is surprisingly powerful by itself, AFNetworking and Alamofire remain unbeaten when it comes to actually managing queues of requests, which is pretty much a requirement of any modern app. We recommend AFNetworking for Objective-C projects and Alamofire for Swift projects. While the two frameworks have subtle differences, they share the same ideology and are published by the same foundation.
As a general rule, don't write your date calculations yourself. Luckily, in DateTools you get an MIT-licensed, thoroughly tested library that covers pretty much all your calendar needs.
If you prefer to write your views in code, chances are you've heard of either Apple's awkward syntaxes – the regular NSLayoutConstraint
factory or the so-called Visual Format Language. The former is extremely verbose and the latter based on strings, which effectively prevents compile-time checking. Fortunately, they've addressed the issue in iOS 9, allowing a more concise specification of constraints.
If you're stuck with an earlier iOS version, Masonry/SnapKit remedies the problem by introducing its own DSL to make, update and replace constraints. PureLayout solves the same problem using Cocoa API style. For Swift, there is also Cartography, which builds on the language's powerful operator overloading features. For the more conservative, FLKAutoLayout offers a clean, but rather non-magical wrapper around the native APIs.
signals
or void
methods with custom completion blocks.UIViewController
subclasses part of the View and keeps them slim by maintaining all state in the ViewModel.These are the idiomatic ways for components to notify others about things:
Keep your models immutable, and use them to translate the remote API's semantics and types to your app. For Objective-C projects, Github's Mantle is a good choice. In Swift, you can use structs instead of classes to ensure immutability, and use Swift's Codable to do the JSON-to-model mapping. There are also few third party libraries available. SwiftyJSON and Argo are the popular among them.
With today's wealth of screen sizes in the Apple ecosystem and the advent of split-screen multitasking on iPad, the boundaries between devices and form factors become increasingly blurred. Much like today's websites are expected to adapt to different browser window sizes, your app should handle changes in available screen real estate in a graceful way. This can happen e.g. if the user rotates the device or swipes in a secondary iPad app next to your own.
Instead of manipulating view frames directly, you should use size classes and Auto Layout to declare constraints on your views. The system will then calculate the appropriate frames based on these rules, and re-evaluate them when the environment changes.
Apple's recommended approach for setting up your layout constraints is to create and activate them once during initialization. If you need to change your constraints dynamically, hold references to them and then deactivate/activate them as required. The main use case for UIView
's updateConstraints
(or its UIViewController
counterpart, updateViewConstraints
) is when you want the system to perform batch updates for better performance. However, this comes at the cost of having to call setNeedsUpdateConstraints
elsewhere in your code, increasing its complexity.
If you override updateConstraints
in a custom view, you should explicitly state that your view requires a constraint-based layout:
Swift:
override class var requiresConstraintBasedLayout: Bool {
return true
}
Objective-C:
+ (BOOL)requiresConstraintBasedLayout {
return YES
}
Otherwise, you may encounter strange bugs when the system doesn't call updateConstraints()
as you would expect it to. This blog post by Edward Huynh offers a more detailed explanation.
Use dependency injection, i.e. pass any required objects in as parameters, instead of keeping all state around in singletons. The latter is okay only if the state really is global.
Swift:
let fooViewController = FooViewController(withViewModel: fooViewModel)
Objective-C:
FooViewController *fooViewController = [[FooViewController alloc] initWithViewModel:fooViewModel];
Try to avoid bloating your view controllers with logic that can safely reside in other places. Soroush Khanlou has a good writeup of how to achieve this, and architectures like MVVM treat view controllers as views, thereby greatly reducing their complexity.
At the "ground level" of a mobile app is usually some kind of model storage, that keeps its data in places such as on disk, in a local database, or on a remote server. This layer is also useful to abstract away any activities related to the vending of model objects, such as caching.
Whether it means kicking off a backend request or deserializing a large file from disk, fetching data is often asynchronous in nature. Your store's API should reflect this by offering some kind of deferral mechanism, as synchronously returning the data would cause the rest of your app to stall.
If you're using ReactiveCocoa, SignalProducer
is a natural choice for the return type. For instance, fetching gigs for a given artist would yield the following signature:
Swift + ReactiveSwift:
func fetchGigs(for artist: Artist) -> SignalProducer<[Gig], Error> {
// ...
}
ObjectiveC + ReactiveObjC:
- (RACSignal<NSArray<Gig *> *> *)fetchGigsForArtist:(Artist *)artist {
// ...
}
Here, the returned SignalProducer
is merely a "recipe" for getting a list of gigs. Only when started by the subscriber, e.g. a view model, will it perform the actual work of fetching the gigs. Unsubscribing before the data has arrived would then cancel the network request.
If you don't want to use signals, futures or similar mechanisms to represent your future data, you can also use a regular callback block. Keep in mind that chaining or nesting such blocks, e.g. in the case where one network request depends on the outcome of another, can quickly become very unwieldy – a condition generally known as "callback hell".
Asset catalogs are the best way to manage all your project's visual assets. They can hold both universal and device-specific (iPhone 4-inch, iPhone Retina, iPad, etc.) assets and will automatically serve the correct ones for a given name. Teaching your designer(s) how to add and commit things there (Xcode has its own built-in Git client) can save a lot of time that would otherwise be spent copying stuff from emails or other channels to the codebase. It also allows them to instantly try out their changes and iterate if needed.
Asset catalogs expose only the names of image sets, abstracting away the actual file names within the set. This nicely prevents asset name conflicts, as files such as button_large@2x.png
are now namespaced inside their image sets. Appending the modifiers -568h
, @2x
, ~iphone
and ~ipad
are not required per se, but having them in the file name when dragging the file to an image set will automatically place them in the right "slot", thereby preventing assignment mistakes that can be hard to hunt down.
You can include the original vector graphics (PDFs) produced by designers into the asset catalogs, and have Xcode automatically generate the bitmaps from that. This reduces the complexity of your project (the number of files to manage.)
Xcode automatically tries to optimise resources living in asset catalogs (yet another reason to use them). Developers can choose from lossless and lossy compression algorithms. App icons are an exception: Apps with large or unoptimised app icons are known to be rejected by Apple. For app icons and more advanced optimisation of PNG files we recommend using pngcrush or ImageOptim, its GUI counterpart.
Apple pays great attention to keeping naming consistent. Adhering to their coding guidelines for Objective-C and API design guidelines for Swift makes it much easier for new people to join the project.
Here are some basic takeaways you can start using right away:
A method beginning with a verb indicates that it performs some side effects, but won't return anything:
- (void)loadView;
- (void)startAnimating;
Any method starting with a noun, however, returns that object and should do so without side effects:
- (UINavigationItem *)navigationItem;
+ (UILabel *)labelWithText:(NSString *)text;
It pays off to keep these two as separated as possible, i.e. not perform side effects when you transform data, and vice versa. That will keep your side effects contained to smaller sections of the code, which makes it more understandable and facilitates debugging.
MARK:
comments (Swift) and pragma marks (Objective-C) are a great way to group your methods, especially in view controllers. Here is a Swift example for a common structure that works with almost any view controller:
import SomeExternalFramework
class FooViewController : UIViewController, FoobarDelegate {
let foo: Foo
private let fooStringConstant = "FooConstant"
private let floatConstant = 1234.5
// MARK: Lifecycle
// Custom initializers go here
// MARK: View Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
// ...
}
// MARK: Layout
private func makeViewConstraints() {
// ...
}
// MARK: User Interaction
func foobarButtonTapped() {
// ...
}
// MARK: FoobarDelegate
func foobar(foobar: Foobar, didSomethingWithFoo foo: Foo) {
// ...
}
// MARK: Additional Helpers
private func displayNameForFoo(foo: Foo) {
// ...
}
}
The most important point is to keep these consistent across your project's classes.
Futurice does not have company-level guidelines for coding style. It can however be useful to peruse the style guides of other software companies, even if some bits can be quite company-specific or opinionated.
Even in an age where we trust our portable devices with the most private data, app security remains an often-overlooked subject. Try to find a good trade-off given the nature of your data; following just a few simple rules can go a long way here. A good resource to get started is Apple's own iOS Security Guide.
If your app needs to store sensitive data, such as a username and password, an authentication token or some personal user details, you need to keep these in a location where they cannot be accessed from outside the app. Never use NSUserDefaults
, other plist files on disk or Core Data for this, as they are not encrypted! In most such cases, the iOS Keychain is your friend. If you're uncomfortable working with the C APIs directly, you can use a wrapper library such as SSKeychain or UICKeyChainStore.
When storing files and passwords, be sure to set the correct protection level, and choose it conservatively. If you need access while the device is locked (e.g. for background tasks), use the "accessible after first unlock" variety. In other cases, you should probably require that the device is unlocked to access the data. Only keep sensitive data around while you need it.
Keep any HTTP traffic to remote servers encrypted with TLS at all times. To avoid man-in-the-middle attacks that intercept your encrypted traffic, you can set up certificate pinning. Popular networking libraries such as AFNetworking and Alamofire support this out of the box.
Take extra care to set up proper log levels before releasing your app. Production builds should never log passwords, API tokens and the like, as this can easily cause them to leak to the public. On the other hand, logging the basic control flow can help you pinpoint issues that your users are experiencing.
When using UITextField
s for password entry, remember to set their secureTextEntry
property to true
to avoid showing the password in cleartext. You should also disable auto-correction for the password field, and clear the field whenever appropriate, such as when your app enters the background.
When this happens, it's also good practice to clear the Pasteboard to avoid passwords and other sensitive data from leaking. As iOS may take screenshots of your app for display in the app switcher, make sure to clear any sensitive data from the UI before returning from applicationDidEnterBackground
.
Enable as many compiler warnings as possible and treat those warnings as errors -- it will be worth it in the long run.
For Objective-C code, add these values to the “Other Warning Flags” build setting:
-Wall
(enables lots of additional warnings)-Wextra
(enables more additional warnings)and then enable the “Treat warnings as errors” build setting.
To treat warnings as errors for Swift code, add -warnings-as-errors
to the "Other Swift Flags" build setting.
The Clang compiler (which Xcode uses) has a static analyzer that performs control and data flow analysis on your code and checks for lots of errors that the compiler cannot.
You can manually run the analyzer from the Product → Analyze menu item in Xcode.
The analyzer can work in either “shallow” or “deep” mode. The latter is much slower but may find more issues due to cross-function control and data flow analysis.
Recommendations:
Created by our very own Ali Rantakari, Faux Pas is a fabulous static error detection tool. It analyzes your codebase and finds issues you had no idea even existed. There is no Swift support yet, but the tool also offers plenty of language-agnostic rules. Be sure to run it before shipping any iOS (or Mac) app!
(Note: all Futurice employees get a free license to this — just ask Ali.)
When your app crashes, Xcode does not break into the debugger by default. To achieve this, add an exception breakpoint (click the "+" at the bottom of Xcode's Breakpoint Navigator) to halt execution whenever an exception is raised. In many cases, you will then see the line of code responsible for the exception. This catches any exception, even handled ones. If Xcode keeps breaking on benign exceptions in third party libraries e.g., you might be able to mitigate this by choosing Edit Breakpoint and setting the Exception drop-down to Objective-C.
For view debugging, Reveal and Spark Inspector are two powerful visual inspectors that can save you hours of time, especially if you're using Auto Layout and want to locate views that are collapsed or off-screen. Xcode also has integrated view debugger which is good enough and free to use.
Xcode comes with a profiling suite called Instruments. It contains a myriad of tools for profiling memory usage, CPU, network communications, graphics and much more. It's a complex beast, but one of its more straight-forward use cases is tracking down memory leaks with the Allocations instrument. Simply choose Product > Profile in Xcode, select the Allocations instrument, hit the Record button and filter the Allocation Summary on some useful string, like the prefix of your own app's class names. The count in the Persistent column then tells you how many instances of each object you have. Any class for which the instance count increases indiscriminately indicates a memory leak.
Pay extra attention to how and where you create expensive classes. NSDateFormatter
, for instance, is very expensive to create and doing so in rapid succession, e.g. inside a tableView:cellForRowAtIndexPath:
method, can really slow down your app. Instead, keep a static instance of it around for each date format that you need.
Including some analytics framework in your app is strongly recommended, as it allows you to gain insights on how people actually use it. Does feature X add value? Is button Y too hard to find? To answer these, you can send events, timings and other measurable information to a service that aggregates and visualizes them – for instance, Google Tag Manager. The latter is more versatile than Google Analytics in that it inserts a data layer between app and Analytics, so that the data logic can be modified through a web service without having to update the app.
A good practice is to create a slim helper class, e.g. AnalyticsHelper
, that handles the translation from app-internal models and data formats (FooModel
, NSTimeInterval
, …) to the mostly string-based data layer:
func pushAddItemEvent(with item: Item, editMode: EditMode) {
let editModeString = name(for: editMode)
pushToDataLayer([
"event": "addItem",
"itemIdentifier": item.identifier,
"editMode": editModeString
])
}
This has the additional advantage of allowing you to swap out the entire Analytics framework behind the scenes if needed, without the rest of the app noticing.
First you should make your app send crash logs onto a server somewhere so that you can access them. You can implement this manually (using PLCrashReporter and your own backend) but it’s recommended that you use an existing service instead — for example one of the following:
Once you have this set up, ensure that you save the Xcode archive (.xcarchive
) of every build you release. The archive contains the built app binary and the debug symbols (dSYM
) which you will need to symbolicate crash reports from that particular version of your app.
This section contains an overview of this topic — please refer here for more comprehensive information:
Even simple apps can be built in different ways. The most basic separation that Xcode gives you is that between debug and release builds. For the latter, there is a lot more optimization going on at compile time, at the expense of debugging possibilities. Apple suggests that you use the debug build configuration for development, and create your App Store packages using the release build configuration. This is codified in the default scheme (the dropdown next to the Play and Stop buttons in Xcode), which commands that debug be used for Run and release for Archive.
However, this is a bit too simple for real-world applications. You might – no, should! – have different environments for testing, staging and other activities related to your service. Each might have its own base URL, log level, bundle identifier (so you can install them side-by-side), provisioning profile and so on. Therefore a simple debug/release distinction won't cut it. You can add more build configurations on the "Info" tab of your project settings in Xcode.
xcconfig
files for build settingsTypically build settings are specified in the Xcode GUI, but you can also use configuration settings files (“.xcconfig
files”) for them. The benefits of using these are:
#include
other build settings files, which helps you avoid repeating yourself:Common.xcconfig
and #include
it in all the other files.#include "MyApp_Debug.xcconfig"
and override one of the settings.Find more information about this topic in these presentation slides.
A target resides conceptually below the project level, i.e. a project can have several targets that may override its project settings. Roughly, each target corresponds to "an app" within the context of your codebase. For instance, you could have country-specific apps (built from the same codebase) for different countries' App Stores. Each of these will need development/staging/release builds, so it's better to handle those through build configurations, not targets. It's not uncommon at all for an app to only have a single target.
Schemes tell Xcode what should happen when you hit the Run, Test, Profile, Analyze or Archive action. Basically, they map each of these actions to a target and a build configuration. You can also pass launch arguments, such as the language the app should run in (handy for testing your localizations!) or set some diagnostic flags for debugging.
A suggested naming convention for schemes is MyApp (<Language>) [Environment]
:
MyApp (English) [Development]
MyApp (German) [Development]
MyApp [Testing]
MyApp [Staging]
MyApp [App Store]
For most environments the language is not needed, as the app will probably be installed through other means than Xcode, e.g. TestFlight, and the launch argument thus be ignored anyway. In that case, the device language should be set manually to test localization.
Deploying software on iOS devices isn't exactly straightforward. That being said, here are some central concepts that, once understood, will help you tremendously with it.
Whenever you want to run software on an actual device (as opposed to the simulator), you will need to sign your build with a certificate issued by Apple. Each certificate is linked to a private/public keypair, the private half of which resides in your Mac's Keychain. There are two types of certificates:
Besides certificates, there are also provisioning profiles, which are basically the missing link between devices and certificates. Again, there are two types to distinguish between development and distribution purposes:
Development provisioning profile: It contains a list of all devices that are authorized to install and run the software. It is also linked to one or more development certificates, one for each developer that is allowed to use the profile. The profile can be tied to a specific app or use a wildcard App ID (*). The latter is discouraged, because Xcode is notoriously bad at picking the correct files for signing unless guided in the right direction. Also, certain capabilities like Push Notifications or App Groups require an explicit App ID.
Distribution provisioning profile: There are three different ways of distribution, each for a different use case. Each distribution profile is linked to a distribution certificate, and will be invalid when the certificate expires.
To sync all certificates and profiles to your machine, go to Accounts in Xcode's Preferences, add your Apple ID if needed, and double-click your team name. There is a refresh button at the bottom, but sometimes you just need to restart Xcode to make everything show up.
Sometimes you need to debug a provisioning issue. For instance, Xcode may refuse to install the build to an attached device, because the latter is not on the (development or ad-hoc) profile's device list. In those cases, you can use Craig Hockenberry's excellent Provisioning plugin by browsing to ~/Library/MobileDevice/Provisioning Profiles
, selecting a .mobileprovision
file and hitting Space to launch Finder's Quick Look feature. It will show you a wealth of information such as devices, entitlements, certificates, and the App ID.
When dealing with an existing app archive (.ipa
), you can inspect its provisioning profile in a similar fashion: Simply rename the *.ipa
to *.zip
, unpack it and find the .app
package within. From its Finder context menu, choose "Show Package Contents" to see a file called embedded.mobileprovision
that you can examine with the above method.
App Store Connect is Apple's portal for managing your apps on the App Store. To upload a build, Xcode requires an Apple ID that is part of the developer account used for signing. Nowadays Apple has allowed for single Apple IDs to be part of multiple App Store Connect accounts (i.e. client organizations) in both Apple's developer portal as well as App Store Connect.
After uploading the build, be patient as it can take up to an hour for it to show up under the Builds section of your app version. When it appears, you can link it to the app version and submit your app for review.
When validating in-app purchase receipts, remember to perform the following checks:
Whenever possible, design your IAP system to store the content for sale server-side, and provide it to the client only in exchange for a valid receipt that passes all of the above checks. This kind of a design thwarts common piracy mechanisms, and — since the validation is performed on the server — allows you to use Apple’s HTTP receipt validation service instead of interpreting the receipt PKCS #7
/ ASN.1
format yourself.
For more information on this topic, check out the Futurice blog: Validating in-app purchases in your iOS app.
Futurice • Creative Commons Attribution 4.0 International (CC BY 4.0)
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。