Mac4Ever Refurb Store Guide d'achat Apple - Des conseils sur toute la gamme Apple Débuter sur Mac
Mac4Ever.com - Toute l\'actualité Apple, iPhone, iPad, iPod et Mac - Mise à jour quotidienne
Actu Dossiers Forum Photos/Podcasts Petites Annonces
Icône recherche
Annuler la recherche
1538 connectés

Tuto : Créer un app de météo avec SwiftUI

Xcode, AppleScript ou Java ? On en parle ici

Modérateurs : Odibi, Grumff, Bicus, Nix, Linschn, Phénix, yellowiscool

Message par Didier le 30/06/19, 15:35

Lorsqu’Apple a annoncé SwiftUI à la WWDC 2019, j’ai tout de suite voulu le prendre en main en regardant les sessions et en suivant des tutoriels sur internet. J’ai eu envie d’aller plus loin qu’une app de démo pour voir ce que SwiftUI pouvait proposer. J’ai donc décidé d’écrire une app de météo en utilisant exclusivement ce framework. Voici comment je m’y suis pris.



Tutoriel rédigé par Benjamin Pisano du studio français Lunabee


Note : pour suivre ce tuto directement sous Xcode, vous pouvez télécharger le projet sur Github.


L’API


Il existe de nombreuses API gratuites qui fournissent des données météo. J’ai choisi Dark Sky pour sa facilité d’utilisation. Vous devrez créer un compte pour générer votre clé d’API et faire fonctionner cette app. Vous pouvez créer votre compte ici et générer votre clé gratuitement.



Pour tester son bon fonctionnement, tapez dans votre terminal :

curl https://api.darksky.net/forecast/your_key/45.572353,5.915807


Cette commande devrait retourner un fichier JSON contenant les données météo de la ville de Chambéry. Vous êtes maintenant prêts à configurer le projet Xcode.

Xcode


Pour utiliser SwiftUI dans votre app, vous devez télécharger Xcode 11 beta sur le site Apple Developper dans la section téléchargements. Prévoyez une bonne connexion internet puisque Xcode 11 pèse un peu plus de 5Go.



Une fois le téléchargement terminé, vous pouvez enfin commencer à créer votre projet avec SwiftUI. Vous êtes impatients ? Moi aussi ! ?

Ouvrez Xcode, cliquez sur « Create New Project » (ou allez dans File > New > Project…). Sélectionnez Single View App. Nommez votre projet “Weather” et vérifiez que la case SwiftUI est bien cochée. Cliquez sur « next » pour créer le projet.


Le panneau de configuration de votre projet.


Nous sommes maintenant prêts à passer au code.

Créer le modèle


Météo

Maintenant que vous connaissez l’url permettant de récupérer les données météo d’une ville, créons notre modèle qui ira récupérer ces données.

Notre modèle devrait ressembler à ceci :

struct Weather: Codable {

var current: HourlyWeather
var hours: Weather.List
var week: Weather.List

enum CodingKeys: String, CodingKey {

case current = "currently"
case hours = "hourly"
case week = "daily"

}

}

Weather.swift


extension Weather {

struct List: Codable {

var list: [T]

enum CodingKeys: String, CodingKey {

case list = "data"

}

}

}

WeatherList.swift


struct DailyWeather: Codable, Identifiable {

var id: Date {
return time
}

var time: Date
var maxTemperature: Double
var minTemperature: Double
var icon: Weather.Icon

enum CodingKeys: String, CodingKey {

case time = "time"
case maxTemperature = "temperatureHigh"
case minTemperature = "temperatureLow"
case icon = "icon"

}

}

DailyWeather.swift


struct HourlyWeather: Codable, Identifiable {

var id: Date {
return time
}

var time: Date
var temperature: Double
var icon: Weather.Icon

}

HourlyWeather.swift


extension Weather {

enum Icon: String, Codable {

case clearDay = "clear-day"
case clearNight = "clear-night"
case rain = "rain"
case snow = "snow"
case sleet = "sleet"
case wind = "wind"
case fog = "fog"
case cloudy = "cloudy"
case partyCloudyDay = "partly-cloudy-day"
case partyCloudyNight = "partly-cloudy-night"

var image: Image {
switch self {
case .clearDay:
return Image(systemName: "sun.max.fill")
case .clearNight:
return Image(systemName: "moon.stars.fill")
case .rain:
return Image(systemName: "cloud.rain.fill")
case .snow:
return Image(systemName: "snow")
case .sleet:
return Image(systemName: "cloud.sleet.fill")
case .wind:
return Image(systemName: "wind")
case .fog:
return Image(systemName: "cloud.fog.fill")
case .cloudy:
return Image(systemName: "cloud.fill")
case .partyCloudyDay:
return Image(systemName: "cloud.sun.fill")
case .partyCloudyNight:
return Image(systemName: "cloud.moon.fill")
}
}

}

}

WeatherIcon.swift


Vous pouvez également créer un WeatherManager qui inclura votre clé d’API Dark Sky.

class WeatherManager {

static let key: String = "" // Enter your darkSky API Key here
static let baseURL: String = "https://api.darksky.net/forecast/\(key)/"

}

WeatherManager.swift


Notre modèle est plutôt simple. Il est conforme au protocole Codable qui nous permet de convertir facilement le JSON retourné par l’API en objet dans notre app. C’est un modèle très classique, c’est pourquoi je ne l’expliquerais pas en détail. Maintenant, créons notre modèle pour chaque ville.

Villes

Le modèle des villes est un peu plus intéressant car il introduit le concept de « Binding ».


Attendez… Quoi ? Le Binding ?


Le concept de binding a été introduit en premier lieu sur Mac OS X dans Interface Builder. Il permettait d’observer une variable et de mettre à jour la partie UI de l’app automatiquement en fonction des changements du modèle. Pas de delegate, pas de completion handler : voici le binding. Dans Mac OS X, il n’était pas très facile d’utiliser le binding. C’était encore pire de debugger avec. Avec le temps, Apple est revenu sur un modèle avec des delegates pour ses composants plutôt que du binding, principalement pour réduire le fossé qui se creusait entre AppKit et UIKit. Ce dernier n’utilisait pas le binding, un concept complètement absent sur iOS.


Le binding avec Storyboard sur macOS.


Note : le concept de binding n’est pas spécifique à Swift ou Objective-C.


Aujourd’hui, avec Combine et SwiftUI, Apple remet le binding au goût du jour, d’une manière bien plus élégante. Découvrons-le en créant notre modèle pour les villes.

Tout d’abord, créons notre objet City qui contiendra le nom de la ville et ses données météo. Notre objet devrait ressembler à cela :

import SwiftUI
import Combine

class City: BindableObject {

var didChange = PassthroughSubject()

var name: String
var weather: Weather? {
didSet {
didChange.send(self)
}
}

init(name: String) {
self.name = name
self.getWeather()
}

private func getWeather() {
guard let url = URL(string: WeatherManager.baseURL + "45.572353,5.915807?units=ca&lang=fr") else {
return
}

URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data else {
return
}

do {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .secondsSince1970

let weatherObject = try decoder.decode(Weather.self, from: data)

DispatchQueue.main.async {
self.weather = weatherObject
}
} catch {
print(error.localizedDescription)
}
}.resume()
}

}

City.swift


Ce code contient plusieurs nouveaux concepts. Pas de panique ! Nous allons les expliquer pas à pas.

Premièrement, vous pouvez voir que l’objet City est conforme au protocole « BindableObject » qui est un nouveau protocole introduit avec « Combine ». « BindableObject » permet à notre objet d’être observé lorsque ses propriétés sont modifiées. Cela requiert un appel fonction dans le setter de nos variables qui ont besoin d’être observées. didChange.send(self) doit être appelé lorsque vous souhaitez notifier que cet objet a subi un changement. Vous pouvez voir ici que nous appelons cette fonction dans le setter de la variable weather. Cela signifie qu’à chaque fois que la variable weather sera modifiée, les observateurs de l’objet City seront notifiés qu’il y a eu un changement sur cet objet et qu’il faut probablement mettre à jour l’interface.

Note : Ici, je n’appelle pas didChange.send(self) dans le setter de la variable name, car on supposera que le nom d’une ville ne change jamais une fois qu’elle est initialisée.


La fonction getWeather() va récupérer les données météo de la ville de manière asynchrone dès que la ville est initialisée. Lorsque les données sont récupérées, elle décode le JSON et modifie la variable weather.

Note importante : d’après la documentation Apple, didChange.send(self) doit être appelée uniquement sur le thread principal. C’est pourquoi j’utilise DispatchQueue.main pour modifier la variable weather puisque URLSession s’exécute en arrière plan.


Vous venez de créer votre objet bindable. Beau travail.



Maintenant, créons un CityStore qui contiendra la liste de nos villes. CityStore devra aussi être bindable pour pouvoir observer si une ville a été ajoutée ou supprimée. Mais maintenant que vous avez compris le principe du binding, ça va être super simple non ?

import SwiftUI
import Combine

class CityStore: BindableObject {

let didChange = PassthroughSubject()

var cities: [City] = [City(name: "Chambery")] {
didSet {
didChange.send(self)
}
}

}

CityStore.swift



Prenons une petite pause pour résumer ce que nous venons de voir.

Le protocole « BindableObject » nous permet d’observer les changements d’un objet en appelant didChange.send(self) dans le setter de ses variables.

Nous avons créé un objet « City » qui est conforme au protocole BindableObject pour observer les changements d’une ville (lorsque les données météo sont récupérées).

Nous avons créé un CityStore qui est conforme au protocole « BindableObject » pour observer les changements dans notre liste de villes (lorsque l’utilisateur ajoute ou supprime une ville).

L’interface utilisateur


Si vous n’avez jamais vu à quoi ressemble une interface faite avec SwiftUI, je vous invite à regarder les tutoriels d’Apple qui sont excellents pour se familiariser avec la conception d’interfaces.

Créer une interface utilisateur avec SwiftUI est très simple et assez amusant. J’ai toujours utilisé Storyboard pour concevoir mes interfaces, mais SwiftUI a mis la barre très haute. En réduisant significativement le code nécéssaire pour concevoir une interface, SwiftUI permet plus d’itérations, ce que je considère comme un réel avantage pour les développeurs et designers.

Storyboard n’existe plus, puisque SwiftUI génère, en direct, un aperçu de votre app. Si votre code change, l’aperçu se met à jour. Mais encore plus fort : si vous changez l'aperçu, votre code se met à jour aussi. C’est presque magique.


Le code et l’aperçu. (Note : vous aurez besoin de macOS 10.15 Catalina pour avoir l’aperçu)


Maintenant, créons notre vue qui contiendra nos villes.

import SwiftUI

struct CityListView : View {

@EnvironmentObject var cityStore: CityStore

@State var isAddingCity: Bool = false
@State private var isEditing: Bool = false

var body: some View {
NavigationView {
List {
Section(header: Text("Your Cities")) {
ForEach(cityStore.cities) { city in
CityRow(city: city)
}
.onDelete(perform: delete)
.onMove(perform: move)
}
}
.navigationBarItems(leading: EditButton(), trailing: addButton)
.navigationBarTitle(Text("Weather"))
}
}

private var addButton: some View {
Button(action: {
self.isAddingCity = true
self.isEditing = false
}) {
Image(systemName: "plus.circle.fill")
.font(.title)
}
.presentation(isAddingCity ? newCityView : nil)
}

private func delete(at offsets: IndexSet) {
for index in offsets {
cityStore.cities.remove(at: index)
}
}

private func move(from source: IndexSet, to destination: Int) {
var removeCities: [City] = []

for index in source {
removeCities.append(cityStore.cities[index])
cityStore.cities.remove(at: index)
}

cityStore.cities.insert(contentsOf: removeCities, at: destination)
}

private var newCityView: Modal {
Modal(NewCityView(isAddingCity: $isAddingCity).environmentObject(cityStore)) {
self.isAddingCity = false
}
}

}

CityListView.swift


Ce code devrait être plutôt facile à comprendre, mais il y quelque nouveaux mots clés. Encore une fois, expliquons-les pas à pas.

Note : Swift 5.1 introduit une nouvelle fonctionnalité appelée Property Wrappers. Je vous recommande cet article qui explique ce concept un peu plus en détail.


var body: some View


Cette variable représente le corps de notre vue. C’est ici que vous allez construire votre interface.

@EnvironmentObject


@EnvironmentObject nous permet d’utiliser une seule instance d’un objet qui sera utilisée globalement dans l’app. Pratique pour des objets comme un utilisateur, qui est utilisé partout dans une app.

@State


@State est un Property Wrapper qui représente un état dans une vue. @State ne doit être utilisé que dans une vue et déclaré private pour éviter de l’utiliser n’importe où. Par exemple, dans notre CityListView, il y a un état qui définit si l’utilisateur est en train d’éditer la liste des villes.

@Binding


@Binding est utilisé pour binder une variable @State. Dans certains cas, vous aurez besoin de passer un état dans une vue enfant de votre hiérarchie. Vous pouvez alors utiliser @Binding dans votre vue enfant et lui passer votre variable @State.

@ObjectBinding


@ObjectBinding est utilisé pour observer les changements d’un objet qui répond au protocole BindableObject que nous avons vu précédemment.

Ces propriétés vont nous permettre de garder notre interface à jour en fonction de notre modèle.

Pour faire de CityListView la première vue qui sera présentée dans notre app, modifiez le fichier SceneDelegate.swift comme ceci :

import UIKit
import SwiftUI

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

var window: UIWindow?

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Use a UIHostingController as window root view controller
let window = UIWindow(frame: UIScreen.main.bounds)
let cityStore = CityStore()
window.rootViewController = UIHostingController(rootView: CityListView().environmentObject(cityStore))
self.window = window
window.makeKeyAndVisible()
}

}

SceneDelegate.swift


Ici, nous instancions un CityStore et le passons en tant qu’EnvironmentObject à notre CityListView.

Nous pouvons maintenant créer une vue météo pour les villes :

import SwiftUI

struct CityView : View {

@ObjectBinding var city = City(name: "Chambéry")

var body: some View {
List {
Section(header: Text("Now")) {
CityHeaderView(city: city)
}

Section(header: Text("Hourly")) {
CityHourlyView(city: city)
}

Section(header: Text("This week")) {
ForEach(city.weather?.week.list ?? []) { day in
CityDailyView(day: day)
}
}
}
.navigationBarTitle(Text(city.name))
}

}

CityView.swift


Note : Nous utilisons un @ObjectBinding dans notre CityView. Cela permet à l’utilisateur d’accéder à cette vue même si les données météo n’ont pas encore été récupérées. Une fois celle-ci chargées, tous les éléments d’interface qui contiennent une référence à cet objet sera re-rendu à l’écran. Cela ne fonctionne que si votre objet est conforme au protocole BindableObject.


Maintenant, toutes les vues des données météo :

struct CityHeaderView: View {

@ObjectBinding var city: City

var temperature: String {
guard let temperature = city.weather?.current.temperature else {
return "-ºC"
}
return temperature.formattedTemperature
}

var body: some View {
HStack(alignment: .center) {
Spacer()
HStack(alignment: .center, spacing: 16) {
city.weather?.current.icon.image
.font(.largeTitle)
Text(temperature)
.font(.largeTitle)
}
Spacer()
}
.frame(height: 110)
}

}

CityHeaderView.swift


struct CityHourlyView : View {

@ObjectBinding var city: City

private let rowHeight: CGFloat = 110

var body: some View {
ScrollView(alwaysBounceHorizontal: true, showsHorizontalIndicator: false) {
HStack(spacing: 16) {
ForEach(city.weather?.hours.list ?? []) { hour in
VStack(spacing: 16) {
Text(hour.time.formattedHour)
.font(.footnote)
hour.icon.image
.font(.body)
Text(hour.temperature.formattedTemperature)
.font(.headline)
}
}
}
.frame(height: rowHeight)
.padding(.trailing)
}
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
.frame(height: rowHeight)
}

}

CityHourlyView.swift


struct CityDailyView : View {

@State var day: DailyWeather

var body: some View {
ZStack {
HStack(alignment: .center) {
Text(day.time.formattedDay)
Spacer()
HStack(spacing: 16) {
verticalTemperatureView(min: true)
verticalTemperatureView(min: false)
}
}
HStack(alignment: .center) {
Spacer()
day.icon.image
.font(.body)
Spacer()
}
}
}

func verticalTemperatureView(min: Bool) -> some View {
VStack(alignment: .trailing) {
Text(min ? "min" : "max")
.font(.footnote)
.color(.gray)
Text(min ? day.minTemperature.formattedTemperature : day.maxTemperature.formattedTemperature)
.font(.headline)
}
}

}

CityDailyView.swift

Note : La CollectionView n’est pas encore un composant disponible dans SwiftUI. Pour pallier cette absence, nous utilisons ici une ScrollView contenant une HStack pour la vue par heure.


Enfin, nous pouvons créer une vue pour l’ajout d’une nouvelle ville. Construisons notre modèle qui nous suggérera des villes en fonction d’une recherche. Encore une fois, ce modèle sera bindable pour pouvoir observer les résultats de manière asynchrone.

import SwiftUI
import Combine
import MapKit

class CityFinder: NSObject, BindableObject {

var didChange = PassthroughSubject()

var results: [String] = [] {
didSet {
didChange.send(self)
}
}

private var searcher: MKLocalSearchCompleter

override init() {
results = []
searcher = MKLocalSearchCompleter()
super.init()
searcher.resultTypes = .address
searcher.delegate = self
}

func search(_ text: String) {
searcher.queryFragment = text
}

}

extension CityFinder: MKLocalSearchCompleterDelegate {

func completerDidUpdateResults(_ completer: MKLocalSearchCompleter) {
results = completer.results.map({ $0.title })
}

}

CityFinder.swift


Nous utiliserons MapKit pour nous suggérer des villes.

Construisons maintenant notre vue modale pour ajouter une ville :

struct NewCityView : View {

@Binding var isAddingCity: Bool
@State private var search: String = ""

@ObjectBinding var cityFinder: CityFinder = CityFinder()
@EnvironmentObject var cityStore: CityStore

var body: some View {
NavigationView {
List {
Section {
TextField($search, placeholder: Text("Search City")) {
self.cityFinder.search(self.search)
}
}

Section {
ForEach(cityFinder.results.identified(by: \.self)) { result in
Button(action: {
self.addCity(from: result)
self.isAddingCity = false
}) {
Text(result)
}
.foregroundColor(.black)
}
}
}
.navigationBarTitle(Text("Add City"))
.navigationBarItems(leading: cancelButton)
.listStyle(.grouped)
}
}

private var cancelButton: some View {
Button(action: {
self.isAddingCity = false
}) {
Text("Cancel")
}
}

private func addCity(from result: String) {
let cityName = result.split(separator: ",").first ?? ""
let city = City(name: String(cityName))
cityStore.cities.append(city)
}

}

NewCityView.swift


Cette vue est intéressante pour deux raisons :

1) Elle nous montre comment afficher une vue modale avec SwiftUI. Pour se faire, vous aurez besoin de passer une variable qui indique si la vue doit être affichée ou non. Actuellement, ce fonctionnement est un peu bugué.

2) @EnvironmentObject nous permet encore une fois d’utiliser une seule instance de notre CityStore à travers notre app avec du binding.

Et voilà ! Vous venez de créer votre première app avec SwiftUI.

Et après ?


Vous pouvez télécharger le projet complet sur mon Github. Vous pouvez aussi me contacter sur Twitter @benjamin_pisano. Et enfin, vous pouvez découvrir mon app sur le Mac AppStore.
Avatar de l’utilisateur
Didier
Mac4Ever's Flood
 
Message(s) : 35625
Inscription : 28/11/00, 1:00
Localisation : Lausanne / Lyon

Re : Tuto : Créer un app de météo avec SwiftUI

Message par Macintosh le 30/06/19, 15:46

Merci !
Avatar de l’utilisateur
Macintosh
Mac4Ever's Killer
 
Message(s) : 1945
Inscription : 30/11/00, 1:00

Re : Tuto : Créer un app de météo avec SwiftUI

Message par Microbd le 30/06/19, 16:18

A quand un bouton suite sur les articles longs ? :o
Avatar de l’utilisateur
Microbd
Mac4Everien confirmé
 
Message(s) : 337
Inscription : 23/07/09, 13:54
Localisation : Tours

Re : Tuto : Créer un app de météo avec SwiftUI

Message par Genki le 30/06/19, 16:27

C'est en voyant tout ce code, que je me dis Didier qu'il faudra pensé à implémenter une petite coloration syntaxique si des tutos de ce genre (qui sont bienvenus !) venaient à être publiés :arrow:
Genki
Mac4Everien confirmé
 
Message(s) : 433
Inscription : 16/12/11, 11:41

Re : Tuto : Créer un app de météo avec SwiftUI

Message par geooooooooffrey le 30/06/19, 16:37

Sympa de faire un article sur ce sujet, par contre il faudrait une coloration syntaxique du code, parce que présenté comme ça c'est pas très incitatif à la lecture.
geooooooooffrey
Jeune Tourteau de Mac4Ever
 
Message(s) : 16
Inscription : 27/08/17, 0:55

Envoyé depuis Mac4Ever Mobile

Message par Didier le 30/06/19, 17:17

Oui vous avez raison, il faudrait de la colo mais les inserts github rament trop :/
Avatar de l’utilisateur
Didier
Mac4Ever's Flood
 
Message(s) : 35625
Inscription : 28/11/00, 1:00
Localisation : Lausanne / Lyon

Re : Tuto : Créer un app de météo avec SwiftUI

Message par spiral le 30/06/19, 19:09

Hmmm, avec Vim par exemple on peut faire un export HTML avec coloration syntaxique, sans besoin d'insert GitHub ?

:TOhtml
spiral
Jeune Tourteau de Mac4Ever
 
Message(s) : 56
Inscription : 06/10/07, 9:58

Envoyé depuis Mac4Ever Mobile

Message par cantor le 30/06/19, 19:58

Je trouve que c’est énormément de code pour pas grand chose au final.

cantor
Mac4Everien confirmé
 
Message(s) : 140
Inscription : 12/03/09, 15:55

Re : Tuto : Créer un app de météo avec SwiftUI

Message par AdK le 30/06/19, 21:12


cantor a écrit :Je trouve que c’est énormément de code pour pas grand chose au final.


Il a TOUJOURS plus de code que ça en à l'air... ?
AdK
Jeune Tourteau de Mac4Ever
 
Message(s) : 69
Inscription : 02/04/19, 15:49

Envoyé depuis Mac4Ever HD pour iPad

Message par RV le 30/06/19, 23:46

C’est du chinois pour un non dev!
Avatar de l’utilisateur
RV
Docteur ès Mac4Ever
 
Message(s) : 959
Inscription : 31/01/03, 1:00
Localisation : France

Re : Tuto : Créer un app de météo avec SwiftUI

Message par Didier le 01/07/19, 8:10


spiral a écrit :Hmmm, avec Vim par exemple on peut faire un export HTML avec coloration syntaxique, sans besoin d'insert GitHub ?

:TOhtml


De nos jours, on évite le HTML brut dans les news... Sinon c'est la galère pour reformater quand tu changes de layout :)
Avatar de l’utilisateur
Didier
Mac4Ever's Flood
 
Message(s) : 35625
Inscription : 28/11/00, 1:00
Localisation : Lausanne / Lyon

Re : Tuto : Créer un app de météo avec SwiftUI

Message par AkdM le 01/07/19, 8:30


cantor a écrit :Je trouve que c’est énormément de code pour pas grand chose au final.


Mh, ne serais-tu pas un fan de langage non typé à la JavaScript ? ?
AkdM
Jeune Tourteau de Mac4Ever
 
Message(s) : 64
Inscription : 08/02/12, 9:11

Re : Tuto : Créer un app de météo avec SwiftUI

Message par Modragor le 01/07/19, 8:36

Super demo de SwiftUI !
Modragor
Mac4Everien confirmé
 
Message(s) : 288
Inscription : 28/11/13, 12:59
Localisation : Paris

Envoyé depuis Mac4Ever Mobile

Message par Xavoo le 01/07/19, 18:50

Je n’ai jamais codé en swift, mais ça donne vraiment envie ce swiftUI !! Et merci pour ce superbe tuto, on en veut bien d’autres :D
Xavoo
VIP
VIP
 
Message(s) : 518
Inscription : 11/06/13, 15:15

Re : Tuto : Créer un app de météo avec SwiftUI

Message par ptinouvo le 02/07/19, 16:21

En fait c'est l'app Météo mais sur fond blanc :arrow:
Karmalolo
:shock:
Avatar de l’utilisateur
ptinouvo
Bots' Hunter
 
Message(s) : 10910
Inscription : 04/02/10, 10:11


Retour vers Développement Apple

Qui est en ligne ?

Utilisateur(s) parcourant ce forum : Aucun utilisateur inscrit et 0 invité(s)

Conseils d'achat

  • Macintosh

  • iPhone, iPad & iPod

iMac 27" 5k Indicateur vert Informations 03/2019 2099 € L'iMac 27" (Retina) a été mis à jour le 18 mars 2019 avec des modèles Coffee Lake ( 6 à 8 coeurs) et des GPU Radeon Vega. Vous pouvez acheter sans crainte, les iMac ne sont mis à jour qu'une fois tous les 12/18 mois en moyenne.
iMac 21,5" 4k Indicateur vert Informations 03/2019 1499 € L'iMac 21,5" (Retina) a été mis à jour le 18 mars 2019 avec des modèles Coffee Lake ( 6 à 8 coeurs) et des GPU Radeon Vega. Vous pouvez acheter sans crainte, les iMac ne sont mis à jour qu'une fois tous les 12/18 mois en moyenne.
Apple TV 4k Indicateur vert Informations 09/2017 199 € L'Apple TV 4k est une petite évolution de l'Apple TV pour les téléviseurs 4k. Il gère le HDR, le HDMI 2a et tous les codecs récents. Si vous avez une télévision OLED 4k, il s'agit du modèle idéal. Son prix est raisonnable par rapport à la version HD, qui reste au catalogue.
iMac Indicateur rouge Informations 06/2017 1299 € L'iMac 21,5" (non Retina) a été mis à jour le 5 juin 2017 avec Thunderbolt 3 et puces Kaby-Lake. On attendait des modèles Coffee Lake (à 6 coeurs) d'ici le courant du printemps/été 2019 mais Apple n'a pas renouvelé cette version.. qui ne vaut plus vraiment le coup en 2019.
iMac Pro Indicateur orange Informations 12/2017 5499 € L'iMac Pro est actuellement le Mac le plus puissant du marché, même s'il a déjà une bonne année d'existence. Apple a rajouté une petite option GPU Vega 64X et 256Go de RAM courant mars 2019, mais rien de bien folichon. N'hésitez pas à consulter nos tests et nos vidéos avant de vous décider ! A noter qu'Apple va sortir un Mac Pro courant/fin 2019, donc si vous n'êtes pas trop pressé, il sera + modulaire...
Mac Mini Indicateur vert Informations 10/2018 899 € Le Mac mini a été mis à jour fin 2018 avec un tout nouveau CPU à 4 coeurs. Pas de mise à jour prévue avant un moment, vous pouvez acheter sans crainte !
Mac Pro Indicateur rouge Informations 10/2013 3339 € Apple a annoncé un nouveau Mac Pro pour la rentrée, plus modulaire et moins fermé que la version actuelle. Attendez la rentrée !
MacBook Pro 13" Indicateur vert Informations 05/2019 1499 € Surprise ! Le MacBook Pro 13" (haut de gamme) a été mis à jour le 21 mai 2019, mais sans changement majeur : le CPU gagne 100MHz et Apple a installé un énième "nouveau" clavier censé régler le problème de blocage des touches. On espère une "vraie" nouvelle version en 2020. A noter que le modèle sans Touch Bar (entrée de gamme) a été remplacé par une version quad-core avec Touch Bar le 9 juillet 2019, plutôt intéressante malgré le SSD faiblard.
MacBook Pro 15" Indicateur vert Informations 05/2019 2799 € Surprise ! Le MacBook Pro 15" a été mis à jour le 21 mai 2019, avec une nouveauté de taille (la seule) : le CPU passe à 8 coeurs sur le haut de gamme et Apple a installé un énième "nouveau" clavier censé régler le problème de blocage des touches. On espère une "vraie" nouvelle version en 2020, il ne devrait (normalement) pas y avoir de nouveau modèle 15" cette année.
MacBook Air Indicateur vert Informations 07/2019 1349 € Le MacBook Air a fait son grand retour après 4 ans sans mise à jour ! Le 9 juillet 2019, il gagne un écran True Tone (mais c'est tout). Préférez plutôt le Refurb qui le propose à moins de 1000€ !
Apple TV 2015 Indicateur orange Informations 10/2015 159 € L'Apple TV 2015 est sortie fin 2015 et Apple le garde au catalogue malgré l'arrivée de la version 4k. Avec en prime, une petite baisse de prix ! Si vous n'avez pas de TV 4k, cela reste une bonne affaire.
À découvrir sur Mac4Ever
b 1  b 2 
app
A propos | Mentions légales | Contacts | Emploi | RSS | Apps